ActivityService.cs 38 KB

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