CrazyIter_Bin 4 ماه پیش
والد
کامیت
2e835bfbd6

+ 80 - 41
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Controllers/ManageController.cs

@@ -6,10 +6,12 @@ using IES.ExamServer.Helper;
 using IES.ExamServer.Helpers;
 using IES.ExamServer.Models;
 using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.SignalR;
 using Microsoft.Extensions.Caching.Memory;
 using Microsoft.Extensions.Configuration;
 using System.Linq.Expressions;
 using System.Net.Http;
+using System.Net.Http.Json;
 using System.Text.Json;
 using System.Text.Json.Nodes;
 
@@ -22,14 +24,14 @@ namespace IES.ExamServer.Controllers
         private readonly IConfiguration _configuration;
         private readonly IHttpClientFactory _httpClientFactory;
         private readonly IMemoryCache _memoryCache;
-        private readonly ILogger _logger;
+        private readonly ILogger<ManageController> _logger;
         private readonly LiteDBFactory _liteDBFactory;
         private readonly DataCenterConnectionService _connectionService;
         private readonly int DelayMicro = 10;//微观数据延迟
         private readonly int DelayMacro = 100;//宏观数据延迟
-        private readonly SignalRExamServerHub _signalRExamServerHub;
-        public ManageController(LiteDBFactory liteDBFactory,ILogger logger, IConfiguration configuration,
-            IHttpClientFactory httpClientFactory, IMemoryCache memoryCache, DataCenterConnectionService connectionService,SignalRExamServerHub signalRExamServerHub)
+        private readonly IHubContext<SignalRExamServerHub> _signalRExamServerHub;
+        public ManageController(LiteDBFactory liteDBFactory,ILogger<ManageController> logger, IConfiguration configuration,
+            IHttpClientFactory httpClientFactory, IMemoryCache memoryCache, DataCenterConnectionService connectionService, IHubContext<SignalRExamServerHub> signalRExamServerHub)
         {
             _logger = logger;
             _configuration=configuration;
@@ -40,10 +42,10 @@ namespace IES.ExamServer.Controllers
             _signalRExamServerHub=signalRExamServerHub;
         }
         [HttpPost("download-package")]
-        [AuthToken("admin","teacher")]
+      // [AuthToken("admin","teacher")]
         public async Task<IActionResult> DownloadPackage(JsonNode json)
         {
-
+     
             //C#.NET 6 后端与前端流式通信
             //https://www.doubao.com/chat/collection/687687510791426?type=Thread
             //下载日志记录:1.步骤,检查,2.获取描述信息,3.分类型,4下载文件,5.前端处理,6.返回结果 , 正在下载...==> [INFO]https://www.doubao.com/chat/collection/687687510791426?type=Thread [Size=180kb] Ok...
@@ -175,12 +177,12 @@ namespace IES.ExamServer.Controllers
                 _liteDBFactory.GetLiteDatabase().GetCollection<EvaluationClient>().Insert(evaluationLocal);
             }
             List<string> file_error = new List<string>();
+            int checkTotal=0, checkSuccess=0, checkError=0,checkWarning = 0;
             if (evaluationLocal!=null)
             {
-
-
-                await _signalRExamServerHub.SendMessage(deviceId, Constant._Message_grant_type_check_file, 
+                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=0, content="开始检查评测信息文件.." });
+                 
                 //校验本地文件数据
                 string packagePath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "package");
                 if (!Directory.Exists(packagePath))
