ManageService.cs 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448
  1. using IES.ExamServer.DI;
  2. using IES.ExamServer.DI.SignalRHost;
  3. using IES.ExamServer.Helper;
  4. using IES.ExamServer.Helpers;
  5. using IES.ExamServer.Models;
  6. using Microsoft.AspNetCore.SignalR;
  7. using Microsoft.Extensions.Caching.Memory;
  8. using System.IO;
  9. using System.Net.Http;
  10. using System.Text.Json.Nodes;
  11. using System.Text.RegularExpressions;
  12. namespace IES.ExamServer.Services
  13. {
  14. public class ManageService
  15. {
  16. public async static Task<(EvaluationClient? evaluationCloud, string centerCode, string centerMsg)> GetEvaluationFromCenter(string? x_auth_token, IConfiguration _configuration,IHttpClientFactory _httpClientFactory, string shortCode, string evaluationId)
  17. {
  18. EvaluationClient? evaluationCloud = null;
  19. string centerCode = string.Empty, centerMsg = string.Empty;
  20. string? CenterUrl = _configuration.GetValue<string>("ExamServer:CenterUrl");
  21. var client = _httpClientFactory.CreateClient();
  22. if (client.DefaultRequestHeaders.Contains(Constant._X_Auth_AuthToken))
  23. {
  24. client.DefaultRequestHeaders.Remove(Constant._X_Auth_AuthToken);
  25. }
  26. client.DefaultRequestHeaders.Add(Constant._X_Auth_AuthToken, x_auth_token);
  27. try
  28. {
  29. HttpResponseMessage message = await client.PostAsJsonAsync($"{CenterUrl}/evaluation-sync/find-sync-info", new { shortCode, evaluationId });
  30. if (message.IsSuccessStatusCode)
  31. {
  32. string content = await message.Content.ReadAsStringAsync();
  33. JsonNode? jsonNode = content.ToObject<JsonNode>();
  34. if (jsonNode != null)
  35. {
  36. centerCode = $"{jsonNode["code"]}";
  37. centerMsg = $"{jsonNode["msg"]}";
  38. if ($"{jsonNode["code"]}".Equals("200"))
  39. {
  40. evaluationCloud = jsonNode["evaluation"]?.ToObject<EvaluationClient>();
  41. }
  42. }
  43. else
  44. {
  45. centerCode = "500";
  46. centerMsg = "数据转换异常";
  47. }
  48. }
  49. else
  50. {
  51. centerCode = $"{message.StatusCode}";
  52. centerMsg = "数据中心访问异常";
  53. }
  54. }
  55. catch (Exception ex)
  56. {
  57. centerCode = $"500";
  58. centerMsg = $"数据中心访问异常:{ex.Message}";
  59. }
  60. return (evaluationCloud, centerCode, centerMsg);
  61. }
  62. /// <summary>
  63. /// 检查本地评测文件,文件包,名单信息
  64. /// </summary>
  65. /// <param name="evaluationLocal"></param>
  66. /// <param name="evaluationCloud"></param>
  67. /// <param name="msgs"></param>
  68. /// <param name="_liteDBFactory"></param>
  69. /// <returns></returns>
  70. public async static Task<(List<string> successMsgs, List<string> errorMsgs)> CheckFile(EvaluationClient evaluationLocal, List<string> successMsgs,List<string> errorMsgs, IHubContext<SignalRExamServerHub> _signalRExamServerHub,
  71. IMemoryCache _memoryCache, ILogger _logger, string deviceId,string evaluationPath)
  72. {
  73. int msg_status = Constant._Message_status_info;
  74. string content = msg_status.Equals(Constant._Message_status_success) ? "成功" : "失败";
  75. if (!Directory.Exists(evaluationPath))
  76. {
  77. errorMsgs.Add($"评测目录不存在:{evaluationPath}");
  78. }
  79. else
  80. {
  81. string evaluationDataPath = Path.Combine(evaluationPath, "data");
  82. string path_groupList = Path.Combine(evaluationDataPath, "groupList.json");
  83. msg_status =Constant._Message_status_info;
  84. if (!System.IO.File.Exists(path_groupList))
  85. {
  86. msg_status=Constant._Message_status_error;
  87. }
  88. else
  89. {
  90. msg_status=Constant._Message_status_success;
  91. }
  92. await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, Constant._Message_grant_type_check_file,
  93. new MessageContent { dataId=evaluationLocal.id, dataName=evaluationLocal.name, messageType= Constant._Message_type_check, status=msg_status, content="评测名单文件(groupList.json)" });
  94. content = msg_status.Equals(Constant._Message_status_success) ? "成功" : "失败";
  95. if (msg_status.Equals(Constant._Message_status_success)|| msg_status.Equals(Constant._Message_status_info))
  96. {
  97. successMsgs.Add($"评测名单文件(groupList.json),检测结果:{content}");
  98. }
  99. else
  100. {
  101. errorMsgs.Add($"评测名单文件(groupList.json),检测结果:{content}");
  102. }
  103. string path_source = Path.Combine(evaluationDataPath, "source.json");
  104. msg_status = Constant._Message_status_info;
  105. if (!System.IO.File.Exists(path_source))
  106. {
  107. msg_status=Constant._Message_status_error;
  108. }
  109. else
  110. {
  111. msg_status=Constant._Message_status_success;
  112. }
  113. await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, Constant._Message_grant_type_check_file,
  114. new MessageContent { dataId=evaluationLocal.id, dataName=evaluationLocal.name, messageType= Constant._Message_type_check, status=msg_status, content="评测原始数据(source.json)" });
  115. content = msg_status.Equals(Constant._Message_status_success) ? "成功" : "失败";
  116. if (msg_status.Equals(Constant._Message_status_success)|| msg_status.Equals(Constant._Message_status_info))
  117. {
  118. successMsgs.Add($"评测原始数据(source.json),检测结果:{content}");
  119. }
  120. else
  121. {
  122. errorMsgs.Add($"评测原始数据(source.json),检测结果:{content}");
  123. }
  124. msg_status =Constant._Message_status_info;
  125. try
  126. {
  127. //TODO 重整本地文件路径。 文件可能不存在D:\VisualStudioProjects\TEAMModelOS\TEAMModelOS.Extension\IES.Exam\IES.ExamServer\wwwroot\package\exam\6af32bbd-144e-4366-8bc0-61ba4c85677c\evaluation.json
  128. string path_evaluation = Path.Combine(evaluationDataPath, "evaluation.json");
  129. if (!System.IO.File.Exists(path_evaluation))
  130. {
  131. msg_status=Constant._Message_status_error;
  132. }
  133. else
  134. {
  135. msg_status=Constant._Message_status_success;
  136. }
  137. //数据格式: [消息][信息/错误/警告][15:43]=>[开始检查评测信息文件...]
  138. //数据格式: [检查][成功/失败][15:43]=>[评测数据文件:/wwwroot/package/623a9fe6-5445-0938-ff77-aeb80066ef27/evaluation.json]
  139. //数据格式: [下载][成功/失败][15:43]=>[评测数据文件:/wwwroot/package/623a9fe6-5445-0938-ff77-aeb80066ef27/evaluation.json][1024kb][15ms]
  140. await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, Constant._Message_grant_type_check_file,
  141. new MessageContent { dataId=evaluationLocal.id, dataName=evaluationLocal.name, messageType= Constant._Message_type_check, status=msg_status, content="评测数据文件(evaluation.json)" });
  142. content = msg_status.Equals(Constant._Message_status_success) ? "成功" : "失败";
  143. if (msg_status.Equals(Constant._Message_status_success)|| msg_status.Equals(Constant._Message_status_info))
  144. {
  145. successMsgs.Add($"评测数据文件(evaluation.json),检测结果:{content}");
  146. }
  147. else
  148. {
  149. errorMsgs.Add($"评测数据文件(evaluation.json),检测结果:{content}");
  150. }
  151. if (System.IO.File.Exists(path_evaluation))
  152. {
  153. string evaluation_str = await System.IO.File.ReadAllTextAsync(path_evaluation);
  154. JsonNode? evaluation_data = evaluation_str.ToObject<JsonNode>();
  155. if (evaluation_data==null)
  156. {
  157. msg_status=Constant._Message_status_error;
  158. await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, Constant._Message_grant_type_check_file,
  159. new MessageContent { dataId=evaluationLocal.id, dataName=evaluationLocal.name, messageType= Constant._Message_type_check, status=msg_status, content="评测数据文件(evaluation.json),文件读取失败!" });
  160. errorMsgs.Add("评测数据文件(evaluation.json),文件读取失败!");
  161. }
  162. else
  163. {
  164. EvaluationClient? evaluationClient = evaluation_data["evaluationClient"]?.ToObject<EvaluationClient>();
  165. if (evaluationClient!=null)
  166. {
  167. await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, Constant._Message_grant_type_check_file,
  168. new MessageContent { dataId=evaluationLocal.id, dataName=evaluationLocal.name, messageType=Constant._Message_type_message, status=msg_status, content="评测数据文件(evaluation.json),读取评测基本信息..." });
  169. successMsgs.Add($"评测数据文件(evaluation.json),读取评测基本信息...");
  170. if (!string.IsNullOrWhiteSpace(evaluationLocal.blobHash) && evaluationLocal.blobHash.Equals(evaluationClient.blobHash)
  171. &&(evaluationLocal.blobTime==evaluationClient.blobTime)
  172. &&(evaluationLocal.blobCount== evaluationClient.blobCount)
  173. &&(evaluationLocal.blobSize== evaluationClient.blobSize)
  174. && (evaluationLocal.dataTime==evaluationClient.dataTime)
  175. &&(evaluationLocal.dataSize==evaluationClient.dataSize)
  176. )
  177. {
  178. msg_status=Constant._Message_status_info;
  179. }
  180. else
  181. {
  182. msg_status=Constant._Message_status_error;
  183. }
  184. await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, Constant._Message_grant_type_check_file,
  185. new MessageContent { dataId=evaluationLocal.id, dataName=evaluationLocal.name, messageType=Constant._Message_type_message, status=msg_status, content="评测数据文件(evaluation.json),校验评测基本信息..." });
  186. content = msg_status.Equals(Constant._Message_status_success)||msg_status.Equals(Constant._Message_status_info) ? "成功" : "失败";
  187. if (msg_status.Equals(Constant._Message_status_success)|| msg_status.Equals(Constant._Message_status_info))
  188. {
  189. successMsgs.Add($"评测数据文件(evaluation.json),校验评测基本信息,检测结果:{content}");
  190. }
  191. else
  192. {
  193. errorMsgs.Add($"评测数据文件(evaluation.json),校验评测基本信息,检测结果:{content}"); ;
  194. }
  195. }
  196. else
  197. {
  198. msg_status=Constant._Message_status_error;
  199. await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, Constant._Message_grant_type_check_file,
  200. new MessageContent { dataId=evaluationLocal.id, dataName=evaluationLocal.name, messageType= Constant._Message_type_check, status=msg_status, content="评测数据文件(evaluation.json),读取评测基本信息失败!" });
  201. errorMsgs.Add("评测数据文件(evaluation.json),读取评测基本信息失败!");
  202. }
  203. List<EvaluationExam>? evaluationExams = evaluation_data["evaluationExams"]?.ToObject<List<EvaluationExam>>();
  204. if (evaluationExams.IsEmpty())
  205. {
  206. msg_status=Constant._Message_status_error;
  207. content = msg_status.Equals(Constant._Message_status_success)||msg_status.Equals(Constant._Message_status_info) ? "成功" : "失败";
  208. errorMsgs.Add($"评测数据文件(evaluation.json),读取评测试卷信息失败");
  209. }
  210. else
  211. {
  212. msg_status=Constant._Message_status_info;
  213. await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, Constant._Message_grant_type_check_file,
  214. new MessageContent { dataId=evaluationLocal.id, dataName=evaluationLocal.name, messageType=Constant._Message_type_message, status=msg_status, content="评测数据文件(evaluation.json),读取评测试卷信息..." });
  215. successMsgs.Add($"评测数据文件(evaluation.json),读取评测试卷信息...");
  216. string pattern = @"paper/[^/]+/([^/]+/[^/]+\.[^/]+)";
  217. foreach (var evaluationExam in evaluationExams!)
  218. {
  219. msg_status=Constant._Message_status_info;
  220. await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, Constant._Message_grant_type_check_file,
  221. new MessageContent { dataId=evaluationLocal.id, dataName=evaluationLocal.name, messageType=Constant._Message_type_message, status=msg_status, content=$"校验评测科目试卷:{evaluationExam.subjectName}-{evaluationExam.examName}" });
  222. successMsgs.Add($"校验评测科目试卷:{evaluationExam.subjectName}-{evaluationExam.examName}");
  223. string path_papers = Path.Combine(evaluationPath, "papers");
  224. var papers_files = FileHelper.ListAllFiles(path_papers);
  225. int paperIndex = 0;
  226. foreach (var paper in evaluationExam.papers)
  227. {
  228. paperIndex++;
  229. List<MessageContent> contents = new List<MessageContent>();
  230. int blob_error_count = 0;
  231. foreach (var blobInfo in paper.blobs)
  232. {
  233. msg_status=Constant._Message_status_info;
  234. if (!string.IsNullOrWhiteSpace(blobInfo.path))
  235. {
  236. Match match = Regex.Match(blobInfo.path, pattern);
  237. if (match.Success)
  238. {
  239. string result = match.Groups[1].Value.Replace("/", "\\");
  240. string extension = Path.GetExtension(result);
  241. if (extension.Equals(extension.ToUpper()))
  242. {
  243. string fileName = Path.GetFileNameWithoutExtension(result);
  244. result= $"{fileName!}_1{extension}";
  245. }
  246. var file = papers_files.Find(x => x.Contains(result));
  247. if (file!=null)
  248. {
  249. msg_status=1;
  250. msg_status=Constant._Message_status_success;
  251. }
  252. else
  253. {
  254. msg_status=Constant._Message_status_error;
  255. blob_error_count++;
  256. errorMsgs.Add($"试卷名称:[{paperIndex}]{evaluationExam.examName}-{evaluationExam.subjectName}-{paper.paperName},未匹配到本地文件:{blobInfo.path}");
  257. }
  258. }
  259. else
  260. {
  261. msg_status=Constant._Message_status_error;
  262. blob_error_count++;
  263. errorMsgs.Add($"试卷名称:[{paperIndex}]{evaluationExam.examName}-{evaluationExam.subjectName}-{paper.paperName},未提取到正则匹配的文件:{blobInfo.path}");
  264. }
  265. }
  266. else
  267. {
  268. msg_status=Constant._Message_status_error; ;
  269. blob_error_count++;
  270. errorMsgs.Add($"试卷名称:[{paperIndex}]{evaluationExam.examName}-{evaluationExam.subjectName}-{paper.paperName},文件路径为空:{blobInfo.ToJsonString()}");
  271. }
  272. contents.Add(new MessageContent
  273. {
  274. dataId=evaluationLocal.id,
  275. dataName=evaluationLocal.name,
  276. messageType=Constant._Message_type_check,
  277. status=msg_status,
  278. content=$"试卷文件信息:{paper.paperName}"
  279. });
  280. }
  281. int paper_msg_status = Constant._Message_status_info;
  282. if (blob_error_count>0)
  283. {
  284. paper_msg_status=Constant._Message_status_error;
  285. errorMsgs.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)}");
  286. }
  287. else
  288. {
  289. paper_msg_status=Constant._Message_status_success;
  290. 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)}");
  291. }
  292. await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, Constant._Message_grant_type_check_file,
  293. new MessageContent
  294. {
  295. dataId=evaluationLocal.id,
  296. dataName=evaluationLocal.name,
  297. messageType=Constant._Message_type_message,
  298. status=paper_msg_status,
  299. 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)}",
  300. contents=contents
  301. });
  302. }
  303. }
  304. }
  305. }
  306. }
  307. }
  308. catch (Exception e)
  309. {
  310. errorMsgs.Add($"校验评测试卷文件信息异常:{e.Message}");
  311. _logger.LogData<object>(new { code = 500, msg = e.Message, data = new { content = e.StackTrace } }, evaluationLocal.id!);
  312. }
  313. }
  314. return (successMsgs , errorMsgs);
  315. }
  316. public static (EvaluationCheckDataResult result, EvaluationClient? evaluationLocal) CheckData( EvaluationClient? evaluationLocal, EvaluationClient? evaluationCloud, List<string> successMsgs,List<string> errorMsgs, LiteDBFactory _liteDBFactory )
  317. {
  318. //数据,文件,页面 0 没有更新,1 有更新
  319. int data=0, blob=0, groupList=0, status=0,zip=0;
  320. long dataSize=0, blobSize=0, studentCount=0;
  321. if (evaluationLocal== null && evaluationCloud==null)
  322. {
  323. //线上线下没有数据
  324. status=1;
  325. errorMsgs.Add($"本地和数据中心均没查询到评测!");
  326. }
  327. else if (evaluationLocal!=null && evaluationCloud!=null)
  328. {
  329. successMsgs.Add($"检测到本地和数据中心均有数据!");
  330. //线上线下有数据
  331. status = 2;
  332. if ((!string.IsNullOrWhiteSpace(evaluationLocal.blobHash) && !evaluationLocal.blobHash.Equals(evaluationCloud.blobHash))
  333. ||(evaluationLocal.blobTime<evaluationCloud.blobTime)
  334. ||(evaluationLocal.blobCount!= evaluationCloud.blobCount)
  335. ||(evaluationLocal.blobSize!= evaluationCloud.blobSize))
  336. {
  337. blob=1;
  338. blobSize=evaluationCloud.blobSize;
  339. errorMsgs.Add($"文件包校验失败,需要更新文件包!");
  340. }
  341. if ((evaluationLocal.dataTime<evaluationCloud.dataTime)
  342. ||(evaluationLocal.dataSize!=evaluationCloud.dataSize)
  343. ||(evaluationLocal.paperCount!= evaluationCloud.paperCount)
  344. )
  345. {
  346. data=1;
  347. errorMsgs.Add($"数据包校验失败,需要更新数据包!");
  348. dataSize=evaluationCloud.dataSize;
  349. }
  350. if ((evaluationLocal.studentCount!= evaluationCloud.studentCount)||(!$"{evaluationLocal.grouplistHash}".Equals(evaluationCloud.grouplistHash)))
  351. {
  352. groupList=1;
  353. studentCount=evaluationCloud.studentCount;
  354. errorMsgs.Add($"名单信息校验失败,需要更新名单信息!");
  355. }
  356. // evaluationLocal=evaluationCloud;
  357. // _liteDBFactory.GetLiteDatabase().GetCollection<EvaluationClient>().Upsert(evaluationLocal);
  358. }
  359. else if (evaluationLocal!=null && evaluationCloud==null)
  360. {
  361. //线下有数据,线上没有数据,可能没联网。
  362. status = 3;
  363. successMsgs.Add($"数据中心未连接,使用本地数据进行作答!");
  364. }
  365. else if (evaluationLocal==null && evaluationCloud!=null)
  366. {
  367. //线下没有数据,线上有数据
  368. evaluationLocal= evaluationCloud;
  369. blob=1;
  370. data=1;
  371. groupList=1;
  372. blobSize=evaluationCloud.blobSize;
  373. dataSize=evaluationCloud.dataSize;
  374. studentCount=evaluationCloud.studentCount;
  375. status = 4;
  376. //foreach (var subject in evaluationLocal.subjects)
  377. //{
  378. // foreach (var paper in subject.papers)
  379. // {
  380. // paper.blob=$"package/{evaluationLocal.id}/papers/{paper.paperId}";
  381. // }
  382. //}
  383. _liteDBFactory.GetLiteDatabase().GetCollection<EvaluationClient>().Upsert(evaluationLocal);
  384. errorMsgs.Add($"获取到最新的评测数据,需要下载数据包,文件包,名单信息!");
  385. }
  386. EvaluationCheckDataResult checkDataResult= new EvaluationCheckDataResult { data = data, blob = blob, groupList = groupList, status = status, dataSize = dataSize, blobSize = blobSize, studentCount = studentCount,zip=zip ,successMsgs=successMsgs,errorMsgs=errorMsgs};
  387. return (checkDataResult, evaluationLocal);
  388. }
  389. public class EvaluationCheckDataResult
  390. {
  391. public int zip { get; set; }
  392. public int data { get; set; }
  393. public int blob { get; set; }
  394. public int groupList { get; set; }
  395. public int status { get; set; }
  396. public long dataSize { get; set; }
  397. public long blobSize { get; set; }
  398. public long studentCount { get; set; }
  399. public List<string> successMsgs { get; set; } = new List<string>();
  400. public List<string> errorMsgs { get; set; } = new List<string>();
  401. }
  402. public class EvaluationCheckFileResult
  403. {
  404. public int checkTotal { get; set; }
  405. public int checkSuccess { get; set; }
  406. public int checkError { get; set; }
  407. public List<string> successMsgs { get; set; } = new List<string>();
  408. public List<string> errorMsgs { get; set; } = new List<string>();
  409. }
  410. }
  411. }