CrazyIter_Bin 3 months ago
parent
commit
6c453ff3a6

+ 86 - 39
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Controllers/ManageController.cs

@@ -15,6 +15,8 @@ using System.Net.Http;
 using System.Net.Http.Json;
 using System.Text.Json;
 using System.Text.Json.Nodes;
+using System.Text.RegularExpressions;
+using static System.Reflection.Metadata.BlobBuilder;
 
 namespace IES.ExamServer.Controllers
 {
@@ -57,7 +59,7 @@ namespace IES.ExamServer.Controllers
             return Ok();
         }
         [HttpPost("download-package")]
-        [AuthToken("admin","teacher", "visitor")]
+        [AuthToken("admin", "teacher", "visitor")]
         public async Task<IActionResult> DownloadPackage(JsonNode json)
         {
             //C#.NET 6 后端与前端流式通信
@@ -77,13 +79,14 @@ namespace IES.ExamServer.Controllers
              */
             //如果要访问中心,则需要教师登录联网。  
             var token = GetAuthTokenInfo();
-            if (token.scope.Equals(ExamConstant.ScopeTeacher))
+            if (token.scope.Equals(ExamConstant.ScopeTeacher) || token.scope.Equals(ExamConstant.ScopeVisitor))
             {
-                if (_connectionService.centerIsConnected) 
+                if (_connectionService.centerIsConnected)
                 {
                     Teacher? teacher = _liteDBFactory.GetLiteDatabase().GetCollection<Teacher>().FindOne(x => x.id!.Equals(token.id));
                     string id = $"{json["evaluationId"]}";
-                    EvaluationClient evaluationClient= _liteDBFactory.GetLiteDatabase().GetCollection<EvaluationClient>().FindOne(x => x.id!.Equals(id));
+                    string shortCode = $"{json["shortCode"]}";
+                    EvaluationClient? evaluationClient = _liteDBFactory.GetLiteDatabase().GetCollection<EvaluationClient>().FindOne(x => x.id!.Equals(id) && x.shortCode!.Equals(shortCode));
                     if (teacher != null && evaluationClient!= null)
                     {
                         string? CenterUrl = _configuration.GetValue<string>("ExamServer:CenterUrl");
@@ -109,17 +112,16 @@ namespace IES.ExamServer.Controllers
                                 url = $"{jsonNode["url"]}";
                             }
                         }
-                        var httpClient= _httpClientFactory.CreateClient();
+                        var httpClient = _httpClientFactory.CreateClient();
                         string packagePath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "package");
                         string evaluationPath = Path.Combine(packagePath, evaluationClient.id!);
                         string evaluationDataPath = Path.Combine(evaluationPath, "data");
-                        if (!Directory.Exists(evaluationDataPath)) 
+                        if (!Directory.Exists(evaluationDataPath))
                         {
                             Directory.CreateDirectory(evaluationDataPath);
                         }
-                        
                         string evaluationData = string.Empty;
-                        if ($"{json["data"]}".Equals("1")) 
+                        if ($"{json["data"]}".Equals("1"))
                         {
                             {
                                 string evaluationUrl = $"{url}/{cnt}/package/{json["evaluationId"]}/data/evaluation.json?{sas}";
@@ -129,7 +131,7 @@ namespace IES.ExamServer.Controllers
                                 string path_evaluation = Path.Combine(evaluationDataPath, "evaluation.json");
                                 await System.IO.File.WriteAllTextAsync(path_evaluation, content);
 
-                               
+
                             }
                             {
                                 //source.json
@@ -143,14 +145,14 @@ namespace IES.ExamServer.Controllers
                         if ($"{json["groupList"]}".Equals("1"))
                         {
                             string grouplistUrl = $"{url}/{cnt}/package/{json["evaluationId"]}/data/grouplist.json?{sas}";
-                            HttpResponseMessage  groupListMessage = await httpClient.GetAsync(grouplistUrl);
+                            HttpResponseMessage groupListMessage = await httpClient.GetAsync(grouplistUrl);
                             var content = await groupListMessage.Content.ReadAsStringAsync();
                             string path_groupList = Path.Combine(evaluationDataPath, "grouplist.json");
                             await System.IO.File.WriteAllTextAsync(path_groupList, content);
                         }
                         if ($"{json["blob"]}".Equals("1"))
                         {
-                            if (string.IsNullOrEmpty(evaluationData)) 
+                            if (string.IsNullOrEmpty(evaluationData))
                             {
                                 string evaluationUrl = $"{url}/{cnt}/package/{json["evaluationId"]}/data/evaluation.json?{sas}";
                                 HttpResponseMessage dataMessage = await httpClient.GetAsync(evaluationUrl);
@@ -167,7 +169,8 @@ namespace IES.ExamServer.Controllers
                                     {
                                         Directory.CreateDirectory(path_paper);
                                     }
-                                    var parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = 50 };
+                                    //最多开启10个线程并行下载
+                                    var parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = 10 };
                                     // 使用 Parallel.ForEachAsync 并行处理每个 blob
                                     await Parallel.ForEachAsync(evaluationPaper.blobs, parallelOptions, async (blob, cancellationToken) =>
                                     {
@@ -179,12 +182,21 @@ namespace IES.ExamServer.Controllers
                                             {
                                                 byte[] bytes = await blobMessage.Content.ReadAsByteArrayAsync(cancellationToken);
                                                 string? fileName = Path.GetFileName(blob.path);
-                                                if (fileName != null)
+                                                string? fileNameWithoutExtension = Path.GetFileNameWithoutExtension(blob.path);
+                                                string? extension = Path.GetExtension(blob.path);
+                                                if (extension!=null)
                                                 {
-                                                    await System.IO.File.WriteAllBytesAsync(Path.Combine(path_paper, fileName), bytes, cancellationToken);
+                                                    if (extension.Equals(extension.ToUpper()))
+                                                    {
+                                                        await System.IO.File.WriteAllBytesAsync(Path.Combine(path_paper, $"{fileNameWithoutExtension!}_1{extension}"), bytes, cancellationToken);
+                                                    }
+                                                    else
+                                                    {
+                                                        await System.IO.File.WriteAllBytesAsync(Path.Combine(path_paper, fileName!), bytes, cancellationToken);
+                                                    }
                                                 }
                                             }
-                                            else 
+                                            else
                                             {
                                                 string? error = await blobMessage.Content.ReadAsStringAsync(cancellationToken);
                                                 Console.WriteLine($"Error downloading {blob.path},{blobMessage.StatusCode},{error}");
@@ -224,11 +236,34 @@ namespace IES.ExamServer.Controllers
                             //    }
                             //}
                         }
+
                         //下载完成后,对数据进行检查,然后在加密压缩。
+                        string zipPath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "zip");
+                        if (!Directory.Exists(zipPath))
+                        {
+                            Directory.CreateDirectory(zipPath);
+                        }
+                        //判断文件包的压缩包是否存在。
+                        string zipFilePath = Path.Combine(zipPath, $"{evaluationClient.id}-{evaluationClient.blobHash}.zip");
+                        ZipHelper.CreatePasswordProtectedZip(evaluationPath, zipFilePath, evaluationClient.openCode!);
+                        return Ok(new { code = 200, msg = "下载成功!", url = zipFilePath });
+                    }
+                    else
+                    {
+                        string teacherMsg = teacher==null ? "未找到教师信息" : "";
+                        string evaluationMsg = evaluationClient==null ? "未找到评测信息" : "";
+                        return Ok(new { code = 1, msg = $"用户信息或未找到评测信息!{teacherMsg}{evaluationMsg}" });
                     }
                 }
+                else { 
+                    return Ok(new { code = 2, msg = "云端数据中心未连接" });
+                }
+            }
+            else
+            {
+                return Ok(new { code = 3, msg = "请使用教师或访客账号登录!" });
             }
-           return Ok();
+           
         }
         [HttpPost("check-evaluation")]
         [AuthToken("admin", "teacher", "visitor")]
