Eden 2 miesięcy temu
rodzic
commit
8a6e5c13f7
24 zmienionych plików z 618 dodań i 3248 usunięć
  1. 11 0
      TEAMModelOS.Extension/IES.Exam/IES.ExamClient/CHANGELOG.md
  2. 3 0
      TEAMModelOS.Extension/IES.Exam/IES.ExamClient/IES.ExamClient.esproj
  3. 0 45
      TEAMModelOS.Extension/IES.Exam/IES.ExamClient/IES.ExamClient.njsproj
  4. 0 3
      TEAMModelOS.Extension/IES.Exam/IES.ExamClient/README.md
  5. 68 0
      TEAMModelOS.Extension/IES.Exam/IES.ExamClient/certificate.bat
  6. BIN
      TEAMModelOS.Extension/IES.Exam/IES.ExamClient/certificate.cer
  7. 1 1
      TEAMModelOS.Extension/IES.Exam/IES.ExamClient/constants.js
  8. 7 0
      TEAMModelOS.Extension/IES.Exam/IES.ExamClient/eslint.config.js
  9. 39 5
      TEAMModelOS.Extension/IES.Exam/IES.ExamClient/main.js
  10. 2 2
      TEAMModelOS.Extension/IES.Exam/IES.ExamClient/menuManager.js
  11. 35 6
      TEAMModelOS.Extension/IES.Exam/IES.ExamClient/networkService.js
  12. 1 1
      TEAMModelOS.Extension/IES.Exam/IES.ExamClient/package.json
  13. 1 0
      TEAMModelOS.Extension/IES.Exam/IES.ExamClient/serverManager.js
  14. 0 3105
      TEAMModelOS.Extension/IES.Exam/IES.ExamClient/yarn.lock
  15. 1 4
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/cer/install_certificate.bat
  16. 1 1
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/cer/modify_hosts.bat
  17. 121 0
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Controllers/IndexController.cs
  18. 84 24
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Controllers/ManageController.cs
  19. 5 2
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/DI/SignalRHost/SignalRExamServerHub.cs
  20. 12 3
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Helpers/ZipHelper.cs
  21. 4 0
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Models/ServerDevice.cs
  22. 40 40
      TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Services/ManageService.cs
  23. 174 0
      TEAMModelOS.Function/Properties/ServiceDependencies/TEAMModelOSFunction - Zip Deploy/profile.arm.json
  24. 8 6
      TEAMModelOS.sln

+ 11 - 0
TEAMModelOS.Extension/IES.Exam/IES.ExamClient/CHANGELOG.md

@@ -0,0 +1,11 @@
+This file explains how Visual Studio created the project.
+
+The following steps were used to generate this project:
+- Create project file (`IES.ExamClient.esproj`).
+- Create `launch.json` to enable debugging.
+- Install npm packages: `npm init && npm i --save-dev eslint`.
+- Create `app.js`.
+- Update `package.json` entry point.
+- Create `eslint.config.js` to enable linting.
+- Add project to solution.
+- Write this file.

+ 3 - 0
TEAMModelOS.Extension/IES.Exam/IES.ExamClient/IES.ExamClient.esproj

@@ -0,0 +1,3 @@
+<Project Sdk="Microsoft.VisualStudio.JavaScript.Sdk/1.0.2191419">
+
+</Project>

+ 0 - 45
TEAMModelOS.Extension/IES.Exam/IES.ExamClient/IES.ExamClient.njsproj

@@ -1,45 +0,0 @@
-<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
-  <PropertyGroup>
-    <VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
-    <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
-    <Name>IES.ExamClient</Name>
-    <RootNamespace>IES.ExamClient</RootNamespace>
-  </PropertyGroup>
-  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
-  <PropertyGroup>
-    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
-    <SchemaVersion>2.0</SchemaVersion>
-    <ProjectGuid>7fa6faa3-e176-4e56-b542-2e8176850924</ProjectGuid>
-    <ProjectHome>.</ProjectHome>
-    <StartupFile>
-    </StartupFile>
-    <StartWebBrowser>False</StartWebBrowser>
-    <SearchPath>
-    </SearchPath>
-    <WorkingDirectory>.</WorkingDirectory>
-    <OutputPath>.</OutputPath>
-    <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
-    <ProjectTypeGuids>{3AF33F2E-1136-4D97-BBB7-1795711AC8B8};{9092AA53-FB77-4645-B42D-1CCCA6BD08BD}</ProjectTypeGuids>
-    <StartWebBrowser>false</StartWebBrowser>
-  </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
-    <DebugSymbols>true</DebugSymbols>
-  </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
-    <DebugSymbols>true</DebugSymbols>
-  </PropertyGroup>
-  <ItemGroup>
-    <Content Include="constants.js" />
-    <Content Include="header.bmp" />
-    <Content Include="logo.ico" />
-    <Content Include="main.js" />
-    <Content Include="menuManager.js" />
-    <Content Include="serverManager.js" />
-    <Content Include="sidebar.bmp" />
-    <Content Include="package.json" />
-    <Content Include="README.md" />
-    <Content Include="updateManager.js" />
-    <Content Include="utils.js" />
-  </ItemGroup>
-  <Import Project="$(VSToolsPath)\Node.js Tools\Microsoft.NodejsToolsV2.targets" />
-</Project>

+ 0 - 3
TEAMModelOS.Extension/IES.Exam/IES.ExamClient/README.md

@@ -1,3 +0,0 @@
-# IES.ExamClient
-
-

+ 68 - 0
TEAMModelOS.Extension/IES.Exam/IES.ExamClient/certificate.bat

@@ -0,0 +1,68 @@
+@echo off
+echo Configuring hosts file
+
+net session >nul 2>&1
+if %errorLevel% neq 0 (
+    echo Please run this script as an administrator
+    exit /b
+)
+
+set "hostsFile=C:\Windows\System32\drivers\etc\hosts"
+set "newEntry=192.168.8.235 exam.habook.local"
+
+if not exist "%hostsFile%" (
+    echo hosts file does not exist:%hostsFile%
+    exit /b
+)
+
+
+findstr /v /i /c:"exam.habook.local" "%hostsFile%" > "%hostsFile%.tmp"
+if %errorLevel% equ 0 (
+    move /y "%hostsFile%.tmp" "%hostsFile%" >nul 2>&1
+    echo Removed all entries containing exam.habook.local
+) else (
+    echo Failed to remove entries containing exam.habook.local
+    exit /b
+)
+
+
+echo %newEntry% >> "%hostsFile%"
+if %errorLevel% equ 0 (
+    echo Hosts file configured successfully
+) else (
+    echo Hosts file configuration failed
+    exit /b
+)
+
+:ImportCert
+echo Importing certificate
+
+if not exist "%~dp0certificate.cer" (
+    echo Certificate file does not exist:%~dp0certificate.cer
+    exit /b
+)
+
+set "certSubject="
+for /f "tokens=*" %%i in ('certutil -dump "%~dp0certificate.cer" ^| findstr /i "CN="') do (
+    set "certSubject=%%i"
+)
+
+if defined certSubject (
+    echo Deleting existing certificate with the same name
+    certutil -delstore "Root" "%certSubject%"
+    if %errorLevel% equ 0 (
+        echo Existing certificate deleted successfully
+    ) else (
+        echo Failed to delete existing certificate (may not exist)
+    )
+)
+
+echo Importing new certificate
+certutil -addstore -f "Root" "%~dp0certificate.cer"
+if %errorLevel% equ 0 (
+    echo Certificate imported successfully
+) else (
+    echo Certificate import failed
+)
+
+echo All operations completed

BIN
TEAMModelOS.Extension/IES.Exam/IES.ExamClient/certificate.cer


+ 1 - 1
TEAMModelOS.Extension/IES.Exam/IES.ExamClient/constants.js

