ActivityService.cs 37 KB

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