@@ -193,34 +195,19 @@ namespace IES.ExamServer.Controllers
                 {
                     file_error.Add("evaluation");
                     msg_status=Constant._Message_status_error;
+                    checkTotal++;
+                    checkError++;
                 }
                 else 
                 {
                     msg_status=Constant._Message_status_success;
-                    //string jsonData =  await System.IO.File.ReadAllTextAsync(path_evaluation);
-                    //EvaluationClient? evaluationFile =jsonData.ToObject<EvaluationClient>();
-                    //if (evaluationFile!=null)
-                    //{
-                    //    if (evaluationFile.dataSize==evaluationLocal.dataSize )
-                    //    {
-                    //        file_error.Add("evaluation");
-                    //        msg_status=Constant._Message_status_error;
-                    //    }
-                    //    else
-                    //    {
-                    //        msg_status=Constant._Message_status_success;
-                    //    }
-                    //}
-                    //else
-                    //{
-                    //    file_error.Add("evaluation");
-                    //    msg_status=Constant._Message_status_error;
-                    //}
+                    checkTotal++;
+                    checkSuccess++;
                 }
                 //数据格式:  [消息][信息/错误/警告][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(deviceId, Constant._Message_grant_type_check_file, 
+                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=$"评测数据文件:{path_evaluation}" });
                 //await Task.Delay(DelayMacro);
                 string path_groupList = Path.Combine(evaluationPath, "groupList.json");
@@ -229,12 +216,16 @@ namespace IES.ExamServer.Controllers
                 {
                     file_error.Add("groupList");
                     msg_status=Constant._Message_status_error;
+                    checkTotal++;
+                    checkError++;
                 }
                 else
                 {
                     msg_status=Constant._Message_status_success;
+                    checkTotal++;
+                    checkSuccess++;
                 }
-                await _signalRExamServerHub.SendMessage(deviceId, Constant._Message_grant_type_check_file, 
+                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=$"评测名单文件:{path_groupList}" });
                 //await Task.Delay(DelayMacro);
                 string path_source = Path.Combine(evaluationPath, "source.json");
@@ -243,12 +234,16 @@ namespace IES.ExamServer.Controllers
                 {
                     file_error.Add("source");
                     msg_status=Constant._Message_status_error;
+                    checkTotal++;
+                    checkError++;
                 }
                 else
                 {
                     msg_status=Constant._Message_status_success;
+                    checkTotal++;
+                    checkSuccess++;
                 }
-                await _signalRExamServerHub.SendMessage(deviceId, Constant._Message_grant_type_check_file,
+                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=$"评测原始数据:{path_source}" });
                // await Task.Delay(DelayMacro);
                 msg_status =Constant._Message_status_info;
@@ -271,14 +266,14 @@ namespace IES.ExamServer.Controllers
                                 &&(evaluationLocal.webviewTime== evaluationClient.webviewTime)
                                 &&(!string.IsNullOrWhiteSpace(evaluationLocal.webviewPath)&&  evaluationLocal.webviewPath.Equals(evaluationClient.webviewPath)))
                             {
-                                msg_status=1;
+                                msg_status=Constant._Message_status_info;
                             }
                             else
                             {
                                 msg_status=Constant._Message_status_error;
                             }
-                            await _signalRExamServerHub.SendMessage(deviceId, Constant._Message_grant_type_check_file,
-                                new MessageContent { dataId=evaluationLocal.id, dataName=evaluationLocal.name, messageType=Constant._Message_type_message, status=msg_status, content="校验本地数据文件..." });
+                            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="校验本地试卷文件..." });
                         }
 
                         List<EvaluationExam>? evaluationExams = evaluation_data["evaluationExams"]?.ToObject<List<EvaluationExam>>();
@@ -330,7 +325,7 @@ namespace IES.ExamServer.Controllers
                                     else {
                                         paper_msg_status=Constant._Message_status_success;
                                     }