@@ -7,7 +7,7 @@ const cert = fs.readFileSync('server\\Configs\\cer\\cert.pem');
 const agent = new https.Agent({
     ca: cert,
     rejectUnauthorized: true,
-   // secureProtocol: 'TLSv1_2_method', // 强制使用 TLS 1.2
+    // secureProtocol: 'TLSv1_2_method', // 强制使用 TLS 1.2
 });
 
 let serverPath;

+ 7 - 0
TEAMModelOS.Extension/IES.Exam/IES.ExamClient/eslint.config.js

@@ -0,0 +1,7 @@
+module.exports = [
+    {
+        rules: {
+            // Add rules here.
+        }
+    }
+];

+ 39 - 5
TEAMModelOS.Extension/IES.Exam/IES.ExamClient/main.js

@@ -1,3 +1,4 @@
+
 const { app, BrowserWindow, Tray, Menu } = require('electron');
 const path = require('path');
 const serverManager = require('./serverManager');
@@ -5,7 +6,8 @@ const menuManager = require('./menuManager');
 const updateManager = require('./updateManager');
 const constants = require('./constants');
 const { getNetworkInterfaces } = require('./networkService');
-
+const { exec } = require('child_process');
+ 
 //app.disableHardwareAcceleration(); //使用windows7 ,或者虚拟机的时候 需要验证 禁用 GPU 加速
 //app.commandLine.appendSwitch('ignore-certificate-errors')
 let win = null;
