ActivityService.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  1. using Azure.Cosmos;
  2. using Azure.Storage.Blobs.Models;
  3. using Azure.Storage.Sas;
  4. using HTEXLib.COMM.Helpers;
  5. using System;
  6. using System.Collections.Generic;
  7. using System.Diagnostics;
  8. using System.Linq;
  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;
  15. using TEAMModelOS.Services;
  16. using Activity = TEAMModelOS.SDK.Models.Activity;
  17. namespace TEAMModelOS.SDK
  18. {
  19. public static class ActivityService
  20. {
  21. /// <summary>
  22. /// 分配作品
  23. /// </summary>
  24. /// <param name="_azureCosmos"></param>
  25. /// <param name="activity"></param>
  26. /// <returns></returns>
  27. public static async Task AllocationTask(AzureCosmosFactory _azureCosmos, Activity activity,List<string> periodSubjectKeys)
  28. {
  29. //检查报名,学段和科目是否匹配
  30. string enrollSQL = "select value c from c where c.pk='ActivityEnroll'";
  31. var resultActivityEnroll= await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher).GetList<ActivityEnroll>(enrollSQL, $"ActivityEnroll-{activity.id}");
  32. List<ActivityEnroll> activityEnrollsInvalid = new List<ActivityEnroll>();
  33. foreach (ActivityEnroll enroll in resultActivityEnroll.list)
  34. {
  35. var period = enroll.contest?.enrollInfos?.Find(z => z.code.Equals("period"));
  36. var subject= enroll.contest?.enrollInfos?.Find(z => z.code.Equals("subject"));
  37. string periodSubjectKey = $"{period?.val}-{subject.val}";
  38. if (periodSubjectKeys.Contains(periodSubjectKey))
  39. {
  40. }
  41. else {
  42. }
  43. }
  44. }
  45. /// <summary>
  46. /// 删除活动关联的数据
  47. /// </summary>
  48. /// <param name="_azureCosmos"></param>
  49. /// <param name="activity"></param>
  50. /// <returns></returns>
  51. public static async Task DeleteActivityRelated(AzureCosmosFactory _azureCosmos,Activity activity)
  52. {
  53. //删除模块,Contest, Training ,Research, ReviewRule规则(ReviewRule-disposable"; 存为活动)
  54. await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Common).DeleteItemStreamAsync(activity.id, new PartitionKey("Contest"));
  55. await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Common).DeleteItemStreamAsync(activity.id, new PartitionKey("Training"));
  56. await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Common).DeleteItemStreamAsync(activity.id, new PartitionKey("Research"));
  57. await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Normal).DeleteItemStreamAsync(activity.id, new PartitionKey("ReviewRule-disposable"));
  58. //删除邀请教师 ActivityTeacher,
  59. var resultTeacher = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher)
  60. .GetList<IdCode>("select c.id ,c.code from c where c.pk='ActivityEnroll' and c.", $"ActivityTeacher-{activity.id}");
  61. if (resultTeacher.list.IsNotEmpty())
  62. {
  63. await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher).DeleteItemsStreamAsync(resultTeacher.list.Select(z => z.id).ToList(), $"ActivityTeacher-{activity.id}");
  64. }
  65. //删除报名数据 ActivityEnroll
  66. var resultEnroll = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher)
  67. .GetList<IdCode>("select c.id ,c.code from c where c.pk='ActivityEnroll' and c.", $"ActivityEnroll-{activity.id}");
  68. if (resultEnroll.list.IsNotEmpty())
  69. {
  70. await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher).DeleteItemsStreamAsync(resultEnroll.list.Select(z => z.id).ToList(), $"ActivityEnroll-{activity.id}");
  71. }
  72. //删除专家数据 ActivityExpert
  73. await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher).DeleteItemStreamAsync(activity.id, new PartitionKey("ActivityExpert"));
  74. }
  75. /// <summary>
  76. /// 生成组队口令
  77. /// </summary>
  78. /// <param name="client"></param>
  79. /// <param name="_dingDing"></param>
  80. /// <param name="_option"></param>
  81. /// <param name="_activityId"></param>
  82. /// <returns></returns>
  83. /// <exception cref="Exception"></exception>
  84. public static async Task<string> GenCipher(CosmosClient client, DingDing _dingDing, TEAMModelOS.Models.Option _option, string _activityId)
  85. {
  86. string _num09 = "123456789";
  87. string no = $"{Utils.CreatSaltString(7, _num09)}";
  88. for (int i = 0; i < 10; i++)
  89. {
  90. List<SheetConfig> sheets = new List<SheetConfig>();
  91. bool hasCurrFrom = false;
  92. string cipherSQL = $"select value c.id from c where c.contest!=null and c.activityId='{_activityId}' and c.contest.type=1 and c.contest.cipher='{no}' ";
  93. var cipherResult = await client.GetContainer(Constant.TEAMModelOS, Constant.Teacher).GetList<string>(cipherSQL, $"ActivityEnroll-{_activityId}");
  94. if (cipherResult.list.IsNotEmpty() && cipherResult.list.Count>0) {
  95. hasCurrFrom = true;
  96. }
  97. if (hasCurrFrom)
  98. {
  99. if (i == 9)
  100. {
  101. string msg = $"{_option.Location},ActivityService/GenCipher\n 组队口令生成异常,重复生成次数超过10次";
  102. await _dingDing.SendBotMsg($"OS,{_option.Location},{msg}", GroupNames.醍摩豆服務運維群組);
  103. throw new Exception(msg);
  104. }
  105. else
  106. {
  107. no = $"{Utils.CreatSaltString(7, _num09)}";
  108. }
  109. }
  110. else { break; }
  111. }
  112. return no;
  113. }
  114. public static async Task<List<TeacherActivityDto>> TeacherActivityList(AzureCosmosFactory _azureCosmos,AzureStorageFactory _azureStorage, JsonElement request, string tmdid)
  115. {
  116. List<TeacherActivityDto> activities = new List<TeacherActivityDto>();
  117. HashSet<string> inviteActivityIds = new HashSet<string>();
  118. string yearSql = $" and c.year={DateTimeOffset.Now.Year}";
  119. if (request.TryGetProperty("year", out JsonElement _year) && int.TryParse($"{_year}",out int year ))
  120. {
  121. yearSql = $" and c.year={year}";
  122. }
  123. //先获取邀请制的
  124. string sqlInvite = $"select value c from c join t in c.inviteTeachers where t.id='{tmdid}' and c.pk='ActivityTeacher'";
  125. var result = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Common).GetList<ActivityTeacher>(sqlInvite);
  126. inviteActivityIds= result.list.Select(z => z.id).ToHashSet();
  127. if (inviteActivityIds.Count>0)
  128. {
  129. string sqlActivity = $"select value c from c where c.id in ({string.Join(",", inviteActivityIds.Select(z => $"'{z}'"))}) {yearSql} ";
  130. var resultActivity = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Common).GetList<TeacherActivityDto>(sqlInvite, "Activity");
  131. if (resultActivity.list.IsNotEmpty())
  132. {
  133. activities.AddRange(resultActivity.list);
  134. }
  135. }
  136. Teacher teacher = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher).ReadItemAsync<Teacher>(tmdid, new PartitionKey("Base"));
  137. string schoolOwnerIn = string.Empty;
  138. string schoolIdIn = string.Empty;
  139. if (teacher.schools.IsNotEmpty())
  140. {
  141. schoolIdIn = $"and i.id in ({string.Join(",", teacher.schools.Select(z => $"'{z.schoolId}'"))})";
  142. schoolOwnerIn= $"and c.owner in ({string.Join(",", teacher.schools.Select(z => $"'{z.schoolId}'"))})";
  143. }
  144. //获取开放的
  145. {
  146. //完全开放 所有的学校
  147. string sqlOpen = $"select value c from c where c.scope='public' and (c.publish=1 or c.publish=2 ) and( ARRAY_LENGTH(c.invitedSchools)=0 or IS_DEFINED(c.invitedSchools) = false ) {yearSql} ";
  148. var resultOpen = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Common).GetList<TeacherActivityDto>(sqlOpen, "Activity");
  149. activities.AddRange(resultOpen.list);
  150. if (!string.IsNullOrWhiteSpace(schoolIdIn))
  151. { //部分学校
  152. string sqlSchool = $"select value c from c join i in c.confirmedSchools where c.scope='public' and c.joinMode='enroll' and (c.publish=1 or c.publish=2 ) and i.status=1 {yearSql} {schoolIdIn} ";
  153. var resultSchool = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Common).GetList<TeacherActivityDto>(sqlSchool, "Activity");
  154. activities.AddRange(resultSchool.list);
  155. }
  156. }
  157. string areaOwnerIn = string.Empty;
  158. var hasAreaSchools = teacher.schools.FindAll(z => !string.IsNullOrWhiteSpace(z.areaId));
  159. if (hasAreaSchools.IsNotEmpty())
  160. {
  161. areaOwnerIn = $"and c.owner in ({string.Join(",", hasAreaSchools.Select(z => $"'{z.areaId}'"))})";
  162. schoolIdIn = $"and i.id in ({string.Join(",", hasAreaSchools.Select(z => $"'{z.schoolId}'"))})";
  163. }
  164. //获取所有区级的
  165. if (!string.IsNullOrWhiteSpace(areaOwnerIn) && !string.IsNullOrEmpty(schoolIdIn))
  166. {
  167. string sqlOpen = $"select value c from c join i in c.confirmedSchools where c.scope='area' and c.joinMode='enroll' and (c.publish=1 or c.publish=2 ) and i.status=1 {yearSql} {areaOwnerIn} {schoolIdIn} ";
  168. var resultOpen = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Common).GetList<TeacherActivityDto>(sqlOpen, "Activity");
  169. activities.AddRange(resultOpen.list);
  170. }
  171. //获取所有学校的
  172. if (!string.IsNullOrWhiteSpace(schoolOwnerIn))
  173. {
  174. string sqlSchool = $"select value c from c where c.scope='school' and c.joinMode='enroll' and (c.publish=1 or c.publish=2 ) {yearSql} {schoolOwnerIn} ";
  175. var resultSchool = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Common).GetList<TeacherActivityDto>(sqlSchool, "Activity");
  176. activities.AddRange(resultSchool.list);
  177. }
  178. activities.ForEach(z =>
  179. {
  180. var (blob_uri, blob_sas) = _azureStorage.GetBlobContainerSAS(z.owner, BlobContainerSasPermissions.Read);
  181. z.sas=blob_sas;
  182. });
  183. if (activities.IsNotEmpty())
  184. {
  185. string sql = $"select value c from c where c.pk='ActivityEnroll' and contains(c.code,'ActivityEnroll-') and c.id='{tmdid}' and c.activityId in ({string.Join(",", activities.Select(z => $"'{z.id}'"))})";
  186. var resultEnroll = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher).GetList<ActivityEnroll>(sql);
  187. foreach (var enroll in resultEnroll.list)
  188. {
  189. var activityDto = activities.Find(z => z.id.Equals(enroll.activityId));
  190. if (activityDto!=null)
  191. {
  192. if (enroll.contest!=null && !string.IsNullOrWhiteSpace(enroll.contest.cipher))
  193. {
  194. activityDto.contestSign=1;
  195. activityDto.signTime=enroll.contest.enrollTime;
  196. activityDto.contestType=enroll.contest.type;
  197. }
  198. if (enroll.upload!=null && (enroll.upload.files.IsNotEmpty() || enroll.upload.sokrates.IsNotEmpty()))
  199. {
  200. activityDto.contestUpload=1;
  201. activityDto.uploadTime=enroll.upload.uploadTime;
  202. activityDto.uploadType=enroll.upload.type;
  203. }
  204. }
  205. }
  206. }
  207. return activities;
  208. }
  209. public static async Task<(ReviewRule reviewRule, int invalidCode, string msg)> UpsertReviewRule(ReviewRuleTree reviewRuleTree,Activity activity, Contest contest, AzureCosmosFactory _azureCosmos)
  210. {
  211. var nodes = new List<RuleConfig>();
  212. nodes= TreeToList(reviewRuleTree.trees, nodes);
  213. ReviewRule reviewRule = new ReviewRule() {
  214. id= activity.id,
  215. code="ReviewRule-disposable",
  216. pk="ReviewRule",
  217. name=reviewRuleTree.name,
  218. desc=reviewRuleTree.desc,
  219. owner=activity.owner,
  220. type="disposable",
  221. configs=nodes,
  222. sourceName=activity.name,
  223. taskCount=reviewRuleTree.taskCount,
  224. scoreRule=reviewRuleTree.scoreRule,
  225. distribute=reviewRuleTree.distribute,
  226. scoreDetail=reviewRuleTree.scoreDetail,
  227. };
  228. //代码顺序不能动
  229. var checkReult = CheckReviewRule(reviewRule, contest);
  230. if (checkReult.invalidCode==200) {
  231. await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Normal).UpsertItemAsync(reviewRule, new Azure.Cosmos.PartitionKey(reviewRule.code));
  232. if (reviewRuleTree.upsertAsTemplate==1)
  233. {
  234. reviewRule.code="ReviewRule-template";
  235. reviewRule.type="template";
  236. await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Normal).UpsertItemAsync(reviewRule, new Azure.Cosmos.PartitionKey(reviewRule.code));
  237. }
  238. }
  239. return (reviewRule,checkReult.invalidCode,checkReult.msg);
  240. }
  241. public static (int invalidCode, string msg) CheckReviewRule(ReviewRule reviewRule,Contest contest)
  242. {
  243. int invalidCode = -1;
  244. string msg = "";
  245. if (reviewRule.taskCount<=0) {
  246. invalidCode=28;
  247. msg="作品分配次数至少一次!";
  248. }
  249. if (reviewRule.taskCount==1 )
  250. {
  251. if (reviewRule.scoreRule.Equals("only"))
  252. {
  253. invalidCode=200;
  254. }
  255. else
  256. {
  257. invalidCode=21;//分配次数一次,必须匹配only
  258. msg="分配次数1次,必须匹配【默认统分】规则";
  259. }
  260. }
  261. if (reviewRule.taskCount>=2 )
  262. {
  263. if ((reviewRule.scoreRule.Equals("avg")||reviewRule.scoreRule.Equals("top"))) {
  264. invalidCode=200;
  265. }
  266. else
  267. {
  268. invalidCode=22;//分配次数2次,必须匹配avg,top
  269. msg="分配次数2次,必须匹配【按平均分】,【按最高分】";
  270. }
  271. }
  272. if (reviewRule.taskCount>=3 )
  273. {if ((reviewRule.scoreRule.Equals("avg")||reviewRule.scoreRule.Equals("top")||reviewRule.scoreRule.Equals("rmLowAvg")||reviewRule.scoreRule.Equals("rmTopAvg")))
  274. {
  275. invalidCode=200;
  276. }
  277. else
  278. {
  279. invalidCode=23;//分配次数2次,必须匹配avg,top,rmLowAvg,rmTopAvg
  280. msg="分配次数3次,必须匹配必须匹配【按平均分】,【按最高分】,【去掉最低分的平均分】,【去掉最高分的平均分】";
  281. }
  282. }
  283. if (reviewRule.taskCount>=4 )
  284. {
  285. if ((reviewRule.scoreRule.Equals("avg")||reviewRule.scoreRule.Equals("top")||reviewRule.scoreRule.Equals("rmLowAvg")||reviewRule.scoreRule.Equals("rmTopAvg")||reviewRule.scoreRule.Equals("rmLowTopAvg")))
  286. {
  287. invalidCode=200;
  288. }
  289. else
  290. {
  291. invalidCode=24;//分配次数2次,必须匹配avg,top,rmLowAvg,rmTopAvg,rmLowTopAvg
  292. msg="分配次数4次,必须匹配必须匹配【按平均分】,【按最高分】,【去掉最低分的平均分】,【去掉最高分的平均分】,【去掉最高分和最低分的平均分】";
  293. }
  294. }
  295. if (invalidCode==200) {
  296. if (!string.IsNullOrWhiteSpace(reviewRule.distribute))
  297. {
  298. if (reviewRule.distribute.Equals("period"))
  299. {
  300. var period = contest.sign?.fields?.Find(z => z.field.Equals("period"));
  301. if (period!= null)
  302. {
  303. invalidCode = 200;
  304. }
  305. else
  306. {
  307. invalidCode=25;
  308. msg="作品分配匹配规则为【学段】,但报名填写表单未配置。";
  309. }
  310. }
  311. else if (reviewRule.distribute.Equals("subject"))
  312. {
  313. var subject = contest.sign?.fields?.Find(z => z.field.Equals("subject"));
  314. if (subject!= null)
  315. {
  316. invalidCode = 200;
  317. }
  318. else
  319. {
  320. invalidCode=26;
  321. msg="作品分配匹配规则为【学科】,但报名填写表单未配置。";
  322. }
  323. }
  324. else if (reviewRule.distribute.Equals("periodAndSubject"))
  325. {
  326. var period = contest.sign?.fields?.Find(z => z.field.Equals("period"));
  327. var subject = contest.sign?.fields?.Find(z => z.field.Equals("subject"));
  328. if (subject!= null && period!= null)
  329. {
  330. invalidCode = 200;
  331. }
  332. else
  333. {
  334. invalidCode=27;
  335. msg="作品分配匹配规则为【学段,学科】,但报名填写表单未配置。";
  336. }
  337. }
  338. else {
  339. invalidCode=30;
  340. msg=$"作品分配匹配规则未识别【{reviewRule.distribute}】。";
  341. }
  342. }
  343. else {
  344. invalidCode=29;
  345. msg="作品分配匹配规则不能为空。";
  346. }
  347. }
  348. return (invalidCode,msg);
  349. }
  350. public static List<RuleConfig> TreeToList(List<RuleConfigTree> trees, List<RuleConfig> nodes) {
  351. trees = trees.OrderBy(x => x.order).ToList();
  352. List<RuleConfig> list = new List<RuleConfig>();
  353. trees.ForEach(x => {
  354. List<string> cids = new List<string>();
  355. if (x.children.IsNotEmpty())
  356. {
  357. x.children.ForEach(y => cids.Add(y.id));
  358. }
  359. var node = new RuleConfig
  360. {
  361. id = x.id,
  362. pid = x.pid,
  363. cids= cids,
  364. label = x.label,
  365. desc = x.desc,
  366. score = x.score,
  367. order = x.order,
  368. };
  369. list.Add(node);
  370. });
  371. nodes.AddRange(list);
  372. foreach (RuleConfigTree tree in trees)
  373. {
  374. if (null != tree.children && tree.children.Count > 0)
  375. {
  376. TreeToList(tree.children, nodes);
  377. }
  378. }
  379. return nodes;
  380. }
  381. public static List<RuleConfigTree> ListToTree(List<RuleConfig> noes)
  382. {
  383. List<RuleConfigTree> list = noes.ToJsonString().ToObject<List<RuleConfigTree>>();
  384. var res = from r in list group r by r.id into g select g;
  385. Dictionary<string, RuleConfigTree> blockDict = new Dictionary<string, RuleConfigTree>();
  386. foreach (var s in res)
  387. {
  388. blockDict.TryAdd(s.First().id, s.First());
  389. }
  390. return GetChild(list, blockDict);
  391. }
  392. private static List<RuleConfigTree> GetChild(List<RuleConfigTree> list, Dictionary<string, RuleConfigTree> dict)
  393. {
  394. List<RuleConfigTree> trees = new List<RuleConfigTree>();
  395. trees = trees.OrderBy(x => x.order).ToList();
  396. foreach (RuleConfigTree node in list)
  397. {
  398. bool flag = dict.TryGetValue(node.pid, out RuleConfigTree syllabus);
  399. if (flag && syllabus != null)
  400. {
  401. syllabus.children.Add(node);
  402. }
  403. else
  404. {
  405. trees.Add(node);
  406. }
  407. }
  408. return trees;
  409. }
  410. }
  411. }