-                                    await _signalRExamServerHub.SendMessage(deviceId, Constant._Message_grant_type_check_file, 
+                                    await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, Constant._Message_grant_type_check_file, 
                                         new MessageContent {
                                             dataId=evaluationLocal.id,
                                             dataName=evaluationLocal.name,
@@ -347,12 +342,56 @@ namespace IES.ExamServer.Controllers
                 catch (Exception e) { 
                 
                 }
-                //检测参考名单
-                await _signalRExamServerHub.SendMessage(deviceId, Constant._Message_grant_type_check_file, 
-                    new MessageContent {
-                        dataId=evaluationLocal.id,
-                        dataName=evaluationLocal.name,
-                        messageType=Constant._Message_type_message, status=0, content="提取评测数据文件..." });
+
+                //检查需要更新的项目:
+                if (data==1) 
+                {
+                    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=Constant._Message_status_warning,
+                            content=$"检查到评测数据需要更新。[{dataSize}]"
+                        });
+                }
+                if (blob==1)
+                {
+                    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=Constant._Message_status_warning,
+                            content=$"检查到评测试卷需要更新。[{blobSize}]"
+                        });
+                }
+                if (webview==1) 
+                {
+                    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=Constant._Message_status_warning,
+                            content=$"检查到评测作答页面需要更新。[{webviewSize}]"
+                        });
+                }
+                if (groupList==1) 
+                {
+                    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=Constant._Message_status_warning,
+                            content=$"检查到评测名单需要更新。[{studentCount}]"
+                        });
+                }
             }
             return Ok(new {code=200, evaluation= evaluationLocal,data,blob,webview,dataSize,blobSize,webviewSize,status ,groupList,studentCount});
         }

+ 55 - 0
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/DI/SignalRHost/SignalRExamServerHub.cs

@@ -10,6 +10,61 @@ using System.Text.Json;
 
 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)
+        {
+            //双向检测是否连接。
+            SignalRClient signalRClient = _memoryCache.Get<SignalRClient>($"{Constant._KeySignalRClientClients}:{clientId}");
+            if (signalRClient!=null)
+            {
+                signalRClient = _memoryCache.Get<SignalRClient>($"{Constant._KeySignalRClientConnects}:{signalRClient.connid}");
+            }
+            int code = 0;
+            string msg = "";
+            CheckFileMessageBody messageBody = new CheckFileMessageBody
+            {
+                content = content.content,
+                status = content.status,
+                clientid = clientId,
+                connid = signalRClient?.connid,
+                grant_type = grant_type,
+                time = DateTimeOffset.Now.ToUnixTimeMilliseconds(),
+                type = Constant._Message_type_message,
+                contents = content.contents
+            };
+            if (signalRClient != null)
+            {
+
+                try
+                {
+                    switch (true)
+                    {
+                        case bool when grant_type.Equals(Constant._Message_grant_type_check_file):
+                            {
+                                await hubContext.Clients.Client(signalRClient.connid!).SendAsync("ReceiveMessage", messageBody);
+                                code=200;
+                                msg = $"发送成功";
+                                break;
+                            }
+                        default:
+                            break;
+                    }
+                }
+                catch (Exception ex)
+                {
+                    code=500;
+                    msg = $"{ex.Message},{ex.StackTrace}";
+                }
+            }
+            else
+            {
+                code= 400;
+                msg="未连接客户端";
+            }
+            logger.LogData<object>(new { code, msg, data = messageBody }, content.dataId!);
+        }
+    }
     public  class SignalRExamServerHub : Hub<IClient>
     {
         private readonly ILogger<SignalRExamServerHub> _logger;

+ 3 - 3
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Program.cs

@@ -49,7 +49,7 @@ namespace IES.ExamServer
             builder.Configuration.SetBasePath(Directory.GetCurrentDirectory()).AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);
             builder.Services.AddSpaStaticFiles(opt => opt.RootPath = "ClientApp/dist");
             // Add services to the container.