@@ -15,10 +17,41 @@ app.isQuitting = false; // 添加标志位
 const createWindow = async () => {
     try {
         const networks = getNetworkInterfaces();
-        console.log('Available networks:', networks);
+      //  console.log('Available networks:', networks);
+        console.log("运行地址:", networks[0].ip)
+        try {
+            let filePath = path.join(constants.serverPath,'certificate.bat');
+            filePath = `"${filePath}"`;
+            // 使用 execFile 执行 .bat 文件
+     
+            // 执行 .bat 文件
+
+            // 设置超时时间为 60 秒
+            exec(filePath,  (error, stdout, stderr) => {
+                if (error) {
+                    if (error.code === 'ETIMEDOUT') {
+                        console.error('执行 .bat 文件超时');
+                    } else {
+                        console.error(`执行 .bat 文件时出错: ${error.message}`);
+                    }
+                    return;
+                }
+                if (stderr) {
+                    console.error(`.bat 文件执行过程中出现错误: ${stderr}`);
+                    return;
+                }
+                console.log(`.bat 文件执行结果: ${stdout}`);
+            });
+        } catch (error)
+        {
+            console.log(`脚本启动执行错误: ${error}`);
+        }
+
+
+        console.log("开始检查是否启动服务...")
         const isServerRunning = await serverManager.checkServerHealth();
         if (!isServerRunning) {
-            console.log('Server is not running, starting it......................................');
+            console.log('Server is not running, starting it...');
             await serverManager.startServer(); // 启动 Web API
         }
         win = new BrowserWindow({
@@ -67,8 +100,9 @@ const createTray = () => {
         {
             label: '退出',
             click: () => {
-                app.isQuitting = true;
-                app.quit();
+                require('electron').app.quit();
+                //app.isQuitting = true;
+                //app.quit();
             }
         }
     ]);

+ 2 - 2
TEAMModelOS.Extension/IES.Exam/IES.ExamClient/menuManager.js

@@ -47,7 +47,7 @@ const createMenu = (checkForUpdatesCallback) => {
                 {
                     label: '退出程序',
                     click: () => {
-                        require('electron'). app.quit();
+                        require('electron').app.quit();
                     }
                 }
             ]
@@ -95,7 +95,7 @@ const openDevTools = () => {
 // 清理缓存
 const clearCache = () => {
     const win = BrowserWindow.getFocusedWindow(); // 获取当前获得焦点的窗口
-   // console.log('win', win);
+    // console.log('win', win);
     if (win) {
         session.defaultSession.clearStorageData({
             storages: ['cookies', 'localstorage', 'shadercache', 'serviceworkers', 'cachestorage'],

+ 35 - 6
TEAMModelOS.Extension/IES.Exam/IES.ExamClient/networkService.js

@@ -1,5 +1,8 @@
 const os = require('os');
 const { exec } = require('child_process');
+const path = require('path');
+const constants = require('./constants');
+const fs = require('fs');
 // 判断是否为物理网卡的函数
 function isPhysicalNetworkInterface(name, description) {
     // 默认的真实物理网卡名称(如 "以太网")
@@ -81,16 +84,13 @@ function getNetworkInterfaces() {
             }
         });
 
-        // 输出网卡描述信息和 IPv4 地址
-        console.log('网卡描述信息和 IPv4 地址:');
-        devices.forEach((device) => {
-            console.log(`名称: ${device.Name}, 描述: ${device.Description}, IPv4 地址: ${device.ipv4}`);
-        });
+       
     });
 
 
     const interfaces = os.networkInterfaces();
-    console.log(interfaces);
+  
+    //console.log(interfaces);
     const result = [];
 
     // 遍历所有网络接口
@@ -111,7 +111,36 @@ function getNetworkInterfaces() {
 
     // 将 physical: 1 的网卡排在前面
     result.sort((a, b) => b.physical - a.physical);
+    // 读取文件内容
+    let filePath = path.join(constants.serverPath, 'certificate.bat');
+ 
+    filePath == `"${filePath}"`;
+    //console.log("filePath:", filePath);
+    fs.readFile(filePath, 'utf8', (err, data) => {
+        if (err) {
+            console.error('读取文件失败:', err);
+            return;
+        }
 
+        // 替换内容
+        const oldContent = '192.168.8.150';
+        const newContent = result[0].ip;
+        const updatedData = data.replace(new RegExp(oldContent, 'g'), newContent);
+        //console.log("新的文件内容", updatedData);
+        // 将更新后的内容写回文件
+        fs.writeFile(filePath, updatedData, 'utf8', (err) => {
+            if (err) {
+                console.error('写入文件失败:', err);
+                return;
+            }
+            console.log('文件内容已成功替换!');
+        });
+    });
+    // 输出网卡描述信息和 IPv4 地址
+    //console.log('网卡描述信息和 IPv4 地址:', result);
+    result.forEach((device) => {
+        console.log(`名称: ${device.name}, 是否物理网卡: ${device.physical},mac地址:${device.mac}, IPv4 地址: ${device.ip}`);
+    });
     return result;
 }
 

+ 1 - 1
TEAMModelOS.Extension/IES.Exam/IES.ExamClient/package.json

@@ -4,7 +4,7 @@
   "description": "IES.ExamClient",
   "main": "main.js",
   "scripts": {
-    "start": "electron .",
+    "start": "chcp 65001 && electron .",
     "test": "echo \"Error: no test specified\" && exit 1",
     "package": "electron-packager . --platform=win32 --arch=ia32 --out=dist --overwrite",
     "build": "electron-builder --win --ia32"

+ 1 - 0
TEAMModelOS.Extension/IES.Exam/IES.ExamClient/serverManager.js

@@ -64,6 +64,7 @@ const startServer = () => {
 // 检查服务器健康状态的函数
 const checkServerHealth = async () => {
     try {
+       
         const response = await axios.get(`${constants.baseUrl}/index/health`, {
             httpsAgent: constants.agent
         });

Plik diff jest za duży
+ 0 - 3105
TEAMModelOS.Extension/IES.Exam/IES.ExamClient/yarn.lock


+ 1 - 4
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/cer/install_certificate.bat

@@ -4,13 +4,11 @@ echo Importing certificate
 net session >nul 2>&1
 if %errorLevel% neq 0 (
     echo Please run this script as an administrator
-    pause
     exit /b
 )
 
 if not exist "%~dp0certificate.cer" (
     echo Certificate file does not exist:%~dp0certificate.cer
-    pause
     exit /b
 )
 
@@ -37,5 +35,4 @@ if %errorLevel% equ 0 (
     echo Certificate import failed
 )
 
-echo Certificate installation completed
-pause
+echo Certificate installation completed

+ 1 - 1
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Configs/cer/modify_hosts.bat

@@ -9,7 +9,7 @@ if %errorLevel% neq 0 (
 )
 
 set "hostsFile=C:\Windows\System32\drivers\etc\hosts"
-set "newEntry=192.168.8.132 exam.habook.local"
+set "newEntry=192.168.8.150 exam.habook.local"
 
 if not exist "%hostsFile%" (
     echo hosts file does not exist:%hostsFile%

+ 121 - 0
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Controllers/IndexController.cs

@@ -14,6 +14,7 @@ using IES.ExamLibrary.Models;
 using System.Threading.Tasks;
 using Hardware.Info;
 using System.Net.NetworkInformation;
+using System.Text.RegularExpressions;
 
 namespace IES.ExamServer.Controllers
 {
@@ -174,6 +175,7 @@ namespace IES.ExamServer.Controllers
         /// </summary>
         /// <returns></returns>
         [HttpGet("music")]
+
         public async Task<IActionResult> Music()
         {
             string e = await System.IO.File.ReadAllTextAsync("F:\\cer\\e.txt");
@@ -335,6 +337,125 @@ namespace IES.ExamServer.Controllers
         {
             return Ok(new { code=200});
         }
+
+        /// <summary>
+        /// 修改Hosts文件的 局域网域名IP映射
+        /// </summary>
+        /// <param name="ip"></param>
+        /// <returns></returns>
+        [HttpGet("modify-hosts")]
+        public async Task<IActionResult> ModifyHosts([FromQuery] string ip ) 
+        {
+            try
+            {
+                ServerDevice serverDevice = _memoryCache.Get<ServerDevice>(Constant._KeyServerDevice);
+                if (serverDevice != null && serverDevice.networks.IsNotEmpty())
+                {
+                  
+                    var network = serverDevice.networks.Find(x => ip.Equals(x.ip));
+                    if (network != null && !string.IsNullOrWhiteSpace(network.ip))
+                    {
+                        string pathBat = Path.Combine(Directory.GetCurrentDirectory(), "Configs", "cer", "modify_hosts.bat");
+                        string text = await System.IO.File.ReadAllTextAsync(pathBat);
+
+                        // 使用正则表达式替换 IP 地址
+                        string pattern = @"\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b";
+                        string result = Regex.Replace(text, pattern, network.ip);
+                        string pathCertificateBat = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "package", "certificate.bat");
+                        await System.IO.File.WriteAllTextAsync(pathCertificateBat, result);
+
+
+                        return Ok(new { code = 200, msg = "执行成功。", bat = "package/certificate.bat" });
+                    }
+                    else
+                    {
+                        msg = "未找到匹配的IP。";
+                    }
+                }
+                else
+                {
+                    msg = "服务端设备未找到,或网卡设备不存在。";
+                }
+            }
+            catch (Exception ex)
+            {
+                _logger.LogError($"域名IP绑定错误。{ex.Message},{ex.StackTrace}");
+                msg = $"域名IP绑定错误,{ex.Message}";
+            }
+            return Ok(new { code = 400, msg = msg });
+        }
+
+        [HttpGet("install-certificate")]
+        public IActionResult InstallCertificate()
+        {
+            try
+            {
+                string pathBat = Path.Combine(Directory.GetCurrentDirectory(), "Configs", "cer", "install_certificate.bat");
+                // 创建一个新的 ProcessStartInfo 对象
+                ProcessStartInfo startInfo = new ProcessStartInfo
+                {
+                    // 指定要执行的 BAT 文件的路径
+                    FileName = "cmd.exe",
+                    // 设置命令行参数,使用 /c 表示执行命令后关闭命令提示符窗口
+                    Arguments = $"/c {pathBat}",
+                    // 设置是否使用操作系统 shell 启动进程
+                    UseShellExecute = false,
+                    // 设置是否创建新的窗口
+                    CreateNoWindow = true,
+                    // 重定向标准输出和标准错误输出
+                    RedirectStandardOutput = true,
+                    RedirectStandardError = true
+                };
+                // 创建一个新的 Process 对象
+                using (Process process = new Process())
+                {
+                    // 将 ProcessStartInfo 对象分配给 Process 对象
+                    process.StartInfo = startInfo;
+
+                    // 启动进程
+                    process.Start();
+
+                    // 读取标准输出和标准错误输出
+                    string output = process.StandardOutput.ReadToEnd();
+                    string error = process.StandardError.ReadToEnd();
+
+                    // 等待进程执行完成
+                    process.WaitForExit();
+
+                    // 输出执行结果
+                    if (!string.IsNullOrEmpty(output))
+                    {
+                        // Console.WriteLine("标准输出:");
+                        // Console.WriteLine(output);
+                        if (output.Contains("successfully", StringComparison.OrdinalIgnoreCase))
+                        {
+                            msg = $"证书安装成功!";
+                            code = 200;
+                        }
+                        else {
+                            msg = $"证书安装异常:{output}";
+                            code = 1;
+                        }
+                      
+                    }
+
+                    if (!string.IsNullOrEmpty(error))
+                    {
+                        // Console.WriteLine("错误输出:");
+                        // Console.WriteLine(error);
+                        msg = $"执行失败:{error}";
+                        code = 2;
+                    }
+                }
+            }
+            catch (Exception ex)
+            {
+                _logger.LogError($"证书安装错误。{ex.Message},{ex.StackTrace}");
+                msg = $"证书安装错误,{ex.Message}";
+                code = 500;
+            }
+            return Ok(new { code = code, msg = msg });
+        }
         [HttpPost("generate-certificate")]
         public async Task<IActionResult> GenerateCertificate(JsonNode json) 
         {

+ 84 - 24
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Controllers/ManageController.cs

@@ -248,6 +248,7 @@ namespace IES.ExamServer.Controllers
             List<string> successMsgs = new List<string>();
             List<string> errorMsgs = new List<string>();
             EvaluationCheckFileResult result = new EvaluationCheckFileResult() { successMsgs=successMsgs, errorMsgs=errorMsgs };
+
             if (token.scope.Equals(ExamConstant.ScopeTeacher) || token.scope.Equals(ExamConstant.ScopeVisitor))
             {
                 if (_connectionService.centerIsConnected)
@@ -257,11 +258,19 @@ namespace IES.ExamServer.Controllers
                     string shortCode = $"{json["shortCode"]}";
                     string deviceId = $"{json["deviceId"]}";
                     EvaluationClient? evaluationClient = _liteDBFactory.GetLiteDatabase().GetCollection<EvaluationClient>().FindOne(x => x.id!.Equals(id) && x.shortCode!.Equals(shortCode));
+                   
                     if (teacher != null && evaluationClient!= null)
                     {
+                        int msg_status = Constant._Message_status_info;
+                        string msg_content = msg_status.Equals(Constant._Message_status_success) ? "成功" : "失败";
+                        await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, Constant._Message_grant_type_download_file,
+                        new MessageContent { dataId = evaluationClient.id, dataName = evaluationClient.name, messageType = Constant._Message_type_message, status = 0, content = "检测云端数据是否匹配..." });
                         var dataInfo=  await GetEvaluationFromCenter(GetXAuthToken(), _configuration, _httpClientFactory, shortCode, evaluationClient.id!);
+                      
                         if (dataInfo.centerCode.Equals("200")&& dataInfo.evaluationCloud!=null)
                         {
+                            await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, Constant._Message_grant_type_download_file,
+                                new MessageContent { dataId = evaluationClient.id, dataName = evaluationClient.name, messageType = Constant._Message_type_message, status = Constant._Message_status_success, content = $"云端数据检测结果:{dataInfo.centerMsg},状态:{dataInfo.centerCode}" },true);
                             string? CenterUrl = _configuration.GetValue<string>("ExamServer:CenterUrl");
                             var client = _httpClientFactory.CreateClient();
                             if (client.DefaultRequestHeaders.Contains(Constant._X_Auth_AuthToken))
@@ -283,8 +292,21 @@ namespace IES.ExamServer.Controllers
                                     sas = $"{jsonNode["sas"]}";
                                     cnt = $"{jsonNode["name"]}";
                                     url = $"{jsonNode["url"]}";
+                                    msg_status = Constant._Message_status_success;
+                                }
+                                else 
+                                {
+                                    msg_status = Constant._Message_status_error;
                                 }
                             }
+                            else 
+                            {
+                                msg_status = Constant._Message_status_error;
+                                
+                            }
+                            msg_content = msg_status.Equals(Constant._Message_status_success) ? "成功" : "失败";
+                            await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, Constant._Message_grant_type_download_file,
+                                new MessageContent { dataId = evaluationClient.id, dataName = evaluationClient.name, messageType = Constant._Message_type_message, status = msg_status, content = $"获取云端下载授权=>{msg_content}" }, true);
                             var httpClient = _httpClientFactory.CreateClient();
                             string packagePath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "package");
                             string evaluationPath = Path.Combine(packagePath, dataInfo.evaluationCloud.id!);
@@ -295,7 +317,9 @@ namespace IES.ExamServer.Controllers
                             {
                                 Directory.CreateDirectory(evaluationDataPath);
                             }
-
+                           
+                            await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, Constant._Message_grant_type_download_file,
+                                new MessageContent { dataId = evaluationClient.id, dataName = evaluationClient.name, messageType = Constant._Message_type_message, status = Constant._Message_status_info, content = $"下载前清理资源" });
                             string evaluationData = string.Empty;
                             {
                                 //evaluation
@@ -308,30 +332,40 @@ namespace IES.ExamServer.Controllers
                                     string path_evaluation = Path.Combine(evaluationDataPath, "evaluation.json");
                                     await System.IO.File.WriteAllTextAsync(path_evaluation, content);
                                     successMsgs.Add("评测信息文件evaluation.json文件下载成功!");
+                                    await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, Constant._Message_grant_type_download_file,
+                                    new MessageContent { dataId = evaluationClient.id, dataName = evaluationClient.name, messageType = Constant._Message_type_message, status = Constant._Message_status_success, content = $"评测信息文件evaluation.json文件下载成功!" });
                                 }
                                 else
                                 {
                                     errorMsgs.Add("评测信息文件evaluation.json文件下载失败!");
+                                    await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, Constant._Message_grant_type_download_file,
+                                    new MessageContent { dataId = evaluationClient.id, dataName = evaluationClient.name, messageType = Constant._Message_type_message, status = Constant._Message_status_error, content = $"评测信息文件evaluation.json文件下载失败!" }, true);
                                 }
 
 
                             }
-                            {
-                                //source.json
-                                string sourceUrl = $"{url}/{cnt}/package/{json["evaluationId"]}/data/source.json?{sas}";
-                                HttpResponseMessage dataMessage = await httpClient.GetAsync(sourceUrl);
-                                if (dataMessage.IsSuccessStatusCode)
-                                {
-                                    var content = await dataMessage.Content.ReadAsStringAsync();
-                                    string path_source = Path.Combine(evaluationDataPath, "source.json");
-                                    await System.IO.File.WriteAllTextAsync(path_source, content);
-                                    successMsgs.Add("评测数据原始文件source.json文件下载成功!");
-                                }
-                                else
-                                {
-                                    errorMsgs.Add("评测数据原始文件source.json文件下载失败!");
-                                }
-                            }
+                            //{
+                            //    //source.json
+                            //    string sourceUrl = $"{url}/{cnt}/package/{json["evaluationId"]}/data/source.json?{sas}";
+                            //    HttpResponseMessage dataMessage = await httpClient.GetAsync(sourceUrl);
+                            //    if (dataMessage.IsSuccessStatusCode)
+                            //    {
+                            //        var content = await dataMessage.Content.ReadAsStringAsync();
+                            //        string path_source = Path.Combine(evaluationDataPath, "source.json");
+                            //        await System.IO.File.WriteAllTextAsync(path_source, content);
+                            //        successMsgs.Add("评测数据原始文件source.json文件下载成功!");
+                            //        await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, Constant._Message_grant_type_download_file,
+                            //       new MessageContent { dataId = evaluationClient.id, dataName = evaluationClient.name, messageType = Constant._Message_type_message, status = Constant._Message_status_success, content = $"评测数据原始文件source.json文件下载成功!" });
+
+                            //    }
+                            //    else
+                            //    {
+                            //        errorMsgs.Add("评测数据原始文件source.json文件下载失败!");
+                            //        await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, Constant._Message_grant_type_download_file,
+                            //        new MessageContent { dataId = evaluationClient.id, dataName = evaluationClient.name, messageType = Constant._Message_type_message, status = Constant._Message_status_error, content = $"评测数据原始文件source.json文件下载失败!" }, true);
+
+                            //    }
+                            //}
                             {
                                 //grouplist.json
                                 string grouplistUrl = $"{url}/{cnt}/package/{json["evaluationId"]}/data/grouplist.json?{sas}";
@@ -342,16 +376,25 @@ namespace IES.ExamServer.Controllers
                                     string path_groupList = Path.Combine(evaluationDataPath, "grouplist.json");
                                     await System.IO.File.WriteAllTextAsync(path_groupList, content);
                                     successMsgs.Add("评测名单grouplist.json文件下载成功!");
+                                    await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, Constant._Message_grant_type_download_file,
+                                   new MessageContent { dataId = evaluationClient.id, dataName = evaluationClient.name, messageType = Constant._Message_type_message, status = Constant._Message_status_success, content = $"评测名单grouplist.json文件下载成功!" });
+
                                 }
                                 else
                                 {
                                     errorMsgs.Add("评测名单grouplist.json文件下载失败!");
+                                    await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, Constant._Message_grant_type_download_file,
+                                   new MessageContent { dataId = evaluationClient.id, dataName = evaluationClient.name, messageType = Constant._Message_type_message, status = Constant._Message_status_error, content = $"评测名单grouplist.json文件下载失败!" }, true);
+
                                 }
                             }
 
                             {
+                             
                                 //下载试卷文件
                                 List<EvaluationExam>? evaluationExams = evaluationData.ToObject<JsonNode>()?["evaluationExams"]?.ToObject<List<EvaluationExam>>();
+                                int blobCount = evaluationExams!.SelectMany(x => x.papers).SelectMany(x=>x.blobs).Count();
+                                int currCount = 0;
                                 foreach (var evaluationExam in evaluationExams!)
                                 {
                                     foreach (var evaluationPaper in evaluationExam.papers)
@@ -366,11 +409,13 @@ namespace IES.ExamServer.Controllers
                                         // 使用 Parallel.ForEachAsync 并行处理每个 blob
                                         await Parallel.ForEachAsync(evaluationPaper.blobs, parallelOptions, async (blob, cancellationToken) =>
                                         {
+                                            currCount++;
                                             try
                                             {
                                                 // 下载 Blob 文件到本地
-                                                httpClient.Timeout = TimeSpan.FromSeconds(300);
+                                                //httpClient.Timeout = TimeSpan.FromSeconds(300);
                                                 HttpResponseMessage blobMessage = await httpClient.GetAsync($"{url}/{cnt}/{blob.path}?{sas}", cancellationToken);
+                                               
                                                 if (blobMessage.IsSuccessStatusCode)
                                                 {
                                                     byte[] bytes = await blobMessage.Content.ReadAsByteArrayAsync(cancellationToken);
@@ -388,17 +433,26 @@ namespace IES.ExamServer.Controllers
                                                             await System.IO.File.WriteAllBytesAsync(Path.Combine(path_paper, fileName!), bytes, cancellationToken);
                                                         }
                                                     }
+
+                                                    await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, Constant._Message_grant_type_download_file,
+                                new MessageContent { dataId = evaluationClient.id, dataName = evaluationClient.name, messageType = Constant._Message_type_message, status = Constant._Message_status_error, content = $"[进度:{currCount}/[{blobCount}]][大小:{blob.size * 1.0 / 1024 / 1024}kb]{evaluationExam.subjectName},{evaluationPaper.paperName},{blob.path}文件下载成功。" });
                                                 }
                                                 else
                                                 {
                                                     string? error = await blobMessage.Content.ReadAsStringAsync(cancellationToken);
                                                     errorMsgs.Add($"{evaluationExam.subjectName},{evaluationPaper.paperName},{blob.path}文件下载失败,{blobMessage.StatusCode},{error}");
+                                                    await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, Constant._Message_grant_type_download_file,
+                                 new MessageContent { dataId = evaluationClient.id, dataName = evaluationClient.name, messageType = Constant._Message_type_message, status = Constant._Message_status_error, content = $"[进度:{currCount}/[{blobCount}]][大小:{blob.size * 1.0 / 1024 / 1024}kb]{evaluationExam.subjectName},{evaluationPaper.paperName},{blob.path}文件下载失败,{blobMessage.StatusCode},{error}" }, true);
+
                                                     // Console.WriteLine($"Error downloading {blob.path},{blobMessage.StatusCode},{error}");
                                                 }
                                             }
                                             catch (Exception ex)
                                             {
                                                 errorMsgs.Add($"{evaluationExam.subjectName},{evaluationPaper.paperName},{blob.path}文件下载错误,{ex.Message}");
+                                                await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, Constant._Message_grant_type_download_file,
+                                 new MessageContent { dataId = evaluationClient.id, dataName = evaluationClient.name, messageType = Constant._Message_type_message, status = Constant._Message_status_error, content = $"[进度:{currCount}/[{blobCount}]][大小:{blob.size * 1.0 / 1024 / 1024}kb]{evaluationExam.subjectName},{evaluationPaper.paperName},{blob.path}文件下载错误,{ex.Message}" }, true);
+
                                                 // 处理异常
                                                 //Console.WriteLine($"Error downloading {blob.path}: {ex.Message}");
                                             }
@@ -409,7 +463,7 @@ namespace IES.ExamServer.Controllers
 
                             _liteDBFactory.GetLiteDatabase().GetCollection<EvaluationClient>().Upsert(dataInfo.evaluationCloud!);
 
-                            (successMsgs, errorMsgs) = await ManageService.CheckFile(dataInfo.evaluationCloud!, successMsgs, errorMsgs, _signalRExamServerHub, _memoryCache, _logger, deviceId, evaluationPath);
+                            (successMsgs, errorMsgs) = await ManageService.CheckFile(dataInfo.evaluationCloud!, successMsgs, errorMsgs, _signalRExamServerHub, _memoryCache, _logger, deviceId, evaluationPath, Constant._Message_grant_type_download_file);
                             //下载完成后,对数据进行检查,然后在加密压缩。
                             string zipPath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "zip");
                             if (!Directory.Exists(zipPath))
