JointService.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. using Azure.Messaging.ServiceBus;
  2. using Azure.Storage.Blobs.Models;
  3. using Azure;
  4. using Microsoft.Azure.Cosmos;
  5. using System;
  6. using System.Collections.Generic;
  7. using System.Linq;
  8. using System.Security.Cryptography;
  9. using System.Text;
  10. using System.Text.Json;
  11. using System.Threading.Tasks;
  12. using TEAMModelOS.SDK.DI;
  13. using TEAMModelOS.SDK.Extension;
  14. using TEAMModelOS.SDK.Models.Service.BI;
  15. using TEAMModelOS.SDK.Services;
  16. using static TEAMModelOS.SDK.Models.JointEventGroupBase;
  17. using Azure.Core;
  18. using Microsoft.Extensions.Configuration;
  19. namespace TEAMModelOS.SDK.Models.Service
  20. {
  21. public static class JointService
  22. {
  23. //取得JointExam生成Exam
  24. public static async Task GenerateExamFromJointExamAsync(CosmosClient client, AzureStorageFactory _azureStorage, AzureServiceBusFactory _serviceBus, CoreAPIHttpService _coreAPIHttpService, AzureRedisFactory _azureRedis, IConfiguration _configuration, DingDing _dingDing, JointExam jointExam)
  25. {
  26. try
  27. {
  28. long now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
  29. //取得JointExam
  30. List<JointEventClassBase> classes = new List<JointEventClassBase>();
  31. List<JointEventGroupBase> stuLists = new List<JointEventGroupBase>();
  32. //取得JointCourse ※examType == "custom" 之後再處理
  33. List<JointEventGroupDb> jointCourses = new List<JointEventGroupDb>();
  34. if (!jointExam.examType.Equals("custom"))
  35. {
  36. string jointCourseSql = $"SELECT * FROM c WHERE c.jointEventId = '{jointExam.jointEventId}' AND c.jointGroupId = '{jointExam.jointGroupId}'";
  37. await foreach (var item in client.GetContainer("TEAMModelOS", "Teacher").GetItemQueryIteratorSql<JointEventGroupDb>(queryText: jointCourseSql, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"JointCourse") }))
  38. {
  39. jointCourses.Add(item);
  40. }
  41. }
  42. //評量資料生成 ExamInfo actExamInfo ※一個課程一個Exam
  43. List<ExamInfo> examList = new List<ExamInfo>();
  44. foreach (JointEventGroupDb jointCourse in jointCourses)
  45. {
  46. string actExamCreatorId = jointCourse.creatorId;
  47. //個人課程
  48. if(jointCourse.scope.Equals("private"))
  49. {
  50. foreach (JointEventGroupCourse jointExamGroupCourse in jointCourse.courseLists)
  51. {
  52. string actExamCourseId = jointExamGroupCourse.courseId;
  53. string actExamCourseName = jointExamGroupCourse.courseName;
  54. //評量資料生成
  55. ExamInfo actExamInfo = new ExamInfo();
  56. ///取得已生成的Exam ※
  57. string examSql = $"SELECT DISTINCT c.id, c.source, c.name, c.jointExamId, c.subjects, c.stuLists, c.targets, c.papers, c.year, c.startTime, c.endTime FROM c JOIN s IN c.subjects WHERE c.jointExamId = '{jointExam.id}' AND s.id = '{actExamCourseId}'";
  58. await foreach (var item in client.GetContainer("TEAMModelOS", "Teacher").GetItemQueryIteratorSql<ExamInfo>(queryText: examSql, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Exam-{actExamCreatorId}") }))
  59. {
  60. actExamInfo = item;
  61. }
  62. ///尚無評量資料
  63. if (string.IsNullOrWhiteSpace(actExamInfo.id))
  64. {
  65. actExamInfo.code = $"Exam-{actExamCreatorId}";
  66. actExamInfo.owner = "teacher";
  67. actExamInfo.scope = jointCourse.scope;
  68. actExamInfo.creatorId = actExamCreatorId;
  69. actExamInfo.id = Guid.NewGuid().ToString();
  70. }
  71. actExamInfo.source = jointExam.source;
  72. actExamInfo.name = jointExam.name;
  73. actExamInfo.jointExamId = jointExam.id;
  74. actExamInfo.subjects = new List<ExamSubject>() { new ExamSubject() { id = actExamCourseId, name = actExamCourseName, classCount = jointExamGroupCourse.groupLists.Count } };
  75. ///評量stuLists
  76. foreach (JointEventGroupCourseGroup actGroup in jointExamGroupCourse.groupLists)
  77. {
  78. if(!actExamInfo.stuLists.Contains(actGroup.id))
  79. {
  80. actExamInfo.stuLists.Add(actGroup.id);
  81. }
  82. List<string> targetRow = new List<string>() { actExamCourseId, actGroup.id };
  83. var targetRowJson = JsonSerializer.SerializeToElement(targetRow);
  84. if(!actExamInfo.targets.Contains(targetRowJson))
  85. {
  86. actExamInfo.targets.Add(targetRowJson);
  87. }
  88. }
  89. ///試卷
  90. actExamInfo.papers = Newtonsoft.Json.JsonConvert.DeserializeObject<List<PaperSimple>>(Newtonsoft.Json.JsonConvert.SerializeObject(jointExam.papers));
  91. ///時間
  92. actExamInfo.year = DateTimeOffset.UtcNow.Year;
  93. actExamInfo.startTime = jointExam.startTime;
  94. actExamInfo.endTime = jointExam.endTime;
  95. examList.Add(actExamInfo);
  96. }
  97. }
  98. //[待做] 學校班級
  99. }
  100. //生成評量
  101. if (examList.Count > 0)
  102. {
  103. foreach (ExamInfo exam in examList)
  104. {
  105. await GenerateExam(client, _azureStorage, _serviceBus, _coreAPIHttpService, _azureRedis, _configuration, _dingDing, jointExam, exam);
  106. }
  107. }
  108. }
  109. catch (Exception)
  110. {
  111. }
  112. }
  113. //生成評量(單)
  114. private static async Task<string> GenerateExam(CosmosClient client, AzureStorageFactory _azureStorage, AzureServiceBusFactory _serviceBus, CoreAPIHttpService _coreAPIHttpService, AzureRedisFactory _azureRedis, IConfiguration _configuration, DingDing _dingDing, JointExam jointExam, ExamInfo exam)
  115. {
  116. long now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
  117. string Result = string.Empty;
  118. exam.createTime = now;
  119. if (exam.startTime <= 0) exam.startTime = now;
  120. List<(string pId, List<string> gid)> ps = new();
  121. var group = exam.groupLists;
  122. if (group.Count > 0)
  123. {
  124. foreach (var keys in group)
  125. {
  126. foreach (KeyValuePair<string, List<string>> pp in keys)
  127. {
  128. ps.Add((pp.Key, pp.Value));
  129. }
  130. }
  131. }
  132. List<string> classes = ExamService.getClasses(exam.classes, exam.stuLists);
  133. (List<RMember> tchList, List<RGroupList> classLists) = await GroupListService.GetMemberByListids(_coreAPIHttpService, client, _dingDing, classes, exam.school, ps);
  134. exam.stuCount = tchList.Count;
  135. string mode = string.Empty;
  136. ResponseMessage response = null;
  137. if (string.IsNullOrEmpty(exam.id))
  138. {
  139. mode = "add";
  140. }
  141. else
  142. {
  143. response = await client.GetContainer("TEAMModelOS", "Common").ReadItemStreamAsync(exam.id, new PartitionKey($"{exam.code}"));
  144. if (response.StatusCode == System.Net.HttpStatusCode.OK) mode = "upd";
  145. else mode = "add";
  146. }
  147. //DB操作
  148. if (mode.Equals("add")) //新建
  149. {
  150. if (string.IsNullOrWhiteSpace(exam.id)) exam.id = Guid.NewGuid().ToString();
  151. exam.progress = (exam.startTime > now) ? "pending" : "going";
  152. var messageBlob = new ServiceBusMessage();
  153. if (exam.scope.Equals("school") && !string.IsNullOrWhiteSpace(exam.school))
  154. {
  155. exam.size = await _azureStorage.GetBlobContainerClient(exam.school).GetBlobsSize($"exam/{exam.id}");
  156. await BlobService.RefreshBlobRoot(new BlobRefreshMessage { progress = "insert", root = $"exam", name = exam.school }, _serviceBus, _configuration, _azureRedis);
  157. }
  158. else
  159. {
  160. exam.size = await _azureStorage.GetBlobContainerClient(exam.creatorId).GetBlobsSize($"exam/{exam.id}");
  161. await BlobService.RefreshBlobRoot(new BlobRefreshMessage { progress = "insert", root = $"exam", name = exam.creatorId }, _serviceBus, _configuration, _azureRedis);
  162. }
  163. int n = 0;
  164. List<string> sheetIds = new List<string>();
  165. foreach (PaperSimple simple in exam.papers)
  166. {
  167. simple.blob = $"/exam/{exam.id}/paper/{exam.subjects[n].id}";
  168. n++;
  169. simple.sheet = null;
  170. }
  171. exam = await client.GetContainer(Constant.TEAMModelOS, "Common").CreateItemAsync(exam, new PartitionKey($"{exam.code}"));
  172. await BIStats.SetTypeAddStats(client, _dingDing, exam.school, "Exam", 1);//BI统计增/减量
  173. }
  174. else if (response != null) //更新
  175. {
  176. using var json = await JsonDocument.ParseAsync(response.Content);
  177. ExamInfo info = json.ToObject<ExamInfo>();
  178. if (info.progress.Equals("going"))
  179. {
  180. Result = "活动正在进行中,无法修改";
  181. }
  182. var messageBlob = new ServiceBusMessage();
  183. if (exam.scope.Equals("school") && !string.IsNullOrWhiteSpace(exam.school))
  184. {
  185. exam.size = await _azureStorage.GetBlobContainerClient(exam.school).GetBlobsSize($"exam/{exam.id}");
  186. await BlobService.RefreshBlobRoot(new BlobRefreshMessage { progress = "update", root = $"exam", name = exam.school }, _serviceBus, _configuration, _azureRedis);
  187. }
  188. else
  189. {
  190. exam.size = await _azureStorage.GetBlobContainerClient(exam.creatorId).GetBlobsSize($"exam/{exam.id}");
  191. await BlobService.RefreshBlobRoot(new BlobRefreshMessage { progress = "update", root = $"exam", name = exam.creatorId }, _serviceBus, _configuration, _azureRedis);
  192. }
  193. exam.progress = info.progress;
  194. int n = 0;
  195. List<string> sheetIds = new List<string>();
  196. foreach (PaperSimple simple in exam.papers)
  197. {
  198. if (!string.IsNullOrEmpty(simple.subjectId))
  199. {
  200. simple.blob = $"/exam/{exam.id}/paper/{simple.subjectId}/{simple.id}";
  201. }
  202. else
  203. {
  204. simple.blob = $"/exam/{exam.id}/paper/{exam.subjects[n].id}";
  205. n++;
  206. }
  207. simple.sheet = null;
  208. }
  209. exam = await client.GetContainer(Constant.TEAMModelOS, "Common").ReplaceItemAsync(exam, exam.id, new PartitionKey($"{exam.code}"));
  210. }
  211. //Blob操作 ※取得試卷源(blob)、複製到評測紀錄下
  212. ///試卷源字典
  213. List<Dictionary<string, string>> sourcePaperInfo = new List<Dictionary<string, string>>();
  214. foreach (PaperSimple paperInfo in jointExam.papers)
  215. {
  216. string paperBlobPath = (!paperInfo.blob.EndsWith("/")) ? paperInfo.blob + "/" : paperInfo.blob;
  217. paperBlobPath = (paperInfo.blob.StartsWith("/")) ? paperBlobPath.Remove(0, 1) : paperBlobPath;
  218. sourcePaperInfo.Add(new Dictionary<string, string>() { { "id", paperInfo.id }, { "blob", paperBlobPath }, { "itemcount", paperInfo.point.Count.ToString() } });
  219. }
  220. bool paperDataCopyErrFlg = false; //試卷資料拷貝錯誤Flag true:拷貝錯誤
  221. //Blob拷貝程序
  222. int paperIndex = 0;
  223. foreach (Dictionary<string, string> sourcePaperInfoDic in sourcePaperInfo)
  224. {
  225. //拷貝源:Container => jointExam.creatorId Path:papers.blob
  226. //拷貝對象:Container => exam.creatorId, Path:exam/{exam.id}/paper/{exam.subjects[paperIndex].id}/
  227. string targetScope = exam.scope; //評測對象 school:校本班級 private:私人課程
  228. var sourceBlobContainer = _azureStorage.GetBlobContainerClient(jointExam.creatorId); //統測活動來源一定是個人
  229. var blobPrivateContainer = (targetScope.Equals("school")) ? _azureStorage.GetBlobContainerClient(exam.school) : _azureStorage.GetBlobContainerClient(exam.creatorId);
  230. string sourceBlobPath = sourcePaperInfoDic["blob"];
  231. string subjectId = exam.subjects[paperIndex].id;
  232. string destBlobPath = $"exam/{exam.id}/paper/{subjectId}/"; //拷貝對象路徑 path:exam/{評測ID}/paper/{subjectID}/
  233. Pageable<BlobItem> sourceBlobs = sourceBlobContainer.GetBlobs(prefix: sourceBlobPath);
  234. if (sourceBlobs.Count() > 0)
  235. {
  236. foreach (var blob in sourceBlobs)
  237. {
  238. var sourceFileBlob = sourceBlobContainer.GetBlobClient(blob.Name);
  239. if (sourceFileBlob.Exists())
  240. {
  241. var sourceFileUri = sourceBlobContainer.GetBlobClient(blob.Name).Uri;
  242. string fileName = blob.Name.Replace(sourceBlobPath, "");
  243. string destBlobFilePath = $"{destBlobPath}{fileName}";
  244. await blobPrivateContainer.GetBlobClient(destBlobFilePath).StartCopyFromUriAsync(sourceFileUri);
  245. }
  246. else
  247. {
  248. paperDataCopyErrFlg = true;
  249. }
  250. }
  251. }
  252. paperIndex++;
  253. }
  254. return Result;
  255. }
  256. }
  257. }