ActivityService.cs 41 KB

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