@@ -422,17 +476,23 @@ namespace IES.ExamServer.Controllers
                             if (zipInfo.res)
                             {
                                 successMsgs.Add("评测数据压缩包创建成功!");
+                                await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, Constant._Message_grant_type_download_file,
+                                   new MessageContent { dataId = evaluationClient.id, dataName = evaluationClient.name, messageType = Constant._Message_type_message, status = Constant._Message_status_success, content = $"评测数据压缩包创建成功!" });
                             }
                             else
                             {
                                 errorMsgs.Add("评测数据压缩包创建失败!");
-                            }
+                                await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, Constant._Message_grant_type_download_file,
+                                  new MessageContent { dataId = evaluationClient.id, dataName = evaluationClient.name, messageType = Constant._Message_type_message, status = Constant._Message_status_error, content = $"评测数据压缩包创建失败!" }, true);
 
+                            }
                         }
                         else {
-                            errorMsgs.Add($"云端数据检测结果:{dataInfo. centerMsg},状态:{dataInfo. centerCode}");
+                            string content = $"云端数据检测结果:{dataInfo.centerMsg},状态:{dataInfo.centerCode}";
+                            errorMsgs.Add(content);
+                            await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, Constant._Message_grant_type_download_file,
+                                new MessageContent { dataId = evaluationClient.id, dataName = evaluationClient.name, messageType = Constant._Message_type_message, status = Constant._Message_status_error, content = content }, true);
                         }
