ActivityService.cs 43 KB

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