using IES.ExamLib.Models; using IES.ExamServer.DI; using IES.ExamServer.DI.SignalRHost; using IES.ExamServer.Filters; using IES.ExamServer.Helper; using IES.ExamServer.Helpers; using IES.ExamServer.Models; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Configuration; using System.Linq.Expressions; using System.Net.Http; using System.Text.Json; using System.Text.Json.Nodes; namespace IES.ExamServer.Controllers { [ApiController] [Route("manage")] public class ManageController:BaseController { private readonly IConfiguration _configuration; private readonly IHttpClientFactory _httpClientFactory; private readonly IMemoryCache _memoryCache; private readonly ILogger _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) { _logger = logger; _configuration=configuration; _httpClientFactory=httpClientFactory; _memoryCache=memoryCache; _liteDBFactory=liteDBFactory; _connectionService=connectionService; _signalRExamServerHub=signalRExamServerHub; } [HttpPost("download-package")] [AuthToken("admin","teacher")] public async Task 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... //进度条 展示下载文件总大小和已下载,末尾展示 文件总个数和已下载个数 //https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/8.0.7/signalr.min.js return Ok(); } [HttpPost("check-short-code")] public async Task CheckShortCode(JsonNode json) { string shortCode = $"{json["shortCode"]}"; string evaluationId = $"{json["evaluationId"]}"; string deviceId = $"{json["deviceId"]}"; Expression> predicate = x => true; if (!string.IsNullOrEmpty(shortCode)) { var codePredicate = ExpressionHelper.Or( x => !string.IsNullOrWhiteSpace(x.shortCode) && x.shortCode == shortCode, x => !string.IsNullOrWhiteSpace(x.password) && x.password == shortCode ); predicate= predicate.And(codePredicate); } else { return Ok(new { code = 400,msg="必须输入开卷码" }); } if (!string.IsNullOrWhiteSpace(evaluationId)) { predicate= predicate.And(x => x.id!.Equals(evaluationId)); } IEnumerable evaluationClients = _liteDBFactory.GetLiteDatabase().GetCollection().Find(predicate); EvaluationClient? evaluationLocal = null; EvaluationClient? evaluationCloud = null; if (evaluationClients.Count()>0) { evaluationLocal= evaluationClients.First(); } //如果要访问中心,则需要教师登录联网。 var token = GetAuthTokenInfo(); if (token.scope.Equals(ExamConstant.ScopeTeacher)) { if ( _connectionService.dataCenterIsConnected) { Teacher teacher= _liteDBFactory.GetLiteDatabase().GetCollection().FindOne(x => x.id!.Equals(token.id)); string? CenterUrl = _configuration.GetValue("ExamServer:CenterUrl"); var client = _httpClientFactory.CreateClient(); if (client.DefaultRequestHeaders.Contains(Constant._X_Auth_AuthToken)) { client.DefaultRequestHeaders.Remove(Constant._X_Auth_AuthToken); } client.DefaultRequestHeaders.Add(Constant._X_Auth_AuthToken, teacher.x_auth_token); HttpResponseMessage message = await client.PostAsJsonAsync($"{CenterUrl}/evaluation-sync/find-sync-info",new { shortCode, evaluationId }); if (message.IsSuccessStatusCode) { string content = await message.Content.ReadAsStringAsync(); JsonNode? jsonNode =content.ToObject(); if (jsonNode!=null) { evaluationCloud=jsonNode["evaluation"]?.ToObject(); } } } } //数据,文件,页面 0 没有更新,1 有更新 int data = 0,blob=0,webview=0, groupList=0, status=0; long dataSize = 0, blobSize=0 , webviewSize=0, studentCount=0; if (evaluationLocal== null && evaluationCloud==null) { //线上线下没有数据 status=1; } else if (evaluationLocal!=null && evaluationCloud!=null) { //线上线下有数据 status = 2; if ((!string.IsNullOrWhiteSpace(evaluationLocal.blobHash) && !evaluationLocal.blobHash.Equals(evaluationCloud.blobHash)) ||(evaluationLocal.blobTime().Insert(evaluationLocal); } List file_error = new List(); if (evaluationLocal!=null) { 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="开始检查评测信息文件.." }); //校验本地文件数据 string packagePath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "package"); if (!Directory.Exists(packagePath)) Directory.CreateDirectory(packagePath); string evaluationPath = Path.Combine(packagePath, evaluationLocal.id!); // await Task.Delay(DelayMacro); int msg_status = Constant._Message_status_info; string path_evaluation = Path.Combine(evaluationPath, "evaluation.json"); if (!System.IO.File.Exists(path_evaluation)) { file_error.Add("evaluation"); msg_status=Constant._Message_status_error; } else { msg_status=Constant._Message_status_success; //string jsonData = await System.IO.File.ReadAllTextAsync(path_evaluation); //EvaluationClient? evaluationFile =jsonData.ToObject(); //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; //} } //数据格式: [消息][信息/错误/警告][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, 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"); msg_status =Constant._Message_status_info; if (!System.IO.File.Exists(path_groupList)) { file_error.Add("groupList"); msg_status=Constant._Message_status_error; } else { msg_status=Constant._Message_status_success; } await _signalRExamServerHub.SendMessage(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"); msg_status = Constant._Message_status_info; if (!System.IO.File.Exists(path_source)) { file_error.Add("source"); msg_status=Constant._Message_status_error; } else { msg_status=Constant._Message_status_success; } await _signalRExamServerHub.SendMessage(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; try { string evaluation_str = await System.IO.File.ReadAllTextAsync(path_evaluation); JsonNode? evaluation_data = evaluation_str.ToObject(); if (evaluation_data!=null) { EvaluationClient? evaluationClient = evaluation_data["evaluationClient"]?.ToObject(); if (evaluationClient!=null) { if ((!string.IsNullOrWhiteSpace(evaluationLocal.blobHash) && evaluationLocal.blobHash.Equals(evaluationClient.blobHash)) &&(evaluationLocal.blobTime==evaluationClient.blobTime) &&(evaluationLocal.blobCount== evaluationClient.blobCount) &&(evaluationLocal.blobSize== evaluationClient.blobSize)&& (evaluationLocal.dataTime==evaluationClient.dataTime) &&(evaluationLocal.dataSize==evaluationClient.dataSize)&&(evaluationLocal.webviewCount==evaluationClient.webviewCount) &&(evaluationLocal.webviewSize== evaluationClient.webviewSize) &&(evaluationLocal.webviewTime== evaluationClient.webviewTime) &&(!string.IsNullOrWhiteSpace(evaluationLocal.webviewPath)&& evaluationLocal.webviewPath.Equals(evaluationClient.webviewPath))) { msg_status=1; } 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="校验本地数据文件..." }); } List? evaluationExams = evaluation_data["evaluationExams"]?.ToObject>(); if (evaluationExams.IsNotEmpty()) { string path_papers = Path.Combine(evaluationPath, "papers"); var papers_files = FileHelper.ListAllFiles(path_papers); foreach (var evaluationExam in evaluationExams!) { int paperIndex = 0; foreach (var paper in evaluationExam.papers) { paperIndex++; List contents = new List(); int paper_error_count = 0; foreach (var blobInfo in paper.blobs) { msg_status=Constant._Message_status_info; if (!string.IsNullOrWhiteSpace(blobInfo.path)) { var file = papers_files.Find(x => x.Contains(blobInfo.path)); if (file!=null) { msg_status=1; msg_status=Constant._Message_status_success; } else { msg_status=Constant._Message_status_error; paper_error_count++; } } else { msg_status=Constant._Message_status_warning; ; paper_error_count++; } contents.Add(new MessageContent { dataId=evaluationLocal.id, dataName=evaluationLocal.name, messageType=Constant._Message_type_check, status=msg_status, content=$"试卷文件信息:{paper.paperName}" }); } int paper_msg_status = Constant. _Message_status_info; if (paper_error_count>0) { paper_msg_status=Constant._Message_status_error; } else { paper_msg_status=Constant._Message_status_success; } await _signalRExamServerHub.SendMessage(deviceId, Constant._Message_grant_type_check_file, new MessageContent { dataId=evaluationLocal.id, dataName=evaluationLocal.name, messageType=Constant._Message_type_message, status=paper_msg_status, content=$"试卷名称:[{paperIndex}]{evaluationExam.examName}-{evaluationExam.subjectName}-{paper.paperName}\r\n文件数量:{paper.blobs.Count()},检测成功数量:{contents.Count(x => x.status==Constant._Message_status_success)},检测异常数量{contents.Count(x => x.status==Constant._Message_status_error)}", contents=contents }); } } } } } 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="提取评测数据文件..." }); } return Ok(new {code=200, evaluation= evaluationLocal,data,blob,webview,dataSize,blobSize,webviewSize,status ,groupList,studentCount}); } /// /// 激活考试 /// /// /// [HttpPost("activate-evaluation")] public IActionResult ActivateEvaluation(JsonNode json) { string id = $"{json["id"]}"; string shortCode = $"{json["shortCode"]}"; if (!string.IsNullOrWhiteSpace(id) && !string.IsNullOrWhiteSpace(shortCode)) { EvaluationClient evaluationClient = _liteDBFactory.GetLiteDatabase().GetCollection().FindOne(x => x.id!.Equals(id) && !string.IsNullOrWhiteSpace(x.shortCode) && x.shortCode.Equals(shortCode)); if (evaluationClient != null) { } } return Ok(); } /// /// 加载本地的活动列表 /// /// /// [HttpPost("list-local-evaluation")] public IActionResult ListLocalEvaluation(JsonNode json) { IEnumerable evaluationClients = _liteDBFactory.GetLiteDatabase().GetCollection().FindAll().OrderByDescending(x=>x.activate).ThenByDescending(x=>x.stime); var result = evaluationClients.Select(client => { var properties = client.GetType().GetProperties(); var anonymousObject = new Dictionary(); foreach (var property in properties) { if (!property.Name .Equals("password") && !property.Name.Equals("shortCode")) { anonymousObject[property.Name] = property.GetValue(client); } } return anonymousObject; }); return Ok(new {code=200, evaluation= result }); } } }