-            builder.Services.AddControllersWithViews().AddJsonOptions(options =>
+            builder.Services.AddControllers().AddJsonOptions(options =>
             {
                 // 设置 JSON 序列化选项
                 options.JsonSerializerOptions.Encoder = JavaScriptEncoder.Create(UnicodeRanges.All); // 允许所有 Unicode 字符
@@ -135,10 +135,10 @@ namespace IES.ExamServer
 
             app.UseAuthorization();
 
-            
+            app.MapHub<SignalRExamServerHub>("/signalr/exam").RequireCors("any");
             app.UseEndpoints(endpoints =>
             {
-                endpoints.MapHub<SignalRExamServerHub>("/signalr/exam").RequireCors("any");
+               // endpoints.MapHub<SignalRExamServerHub>("/signalr/exam").RequireCors("any");
                 endpoints.MapControllers();
 
                 // NOTE: VueCliProxy is meant for developement and hot module reload

+ 39 - 4
TEAMModelOS.SDK/Models/Service/EvaluationSyncInfoService.cs

@@ -22,6 +22,42 @@ namespace TEAMModelOS.SDK.Models.Service
 {
     public sealed class EvaluationSyncInfoService
     {
+
+        /// <summary>
+        /// 重新对名单的hash值计算,因为可能会存在临时将学生加入名单,而没有修改评测信息的情况。
+        /// </summary>
+        /// <param name="evaluationSyncInfo"></param>
+        /// <param name="_coreAPIHttpService"></param>
+        /// <param name="_azureCosmos"></param>
+        /// <param name="_dingDing"></param>
+        /// <returns></returns>
+        public static async Task<(EvaluationSyncInfo evaluation, bool change,string newGrouplistHash, List<RMember> members, List<RGroupList> groupLists)> CheckEvaluationGroupList(EvaluationSyncInfo evaluationSyncInfo, CoreAPIHttpService _coreAPIHttpService, AzureCosmosFactory _azureCosmos, DingDing _dingDing)
+        {
+            var listInfo = await GroupListService.GetMemberByListids(_coreAPIHttpService, _azureCosmos.GetCosmosClient(), _dingDing, evaluationSyncInfo.grouplist, evaluationSyncInfo.owner);
+            //计算数据的hash值
+            StringBuilder groupListData = new StringBuilder();
+            //名单的hash值
+            var orderList = listInfo.groups.OrderBy(x => x.id);
+            foreach (var item in orderList)
+            {
+                groupListData.Append($"{item.id}-{item.name}");
+                var orderMembers = item.members.OrderBy(x => x.id);
+                foreach (var member in orderMembers)
+                {
+                    groupListData.Append($"{member.id}-{member.name}");
+                }
+            }
+            bool change = false;
+            string grouplistHash = ShaHashHelper.GetSHA1(groupListData.ToString());
+            if (!evaluationSyncInfo.grouplistHash .Equals(grouplistHash))
+            {
+                evaluationSyncInfo.grouplistHash = grouplistHash;
+                change = true;
+            }
+            return (evaluationSyncInfo,change, grouplistHash,listInfo.rmembers, listInfo.groups);
+        }
+
+
         /// <summary>
         /// 活动数据打包
         /// </summary>
@@ -42,7 +78,6 @@ namespace TEAMModelOS.SDK.Models.Service
             long? dataTime = 0;
             long stime = 0;
             long etime = 0;
-
             
             string? ownerName=string.Empty;
             string? ownerPicture = string.Empty;
@@ -401,9 +436,9 @@ namespace TEAMModelOS.SDK.Models.Service
                 string evaluationSyncInfoSJson = evaluationSyncInfo.ToJsonString();
                 dataSize+= Encoding.UTF8.GetByteCount(evaluationSyncInfoSJson);
                 evaluationClient.dataSize = dataSize;
-                await azureStorage.GetBlobContainerClient(ownerId).UploadFileByContainer(sourceJson, $"package/{id}", "source.json");
-                await azureStorage.GetBlobContainerClient(ownerId).UploadFileByContainer(groupListJson, $"package/{id}", "groupList.json");
-                await azureStorage.GetBlobContainerClient(ownerId).UploadFileByContainer(new { evaluationClient, evaluationExams }.ToJsonString(), $"package/{id}", "evaluation.json");
+                await azureStorage.GetBlobContainerClient(ownerId).UploadFileByContainer(sourceJson, $"exam/{id}/package", "source.json");
+                await azureStorage.GetBlobContainerClient(ownerId).UploadFileByContainer(groupListJson, $"exam/{id}/package", "grouplist.json");
+                await azureStorage.GetBlobContainerClient(ownerId).UploadFileByContainer(new { evaluationClient, evaluationExams }.ToJsonString(), $"exam/{id}/package", "evaluation.json");
                 evaluationSyncInfo.dataSize = dataSize;
               
                 await azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Normal).UpsertItemAsync<EvaluationSyncInfo>(evaluationSyncInfo, new PartitionKey("EvaluationSyncInfo"));

+ 128 - 14
TEAMModelOS/Controllers/Both/EvaluationSyncInfoController.cs

@@ -17,6 +17,13 @@ using TEAMModelOS.SDK.Models.Cosmos;
 using System.Text;
 using System.Linq;
 using System.Net.Http;
+using Pipelines.Sockets.Unofficial.Buffers;
+using Azure.Storage.Blobs.Models;
+using System;
+using IES.ExamServer.Models;
+using TEAMModelOS.SDK.Extension;
+using TEAMModelOS.SDK.Models;
+using DocumentFormat.OpenXml.Office2010.Excel;
 
 
 namespace TEAMModelOS.Controllers.Both
@@ -62,25 +69,132 @@ namespace TEAMModelOS.Controllers.Both
         //#if !DEBUG
         //        [Authorize(Roles = "IES")]
         //#endif
-        public async Task<IActionResult> FindSyncInfo(JsonNode request) 
+        public async Task<IActionResult> FindSyncInfo(JsonNode json) 
         {
-            EvaluationSyncInfo evaluationSyncInfo =       new EvaluationSyncInfo();
-            var listInfo = await GroupListService.GetMemberByListids(_coreAPIHttpService, _azureCosmos.GetCosmosClient(), _dingDing, evaluationSyncInfo.grouplist, evaluationSyncInfo.owner);
-            //计算数据的hash值
-            var groupList = new { members = listInfo.rmembers, groupList = listInfo.groups };
-            StringBuilder groupListData = new StringBuilder();
-            //名单的hash值
-            var orderList = listInfo.groups.OrderBy(x => x.id);
-            foreach (var item in orderList)
+            var tokenInfo =  HttpContext.GetAuthTokenInfo();
+
+            string shortCode = $"{json["shortCode"]}";
+            string evaluationId = $"{json["evaluationId"]}";
+
+            if (string.IsNullOrWhiteSpace(shortCode))
+            {
+                return BadRequest();
+            }
+
+            string sql = $"select value  c from c where c.shortCode='{shortCode}'";
+            if (!string.IsNullOrWhiteSpace(evaluationId)) 
+            {
+                sql=$"{sql} and c.id='{evaluationId}'";
+            }
+            EvaluationSyncInfo evaluationSyncInfo = null;
+            var result=   await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Normal).GetList<EvaluationSyncInfo>(sql, "EvaluationSyncInfo");
+            if (result.list.Count==1)
+            {
+                evaluationSyncInfo= result.list.First();
+            }
+            else {
+                if (result.list.Count>1) 
+                {
+                   await _dingDing.SendBotMsg($"根据试卷开卷码搜索评测,查询到多条数据,请检查,开卷码{shortCode},试卷id:{string.Join(",",result.list.Select(x=>x.id))}", GroupNames.成都开发測試群組);
+                }
+                return BadRequest();
+            }
+            if (evaluationSyncInfo.scope.Equals("school"))
             {
-                groupListData.Append($"{item.id}-{item.name}");
-                var orderMembers = item.members.OrderBy(x => x.id);
-                foreach (var member in orderMembers)
+                ResponseMessage  response=  await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.School).ReadItemStreamAsync(tokenInfo.id, new PartitionKey($"Teacher-{evaluationSyncInfo.ownerId}"));
+                if (!response.IsSuccessStatusCode) 
                 {
-                    groupListData.Append($"{member.id}-{member.name}");
+                    await _dingDing.SendBotMsg($"根据试卷开卷码搜索评测,检测到请求的教师未加入学校,数据归属{evaluationSyncInfo.scope},教师id:{tokenInfo.id},ownerId:{evaluationSyncInfo.ownerId}", GroupNames.成都开发測試群組);
+                    return BadRequest();
                 }
             }
-            evaluationSyncInfo.grouplistHash= ShaHashHelper.GetSHA1(groupListData.ToString());
+            else 
+            {
+                if (!tokenInfo.id.Equals(evaluationSyncInfo.ownerId))
+                {
+                    await _dingDing.SendBotMsg($"根据试卷开卷码搜索评测,检测到请求的教师与数据的ownerId不一致,数据归属{evaluationSyncInfo.scope},教师id:{tokenInfo.id},ownerId:{evaluationSyncInfo.ownerId}", GroupNames.成都开发測試群組);
+                    return BadRequest();
+                }
+            }
+
+            var listInfo  =  await EvaluationSyncInfoService.CheckEvaluationGroupList(evaluationSyncInfo, _coreAPIHttpService, _azureCosmos, _dingDing);
+            evaluationSyncInfo=listInfo.evaluation;
+            if (listInfo.change) 
+            {
+                await  _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS,Constant.Normal).UpsertItemAsync(evaluationSyncInfo,new PartitionKey("EvaluationSyncInfo"));
+            }
+            try 
+            {
+                BlobDownloadResult downloadResult = await _azureStorage.GetBlobContainerClient(evaluationSyncInfo.ownerId).GetBlobClient($"exam/{evaluationSyncInfo.id}/package/evaluation.json").DownloadContentAsync();
+                var evaluationData = JsonDocument.Parse(downloadResult.Content).RootElement;
+                EvaluationClient evaluationClient= evaluationData.GetProperty("evaluationClient").ToObject<EvaluationClient>();
+                List<EvaluationExam> evaluationExams = evaluationData.GetProperty("evaluationExams").ToObject<List<EvaluationExam>>();
+                if (!evaluationClient.dataHash.Equals(evaluationSyncInfo.dataHash)) 
+                {
+                    await _dingDing.SendBotMsg($"评测{evaluationSyncInfo.id}数据同步信息的dataHash与evaluation.json文件中的不一致,请检查,或重新生成。", GroupNames.成都开发測試群組);
+                    return BadRequest();
+                }
+                if (!evaluationClient.blobHash.Equals(evaluationSyncInfo.blobHash))
+                {
+                    await _dingDing.SendBotMsg($"评测{evaluationSyncInfo.id}数据同步信息的blobHash与evaluation.json文件中的不一致,请检查,或重新生成。", GroupNames.成都开发測試群組);
+                    return BadRequest();
+                }
+
+                if (listInfo.change)
+                {
+                    evaluationClient.grouplistHash=listInfo.newGrouplistHash;
+                    await _azureStorage.GetBlobContainerClient(evaluationClient.ownerId).UploadFileByContainer(new { evaluationClient, evaluationExams }.ToJsonString(), $"exam/{evaluationClient.id}/package", "evaluation.json");
+                }
+                else {
+                    if (!evaluationClient.grouplistHash.Equals(evaluationSyncInfo.grouplistHash))
+                    {
+                        await _dingDing.SendBotMsg($"评测{evaluationSyncInfo.id}数据同步信息的grouplistHash与文件中的不一致,请检查,或重新生成。", GroupNames.成都开发測試群組);
+                        return BadRequest();
+                    }
+                }
+            } catch (Exception ex)
+            {
+                await _dingDing.SendBotMsg($"评测{evaluationSyncInfo.id}数据同步信息的文件异常,evaluation.json,{ex.Message}\n{ex.StackTrace}", GroupNames.成都开发測試群組);
+                return BadRequest();
+            }
+            try
+            {
+                //如果有变动,则重新生成
+                if (listInfo.change)
+                {
+                    await _azureStorage.GetBlobContainerClient(evaluationSyncInfo.ownerId).UploadFileByContainer(new { members = listInfo.members, groupList = listInfo.groupLists }.ToJsonString(), $"exam/{evaluationSyncInfo.id}/package", "grouplist.json");
+                }
+                else 
+                {
+                    BlobDownloadResult downloadResult = await _azureStorage.GetBlobContainerClient(evaluationSyncInfo.ownerId).GetBlobClient($"exam/{evaluationSyncInfo.id}/package/grouplist.json").DownloadContentAsync();
+                    var grouplistJson = JsonDocument.Parse(downloadResult.Content).RootElement;
+                    var groupList = grouplistJson.GetProperty("groupList").ToObject<List<RGroupList>>();
+                    StringBuilder groupListData = new StringBuilder();
+                    //名单的hash值
+                    var orderList = groupList.OrderBy(x => x.id);
+                    foreach (var item in orderList)
+                    {
+                        groupListData.Append($"{item.id}-{item.name}");
+                        var orderMembers = item.members.OrderBy(x => x.id);
+                        foreach (var member in orderMembers)
+                        {
+                            groupListData.Append($"{member.id}-{member.name}");
+                        }
+                    }
+                    string grouplistHash = ShaHashHelper.GetSHA1(groupListData.ToString());
+                    if (!evaluationSyncInfo.grouplistHash.Equals(grouplistHash))
+                    {
+                        await _dingDing.SendBotMsg($"评测{evaluationSyncInfo.id}数据同步信息的grouplistHash与文件中的不一致,请检查,或重新生成。", GroupNames.成都开发測試群組);
+                        return BadRequest();
+                    }
+                }
+            }
+            catch (Exception ex)
+            {
+                await _dingDing.SendBotMsg($"评测{evaluationSyncInfo.id}数据同步信息的文件异常,grouplist.json,{ex.Message}\n{ex.StackTrace}", GroupNames.成都开发測試群組);
+                return BadRequest();
+            }
+
             return Ok();
         }
 

+ 11 - 10
TEAMModelOS/Controllers/System/CoreController.cs

@@ -98,6 +98,7 @@ namespace TEAMModelOS.Controllers
             var jsonAuth = System.IO.File.ReadAllText(path, Encoding.UTF8);
             var jsonData = jsonAuth.ToObject<JsonElement>();
             var status = jsonData.GetProperty("notify-status");
+            Teacher teacher = null;
             try {
                 string opttmdid = "";
               
@@ -150,7 +151,7 @@ namespace TEAMModelOS.Controllers
                     ResponseMessage teacherResponse = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher)
                                         .ReadItemStreamAsync($"{_tmdid}", new PartitionKey("Base"));
                     if (teacherResponse.StatusCode==System.Net.HttpStatusCode.OK) {
-                        var teacher = JsonDocument.Parse(teacherResponse.Content).RootElement.Deserialize<Teacher>();
+                        teacher = JsonDocument.Parse(teacherResponse.Content).RootElement.Deserialize<Teacher>();
                         if (!string.IsNullOrWhiteSpace(teacher.lang) &&(teacher.lang.Equals("zh-cn")||teacher.lang.Equals("zh-tw") || teacher.lang.Equals("en-us"))) { 
                             lang= teacher.lang;
                             path = Path.Combine("", $"Lang/{lang}.json");
@@ -173,9 +174,9 @@ namespace TEAMModelOS.Controllers
                                     {
                                         ResponseMessage schoolTeacherResponse = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.School)
                                                .ReadItemStreamAsync($"{_tmdid}", new PartitionKey($"Teacher-{_schoolId}"));
-                                        if (teacherResponse.StatusCode == HttpStatusCode.OK && schoolTeacherResponse.StatusCode==System.Net.HttpStatusCode.OK)
+                                        if (teacher!=null && schoolTeacherResponse.StatusCode==System.Net.HttpStatusCode.OK)
                                         {
-                                            var teacher = JsonDocument.Parse(teacherResponse.Content).RootElement.Deserialize<Teacher>();
+                                           // var teacher = JsonDocument.Parse(teacherResponse.Content).RootElement.Deserialize<Teacher>();
                                             var schoolTeacher = JsonDocument.Parse(schoolTeacherResponse.Content).RootElement.Deserialize<SchoolTeacher>();
                                             //同意
                                             if (notifyData.notifyEvent == 1)
@@ -240,9 +241,9 @@ namespace TEAMModelOS.Controllers
                                       
                                         ResponseMessage schoolTeacherResponse = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.School)
                                                .ReadItemStreamAsync($"{_tmdid}", new PartitionKey($"Teacher-{_schoolId}"));
-                                        if (teacherResponse.StatusCode == HttpStatusCode.OK && schoolTeacherResponse.StatusCode==System.Net.HttpStatusCode.OK)
+                                        
+                                        if (teacher!=null && schoolTeacherResponse.StatusCode==System.Net.HttpStatusCode.OK)
                                         {
-                                            var teacher = JsonDocument.Parse(teacherResponse.Content).RootElement.Deserialize<Teacher>();
                                             var schoolTeacher = JsonDocument.Parse(schoolTeacherResponse.Content).RootElement.Deserialize<SchoolTeacher>();
                                             //同意
                                             if (notifyData.notifyEvent == 1)
@@ -325,12 +326,12 @@ namespace TEAMModelOS.Controllers
                                       .ReadItemStreamAsync($"{opttmdid}", new PartitionKey($"Base"));
                                 if (optTeacherResponse.StatusCode == HttpStatusCode.OK && schoolTeacherResponse.StatusCode==System.Net.HttpStatusCode.OK)
                                 {
-                                    var teacher = JsonDocument.Parse(optTeacherResponse.Content).RootElement.Deserialize<Teacher>();
+                                    var optteacher = JsonDocument.Parse(optTeacherResponse.Content).RootElement.Deserialize<Teacher>();
                                     var schoolTeacher = JsonDocument.Parse(schoolTeacherResponse.Content).RootElement.Deserialize<SchoolTeacher>();
                                     //同意
                                     if (notifyData.notifyEvent == 1)
                                     {
-                                        teacher.schools.ForEach(school =>
+                                        optteacher.schools.ForEach(school =>
                                         {
                                             if (school.schoolId.Equals($"{_schoolId}"))
                                             {
@@ -341,7 +342,7 @@ namespace TEAMModelOS.Controllers
                                         try { 
 
                                         await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.School).ReplaceItemAsync(schoolTeacher, schoolTeacher.id, new PartitionKey(schoolTeacher.code));
-                                        await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher).ReplaceItemAsync(teacher, teacher.id, new PartitionKey(teacher.code));
+                                        await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher).ReplaceItemAsync(optteacher, optteacher.id, new PartitionKey(optteacher.code));
                                         }
                                         catch (CosmosException ex) when (ex.StatusCode == HttpStatusCode.NotFound)
                                         {
@@ -356,12 +357,12 @@ namespace TEAMModelOS.Controllers
                                     if (notifyData.notifyEvent == 2)
                                     {
                                         //只有在未正式加入成功才能拒绝成功,防止HiTA消息未清除,再次操作。
-                                        count = teacher.schools.RemoveAll(z => z.schoolId.Equals($"{_schoolId}") && (z.status.Equals("request") || z.status.Equals("invite")));
+                                        count = optteacher.schools.RemoveAll(z => z.schoolId.Equals($"{_schoolId}") && (z.status.Equals("request") || z.status.Equals("invite")));
                                         if (count >0)
                                         {
                                             try {
                                             await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.School).DeleteItemStreamAsync($"{opttmdid}", new PartitionKey($"Teacher-{_schoolId}"));
-                                            await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher).ReplaceItemAsync(teacher, teacher.id, new PartitionKey(teacher.code));
+                                            await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher).ReplaceItemAsync(optteacher, optteacher.id, new PartitionKey(optteacher.code));
                                             }
                                             catch (CosmosException ex) when (ex.StatusCode == HttpStatusCode.NotFound)
                                             {