EvaluationSyncInfoService.cs 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450
  1. using Azure.Storage.Blobs.Models;
  2. using IES.ExamServer.Models;
  3. using Microsoft.Azure.Cosmos;
  4. using System;
  5. using System.Collections.Generic;
  6. using System.Linq;
  7. using System.Net.Http;
  8. using System.Reflection.Metadata.Ecma335;
  9. using System.Text;
  10. using System.Text.Json;
  11. using System.Text.Json.Nodes;
  12. using System.Threading.Tasks;
  13. using TEAMModelOS.SDK.DI;
  14. using TEAMModelOS.SDK.Extension;
  15. using TEAMModelOS.SDK.Models.Cosmos;
  16. using TEAMModelOS.SDK.Models.Cosmos.Normal;
  17. using TEAMModelOS.SDK.Models.Cosmos.Student;
  18. using TEAMModelOS.SDK.Models.Dtos;
  19. namespace TEAMModelOS.SDK.Models.Service
  20. {
  21. public sealed class EvaluationSyncInfoService
  22. {
  23. /// <summary>
  24. /// 重新对名单的hash值计算,因为可能会存在临时将学生加入名单,而没有修改评测信息的情况。
  25. /// </summary>
  26. /// <param name="evaluationSyncInfo"></param>
  27. /// <param name="_coreAPIHttpService"></param>
  28. /// <param name="_azureCosmos"></param>
  29. /// <param name="_dingDing"></param>
  30. /// <returns></returns>
  31. 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)
  32. {
  33. var listInfo = await GroupListService.GetMemberByListids(_coreAPIHttpService, _azureCosmos.GetCosmosClient(), _dingDing, evaluationSyncInfo.grouplist, evaluationSyncInfo.owner);
  34. //计算数据的hash值
  35. StringBuilder groupListData = new StringBuilder();
  36. //名单的hash值
  37. var orderList = listInfo.groups.OrderBy(x => x.id);
  38. foreach (var item in orderList)
  39. {
  40. groupListData.Append($"{item.id}-{item.name}");
  41. var orderMembers = item.members.OrderBy(x => x.id);
  42. foreach (var member in orderMembers)
  43. {
  44. groupListData.Append($"{member.id}-{member.name}");
  45. }
  46. }
  47. bool change = false;
  48. string grouplistHash = ShaHashHelper.GetSHA1(groupListData.ToString());
  49. if (!evaluationSyncInfo.grouplistHash .Equals(grouplistHash))
  50. {
  51. evaluationSyncInfo.grouplistHash = grouplistHash;
  52. change = true;
  53. }
  54. return (evaluationSyncInfo,change, grouplistHash,listInfo.rmembers, listInfo.groups);
  55. }
  56. /// <summary>
  57. /// 活动数据打包
  58. /// </summary>
  59. /// <param name="id"></param>
  60. /// <param name="scope"></param>
  61. /// <param name="owner"></param>
  62. /// <param name="type"></param>
  63. /// <param name="azureCosmos"></param>
  64. /// <param name="azureStorage"></param>
  65. public static async Task<EvaluationSyncInfo> PackageEvaluation( string id,string scope, string ownerId, string type, AzureCosmosFactory azureCosmos, AzureStorageFactory azureStorage,
  66. CoreAPIHttpService _coreAPIHttpService, DingDing _dingDing, IHttpClientFactory _httpClientFactory)
  67. {
  68. EvaluationSource evaluationSource = new EvaluationSource() { type=type,id=id};
  69. EvaluationSyncInfo evaluationSyncInfo= null;
  70. EvaluationClient evaluationClient = null;
  71. List<EvaluationExam> evaluationExams = new List<EvaluationExam>();
  72. long? dataTime = 0;
  73. long stime = 0;
  74. long etime = 0;
  75. string? ownerName=string.Empty;
  76. string? ownerPicture = string.Empty;
  77. string schoolCode = null;
  78. if (scope.Equals("school"))
  79. {
  80. try {
  81. School school = await azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.School).ReadItemAsync<School>(ownerId, new PartitionKey("Base"));
  82. schoolCode= ownerId;
  83. evaluationSource.school = school;
  84. ownerName = school.name;
  85. ownerPicture = school.picture;
  86. if (!string.IsNullOrWhiteSpace(ownerPicture))
  87. {
  88. //学校logo 下载到本地、
  89. HttpResponseMessage message = await _httpClientFactory.CreateClient().GetAsync(ownerPicture);
  90. if (message.IsSuccessStatusCode)
  91. {
  92. string base64Prefix = "data:image/jpeg;base64,";
  93. byte[] fileBytes = await message.Content.ReadAsByteArrayAsync();
  94. ownerPicture =$"{base64Prefix}{Convert.ToBase64String(fileBytes)}";
  95. }
  96. }
  97. }
  98. catch (Exception ex) {
  99. await _dingDing.SendBotMsg($"打包评测数据,获取学校信息报错,{ex.Message}\n{ex.StackTrace}", GroupNames.成都开发測試群組);
  100. return null;
  101. }
  102. }
  103. else
  104. {
  105. try {
  106. Teacher teacher = await azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher).ReadItemAsync<Teacher>(ownerId, new PartitionKey("Base"));
  107. evaluationSource.teacher = teacher;
  108. ownerName = teacher.name;
  109. ownerPicture = teacher.picture;
  110. if (!string.IsNullOrWhiteSpace(ownerPicture))
  111. {
  112. //学校logo 下载到本地、
  113. HttpResponseMessage message = await _httpClientFactory.CreateClient().GetAsync(ownerPicture);
  114. if (message.IsSuccessStatusCode)
  115. {
  116. string base64Prefix = "data:image/jpeg;base64,";
  117. byte[] fileBytes = await message.Content.ReadAsByteArrayAsync();
  118. ownerPicture =$"{base64Prefix}{Convert.ToBase64String(fileBytes)}";
  119. }
  120. }
  121. }
  122. catch (Exception ex) {
  123. await _dingDing.SendBotMsg($"打包评测数据,获取教师信息报错,{ex.Message}\n{ex.StackTrace}", GroupNames.成都开发測試群組);
  124. return null;
  125. }
  126. }
  127. var responseEvaluationSyncInfo = await azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Normal).ReadItemStreamAsync(id, new PartitionKey("EvaluationSyncInfo"));
  128. if (responseEvaluationSyncInfo.IsSuccessStatusCode)
  129. {
  130. evaluationSyncInfo= JsonDocument.Parse(responseEvaluationSyncInfo.Content).RootElement.Deserialize<EvaluationSyncInfo>();
  131. }
  132. else {
  133. evaluationSyncInfo=new EvaluationSyncInfo {
  134. id = id,
  135. scope = scope,
  136. type = type,
  137. pk="EvaluationSyncInfo",
  138. code="ActivitySyncInfo",
  139. ownerId = ownerId,
  140. ownerPicture = ownerPicture,
  141. ownerName = ownerName,
  142. };
  143. }
  144. switch (true)
  145. {
  146. case bool when (type == "Exam"):
  147. {
  148. string code = $"Exam-{ownerId}";
  149. var response = await azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Common).ReadItemStreamAsync(id, new PartitionKey(code));
  150. if (response.IsSuccessStatusCode)
  151. {
  152. ExamInfo exam= JsonDocument.Parse(response.Content).RootElement.Deserialize<ExamInfo>();
  153. dataTime= exam._ts*1000;
  154. evaluationSyncInfo.name=exam.name;
  155. evaluationSyncInfo.subjects = exam.subjects?.Select(x=>new IES.ExamServer.Models.SubjectExam { subjectId=x.id,subjectName=x.name,examId=id}).ToList();
  156. evaluationSyncInfo.dataTime= DateTimeOffset.Now.ToUnixTimeMilliseconds();
  157. evaluationSyncInfo.scode=exam.code;
  158. stime=exam.startTime;
  159. etime=exam.endTime;
  160. HashSet<string> grouplist = new HashSet<string>();
  161. if (exam.classes.IsNotEmpty()) {
  162. exam.classes.ForEach(x => { grouplist.Add(x); });
  163. }
  164. if (exam.stuLists.IsNotEmpty())
  165. {
  166. exam.stuLists.ForEach(x => { grouplist.Add(x); });
  167. }
  168. evaluationSyncInfo.grouplist=grouplist.ToList() ;
  169. evaluationSyncInfo.paperCount=exam.papers.IsNotEmpty()? exam.papers.Count():0;
  170. foreach (var group in exam.papers.GroupBy(x=>x.subjectId).Select(x=>new { key = x.Key,list= x.ToList()}))
  171. {
  172. var subject= exam.subjects.Find(x => x.id.Equals(group.key));
  173. if (subject!=null)
  174. {
  175. evaluationSyncInfo.subjects.Add(new IES.ExamServer.Models.SubjectExam {
  176. subjectId=subject.id,
  177. subjectName=subject.name,
  178. examId=id,
  179. papers= group.list.Select(x=>new SubjectExamPaper {paperId= x.id,paperName=x.name,blob=x.blob }).ToList(),
  180. } );
  181. EvaluationExam evaluationExam = new EvaluationExam()
  182. {
  183. examId=exam.id,
  184. evaluationId=evaluationSyncInfo.id,
  185. examName=evaluationSyncInfo.name,
  186. subjectId=subject.id,
  187. subjectName=subject.name,
  188. classes= evaluationSyncInfo.grouplist,
  189. owner=exam.owner,
  190. scope=scope,
  191. stime=stime,
  192. etime=etime,
  193. papers= group.list.Select(x => new EvaluationPaper { paperId= x.id, paperName=x.name, blob=x.blob, point=x.point,knowledge=x.knowledge,type=x.type,field=x.field }).ToList(),
  194. };
  195. evaluationExams.Add(evaluationExam);
  196. }
  197. }
  198. evaluationSource.exam=exam;
  199. }
  200. break;
  201. }
  202. case bool when (type == "Art"):
  203. {
  204. string code = $"Art-{ownerId}";
  205. var response = await azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Common).ReadItemStreamAsync(id, new PartitionKey(code));
  206. if (response.IsSuccessStatusCode)
  207. {
  208. ArtEvaluation art = JsonDocument.Parse(response.Content).RootElement.Deserialize<ArtEvaluation>();
  209. // evaluationSyncInfo.subjects = art.subjects?.Select(x => new IES.ExamServer.Models.SubjectExam { id=x.id, name=x.name, examId=id }).ToList();
  210. evaluationSyncInfo.name = art.name;
  211. evaluationSyncInfo.pid= art.pId;
  212. evaluationSyncInfo.scode=art.code;
  213. evaluationSyncInfo.dataTime = DateTimeOffset.Now.ToUnixTimeMilliseconds();
  214. HashSet<string> grouplist = new HashSet<string>();
  215. if (art.classes.IsNotEmpty())
  216. {
  217. art.classes.ForEach(x => { grouplist.Add(x); });
  218. }
  219. if (art.stuLists.IsNotEmpty())
  220. {
  221. art.stuLists.ForEach(x => { grouplist.Add(x); });
  222. }
  223. if (art.tchLists.IsNotEmpty())
  224. {
  225. art.tchLists.ForEach(x => { grouplist.Add(x); });
  226. }
  227. evaluationSyncInfo.grouplist=grouplist.ToList();
  228. evaluationSource.art=art;
  229. dataTime= art._ts*1000;
  230. stime=art.startTime;
  231. etime=art.endTime;
  232. var quota_22 = art.settings.Find(x => x.id.Equals("quota_21"));
  233. foreach (var item in quota_22.task)
  234. {
  235. if (!string.IsNullOrWhiteSpace(item.acId))
  236. {
  237. var subject = art.subjects.Find(x => x.id.Equals(item.subject));
  238. var examResponse= await azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Common).ReadItemStreamAsync(item.acId, new PartitionKey($"Exam-{ownerId}"));
  239. if (examResponse.IsSuccessStatusCode)
  240. {
  241. ExamInfo exam = JsonDocument.Parse(examResponse.Content).RootElement.Deserialize<ExamInfo>() ;
  242. var papers = exam.papers.FindAll(x => x.subjectId.Equals(item.subject));
  243. evaluationSyncInfo.subjects.Add(new IES.ExamServer.Models.SubjectExam
  244. {
  245. subjectId=item.subject,
  246. subjectName=subject.name,
  247. examId=item.acId,
  248. papers=papers?.Select(x => new SubjectExamPaper { paperId=x.id,paperName=x.name, blob=x.blob }).ToList(),
  249. });
  250. dataTime= dataTime<exam._ts*1000 ? exam._ts*1000 : dataTime;
  251. evaluationSource.artExams.Add(exam);
  252. EvaluationExam evaluationExam = new EvaluationExam()
  253. {
  254. examId=exam.id,
  255. evaluationId=evaluationSyncInfo.id,
  256. examName=evaluationSyncInfo.name,
  257. subjectId=subject.id,
  258. subjectName=subject.name,
  259. classes= evaluationSyncInfo.grouplist,
  260. owner=exam.owner,
  261. scope=scope,
  262. stime=stime,
  263. etime=etime,
  264. papers= papers.Select(x => new EvaluationPaper { paperId= x.id, paperName=x.name, blob=x.blob, point=x.point, knowledge=x.knowledge, type=x.type, field=x.field }).ToList(),
  265. };
  266. evaluationExams.Add(evaluationExam);
  267. }
  268. }
  269. }
  270. }
  271. }
  272. break;
  273. default:
  274. break;
  275. }
  276. if (evaluationSyncInfo.subjects.IsNotEmpty() && evaluationSyncInfo.grouplist.IsNotEmpty())
  277. {
  278. long blobTime =-1;
  279. long blobSize = 0;
  280. long blobCount = 0;
  281. if (!string.IsNullOrWhiteSpace(evaluationSyncInfo.shortCode))
  282. {
  283. evaluationSyncInfo.shortCode = $"{MurmurHash3.Hash32(evaluationSyncInfo.id)}";
  284. }
  285. var listInfo = await GroupListService.GetMemberByListids(_coreAPIHttpService, azureCosmos.GetCosmosClient(), _dingDing, evaluationSyncInfo.grouplist, schoolCode);
  286. evaluationSyncInfo.studentCount = listInfo.rmembers.Count();
  287. evaluationSyncInfo.paperCount =evaluationSyncInfo.subjects.Select(x => x.papers).Count();
  288. var client = azureStorage.GetBlobContainerClient(ownerId);
  289. foreach (var subject in evaluationSyncInfo.subjects)
  290. {
  291. var evaluationExam = evaluationExams.FindAll(x => x.subjectId.Equals(subject.subjectId)).FirstOrDefault();
  292. foreach (var paper in subject.papers)
  293. {
  294. List<BlobHashInfo> blobs = new List<BlobHashInfo>();
  295. try
  296. {
  297. await foreach (BlobItem blobItem in client.GetBlobsAsync(BlobTraits.None, BlobStates.None, paper.blob))
  298. {
  299. var lastModified = blobItem.Properties.LastModified;
  300. if (lastModified.HasValue)
  301. {
  302. lastModified.Value.ToUnixTimeMilliseconds();
  303. }
  304. var hash = blobItem.Properties.ContentHash;
  305. var path = blobItem.Name;
  306. var size = blobItem.Properties.ContentLength;
  307. blobs.Add(new BlobHashInfo
  308. {
  309. hash = Md5Hash.GetbyteToString(hash),
  310. last = lastModified.HasValue ? lastModified.Value.ToUnixTimeMilliseconds() : 0,
  311. path = path,
  312. size = size.HasValue ? size.Value : 0
  313. });
  314. };
  315. var evaluationPaper= evaluationExam.papers.Find(x => x.paperId.Equals(paper.paperId));
  316. evaluationPaper.blobs=blobs;
  317. }
  318. catch
  319. {
  320. }
  321. long lastTime = blobs.Max(x => x.last);
  322. blobTime= lastTime>blobTime?lastTime:blobTime;
  323. if (blobs.IsNotEmpty())
  324. {
  325. blobSize+=blobs.Sum(x => x.size);
  326. blobCount+=blobs.Count;
  327. var order = blobs.OrderBy(x => $"{x.path}-{x.hash}-{x.size}-{x.last}");
  328. string blobStr = string.Join(",", order.Select(x=> $"{x.path}-{x.hash}-{x.size}-{x.last}"));
  329. // 计算hash,校准路径,文件hash,文件大小,最后修改时间
  330. paper.paperHash= ShaHashHelper.GetSHA1(blobStr);
  331. }
  332. }
  333. }
  334. evaluationSyncInfo.blobTime = blobTime> evaluationSyncInfo.blobTime ? blobTime : evaluationSyncInfo.blobTime;
  335. evaluationSyncInfo.blobSize = blobSize;
  336. evaluationSyncInfo.blobCount = blobCount;
  337. evaluationSyncInfo.blobLastHash = evaluationSyncInfo.blobHash;
  338. evaluationSyncInfo.blobHash = ShaHashHelper.GetSHA1(string.Join("-", evaluationSyncInfo.subjects.SelectMany(x => x.papers).Select(x => x.paperHash)));
  339. evaluationSyncInfo.dataTime = dataTime.Value;
  340. evaluationSource.updateTime = dataTime.Value;
  341. var groupList = new { members = listInfo.rmembers, groupList = listInfo.groups };
  342. {
  343. //计算数据的hash值
  344. StringBuilder groupListData = new StringBuilder();
  345. //名单的hash值
  346. var orderList = listInfo.groups.OrderBy(x => x.id);
  347. foreach (var item in orderList)
  348. {
  349. groupListData.Append($"{item.id}-{item.name}");
  350. var orderMembers = item.members.OrderBy(x => x.id);
  351. foreach (var member in orderMembers)
  352. {
  353. groupListData.Append($"{member.id}-{member.name}");
  354. }
  355. }
  356. evaluationSyncInfo.grouplistHash= ShaHashHelper.GetSHA1(groupListData.ToString());
  357. var order= evaluationSyncInfo.subjects.OrderBy(x => x.subjectId);
  358. StringBuilder dataStr = new StringBuilder();
  359. dataStr.Append(evaluationSyncInfo.id);
  360. dataStr.Append(evaluationSyncInfo.name);
  361. dataStr.Append(evaluationSyncInfo.type);
  362. dataStr.Append(evaluationSyncInfo.owner);
  363. dataStr.Append(evaluationSyncInfo.ownerId);
  364. dataStr.Append(evaluationSyncInfo.scode);
  365. dataStr.Append(evaluationSyncInfo.scope);
  366. dataStr.Append(evaluationSyncInfo.grouplistHash);
  367. dataStr.Append(evaluationSyncInfo.blobHash);
  368. dataStr.Append(evaluationSyncInfo.shortCode);
  369. dataStr.Append($"{stime}{etime}");
  370. dataStr.Append(string.Join("", order.Select(x=>x.subjectId)));
  371. //计算dataHash
  372. evaluationSyncInfo.dataHash = ShaHashHelper.GetSHA1(dataStr.ToString());
  373. }
  374. evaluationClient= new EvaluationClient
  375. {
  376. id = evaluationSyncInfo.id,
  377. pid = evaluationSyncInfo.pid,
  378. name = evaluationSyncInfo.name,
  379. type = evaluationSyncInfo.type,
  380. owner = evaluationSyncInfo.owner,
  381. scode = evaluationSyncInfo.scode,
  382. scope = evaluationSyncInfo.scope,
  383. subjects = evaluationSyncInfo.subjects,
  384. dataTime = evaluationSyncInfo.dataTime,
  385. dataSize = evaluationSyncInfo.dataSize,
  386. blobTime = evaluationSyncInfo.blobTime,
  387. blobSize = evaluationSyncInfo.blobSize,
  388. blobCount = evaluationSyncInfo.blobCount,
  389. blobHash = evaluationSyncInfo.blobHash,
  390. blobLastHash = evaluationSyncInfo.blobLastHash,
  391. webviewCount = evaluationSyncInfo.webviewCount,
  392. webviewPath = evaluationSyncInfo.webviewPath,
  393. webviewSize = evaluationSyncInfo.webviewSize,
  394. webviewTime = evaluationSyncInfo.webviewTime,
  395. studentCount = evaluationSyncInfo.studentCount,
  396. paperCount = evaluationSyncInfo.paperCount,
  397. grouplist = evaluationSyncInfo.grouplist,
  398. shortCode = evaluationSyncInfo.shortCode,
  399. stime=stime,
  400. etime=etime,
  401. dataHash = evaluationSyncInfo.dataHash,
  402. grouplistHash = evaluationSyncInfo.grouplistHash,
  403. ownerId = evaluationSyncInfo.ownerId,
  404. ownerPicture =evaluationSyncInfo.ownerPicture,
  405. ownerName = evaluationSyncInfo.ownerName,
  406. //password = evaluationSyncInfo.password,
  407. //recordUrl = evaluationSyncInfo.recordUrl
  408. };
  409. long dataSize = 0;
  410. string sourceJson = evaluationSource.ToJsonString();
  411. dataSize+= Encoding.UTF8.GetByteCount(sourceJson);
  412. string groupListJson = groupList.ToJsonString();
  413. dataSize+= Encoding.UTF8.GetByteCount(groupListJson);
  414. string evaluationJson = new { evaluationClient, evaluationExams }.ToJsonString().ToJsonString();
  415. dataSize+= Encoding.UTF8.GetByteCount(evaluationJson);
  416. string evaluationSyncInfoSJson = evaluationSyncInfo.ToJsonString();
  417. dataSize+= Encoding.UTF8.GetByteCount(evaluationSyncInfoSJson);
  418. evaluationClient.dataSize = dataSize;
  419. await azureStorage.GetBlobContainerClient(ownerId).UploadFileByContainer(sourceJson, $"exam/{id}/package", "source.json");
  420. await azureStorage.GetBlobContainerClient(ownerId).UploadFileByContainer(groupListJson, $"exam/{id}/package", "grouplist.json");
  421. await azureStorage.GetBlobContainerClient(ownerId).UploadFileByContainer(new { evaluationClient, evaluationExams }.ToJsonString(), $"exam/{id}/package", "evaluation.json");
  422. evaluationSyncInfo.dataSize = dataSize;
  423. await azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Normal).UpsertItemAsync<EvaluationSyncInfo>(evaluationSyncInfo, new PartitionKey("EvaluationSyncInfo"));
  424. // await azureStorage.GetBlobContainerClient(owner).UploadFileByContainer(evaluationSyncInfo.ToJsonString(), $"package/{id}", "syncinfo.json");
  425. }
  426. return evaluationSyncInfo;
  427. }
  428. }
  429. }