ActivityService.cs 52 KB

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