-
                     }
                     else
                     {
@@ -512,12 +572,12 @@ namespace IES.ExamServer.Controllers
                             Directory.CreateDirectory(evaluationPath);
                         }
                         //解压文件包
-                        var extractRes = ZipHelper.ExtractPasswordProtectedZip(Path.Combine(zipPath, $"{evaluationLocal.id}-{evaluationLocal.blobHash}.zip"), evaluationPath, evaluationLocal.openCode!);
+                        var extractRes = await ZipHelper.ExtractPasswordProtectedZip(Path.Combine(zipPath, $"{evaluationLocal.id}-{evaluationLocal.blobHash}.zip"), evaluationPath, evaluationLocal.openCode!, _signalRExamServerHub,_memoryCache,_logger,deviceId,evaluationLocal);
                         if (extractRes.res)
                         {
                             _memoryCache.Set(key,key,TimeSpan.FromSeconds(30));
                             successMsgs.Add("评测试卷文件包解压成功!");
-                            (successMsgs, errorMsgs) =  await ManageService.CheckFile(evaluationLocal, successMsgs, errorMsgs, _signalRExamServerHub, _memoryCache, _logger, deviceId, evaluationPath);
+                            (successMsgs, errorMsgs) =  await ManageService.CheckFile(evaluationLocal, successMsgs, errorMsgs, _signalRExamServerHub, _memoryCache, _logger, deviceId, evaluationPath, Constant._Message_grant_type_check_file);
                         }
                         else {
                             errorMsgs.Add("评测试卷文件包解压失败!");

+ 5 - 2
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/DI/SignalRHost/SignalRExamServerHub.cs

@@ -12,7 +12,7 @@ namespace IES.ExamServer.DI.SignalRHost
 {
     public static class SignalRExamServerHubExtension
     {
-        public async static Task SendMessage(this IHubContext<SignalRExamServerHub> hubContext, IMemoryCache _memoryCache, ILogger logger, string clientId, string grant_type, MessageContent content)
+        public async static Task SendMessage(this IHubContext<SignalRExamServerHub> hubContext, IMemoryCache _memoryCache, ILogger logger, string clientId, string grant_type, MessageContent content,bool isLog=false)
         {
             try
             {//双向检测是否连接。
@@ -63,7 +63,10 @@ namespace IES.ExamServer.DI.SignalRHost
                     code= 400;
                     msg="未连接客户端";
                 }
-                logger.LogData<object>(new { code, msg, data = messageBody }, content.dataId!);
+                if (isLog) 
+                {
+                    logger.LogData<object>(new { code, msg, data = messageBody }, content.dataId!);
+                }
             } catch (Exception ex) {
                 logger.LogError(ex.Message, ex.StackTrace);
             }

+ 12 - 3
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Helpers/ZipHelper.cs

@@ -3,6 +3,11 @@ using System;
 using System.IO;
 using ICSharpCode.SharpZipLib.Zip;
 using ICSharpCode.SharpZipLib.Core;
+using IES.ExamServer.DI.SignalRHost;
+using IES.ExamServer.Models;
+using Microsoft.AspNetCore.SignalR;
+using Microsoft.Extensions.Caching.Memory;
+using IES.ExamServer.Helper;
 namespace IES.ExamServer.Helpers
 {
     public static class ZipHelper
@@ -82,7 +87,8 @@ namespace IES.ExamServer.Helpers
         /// <param name="extractPath">解压目标目录</param>
         /// <param name="password">ZIP 文件密码</param>
         /// <returns>是否成功</returns>
-        public static (bool res,string msg) ExtractPasswordProtectedZip(string zipFilePath, string extractPath, string password)
+        public static async Task<(bool res, string msg)> ExtractPasswordProtectedZip(string zipFilePath, string extractPath, string password, 
+            IHubContext<SignalRExamServerHub> _signalRExamServerHub,IMemoryCache _memoryCache,ILogger _logger,string deviceId, EvaluationClient evaluationClient)
         {
             if (!File.Exists(zipFilePath))
             {
@@ -95,11 +101,15 @@ namespace IES.ExamServer.Helpers
                     zipInputStream.Password = password; // 设置解压密码
 
                     ZipEntry entry;
+                    long blobCount = evaluationClient.blobCount + 3;
+                    long currCount = 0 ;
                     while ((entry = zipInputStream.GetNextEntry()) != null)
                     {
+                        currCount++;
                         string entryFileName = entry.Name;
                         string fullPath = Path.Combine(extractPath, entryFileName);
-
+                        await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, Constant._Message_grant_type_download_file,
+                                   new MessageContent { dataId = evaluationClient.id, dataName = evaluationClient.name, messageType = Constant._Message_type_message, status = Constant._Message_status_success, content = $"[进度:{currCount}/[{blobCount}]],正在解压:{entryFileName}" });
                         // 创建目录(如果条目是目录)
                         string? directoryName = Path.GetDirectoryName(fullPath);
                         if (!string.IsNullOrEmpty(directoryName) && !Directory.Exists(directoryName))
@@ -122,7 +132,6 @@ namespace IES.ExamServer.Helpers
                         }
                     }
                 }
-
                 return  (true, $"ZIP 文件已成功解压到:{extractPath}");
             }
             catch (Exception ex)

+ 4 - 0
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Models/ServerDevice.cs

@@ -88,5 +88,9 @@ namespace IES.ExamServer.Models
         /// </summary>
         public string? domain { get; set; }
         public int physical {  get; set; }
+        /// <summary>
+        /// 当前主站域名
+        /// </summary>
+        public int primary { get; set; }
     }
 }