@@ -417,12 +452,14 @@ namespace IES.ExamServer.Controllers
                 //校验本地文件数据
                 string packagePath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "package");
                 string zipPath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "zip");
+                string evaluationPath = Path.Combine(packagePath, evaluationLocal.id!);
                 if (!Directory.Exists(packagePath))
                 {
                     Directory.CreateDirectory(packagePath);
                 }
+               
                 //判断文件包的压缩包是否存在。
-                if (!System.IO.File.Exists(Path.Combine(zipPath, $"{evaluationLocal.id}-{evaluationLocal}.zip")))
+                if (!System.IO.File.Exists(Path.Combine(zipPath, $"{evaluationLocal.id}-{evaluationLocal.blobHash}.zip")))
                 {
                     blob=1;//压缩包不存在
                     msg_status=Constant._Message_status_error;
@@ -434,8 +471,9 @@ namespace IES.ExamServer.Controllers
                     msg_status=Constant._Message_status_success;
                     checkTotal++;
                     checkSuccess++;
+                    FileHelper.DeleteFolder(evaluationPath);
                     //解压操作。
-                   var extractRes=  ZipHelper.ExtractPasswordProtectedZip(Path.Combine(zipPath, $"{evaluationLocal.id}-{evaluationLocal}.zip"), packagePath, evaluationLocal.openCode!);
+                    var extractRes = ZipHelper.ExtractPasswordProtectedZip(Path.Combine(zipPath, $"{evaluationLocal.id}-{evaluationLocal.blobHash}.zip"), evaluationPath, evaluationLocal.openCode!);
                     if (extractRes.res)
                     {
                         checkTotal++;
@@ -449,8 +487,6 @@ namespace IES.ExamServer.Controllers
                         checkError++;
                     }
                 }
