ActivityService.cs 45 KB

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