+ 40 - 40
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Services/ManageService.cs

@@ -278,7 +278,7 @@ namespace IES.ExamServer.Services
         /// <param name="_liteDBFactory"></param>
         /// <returns></returns>
         public async static Task<(List<string> successMsgs, List<string> errorMsgs)> CheckFile(EvaluationClient evaluationLocal, List<string> successMsgs,List<string> errorMsgs, IHubContext<SignalRExamServerHub> _signalRExamServerHub, 
-            IMemoryCache _memoryCache, ILogger _logger, string deviceId,string evaluationPath)
+            IMemoryCache _memoryCache, ILogger _logger, string deviceId,string evaluationPath,string grant_type)
         {
            
         
@@ -305,8 +305,8 @@ namespace IES.ExamServer.Services
                    
                     
                 }
-                await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, Constant._Message_grant_type_check_file,
-                    new MessageContent { dataId=evaluationLocal.id, dataName=evaluationLocal.name, messageType= Constant._Message_type_check, status=msg_status, content="评测名单文件(groupList.json)" });
+                await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, grant_type,
+                    new MessageContent { dataId=evaluationLocal.id, dataName=evaluationLocal.name, messageType= Constant._Message_type_check, status=msg_status, content="评测名单文件(groupList.json)" }, true);
                 content = msg_status.Equals(Constant._Message_status_success) ? "成功" : "失败";
                 if (msg_status.Equals(Constant._Message_status_success)|| msg_status.Equals(Constant._Message_status_info))
                 {
@@ -316,32 +316,32 @@ namespace IES.ExamServer.Services
                 {
                     errorMsgs.Add($"评测名单文件(groupList.json),检测结果:{content}");
                 }
-                string path_source = Path.Combine(evaluationDataPath, "source.json");
-                msg_status = Constant._Message_status_info;
-                if (!System.IO.File.Exists(path_source))
-                {
-                    msg_status=Constant._Message_status_error;
+                //string path_source = Path.Combine(evaluationDataPath, "source.json");
+                //msg_status = Constant._Message_status_info;
+                //if (!System.IO.File.Exists(path_source))
+                //{
+                //    msg_status=Constant._Message_status_error;
                    
                    
-                }
-                else
-                {
-                    msg_status=Constant._Message_status_success;
+                //}
+                //else
+                //{
+                //    msg_status=Constant._Message_status_success;
                    
                     
-                }
-                await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, Constant._Message_grant_type_check_file,
-                    new MessageContent { dataId=evaluationLocal.id, dataName=evaluationLocal.name, messageType= Constant._Message_type_check, status=msg_status, content="评测原始数据(source.json)" });
-                content = msg_status.Equals(Constant._Message_status_success) ? "成功" : "失败";
+                //}
+                //await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, grant_type,
+                //    new MessageContent { dataId=evaluationLocal.id, dataName=evaluationLocal.name, messageType= Constant._Message_type_check, status=msg_status, content="评测原始数据(source.json)" }, true);
+                //content = msg_status.Equals(Constant._Message_status_success) ? "成功" : "失败";
                
-                if (msg_status.Equals(Constant._Message_status_success)|| msg_status.Equals(Constant._Message_status_info))
-                {
-                    successMsgs.Add($"评测原始数据(source.json),检测结果:{content}");
-                }
-                else
-                {
-                    errorMsgs.Add($"评测原始数据(source.json),检测结果:{content}");
-                }
+                //if (msg_status.Equals(Constant._Message_status_success)|| msg_status.Equals(Constant._Message_status_info))
+                //{
+                //    successMsgs.Add($"评测原始数据(source.json),检测结果:{content}");
+                //}
+                //else
+                //{
+                //    errorMsgs.Add($"评测原始数据(source.json),检测结果:{content}");
+                //}
                 msg_status =Constant._Message_status_info;
                 try
                 {
@@ -362,8 +362,8 @@ namespace IES.ExamServer.Services
                     //数据格式:  [消息][信息/错误/警告][15:43]=>[开始检查评测信息文件...]
                     //数据格式:  [检查][成功/失败][15:43]=>[评测数据文件:/wwwroot/package/623a9fe6-5445-0938-ff77-aeb80066ef27/evaluation.json]
                     //数据格式:  [下载][成功/失败][15:43]=>[评测数据文件:/wwwroot/package/623a9fe6-5445-0938-ff77-aeb80066ef27/evaluation.json][1024kb][15ms]
-                    await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, Constant._Message_grant_type_check_file,
-                        new MessageContent { dataId=evaluationLocal.id, dataName=evaluationLocal.name, messageType= Constant._Message_type_check, status=msg_status, content="评测数据文件(evaluation.json)" });
+                    await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, grant_type,
+                        new MessageContent { dataId=evaluationLocal.id, dataName=evaluationLocal.name, messageType= Constant._Message_type_check, status=msg_status, content="评测数据文件(evaluation.json)" }, true);
                     content = msg_status.Equals(Constant._Message_status_success) ? "成功" : "失败";
                     if (msg_status.Equals(Constant._Message_status_success)|| msg_status.Equals(Constant._Message_status_info))
                     {
@@ -382,8 +382,8 @@ namespace IES.ExamServer.Services
                            
                            
                             msg_status=Constant._Message_status_error;
-                            await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, Constant._Message_grant_type_check_file,
-                                new MessageContent { dataId=evaluationLocal.id, dataName=evaluationLocal.name, messageType= Constant._Message_type_check, status=msg_status, content="评测数据文件(evaluation.json),文件读取失败!" });
+                            await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, grant_type,
+                                new MessageContent { dataId=evaluationLocal.id, dataName=evaluationLocal.name, messageType= Constant._Message_type_check, status=msg_status, content="评测数据文件(evaluation.json),文件读取失败!" }, true);
                             errorMsgs.Add("评测数据文件(evaluation.json),文件读取失败!");
                         }
                         else
