ActivityService.cs 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832
  1. using Azure.Cosmos;
  2. using Azure.Storage.Blobs.Models;
  3. using Azure.Storage.Sas;
  4. using DocumentFormat.OpenXml.Bibliography;
  5. using DocumentFormat.OpenXml.Drawing;
  6. using DocumentFormat.OpenXml.Drawing.Charts;
  7. using DocumentFormat.OpenXml.Math;
  8. using DocumentFormat.OpenXml.Spreadsheet;
  9. using DocumentFormat.OpenXml.Wordprocessing;
  10. using HTEXLib.COMM.Helpers;
  11. using System;
  12. using System.Collections.Generic;
  13. using System.Diagnostics;
  14. using System.Linq;
  15. using System.Text;
  16. using System.Text.Json;
  17. using System.Threading.Tasks;
  18. using TEAMModelOS.SDK.DI;
  19. using TEAMModelOS.SDK.Extension;
  20. using TEAMModelOS.SDK.Models;
  21. using TEAMModelOS.Services;
  22. using Activity = TEAMModelOS.SDK.Models.Activity;
  23. namespace TEAMModelOS.SDK
  24. {
  25. public static class ActivityService
  26. {
  27. /// <summary>
  28. /// 分配作品
  29. /// </summary>
  30. /// <param name="_azureCosmos"></param>
  31. /// <param name="activity"></param>
  32. /// <returns></returns>
  33. public static async Task<(List<ExpertContestTaskDto> expertContestTasks, List<ExpertContestTaskDto > activityEnrollsInvalid, List<ExpertContestTaskDto> expertContestTasksDB)> AllocationTask(AzureCosmosFactory _azureCosmos, IEnumerable<ExpertPeriodSubjectDto> experts, Contest activity,HashSet<string> periodSubjectKeys,string distribute, int taskCount)
  34. {
  35. //检查报名,学段和科目是否匹配
  36. string enrollSQL = "select value c from c where c.pk='ActivityEnroll'";
  37. var resultActivityEnroll= await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher).GetList<ActivityEnroll>(enrollSQL, $"ActivityEnroll-{activity.id}");
  38. List<ExpertContestTaskDto > activityEnrollsInvalid = new List<ExpertContestTaskDto>();
  39. Dictionary<string, List<ActivityEnroll>> uploads = new Dictionary<string, List<ActivityEnroll>>();
  40. foreach (ActivityEnroll enroll in resultActivityEnroll.list)
  41. {
  42. var period = enroll.contest?.enrollInfos?.Find(z => z.code.Equals("period"));
  43. var subject = enroll.contest?.enrollInfos?.Find(z => z.code.Equals("subject"));
  44. string periodSubjectKey =string.Empty;
  45. int count = 0;
  46. if (enroll.upload != null && enroll.upload.sokrates.IsNotEmpty())
  47. {
  48. count += enroll.upload.sokrates.Count;
  49. }
  50. if (enroll.upload != null && enroll.upload.files.IsNotEmpty())
  51. {
  52. count += enroll.upload.files.Count;
  53. }
  54. //学段学科不匹配
  55. var taskDto = new ExpertContestTaskDto
  56. {
  57. uploadId = enroll.upload?.uploadId,
  58. name = $"{enroll?.schoolName}-{enroll?.tmdName}",
  59. uploadTypes = new List<string> { enroll?.upload.type },
  60. count = count,
  61. cipher = enroll.contest?.cipher,
  62. type = enroll.contest != null ? enroll.contest.type : 0,
  63. leader = enroll.contest != null ? enroll.contest.type : 0,
  64. tmdid = enroll.id,
  65. status = -1,
  66. score = -1,
  67. //detailScore=new List<RuleConfig>()
  68. members = new List<IdNameCode>(),
  69. periodSubjectKey = periodSubjectKey,
  70. period = period?.val,
  71. subject = subject?.val,
  72. };
  73. if (distribute.Equals("period"))
  74. {
  75. if (period != null)
  76. {
  77. periodSubjectKey = $"{period.val}-";
  78. }
  79. else {
  80. taskDto.available = 2;
  81. activityEnrollsInvalid.Add(taskDto);
  82. break;
  83. }
  84. }
  85. else if (distribute.Equals("subject"))
  86. {
  87. if (subject != null)
  88. {
  89. periodSubjectKey = $"-{subject.val}";
  90. }
  91. else {
  92. taskDto.available = 3;
  93. activityEnrollsInvalid.Add(taskDto);
  94. break;
  95. }
  96. }
  97. else if (distribute.Equals("periodAndSubject"))
  98. {
  99. if (period != null && subject != null)
  100. {
  101. periodSubjectKey = $"{period.val}-{subject.val}";
  102. }
  103. else {
  104. taskDto.available = 4;
  105. activityEnrollsInvalid.Add(taskDto);
  106. break;
  107. }
  108. }
  109. //else if (distribute.Equals("none"))
  110. //{
  111. //}
  112. else {
  113. periodSubjectKey = "-";
  114. }
  115. if (periodSubjectKeys.Contains(periodSubjectKey))
  116. {
  117. if (!string.IsNullOrWhiteSpace(enroll.upload?.uploadId))
  118. {
  119. if (uploads.ContainsKey(enroll.upload.uploadId))
  120. {
  121. uploads[enroll.upload.uploadId].Add(enroll);
  122. }
  123. else
  124. {
  125. uploads[enroll.upload.uploadId]= new List<ActivityEnroll>() { enroll};
  126. }
  127. }
  128. else {
  129. taskDto.available = 5;
  130. activityEnrollsInvalid.Add(taskDto);
  131. //未上传作品
  132. //activityEnrollsInvalid.Add((enroll, 2));
  133. }
  134. }
  135. else {
  136. taskDto.available =6;
  137. activityEnrollsInvalid.Add(taskDto);
  138. //学段学科不匹配
  139. //activityEnrollsInvalid.Add((enroll, 1));
  140. }
  141. }
  142. List<ExpertContestTaskDto> expertContestTasks = new List<ExpertContestTaskDto>();
  143. List<ExpertContestTaskDto> expertContestTasksDB = new List<ExpertContestTaskDto>();
  144. foreach (var upload in uploads.Keys)
  145. {
  146. var team = uploads[upload].FindAll(z => z.contest?.type==1);
  147. if (team.IsNotEmpty())
  148. {
  149. var leader = team.FindAll(z => z.contest?.leader==1);
  150. //有队长,说明是作品是一个人的队伍的作品。或者是由队长统一上传的作品。
  151. if (leader.IsNotEmpty())
  152. {
  153. List<IdNameCode> members = team.Select(z => new IdNameCode { id=z.id,code=z.schoolId,picture=z.tmdPicture,nickname=z.tmdName,name=z.contest?.enrollInfos?.Find(e=>e.code.Equals("name")).val}).ToList();
  154. //队长的报名信息
  155. var enroll = leader?.First();
  156. if (enroll!= null)
  157. {
  158. string name = enroll.contest?.teamName;
  159. var period = enroll.contest?.enrollInfos?.Find(z => z.code.Equals("period"));
  160. var subject = enroll.contest?.enrollInfos?.Find(z => z.code.Equals("subject"));
  161. string periodSubjectKey = $"{period?.val}-{subject?.val}";
  162. if (string.IsNullOrEmpty(name))
  163. {
  164. name=$"{enroll.contest?.enrollInfos?.Find(z => z.code.Equals("name"))?.val}({team.Count})";
  165. }
  166. int count = 0;
  167. if (enroll.upload.sokrates.IsNotEmpty())
  168. {
  169. count+=enroll.upload.sokrates.Count;
  170. }
  171. if (enroll.upload.files.IsNotEmpty())
  172. {
  173. count+=enroll.upload.files.Count;
  174. }
  175. expertContestTasks.Add(new ExpertContestTaskDto
  176. {
  177. uploadId = upload,
  178. name = $"{uploads[upload].First()?.schoolName}-{name}",
  179. uploadTypes = new List<string> { enroll?.upload.type },
  180. count = count,
  181. cipher = enroll.contest?.cipher,
  182. type = 1,
  183. leader = 1,
  184. tmdid = enroll.id,
  185. status = -1,
  186. score = -1,
  187. //detailScore=new List<RuleConfig>()
  188. members = members,
  189. periodSubjectKey = periodSubjectKey,
  190. period = period?.val,
  191. subject = subject?.val
  192. }) ;
  193. }
  194. }
  195. //没有队长,则是队员上传的作品(队员自己上传,不是队长统一上传的)、
  196. else {
  197. if (team.Count==1) {
  198. var enroll = team?.First();
  199. if (enroll!= null)
  200. {
  201. var period = enroll.contest?.enrollInfos?.Find(z => z.code.Equals("period"));
  202. var subject = enroll.contest?.enrollInfos?.Find(z => z.code.Equals("subject"));
  203. string periodSubjectKey = $"{period?.val}-{subject?.val}";
  204. string name = enroll.contest?.teamName;
  205. if (string.IsNullOrEmpty(name))
  206. {
  207. name=$"{enroll.contest?.enrollInfos?.Find(z => z.code.Equals("name"))?.val}({team.Count})";
  208. }
  209. int count = 0;
  210. if (enroll.upload.sokrates.IsNotEmpty())
  211. {
  212. count+=enroll.upload.sokrates.Count;
  213. }
  214. if (enroll.upload.files.IsNotEmpty())
  215. {
  216. count+=enroll.upload.files.Count;
  217. }
  218. expertContestTasks.Add(new ExpertContestTaskDto
  219. {
  220. uploadId = upload,
  221. name=$"{uploads[upload].First()?.schoolName}-{name}",
  222. uploadTypes=new List<string> { enroll?.upload.type },
  223. count=count,
  224. cipher=enroll.contest?.cipher,
  225. type=1,
  226. leader=0,
  227. tmdid=enroll.id,
  228. status=-1,
  229. score=-1,
  230. //detailScore=new List<RuleConfig>()
  231. // members= new List<IdNameCode>()
  232. periodSubjectKey=periodSubjectKey,
  233. period = period?.val,
  234. subject = subject?.val
  235. }) ;
  236. }
  237. }
  238. }
  239. }
  240. else {
  241. //个人
  242. var enroll = uploads[upload]?.First();
  243. if (enroll!= null) {
  244. var period = enroll.contest?.enrollInfos?.Find(z => z.code.Equals("period"));
  245. var subject = enroll.contest?.enrollInfos?.Find(z => z.code.Equals("subject"));
  246. string periodSubjectKey = $"{period?.val}-{subject?.val}";
  247. string name = enroll.contest?.enrollInfos?.Find(z => z.code.Equals("name"))?.val;
  248. if (!string.IsNullOrEmpty(name))
  249. {
  250. name=$"({name})";
  251. }
  252. else
  253. {
  254. name=string.Empty;
  255. }
  256. int count = 0;
  257. if (enroll.upload.sokrates.IsNotEmpty()) {
  258. count+=enroll.upload.sokrates.Count;
  259. }
  260. if (enroll.upload.files.IsNotEmpty())
  261. {
  262. count+=enroll.upload.files.Count;
  263. }
  264. expertContestTasks.Add(new ExpertContestTaskDto
  265. {
  266. uploadId = upload,
  267. name=$"{uploads[upload].First()?.schoolName}-{uploads[upload].First()?.tmdName}{name}",
  268. uploadTypes=new List<string> { enroll?.upload.type },
  269. count=count,
  270. cipher=null,
  271. type=0,
  272. leader=-1,
  273. tmdid=enroll.id,
  274. status=-1,
  275. score=-1,
  276. //detailScore=new List<RuleConfig>()
  277. //members= new List<IdNameCode>()
  278. periodSubjectKey=periodSubjectKey,
  279. period = period?.val,
  280. subject = subject?.val
  281. });
  282. }
  283. }
  284. }
  285. if (expertContestTasks.IsNotEmpty() && experts!=null && experts.Count()>0 && taskCount > 0) {
  286. string taskSQL = $"select value c from c where c.pk='ActivityExpertTask'";
  287. List<ExpertContestTaskDto> worksDB= new List<ExpertContestTaskDto>();
  288. var result = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher).GetList<ActivityExpertTask>(taskSQL, $"ActivityExpertTask-{activity.id}") ;
  289. if (result.list.IsNotEmpty()) {
  290. foreach(var item in result.list)
  291. {
  292. worksDB.AddRange(item.contestTasks.Select(z =>
  293. new ExpertContestTaskDto
  294. {
  295. expertId = item.id,
  296. available = 1,
  297. uploadId = z.uploadId,
  298. name = z.name,
  299. uploadTypes = z.uploadTypes,
  300. count = z.count,
  301. cipher=z.cipher,type=z.type,
  302. leader=z.leader,members=z.members,
  303. tmdid=z.tmdid,
  304. score=z.score,
  305. status=z.status,
  306. detailScore=z.detailScore
  307. }));
  308. }
  309. }
  310. var data = AssignWorksToExperts(expertContestTasks, worksDB, experts, taskCount);
  311. expertContestTasks = data.assignmentsAdd;
  312. expertContestTasksDB = worksDB;
  313. activityEnrollsInvalid.AddRange(data.assignmentsInvalid);
  314. }
  315. return (expertContestTasks, activityEnrollsInvalid, expertContestTasksDB);
  316. }
  317. public static (int available, string periodSubjectKey) CheckPeriodSubject(string distribute , EnrollInfo period, EnrollInfo subject, HashSet<string> periodSubjectKeys) {
  318. int available = -1;
  319. string periodSubjectKey= string.Empty;
  320. if (distribute.Equals("period"))
  321. {
  322. if (period != null)
  323. {
  324. periodSubjectKey = $"{period.val}-";
  325. }
  326. else
  327. {
  328. available = 2;
  329. }
  330. }
  331. else if (distribute.Equals("subject"))
  332. {
  333. if (subject != null)
  334. {
  335. periodSubjectKey = $"-{subject.val}";
  336. }
  337. else
  338. {
  339. available = 3;
  340. }
  341. }
  342. else if (distribute.Equals("periodAndSubject"))
  343. {
  344. if (period != null && subject != null)
  345. {
  346. periodSubjectKey = $"{period.val}-{subject.val}";
  347. }
  348. else
  349. {
  350. available = 4;
  351. }
  352. }
  353. else
  354. {
  355. periodSubjectKey = "-";
  356. }
  357. if (!string.IsNullOrWhiteSpace(periodSubjectKey)) {
  358. if (periodSubjectKeys.Contains(periodSubjectKey))
  359. {
  360. available = 0;
  361. }
  362. else
  363. {
  364. //学段学科不匹配
  365. available = 6;
  366. }
  367. }
  368. return (available, periodSubjectKey);
  369. }
  370. /// <summary>
  371. /// 将作品按分配次数,分配给不同的专家
  372. /// </summary>
  373. /// <param name="works"></param>
  374. /// <param name="experts"></param>
  375. /// <param name="N"></param>
  376. /// <returns></returns>
  377. static (List<ExpertContestTaskDto> assignmentsAdd, List<ExpertContestTaskDto> assignmentsInvalid) AssignWorksToExperts(List<ExpertContestTaskDto> works,List<ExpertContestTaskDto> worksDB, IEnumerable<ExpertPeriodSubjectDto> experts, int N)
  378. {
  379. //增加的
  380. var assignmentsAdd = new List<ExpertContestTaskDto>();
  381. //未分配到的
  382. var assignmentsInvalid = new List<ExpertContestTaskDto>();
  383. var random = new Random();
  384. //用于处理数据库和新分配的结果中检查已经分配过的作品和专家
  385. var assignmentsAll = worksDB.ToJsonString().ToObject<List<ExpertContestTaskDto>>();
  386. foreach (var work in works)
  387. {
  388. for (int i = 0; i < N; i++)
  389. {
  390. //分配次数已经Ok,分配结果完善的,不需要再去处理
  391. if (assignmentsAll.Count(z => z.uploadId.Equals(work.uploadId))>=N) {
  392. continue;
  393. }
  394. var availableExperts = experts
  395. .OrderBy(expert => assignmentsAll.Count(a => a.expertId == expert.expertId))
  396. .Where(expert => expert.periodSubjects.Contains(work.periodSubjectKey) && !assignmentsAll.Any(a => a.uploadId == work.uploadId && a.expertId == expert.expertId))
  397. .ToList();
  398. if (availableExperts.Count > 0)
  399. {
  400. var selectedExpert = availableExperts[random.Next(availableExperts.Count)];
  401. var newWork= work.DeepCopy<ExpertContestTaskDto>();
  402. newWork.expertId = selectedExpert.expertId;
  403. newWork.available = 1;
  404. newWork.turn = i + 1;
  405. assignmentsAdd.Add(newWork);
  406. assignmentsAll.Add(newWork);
  407. }
  408. else
  409. {
  410. var newWork = work.DeepCopy<ExpertContestTaskDto>();
  411. newWork.expertId =null;
  412. newWork.available = 7;
  413. newWork.turn = i + 1;
  414. //作品在第几轮未匹配到
  415. assignmentsInvalid.Add(newWork);
  416. //Console.WriteLine($"No available expert for WorkId {work.uploadId} and subject {work.periodSubjectKey} in attempt {i + 1}.");
  417. break;
  418. }
  419. }
  420. }
  421. return (assignmentsAdd, assignmentsInvalid);
  422. }
  423. /// <summary>
  424. /// 删除活动关联的数据
  425. /// </summary>
  426. /// <param name="_azureCosmos"></param>
  427. /// <param name="activity"></param>
  428. /// <returns></returns>
  429. public static async Task DeleteActivityRelated(AzureCosmosFactory _azureCosmos,Activity activity)
  430. {
  431. //删除模块,Contest, Training ,Research, ReviewRule规则(ReviewRule-disposable"; 存为活动)
  432. await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Common).DeleteItemStreamAsync(activity.id, new PartitionKey("Contest"));
  433. await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Common).DeleteItemStreamAsync(activity.id, new PartitionKey("Training"));
  434. await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Common).DeleteItemStreamAsync(activity.id, new PartitionKey("Research"));
  435. await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Normal).DeleteItemStreamAsync(activity.id, new PartitionKey("ReviewRule-disposable"));
  436. //删除邀请教师 ActivityTeacher,
  437. var resultTeacher = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher)
  438. .GetList<IdCode>("select c.id ,c.code from c where c.pk='ActivityEnroll' and c.", $"ActivityTeacher-{activity.id}");
  439. if (resultTeacher.list.IsNotEmpty())
  440. {
  441. await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher).DeleteItemsStreamAsync(resultTeacher.list.Select(z => z.id).ToList(), $"ActivityTeacher-{activity.id}");
  442. }
  443. //删除报名数据 ActivityEnroll
  444. var resultEnroll = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher)
  445. .GetList<IdCode>("select c.id ,c.code from c where c.pk='ActivityEnroll' and c.", $"ActivityEnroll-{activity.id}");
  446. if (resultEnroll.list.IsNotEmpty())
  447. {
  448. await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher).DeleteItemsStreamAsync(resultEnroll.list.Select(z => z.id).ToList(), $"ActivityEnroll-{activity.id}");
  449. }
  450. //删除专家数据 ActivityExpert
  451. await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher).DeleteItemStreamAsync(activity.id, new PartitionKey("ActivityExpert"));
  452. }
  453. /// <summary>
  454. /// 生成组队口令
  455. /// </summary>
  456. /// <param name="client"></param>
  457. /// <param name="_dingDing"></param>
  458. /// <param name="_option"></param>
  459. /// <param name="_activityId"></param>
  460. /// <returns></returns>
  461. /// <exception cref="Exception"></exception>
  462. public static async Task<string> GenCipher(CosmosClient client, DingDing _dingDing, TEAMModelOS.Models.Option _option, string _activityId)
  463. {
  464. string _num09 = "123456789";
  465. string no = $"{Utils.CreatSaltString(7, _num09)}";
  466. for (int i = 0; i < 10; i++)
  467. {
  468. List<SheetConfig> sheets = new List<SheetConfig>();
  469. bool hasCurrFrom = false;
  470. 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}' ";
  471. var cipherResult = await client.GetContainer(Constant.TEAMModelOS, Constant.Teacher).GetList<string>(cipherSQL, $"ActivityEnroll-{_activityId}");
  472. if (cipherResult.list.IsNotEmpty() && cipherResult.list.Count>0) {
  473. hasCurrFrom = true;
  474. }
  475. if (hasCurrFrom)
  476. {
  477. if (i == 9)
  478. {
  479. string msg = $"{_option.Location},ActivityService/GenCipher\n 组队口令生成异常,重复生成次数超过10次";
  480. await _dingDing.SendBotMsg($"OS,{_option.Location},{msg}", GroupNames.醍摩豆服務運維群組);
  481. throw new Exception(msg);
  482. }
  483. else
  484. {
  485. no = $"{Utils.CreatSaltString(7, _num09)}";
  486. }
  487. }
  488. else { break; }
  489. }
  490. return no;
  491. }
  492. public static async Task<List<TeacherActivityDto>> TeacherActivityList(AzureCosmosFactory _azureCosmos,AzureStorageFactory _azureStorage, JsonElement request, string tmdid)
  493. {
  494. List<TeacherActivityDto> activities = new List<TeacherActivityDto>();
  495. HashSet<string> inviteActivityIds = new HashSet<string>();
  496. string yearSql = $" and c.year={DateTimeOffset.Now.Year}";
  497. if (request.TryGetProperty("year", out JsonElement _year) && int.TryParse($"{_year}",out int year ))
  498. {
  499. yearSql = $" and c.year={year}";
  500. }
  501. //先获取邀请制的
  502. string sqlInvite = $"select value c from c join t in c.inviteTeachers where t.id='{tmdid}' and c.pk='ActivityTeacher'";
  503. var result = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Common).GetList<ActivityTeacher>(sqlInvite);
  504. inviteActivityIds= result.list.Select(z => z.id).ToHashSet();
  505. if (inviteActivityIds.Count>0)
  506. {
  507. string sqlActivity = $"select value c from c where c.id in ({string.Join(",", inviteActivityIds.Select(z => $"'{z}'"))}) {yearSql} ";
  508. var resultActivity = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Common).GetList<TeacherActivityDto>(sqlInvite, "Activity");
  509. if (resultActivity.list.IsNotEmpty())
  510. {
  511. activities.AddRange(resultActivity.list);
  512. }
  513. }
  514. Teacher teacher = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher).ReadItemAsync<Teacher>(tmdid, new PartitionKey("Base"));
  515. string schoolOwnerIn = string.Empty;
  516. string schoolIdIn = string.Empty;
  517. if (teacher.schools.IsNotEmpty())
  518. {
  519. schoolIdIn = $"and i.id in ({string.Join(",", teacher.schools.Select(z => $"'{z.schoolId}'"))})";
  520. schoolOwnerIn= $"and c.owner in ({string.Join(",", teacher.schools.Select(z => $"'{z.schoolId}'"))})";
  521. }
  522. //获取开放的
  523. {
  524. //完全开放 所有的学校
  525. 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} ";
  526. var resultOpen = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Common).GetList<TeacherActivityDto>(sqlOpen, "Activity");
  527. activities.AddRange(resultOpen.list);
  528. if (!string.IsNullOrWhiteSpace(schoolIdIn))
  529. { //部分学校
  530. 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} ";
  531. var resultSchool = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Common).GetList<TeacherActivityDto>(sqlSchool, "Activity");
  532. activities.AddRange(resultSchool.list);
  533. }
  534. }
  535. string areaOwnerIn = string.Empty;
  536. var hasAreaSchools = teacher.schools.FindAll(z => !string.IsNullOrWhiteSpace(z.areaId));
  537. if (hasAreaSchools.IsNotEmpty())
  538. {
  539. areaOwnerIn = $"and c.owner in ({string.Join(",", hasAreaSchools.Select(z => $"'{z.areaId}'"))})";
  540. schoolIdIn = $"and i.id in ({string.Join(",", hasAreaSchools.Select(z => $"'{z.schoolId}'"))})";
  541. }
  542. //获取所有区级的
  543. if (!string.IsNullOrWhiteSpace(areaOwnerIn) && !string.IsNullOrEmpty(schoolIdIn))
  544. {
  545. 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} ";
  546. var resultOpen = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Common).GetList<TeacherActivityDto>(sqlOpen, "Activity");
  547. activities.AddRange(resultOpen.list);
  548. }
  549. //获取所有学校的
  550. if (!string.IsNullOrWhiteSpace(schoolOwnerIn))
  551. {
  552. 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} ";
  553. var resultSchool = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Common).GetList<TeacherActivityDto>(sqlSchool, "Activity");
  554. activities.AddRange(resultSchool.list);
  555. }
  556. activities.ForEach(z =>
  557. {
  558. var (blob_uri, blob_sas) = _azureStorage.GetBlobContainerSAS(z.owner, BlobContainerSasPermissions.Read);
  559. z.sas=blob_sas;
  560. });
  561. if (activities.IsNotEmpty())
  562. {
  563. 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}'"))})";
  564. var resultEnroll = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher).GetList<ActivityEnroll>(sql);
  565. foreach (var enroll in resultEnroll.list)
  566. {
  567. var activityDto = activities.Find(z => z.id.Equals(enroll.activityId));
  568. if (activityDto!=null)
  569. {
  570. if (enroll.contest!=null && !string.IsNullOrWhiteSpace(enroll.contest.cipher))
  571. {
  572. activityDto.contestSign=1;
  573. activityDto.signTime=enroll.contest.enrollTime;
  574. activityDto.contestType=enroll.contest.type;
  575. }
  576. if (enroll.upload!=null && (enroll.upload.files.IsNotEmpty() || enroll.upload.sokrates.IsNotEmpty()))
  577. {
  578. activityDto.contestUpload=1;
  579. activityDto.uploadTime=enroll.upload.uploadTime;
  580. activityDto.uploadType=enroll.upload.type;
  581. }
  582. }
  583. }
  584. }
  585. return activities;
  586. }
  587. public static async Task<(ReviewRule reviewRule, int invalidCode, string msg)> UpsertReviewRule(ReviewRuleTree reviewRuleTree,Activity activity, Contest contest, AzureCosmosFactory _azureCosmos)
  588. {
  589. var nodes = new List<RuleConfig>();
  590. nodes= TreeToList(reviewRuleTree.trees, nodes);
  591. ReviewRule reviewRule = new ReviewRule() {
  592. id= activity.id,
  593. code="ReviewRule-disposable",
  594. pk="ReviewRule",
  595. name=reviewRuleTree.name,
  596. desc=reviewRuleTree.desc,
  597. owner=activity.owner,
  598. type="disposable",
  599. configs=nodes,
  600. sourceName=activity.name,
  601. taskCount=reviewRuleTree.taskCount,
  602. scoreRule=reviewRuleTree.scoreRule,
  603. distribute=reviewRuleTree.distribute,
  604. scoreDetail=reviewRuleTree.scoreDetail,
  605. };
  606. //代码顺序不能动
  607. var checkReult = CheckReviewRule(reviewRule, contest);
  608. if (checkReult.invalidCode==200) {
  609. await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Normal).UpsertItemAsync(reviewRule, new Azure.Cosmos.PartitionKey(reviewRule.code));
  610. if (reviewRuleTree.upsertAsTemplate==1)
  611. {
  612. reviewRule.code="ReviewRule-template";
  613. reviewRule.type="template";
  614. reviewRule.taskCount = reviewRuleTree.taskCount;
  615. reviewRule.scoreRule = reviewRuleTree.scoreRule;
  616. reviewRule.distribute = reviewRuleTree.distribute;
  617. reviewRule.scoreDetail = reviewRuleTree.scoreDetail;
  618. await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Normal).UpsertItemAsync(reviewRule, new Azure.Cosmos.PartitionKey(reviewRule.code));
  619. }
  620. }
  621. return (reviewRule,checkReult.invalidCode,checkReult.msg);
  622. }
  623. public static (int invalidCode, string msg) CheckReviewRule(ReviewRule reviewRule,Contest contest)
  624. {
  625. int invalidCode = -1;
  626. string msg = "";
  627. if (reviewRule.taskCount<=0) {
  628. invalidCode=28;
  629. msg="作品分配次数至少一次!";
  630. }
  631. if (reviewRule.taskCount==1 )
  632. {
  633. if (reviewRule.scoreRule.Equals("only"))
  634. {
  635. invalidCode=200;
  636. }
  637. else
  638. {
  639. invalidCode=21;//分配次数一次,必须匹配only
  640. msg="分配次数1次,必须匹配【默认统分】规则";
  641. }
  642. }
  643. if (reviewRule.taskCount>=2 )
  644. {
  645. if ((reviewRule.scoreRule.Equals("avg")||reviewRule.scoreRule.Equals("top"))) {
  646. invalidCode=200;
  647. }
  648. else
  649. {
  650. invalidCode=22;//分配次数2次,必须匹配avg,top
  651. msg="分配次数2次,必须匹配【按平均分】,【按最高分】";
  652. }
  653. }
  654. if (reviewRule.taskCount>=3 )
  655. {if ((reviewRule.scoreRule.Equals("avg")||reviewRule.scoreRule.Equals("top")||reviewRule.scoreRule.Equals("rmLowAvg")||reviewRule.scoreRule.Equals("rmTopAvg")))
  656. {
  657. invalidCode=200;
  658. }
  659. else
  660. {
  661. invalidCode=23;//分配次数2次,必须匹配avg,top,rmLowAvg,rmTopAvg
  662. msg="分配次数3次,必须匹配必须匹配【按平均分】,【按最高分】,【去掉最低分的平均分】,【去掉最高分的平均分】";
  663. }
  664. }
  665. if (reviewRule.taskCount>=4 )
  666. {
  667. if ((reviewRule.scoreRule.Equals("avg")||reviewRule.scoreRule.Equals("top")||reviewRule.scoreRule.Equals("rmLowAvg")||reviewRule.scoreRule.Equals("rmTopAvg")||reviewRule.scoreRule.Equals("rmLowTopAvg")))
  668. {
  669. invalidCode=200;
  670. }
  671. else
  672. {
  673. invalidCode=24;//分配次数2次,必须匹配avg,top,rmLowAvg,rmTopAvg,rmLowTopAvg
  674. msg="分配次数4次,必须匹配必须匹配【按平均分】,【按最高分】,【去掉最低分的平均分】,【去掉最高分的平均分】,【去掉最高分和最低分的平均分】";
  675. }
  676. }
  677. if (invalidCode==200) {
  678. if (!string.IsNullOrWhiteSpace(reviewRule.distribute))
  679. {
  680. if (reviewRule.distribute.Equals("period"))
  681. {
  682. var period = contest.sign?.fields?.Find(z => z.field.Equals("period"));
  683. if (period!= null)
  684. {
  685. invalidCode = 200;
  686. }
  687. else
  688. {
  689. invalidCode=25;
  690. msg="作品分配匹配规则为【学段】,但报名填写表单未配置。";
  691. }
  692. }
  693. else if (reviewRule.distribute.Equals("subject"))
  694. {
  695. var subject = contest.sign?.fields?.Find(z => z.field.Equals("subject"));
  696. if (subject!= null)
  697. {
  698. invalidCode = 200;
  699. }
  700. else
  701. {
  702. invalidCode=26;
  703. msg="作品分配匹配规则为【学科】,但报名填写表单未配置。";
  704. }
  705. }
  706. else if (reviewRule.distribute.Equals("periodAndSubject"))
  707. {
  708. var period = contest.sign?.fields?.Find(z => z.field.Equals("period"));
  709. var subject = contest.sign?.fields?.Find(z => z.field.Equals("subject"));
  710. if (subject!= null && period!= null)
  711. {
  712. invalidCode = 200;
  713. }
  714. else
  715. {
  716. invalidCode=27;
  717. msg="作品分配匹配规则为【学段,学科】,但报名填写表单未配置。";
  718. }
  719. }
  720. else if (reviewRule.distribute.Equals("none"))
  721. {
  722. invalidCode=200;
  723. }
  724. else
  725. {
  726. invalidCode=30;
  727. msg=$"作品分配匹配规则未识别【{reviewRule.distribute}】。";
  728. }
  729. }
  730. else {
  731. invalidCode=29;
  732. msg="作品分配匹配规则不能为空。";
  733. }
  734. }
  735. return (invalidCode,msg);
  736. }
  737. public static List<RuleConfig> TreeToList(List<RuleConfigTree> trees, List<RuleConfig> nodes) {
  738. trees = trees.OrderBy(x => x.order).ToList();
  739. List<RuleConfig> list = new List<RuleConfig>();
  740. trees.ForEach(x => {
  741. List<string> cids = new List<string>();
  742. if (x.children.IsNotEmpty())
  743. {
  744. x.children.ForEach(y => cids.Add(y.id));
  745. }
  746. var node = new RuleConfig
  747. {
  748. id = x.id,
  749. pid = x.pid,
  750. cids= cids,
  751. label = x.label,
  752. desc = x.desc,
  753. score = x.score,
  754. order = x.order,
  755. };
  756. list.Add(node);
  757. });
  758. nodes.AddRange(list);
  759. foreach (RuleConfigTree tree in trees)
  760. {
  761. if (null != tree.children && tree.children.Count > 0)
  762. {
  763. TreeToList(tree.children, nodes);
  764. }
  765. }
  766. return nodes;
  767. }
  768. public static List<RuleConfigTree> ListToTree(List<RuleConfig> noes)
  769. {
  770. List<RuleConfigTree> list = noes.ToJsonString().ToObject<List<RuleConfigTree>>();
  771. var res = from r in list group r by r.id into g select g;
  772. Dictionary<string, RuleConfigTree> blockDict = new Dictionary<string, RuleConfigTree>();
  773. foreach (var s in res)
  774. {
  775. blockDict.TryAdd(s.First().id, s.First());
  776. }
  777. return GetChild(list, blockDict);
  778. }
  779. private static List<RuleConfigTree> GetChild(List<RuleConfigTree> list, Dictionary<string, RuleConfigTree> dict)
  780. {
  781. List<RuleConfigTree> trees = new List<RuleConfigTree>();
  782. trees = trees.OrderBy(x => x.order).ToList();
  783. foreach (RuleConfigTree node in list)
  784. {
  785. bool flag = dict.TryGetValue(node.pid, out RuleConfigTree syllabus);
  786. if (flag && syllabus != null)
  787. {
  788. syllabus.children.Add(node);
  789. }
  790. else
  791. {
  792. trees.Add(node);
  793. }
  794. }
  795. return trees;
  796. }
  797. }
  798. }