ActivityService.cs 46 KB

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