-
-                string evaluationPath = Path.Combine(packagePath, evaluationLocal.id!);
                 string evaluationDataPath = Path.Combine(evaluationPath, "data");
                 
                 string path_groupList = Path.Combine(evaluationDataPath, "groupList.json");
@@ -559,7 +595,7 @@ namespace IES.ExamServer.Controllers
                             }
                             else
                             {
-
+                                string pattern = @"paper/[^/]+/([^/]+/[^/]+\.[^/]+)";
                                 foreach (var evaluationExam in evaluationExams!)
                                 {
                                     string path_papers = Path.Combine(evaluationPath, "papers");
@@ -575,19 +611,40 @@ namespace IES.ExamServer.Controllers
                                             msg_status=Constant._Message_status_info;
                                             if (!string.IsNullOrWhiteSpace(blobInfo.path))
                                             {
+                                                // exam/57de4dfe-944e-6d5a-98bb-6803b386f8c1/paper/subject_painting/4cce6c82-5e17-72e7-8e53-a2c45354b4a1/0abe9a37-90f8-baa9-f188-3f19574a880b.json
+                                               
 
-                                                var file = papers_files.Find(x => x.Contains(blobInfo.path));
-                                                if (file!=null)
+                                                Match match = Regex.Match(blobInfo.path, pattern);
+                                                if (match.Success)
                                                 {
-                                                    msg_status=1;
-                                                    msg_status=Constant._Message_status_success;
+                                                    string result = match.Groups[1].Value.Replace("/", "\\");
+                                                    var file = papers_files.Find(x => x.Contains(result));
+                                                    if (file!=null)
+                                                    {
+                                                        msg_status=1;
+                                                        msg_status=Constant._Message_status_success;
+                                                    }
+                                                    else
+                                                    {
+                                                        msg_status=Constant._Message_status_error;
+                                                        paper_error_count++;
+                                                    }
+
+                                                    //if (result.EndsWith("mp4", StringComparison.OrdinalIgnoreCase)) 
+                                                    //{
+                                                    //    Console.WriteLine(result); // 输出: 4cce6c82-5e17-72e7-8e53-a2c45354b4a1/0abe9a37-90f8-baa9-f188-3f19574a880b.json
+                                                    //}
+
                                                 }
-                                                else
+                                                else 
                                                 {
                                                     msg_status=Constant._Message_status_error;
                                                     paper_error_count++;
                                                 }
 
+
+                                              
+
                                             }
                                             else
                                             {
@@ -659,18 +716,7 @@ namespace IES.ExamServer.Controllers
                             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,
@@ -704,8 +750,9 @@ namespace IES.ExamServer.Controllers
                 });
             return Ok(new
             {
+                finalStatus,
                 code = 200,
-                evaluation = evaluationLocal,
+                //evaluation = evaluationLocal,
                 data,
                 blob,
                // webview,

+ 31 - 0
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Helpers/FileHelper.cs

@@ -2,6 +2,37 @@
 {
     public class FileHelper
     {
+        /// <summary>
+        /// 删除整个文件夹及其内容
+        /// </summary>
+        /// <param name="folderPath"></param>
+        /// <returns></returns>
+        public static bool DeleteFolder(string folderPath) 
+        {
+            bool result = false;
+            try
+            {
+                if (Directory.Exists(folderPath))
+                {
+                    // 删除文件夹及其所有内容
+                    Directory.Delete(folderPath, true);
+                    result = true;
+                    //Console.WriteLine($"Folder '{folderPath}' deleted successfully.");
+                }
+                else
+                {
+                   //Console.WriteLine($"Folder '{folderPath}' does not exist.");
+                    result = true;
+                }
+            }
+            catch (Exception ex)
+            {
+                result = false;
+               //Console.WriteLine($"Error: {ex.Message}");
+            }
+            return result;
+
+        }
         /// <summary>
         /// 列出文件夹下的所有子文件及子文件夹的子文件
         /// </summary>

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

@@ -59,7 +59,7 @@ namespace IES.ExamServer.Helpers
                 using (FileStream fsOut = File.Create(zipFilePath))
                 using (ZipOutputStream zipStream = new ZipOutputStream(fsOut))
                 {
-                    zipStream.SetLevel(9); // 设置压缩级别 (0-9)
+                    zipStream.SetLevel(0); // 设置压缩级别 (0-9)
                     zipStream.Password = password; // 设置密码
 
                     CompressFolder(sourceDirectory, zipStream, "");

+ 131 - 0
TEAMModelOS/Controllers/Both/EvaluationSyncInfoController.cs

@@ -57,7 +57,138 @@ namespace TEAMModelOS.Controllers.Both
             _azureRedis = azureRedis; _coreAPIHttpService = coreAPIHttpService;
             _httpClientFactory = httpClientFactory;
         }
+        /// <summary>
+        /// 局域网端搜寻云端评测
+        /// </summary>
+        /// <param name="request"></param>
+        /// <returns></returns>
+        [ProducesDefaultResponseType]
+       
+        [HttpPost("download")]
+        //#if !DEBUG
+        //        [Authorize(Roles = "IES")]
+        //#endif
+        public async Task<IActionResult> Download(JsonNode json)
+        {
+            string shortCode = $"{json["shortCode"]}";
+            string evaluationId = $"{json["evaluationId"]}";
+
+            if (string.IsNullOrWhiteSpace(shortCode))
+            {
+
+                return Ok(new { code = 1, msg = "提取码为空!" });
+            }
+            
+            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 Ok(new { code = 3, msg = "提取码匹配到多个评测,需要管理员处理!" });
+                }
+                return Ok(new { code = 2, msg = "未找到评测!" });
+            }
+
+            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"));
+            }
+
+
+            EvaluationClient evaluationClient = null;
+            try
+            {
+                BlobDownloadResult downloadResult = await _azureStorage.GetBlobContainerClient(evaluationSyncInfo.ownerId).GetBlobClient($"package/{evaluationSyncInfo.id}/data/evaluation.json").DownloadContentAsync();
+                var evaluationData = JsonDocument.Parse(downloadResult.Content).RootElement;
+                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 Ok(new { code = 6, msg = "数据包校验异常!" });
+                }
+                if (!evaluationClient.blobHash.Equals(evaluationSyncInfo.blobHash))
+                {
+                    await _dingDing.SendBotMsg($"评测{evaluationSyncInfo.id}数据同步信息的blobHash与evaluation.json文件中的不一致,请检查,或重新生成。", GroupNames.成都开发測試群組);
+                    return Ok(new { code = 7, msg = "文件包校验异常!" });
+                }
+
+                if (listInfo.change)
+                {
 
+                    evaluationClient.grouplistHash=listInfo.newGrouplistHash;
+                    evaluationClient.studentCount=evaluationSyncInfo.studentCount;
+                    evaluationClient.grouplist=evaluationSyncInfo.grouplist;
+                    await _azureStorage.GetBlobContainerClient(evaluationClient.ownerId).UploadFileByContainer(new { evaluationClient, evaluationExams }.ToJsonString(), $"package/{evaluationSyncInfo.id}/data", "evaluation.json");
+                }
+                else
+                {
+                    if (!evaluationClient.grouplistHash.Equals(evaluationSyncInfo.grouplistHash))
+                    {
+                        await _dingDing.SendBotMsg($"评测{evaluationSyncInfo.id}数据同步信息的grouplistHash与文件中的不一致,请检查,或重新生成。", GroupNames.成都开发測試群組);
+                        return Ok(new { code = 8, msg = "名单校验异常!" });
+                    }
+                }
+            }
+            catch (Exception ex)
+            {
+                await _dingDing.SendBotMsg($"评测{evaluationSyncInfo.id}数据同步信息的文件异常,evaluation.json,{ex.Message}\n{ex.StackTrace}", GroupNames.成都开发測試群組);
+                return Ok(new { code = 9, msg = "评测数据文件检测异常!" });
+            }
+            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($"package/{evaluationSyncInfo.id}/data/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 Ok(new { code = 10, msg = "名单与数据包的名单校验不一致!" });
+                    }
+                }
+            }
+            catch (Exception ex)
+            {
+                await _dingDing.SendBotMsg($"评测{evaluationSyncInfo.id}数据同步信息的文件异常,grouplist.json,{ex.Message}\n{ex.StackTrace}", GroupNames.成都开发測試群組);
+                return Ok(new { code = 11, msg = "评测名单文件检测异常!" });
+            }
+            var  blobSas=   _azureStorage.GetContainerSasUri($"{evaluationClient.ownerId}");
+            evaluationClient.openCode=null;
+            return Ok(new { code = 200, msg = "检测成功!", evaluation = evaluationClient, blobSas.url, blobSas.name, blobSas.sas });
+        }
         /// <summary>
         /// 局域网端搜寻云端评测
         /// </summary>