@@ -393,8 +393,8 @@ namespace IES.ExamServer.Services
                             {
                                
                                 
-                                await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, Constant._Message_grant_type_check_file,
-                                    new MessageContent { dataId=evaluationLocal.id, dataName=evaluationLocal.name, messageType=Constant._Message_type_message, status=msg_status, content="评测数据文件(evaluation.json),读取评测基本信息..." });
+                                await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, grant_type,
+                                    new MessageContent { dataId=evaluationLocal.id, dataName=evaluationLocal.name, messageType=Constant._Message_type_message, status=msg_status, content="评测数据文件(evaluation.json),读取评测基本信息..." }, true);
                                 successMsgs.Add($"评测数据文件(evaluation.json),读取评测基本信息...");
                                 if (!string.IsNullOrWhiteSpace(evaluationLocal.blobHash) && evaluationLocal.blobHash.Equals(evaluationClient.blobHash)
                                     &&(evaluationLocal.blobTime==evaluationClient.blobTime)
@@ -411,8 +411,8 @@ namespace IES.ExamServer.Services
                                     msg_status=Constant._Message_status_error;
                                 }
 
-                                await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, Constant._Message_grant_type_check_file,
-                                    new MessageContent { dataId=evaluationLocal.id, dataName=evaluationLocal.name, messageType=Constant._Message_type_message, status=msg_status, content="评测数据文件(evaluation.json),校验评测基本信息..." });
+                                await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, grant_type,
+                                    new MessageContent { dataId=evaluationLocal.id, dataName=evaluationLocal.name, messageType=Constant._Message_type_message, status=msg_status, content="评测数据文件(evaluation.json),校验评测基本信息..." }, true);
                                 content = msg_status.Equals(Constant._Message_status_success)||msg_status.Equals(Constant._Message_status_info) ? "成功" : "失败";
                                 if (msg_status.Equals(Constant._Message_status_success)|| msg_status.Equals(Constant._Message_status_info))
                                 {
@@ -428,8 +428,8 @@ namespace IES.ExamServer.Services
                                
                                
                                 msg_status=Constant._Message_status_error;
-                                await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, Constant._Message_grant_type_check_file,
-                                new MessageContent { dataId=evaluationLocal.id, dataName=evaluationLocal.name, messageType= Constant._Message_type_check, status=msg_status, content="评测数据文件(evaluation.json),读取评测基本信息失败!" });
+                                await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, grant_type,
+                                new MessageContent { dataId=evaluationLocal.id, dataName=evaluationLocal.name, messageType= Constant._Message_type_check, status=msg_status, content="评测数据文件(evaluation.json),读取评测基本信息失败!" }, true);
                                 errorMsgs.Add("评测数据文件(evaluation.json),读取评测基本信息失败!");
                             }
 
@@ -445,15 +445,15 @@ namespace IES.ExamServer.Services
                             else
                             {
                                 msg_status=Constant._Message_status_info;
-                                await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, Constant._Message_grant_type_check_file,
-                                    new MessageContent { dataId=evaluationLocal.id, dataName=evaluationLocal.name, messageType=Constant._Message_type_message, status=msg_status, content="评测数据文件(evaluation.json),读取评测试卷信息..." });
+                                await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, grant_type,
+                                    new MessageContent { dataId=evaluationLocal.id, dataName=evaluationLocal.name, messageType=Constant._Message_type_message, status=msg_status, content="评测数据文件(evaluation.json),读取评测试卷信息..." }, true);
                                 successMsgs.Add($"评测数据文件(evaluation.json),读取评测试卷信息...");
                                 string pattern = @"paper/[^/]+/([^/]+/[^/]+\.[^/]+)";
                                 foreach (var evaluationExam in evaluationExams!)
                                 {
                                     msg_status=Constant._Message_status_info;
-                                    await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, Constant._Message_grant_type_check_file,
-                                        new MessageContent { dataId=evaluationLocal.id, dataName=evaluationLocal.name, messageType=Constant._Message_type_message, status=msg_status, content=$"校验评测科目试卷:{evaluationExam.subjectName}-{evaluationExam.examName}" });
+                                    await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, grant_type,
+                                        new MessageContent { dataId=evaluationLocal.id, dataName=evaluationLocal.name, messageType=Constant._Message_type_message, status=msg_status, content=$"校验评测科目试卷:{evaluationExam.subjectName}-{evaluationExam.examName}" }, true);
                                     successMsgs.Add($"校验评测科目试卷:{evaluationExam.subjectName}-{evaluationExam.examName}");
                                     string path_papers = Path.Combine(evaluationPath, "papers");
                                     var papers_files = FileHelper.ListAllFiles(path_papers);
@@ -528,7 +528,7 @@ namespace IES.ExamServer.Services
                                             paper_msg_status=Constant._Message_status_success;
                                             successMsgs.Add($"试卷名称:[{paperIndex}]{evaluationExam.examName}-{evaluationExam.subjectName}-{paper.paperName},文件数量:{paper.blobs.Count()},检测成功数量:{contents.Count(x => x.status==Constant._Message_status_success)},检测异常数量{contents.Count(x => x.status==Constant._Message_status_error)}");
                                         }
-                                        await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, Constant._Message_grant_type_check_file,
+                                        await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, grant_type,
                                             new MessageContent
                                             {
                                                 dataId=evaluationLocal.id,
@@ -537,7 +537,7 @@ namespace IES.ExamServer.Services
                                                 status=paper_msg_status,
                                                 content=$"试卷名称:[{paperIndex}]{evaluationExam.examName}-{evaluationExam.subjectName}-{paper.paperName},文件数量:{paper.blobs.Count()},检测成功数量:{contents.Count(x => x.status==Constant._Message_status_success)},检测异常数量{contents.Count(x => x.status==Constant._Message_status_error)}",
                                                 contents=contents
-                                            });
+                                            }, true);
                                     }
                                 }
                             }

+ 174 - 0
TEAMModelOS.Function/Properties/ServiceDependencies/TEAMModelOSFunction - Zip Deploy/profile.arm.json

@@ -0,0 +1,174 @@
+{
+  "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#",
+  "contentVersion": "1.0.0.0",
+  "metadata": {
+    "_dependencyType": "compute.function.windows.appService"
+  },
+  "parameters": {
+    "resourceGroupName": {
+      "type": "string",
+      "defaultValue": "TEAMModelChengdu",
+      "metadata": {
+        "description": "Name of the resource group for the resource. It is recommended to put resources under same resource group for better tracking."
+      }
+    },
+    "resourceGroupLocation": {
+      "type": "string",
+      "defaultValue": "",
+      "metadata": {
+        "description": "Location of the resource group. Resource groups could have different location than resources, however by default we use API versions from latest hybrid profile which support all locations for resource types we support."
+      }
+    },
+    "resourceName": {
+      "type": "string",
+      "defaultValue": "TEAMModelOSFunction",
+      "metadata": {
+        "description": "Name of the main resource to be created by this template."
+      }
+    },
+    "resourceLocation": {
+      "type": "string",
+      "defaultValue": "[parameters('resourceGroupLocation')]",
+      "metadata": {
+        "description": "Location of the resource. By default use resource group's location, unless the resource provider is not supported there."
+      }
+    }
+  },
+  "resources": [
+    {
+      "type": "Microsoft.Resources/resourceGroups",
+      "name": "[parameters('resourceGroupName')]",
+      "location": "[parameters('resourceGroupLocation')]",
+      "apiVersion": "2019-10-01"
+    },
+    {
+      "type": "Microsoft.Resources/deployments",
+      "name": "[concat(parameters('resourceGroupName'), 'Deployment', uniqueString(concat(parameters('resourceName'), subscription().subscriptionId)))]",
+      "resourceGroup": "[parameters('resourceGroupName')]",
+      "apiVersion": "2019-10-01",
+      "dependsOn": [
+        "[parameters('resourceGroupName')]"
+      ],
+      "properties": {
+        "mode": "Incremental",
+        "expressionEvaluationOptions": {
+          "scope": "inner"
+        },
+        "parameters": {
+          "resourceGroupName": {
+            "value": "[parameters('resourceGroupName')]"
+          },
+          "resourceGroupLocation": {
+            "value": "[parameters('resourceGroupLocation')]"
+          },
+          "resourceName": {
+            "value": "[parameters('resourceName')]"
+          },
+          "resourceLocation": {
+            "value": "[parameters('resourceLocation')]"
+          }
+        },
+        "template": {
+          "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
+          "contentVersion": "1.0.0.0",
+          "parameters": {
+            "resourceGroupName": {
+              "type": "string"
+            },
+            "resourceGroupLocation": {
+              "type": "string"
+            },
+            "resourceName": {
+              "type": "string"
+            },
+            "resourceLocation": {
+              "type": "string"
+            }
+          },
+          "variables": {
+            "storage_name": "[toLower(concat('storage', uniqueString(concat(parameters('resourceName'), subscription().subscriptionId))))]",
+            "appServicePlan_name": "[concat('Plan', uniqueString(concat(parameters('resourceName'), subscription().subscriptionId)))]",
+            "storage_ResourceId": "[concat('/subscriptions/', subscription().subscriptionId, '/resourceGroups/', parameters('resourceGroupName'), '/providers/Microsoft.Storage/storageAccounts/', variables('storage_name'))]",
+            "appServicePlan_ResourceId": "[concat('/subscriptions/', subscription().subscriptionId, '/resourceGroups/', parameters('resourceGroupName'), '/providers/Microsoft.Web/serverFarms/', variables('appServicePlan_name'))]",
+            "function_ResourceId": "[concat('/subscriptions/', subscription().subscriptionId, '/resourceGroups/', parameters('resourceGroupName'), '/providers/Microsoft.Web/sites/', parameters('resourceName'))]"
+          },
+          "resources": [
+            {
+              "location": "[parameters('resourceLocation')]",
+              "name": "[parameters('resourceName')]",
+              "type": "Microsoft.Web/sites",
+              "apiVersion": "2015-08-01",
+              "tags": {
+                "[concat('hidden-related:', variables('appServicePlan_ResourceId'))]": "empty"
+              },
+              "dependsOn": [
+                "[variables('appServicePlan_ResourceId')]",
+                "[variables('storage_ResourceId')]"
+              ],
+              "kind": "functionapp",
+              "properties": {
+                "name": "[parameters('resourceName')]",
+                "kind": "functionapp",
+                "httpsOnly": true,
+                "reserved": false,
+                "serverFarmId": "[variables('appServicePlan_ResourceId')]",
+                "siteConfig": {
+                  "alwaysOn": true
+                }
+              },
+              "identity": {
+                "type": "SystemAssigned"
+              },
+              "resources": [
+                {
+                  "name": "appsettings",
+                  "type": "config",
+                  "apiVersion": "2015-08-01",
+                  "dependsOn": [
+                    "[variables('function_ResourceId')]"
+                  ],
+                  "properties": {
+                    "AzureWebJobsStorage": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storage_name'), ';AccountKey=', listKeys(variables('storage_ResourceId'), '2017-10-01').keys[0].value, ';EndpointSuffix=', 'core.windows.net')]",
+                    "FUNCTIONS_EXTENSION_VERSION": "~3",
+                    "FUNCTIONS_WORKER_RUNTIME": "dotnet"
+                  }
+                }
+              ]
+            },
+            {
+              "location": "[parameters('resourceGroupLocation')]",
+              "name": "[variables('storage_name')]",
+              "type": "Microsoft.Storage/storageAccounts",
+              "apiVersion": "2017-10-01",
+              "tags": {
+                "[concat('hidden-related:', concat('/providers/Microsoft.Web/sites/', parameters('resourceName')))]": "empty"
+              },
+              "properties": {
+                "supportsHttpsTrafficOnly": true
+              },
+              "sku": {
+                "name": "Standard_LRS"
+              },
+              "kind": "Storage"
+            },
+            {
+              "location": "[parameters('resourceGroupLocation')]",
+              "name": "[variables('appServicePlan_name')]",
+              "type": "Microsoft.Web/serverFarms",
+              "apiVersion": "2015-08-01",
+              "sku": {
+                "name": "S1",
+                "tier": "Standard",
+                "family": "S",
+                "size": "S1"
+              },
+              "properties": {
+                "name": "[variables('appServicePlan_name')]"
+              }
+            }
+          ]
+        }
+      }
+    }
+  ]
+}

+ 8 - 6
TEAMModelOS.sln

@@ -49,7 +49,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IES.ExamServer", "TEAMModel
 EndProject
 Project("{54A90642-561A-4BB1-A94E-469ADEE60C69}") = "IES.ExamViews", "TEAMModelOS.Extension\IES.Exam\IES.ExamViews\IES.ExamViews.esproj", "{837FAA7E-E897-49EC-905E-4071CA25539A}"
 EndProject
-Project("{9092AA53-FB77-4645-B42D-1CCCA6BD08BD}") = "IES.ExamClient", "TEAMModelOS.Extension\IES.Exam\IES.ExamClient\IES.ExamClient.njsproj", "{7FA6FAA3-E176-4E56-B542-2E8176850924}"
+Project("{54A90642-561A-4BB1-A94E-469ADEE60C69}") = "IES.ExamClient", "TEAMModelOS.Extension\IES.Exam\IES.ExamClient\IES.ExamClient.esproj", "{594ACBC7-C85D-94F2-3914-47596E2C15C9}"
 EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -133,10 +133,12 @@ Global
 		{837FAA7E-E897-49EC-905E-4071CA25539A}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{837FAA7E-E897-49EC-905E-4071CA25539A}.Release|Any CPU.Build.0 = Release|Any CPU
 		{837FAA7E-E897-49EC-905E-4071CA25539A}.Release|Any CPU.Deploy.0 = Release|Any CPU
-		{7FA6FAA3-E176-4E56-B542-2E8176850924}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{7FA6FAA3-E176-4E56-B542-2E8176850924}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{7FA6FAA3-E176-4E56-B542-2E8176850924}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{7FA6FAA3-E176-4E56-B542-2E8176850924}.Release|Any CPU.Build.0 = Release|Any CPU
+		{594ACBC7-C85D-94F2-3914-47596E2C15C9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{594ACBC7-C85D-94F2-3914-47596E2C15C9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{594ACBC7-C85D-94F2-3914-47596E2C15C9}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
+		{594ACBC7-C85D-94F2-3914-47596E2C15C9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{594ACBC7-C85D-94F2-3914-47596E2C15C9}.Release|Any CPU.Build.0 = Release|Any CPU
+		{594ACBC7-C85D-94F2-3914-47596E2C15C9}.Release|Any CPU.Deploy.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
@@ -160,7 +162,7 @@ Global
 		{6B34DA2A-7527-426F-AD36-F5CE923CED62} = {9B74B53F-20E8-46CC-903B-62AEB1583DD7}
 		{41AABD1E-815E-4981-892F-074ED0A18EE4} = {50CC8BCA-FB13-492E-849D-1E1870DEBBAB}
 		{837FAA7E-E897-49EC-905E-4071CA25539A} = {50CC8BCA-FB13-492E-849D-1E1870DEBBAB}
-		{7FA6FAA3-E176-4E56-B542-2E8176850924} = {50CC8BCA-FB13-492E-849D-1E1870DEBBAB}
+		{594ACBC7-C85D-94F2-3914-47596E2C15C9} = {50CC8BCA-FB13-492E-849D-1E1870DEBBAB}
 	EndGlobalSection
 	GlobalSection(ExtensibilityGlobals) = postSolution
 		SolutionGuid = {76440725-5E50-4288-851F-BA5C0BC8E8C6}