StudentService.cs 131 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356
  1. using Azure;
  2. using Azure.Cosmos;
  3. using Azure.Messaging.ServiceBus;
  4. using HTEXLib.COMM.Helpers;
  5. using Microsoft.Extensions.Configuration;
  6. using System;
  7. using System.Collections.Generic;
  8. using System.IO;
  9. using System.Linq;
  10. using System.Net;
  11. using System.Text;
  12. using System.Text.Json;
  13. using System.Threading.Tasks;
  14. using TEAMModelOS.Models;
  15. using TEAMModelOS.SDK.DI;
  16. using TEAMModelOS.SDK.Extension;
  17. using TEAMModelOS.SDK.Models;
  18. namespace TEAMModelOS.SDK
  19. {
  20. public class StudentService
  21. {
  22. /// <summary>
  23. ///
  24. /// </summary>
  25. /// <param name="schoolId"></param>
  26. /// <param name="students"></param>
  27. /// <returns></returns>
  28. public static async Task<List<Student>> GeStudentData(AzureCosmosFactory _azureCosmos, string schoolId, IEnumerable<string> students)
  29. {
  30. List<Student> studentDatas = new List<Student>();
  31. if (students.Any())
  32. {
  33. string queryText = $"SELECT c.id, c.code ,c.classId FROM c WHERE c.id IN ({string.Join(",", students.Select(o => $"'{o}'"))})";
  34. await foreach (var item in _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Student").GetItemQueryIterator<Student>(queryText: queryText, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Base-{schoolId}") }))
  35. {
  36. studentDatas.Add(item);
  37. }
  38. }
  39. return studentDatas;
  40. }
  41. /// <summary>
  42. ///
  43. /// </summary>
  44. /// <param name="schoolId">学校编码</param>
  45. /// <param name="students">前端要修改的学生</param>
  46. /// <param name="opt">操作</param>
  47. /// <param name="prestudents">变更前的学生</param>
  48. /// <returns></returns>
  49. public static async Task<Dictionary<string, GroupChange>> CheckStudent(AzureServiceBusFactory _serviceBus, IConfiguration _configuration, AzureCosmosFactory _azureCosmos, string schoolId, List<Student> students, List<Student> prestudents)
  50. {
  51. List<Student> aftstudents = await StudentService.GeStudentData(_azureCosmos, schoolId, students?.Select(x => x.id));
  52. Dictionary<string, GroupChange> dictChange = new Dictionary<string, GroupChange>();
  53. if (prestudents.Count >= aftstudents.Count)
  54. {
  55. foreach (var pstu in prestudents)
  56. {
  57. var afstu = aftstudents.Find(x => x.id.Equals(pstu.id));
  58. if (afstu != null)
  59. {
  60. if (string.IsNullOrEmpty(pstu.classId) && !string.IsNullOrEmpty(afstu.classId))
  61. {
  62. //则是加入的学生
  63. if (dictChange.ContainsKey(afstu.classId))
  64. {
  65. dictChange[afstu.classId].stujoin.Add(
  66. new Member
  67. {
  68. id = afstu.id,
  69. code = $"{schoolId }",
  70. type = 2
  71. });
  72. }
  73. else
  74. {
  75. GroupChange change = new GroupChange
  76. {
  77. scope = "school",
  78. school = schoolId,
  79. type = "student",
  80. originCode = schoolId,
  81. listid = afstu.classId,
  82. stujoin = new List<Member>
  83. {
  84. new Member
  85. {
  86. id= afstu.id,
  87. code= $"{schoolId}",
  88. type =2
  89. }
  90. }
  91. };
  92. dictChange.Add(afstu.classId, change);
  93. }
  94. }
  95. else if (!string.IsNullOrEmpty(pstu.classId) && string.IsNullOrEmpty(afstu.classId))
  96. {
  97. //则该学生是解除了班级状态
  98. if (dictChange.ContainsKey(pstu.classId))
  99. {
  100. dictChange[pstu.classId].stuleave.Add(
  101. new Member
  102. {
  103. id = pstu.id,
  104. code = $"{schoolId }",
  105. type = 2
  106. });
  107. }
  108. else
  109. {
  110. GroupChange change = new GroupChange
  111. {
  112. scope = "school",
  113. school = schoolId,
  114. type = "student",
  115. originCode = schoolId,
  116. listid = pstu.classId,
  117. stuleave = new List<Member>
  118. {
  119. new Member
  120. {
  121. id= pstu.id,
  122. code= $"{schoolId}",
  123. type=2
  124. }
  125. }
  126. };
  127. dictChange.Add(pstu.classId, change);
  128. }
  129. }
  130. else if (!string.IsNullOrEmpty(pstu.classId) && !string.IsNullOrEmpty(afstu.classId))
  131. {
  132. //id不同则有异动
  133. if (!pstu.classId.Equals(afstu.classId))
  134. {
  135. //则是加入的学生
  136. if (dictChange.ContainsKey(afstu.classId))
  137. {
  138. dictChange[afstu.classId].stujoin.Add(
  139. new Member
  140. {
  141. id = afstu.id,
  142. code = $"{schoolId }",
  143. type = 2
  144. });
  145. }
  146. else
  147. {
  148. GroupChange change = new GroupChange
  149. {
  150. scope = "school",
  151. school = schoolId,
  152. type = "student",
  153. originCode = schoolId,
  154. listid = afstu.classId,
  155. stujoin = new List<Member>
  156. {
  157. new Member
  158. {
  159. id= afstu.id,
  160. code= $"{schoolId}",
  161. type = 2
  162. }
  163. }
  164. };
  165. dictChange.Add(afstu.classId, change);
  166. }//则该学生是解除了班级状态
  167. if (dictChange.ContainsKey(pstu.classId))
  168. {
  169. dictChange[pstu.classId].stuleave.Add(
  170. new Member
  171. {
  172. id = pstu.id,
  173. code = $"{schoolId }",
  174. type = 2
  175. });
  176. }
  177. else
  178. {
  179. GroupChange change = new GroupChange
  180. {
  181. scope = "school",
  182. school = schoolId,
  183. type = "student",
  184. originCode = schoolId,
  185. listid = pstu.classId,
  186. stuleave = new List<Member>
  187. {
  188. new Member
  189. {
  190. id= pstu.id,
  191. code= $"{schoolId}",
  192. type= 2
  193. }
  194. }
  195. };
  196. dictChange.Add(pstu.classId, change);
  197. }
  198. }
  199. else
  200. {
  201. //相同则不管
  202. }
  203. }
  204. else
  205. {
  206. //变更前后都没绑定班级则不管。
  207. }
  208. }
  209. else
  210. {
  211. if (!string.IsNullOrEmpty(pstu.classId))
  212. {
  213. //则该学生是被删除的学生
  214. if (dictChange.ContainsKey(pstu.classId))
  215. {
  216. dictChange[pstu.classId].stuleave.Add(
  217. new Member
  218. {
  219. id = pstu.id,
  220. code = $"Base-{schoolId }",
  221. type = 2
  222. });
  223. }
  224. else
  225. {
  226. GroupChange change = new GroupChange
  227. {
  228. scope = "school",
  229. school = schoolId,
  230. type = "student",
  231. originCode = schoolId,
  232. listid = pstu.classId,
  233. stuleave = new List<Member>
  234. {
  235. new Member
  236. {
  237. id= pstu.id,
  238. code= $"{schoolId}",
  239. type = 2
  240. }
  241. }
  242. };
  243. dictChange.Add(pstu.classId, change);
  244. }
  245. }
  246. }
  247. }
  248. }
  249. else
  250. {
  251. foreach (var afstu in aftstudents)
  252. {
  253. var pstu = prestudents.Find(x => x.id.Equals(afstu.id));
  254. if (pstu != null)
  255. {
  256. if (string.IsNullOrEmpty(pstu.classId) && !string.IsNullOrEmpty(afstu.classId))
  257. {
  258. //则是加入的学生
  259. if (dictChange.ContainsKey(afstu.classId))
  260. {
  261. dictChange[afstu.classId].stujoin.Add(
  262. new Member
  263. {
  264. id = afstu.id,
  265. code = $"{schoolId }",
  266. type = 2
  267. });
  268. }
  269. else
  270. {
  271. GroupChange change = new GroupChange
  272. {
  273. scope = "school",
  274. school = schoolId,
  275. type = "student",
  276. originCode = schoolId,
  277. listid = afstu.classId,
  278. stujoin = new List<Member>
  279. {
  280. new Member
  281. {
  282. id= afstu.id,
  283. code= $"Base-{schoolId}",
  284. type = 2
  285. }
  286. }
  287. };
  288. dictChange.Add(afstu.classId, change);
  289. }
  290. }
  291. else if (!string.IsNullOrEmpty(pstu.classId) && string.IsNullOrEmpty(afstu.classId))
  292. {
  293. //则该学生是解除了班级状态
  294. if (dictChange.ContainsKey(pstu.classId))
  295. {
  296. dictChange[pstu.classId].stuleave.Add(
  297. new Member
  298. {
  299. id = pstu.id,
  300. code = $"{schoolId }",
  301. type = 2
  302. });
  303. }
  304. else
  305. {
  306. GroupChange change = new GroupChange
  307. {
  308. scope = "school",
  309. school = schoolId,
  310. type = "student",
  311. originCode = schoolId,
  312. listid = pstu.classId,
  313. stuleave = new List<Member>
  314. {
  315. new Member
  316. {
  317. id= pstu.id,
  318. code= $"{schoolId}",
  319. type= 2
  320. }
  321. }
  322. };
  323. dictChange.Add(pstu.classId, change);
  324. }
  325. }
  326. else if (!string.IsNullOrEmpty(pstu.classId) && !string.IsNullOrEmpty(afstu.classId))
  327. {
  328. //id不同则有异动
  329. if (!pstu.classId.Equals(afstu.classId))
  330. {
  331. //则是加入的学生
  332. if (dictChange.ContainsKey(afstu.classId))
  333. {
  334. dictChange[afstu.classId].stujoin.Add(
  335. new Member
  336. {
  337. id = afstu.id,
  338. code = $"{schoolId }",
  339. type = 2
  340. });
  341. }
  342. else
  343. {
  344. GroupChange change = new GroupChange
  345. {
  346. scope = "school",
  347. school = schoolId,
  348. type = "student",
  349. originCode = schoolId,
  350. listid = afstu.classId,
  351. stujoin = new List<Member>
  352. {
  353. new Member
  354. {
  355. id= afstu.id,
  356. code= $"{schoolId}",
  357. type = 2
  358. }
  359. }
  360. };
  361. dictChange.Add(afstu.classId, change);
  362. }//则该学生是解除了班级状态
  363. if (dictChange.ContainsKey(pstu.classId))
  364. {
  365. dictChange[pstu.classId].stuleave.Add(
  366. new Member
  367. {
  368. id = pstu.id,
  369. code = $"{schoolId }",
  370. type = 2
  371. });
  372. }
  373. else
  374. {
  375. GroupChange change = new GroupChange
  376. {
  377. scope = "school",
  378. school = schoolId,
  379. type = "student",
  380. originCode = schoolId,
  381. listid = pstu.classId,
  382. stuleave = new List<Member>
  383. {
  384. new Member
  385. {
  386. id= pstu.id,
  387. code= $"{schoolId}",
  388. type=2
  389. }
  390. }
  391. };
  392. dictChange.Add(pstu.classId, change);
  393. }
  394. }
  395. else
  396. {
  397. //相同则不管
  398. }
  399. }
  400. else
  401. {
  402. //变更前后都没绑定班级则不管。
  403. }
  404. }
  405. else
  406. {
  407. if (!string.IsNullOrEmpty(afstu.classId))
  408. {
  409. //代表学生是新增的
  410. if (dictChange.ContainsKey(afstu.classId))
  411. {
  412. dictChange[afstu.classId].stujoin.Add(
  413. new Member
  414. {
  415. id = afstu.id,
  416. code = $"{schoolId }",
  417. type = 2
  418. });
  419. }
  420. else
  421. {
  422. GroupChange change = new GroupChange
  423. {
  424. scope = "school",
  425. school = schoolId,
  426. type = "student",
  427. originCode = schoolId,
  428. listid = afstu.classId,
  429. stujoin = new List<Member>
  430. {
  431. new Member
  432. {
  433. id= afstu.id,
  434. code= $"{schoolId}",
  435. type = 2
  436. }
  437. }
  438. };
  439. dictChange.Add(afstu.classId, change);
  440. }
  441. }
  442. }
  443. }
  444. }
  445. foreach (var changed in dictChange.Keys)
  446. {
  447. var change = dictChange[changed];
  448. if (change.stujoin.Count != 0 || change.stuleave.Count != 0)
  449. {
  450. var messageChange = new ServiceBusMessage(change.ToJsonString());
  451. messageChange.ApplicationProperties.Add("name", "GroupChange");
  452. var ActiveTask = _configuration.GetValue<string>("Azure:ServiceBus:ActiveTask");
  453. await _serviceBus.GetServiceBusClient().SendMessageAsync(ActiveTask, messageChange);
  454. }
  455. }
  456. return dictChange;
  457. }
  458. public record StudentInfo
  459. {
  460. public string studentId { get; set; }
  461. public string picture { get; set; }
  462. public string name { get; set; }
  463. public string mobile { get; set; }
  464. public string mail { get; set; }
  465. /// <summary>
  466. /// f女性 m男性 n 保密
  467. /// </summary>
  468. public string gender { get; set; }
  469. }
  470. /// <summary>
  471. /// 整理前端匯入的學生資訊
  472. /// </summary>
  473. /// <param name="schoolId"></param>
  474. /// <param name="students"></param>
  475. /// <returns></returns>
  476. private static (Dictionary<string, (string name, string no, int year, string salt, string pw, string classNo, string className, string periodId, int classYear, string guardian, string gName, string gPhone,string imei, List<StudentGuardian> guardians)> studs,
  477. Dictionary<string, (string className, string periodId, int year, string no)> classInfo,
  478. Dictionary<string, List<(string id, string no)>> classStudNo,
  479. List<string> errorYear,
  480. List<string> duplId) doSortImpStuds(string schoolId, JsonElement.ArrayEnumerator students)
  481. {
  482. //批量匯入 檢查輸入數據 確認座號 確認教室(創建教室) 確認學生存不存在或是要不要更新
  483. //存放輸入的學生資訊 key:stud id value:學生詳細資料
  484. Dictionary<string, (string name, string no, int year, string salt, string pw, string classNo, string className, string periodId, int classYear, string guardian, string gName, string gPhone, string imei, List<StudentGuardian> guardians) > dicStuds =
  485. new Dictionary<string, (string name, string no, int year, string salt, string pw, string classNo, string className, string periodId, int classYear, string guardian, string gName, string gPhone, string imei, List<StudentGuardian> guardians)>();
  486. //存放教室資訊用 key:classNo value:className
  487. Dictionary<string, (string className, string periodId, int year, string classNo)> dicClassInfo = new Dictionary<string, (string className, string periodId, int year, string classNo)>();
  488. //存放欲加入該間教室的學生座號清單 key:classNo value:no list
  489. Dictionary<string, List<(string id, string no)>> dicClassStudNo = new Dictionary<string, List<(string id, string no)>>();
  490. //存放輸入id重複
  491. List<string> duplId = new List<string>();
  492. List<string> errorYear = new List<string>();
  493. while (students.MoveNext())
  494. {
  495. //string id = null, no = null, name = null, year = null, pw = null, classNo = null, className = null;
  496. JsonElement student = students.Current;
  497. //ClassNo內的座號
  498. //欲加入的教室
  499. //查學生
  500. //該間教室的座號與目前欲更新的是否有重複 有些可能是同教室換座號 反正都要將學生讀出來
  501. //舊學生完整資料+新學生資料
  502. //進行輸入資料的整理
  503. if (student.TryGetProperty("id", out var tmpId))
  504. {
  505. string id = tmpId.GetString();
  506. //如果id欄位是空的,則跳過該筆資料
  507. if (string.IsNullOrWhiteSpace(id)) continue;
  508. //輸入的id不應有重複
  509. if (dicStuds.ContainsKey(id))
  510. {
  511. //如果id重複,則將之從整理清單內清除
  512. duplId.Add(id);
  513. dicStuds.Remove(id);
  514. }
  515. (string name, string no, int year, string salt, string pw, string classNo, string className, string periodId, int classYear, string guardian, string gName, string gPhone, string imei, List<StudentGuardian> guardians) studentInfo = (null, null, 0, null, null, null, null, null, 0, null, null, null, null, null) ;
  516. if (student.TryGetProperty("name", out var tmpName) && !string.IsNullOrWhiteSpace(tmpName.GetString())) studentInfo.name = tmpName.GetString();
  517. //入學學年為必須,故若是無給值則將之紀錄並跳過該筆資料
  518. if (student.TryGetProperty("year", out var tmpYear) && !string.IsNullOrWhiteSpace(Convert.ToString(tmpYear))) studentInfo.year = tmpYear.GetInt32();
  519. else
  520. {
  521. errorYear.Add(id);
  522. continue;
  523. }
  524. //Password,若沒給則使用學號當密碼
  525. studentInfo.salt = Utils.CreatSaltString(8);
  526. studentInfo.pw = student.TryGetProperty("pw", out var tmpPw) && !string.IsNullOrWhiteSpace(tmpPw.GetString())
  527. ? Utils.HashedPassword(tmpPw.GetString(), studentInfo.salt)
  528. : Utils.HashedPassword(id, studentInfo.salt);
  529. if (student.TryGetProperty("periodId", out var tmpPeriodId) && !string.IsNullOrWhiteSpace(tmpPeriodId.GetString())) studentInfo.periodId = tmpPeriodId.GetString();
  530. // if (student.TryGetProperty("gradeIndex", out var tmpGradeIndex)) studentInfo.gradeIndex = tmpGradeIndex.GetInt32();
  531. if (student.TryGetProperty("classNo", out var tmpClassNo) && !string.IsNullOrWhiteSpace(tmpClassNo.GetString()))
  532. {
  533. studentInfo.classNo = tmpClassNo.GetString();
  534. //在新建帳號上,應要給classNo才能設定no,但是若只是已存在的帳號要進行座號更新呢? 為避免使用者出錯,故已存在的帳號也應當給classNo。
  535. if (student.TryGetProperty("no", out var tmpNo) && !string.IsNullOrWhiteSpace(tmpNo.GetString()))
  536. {
  537. studentInfo.no = tmpNo.GetString();
  538. //這邊先將該間教室欲使用到的no整理出來
  539. if (dicClassStudNo.ContainsKey(tmpClassNo.GetString())) dicClassStudNo[tmpClassNo.GetString()].Add((id, tmpNo.GetString()));
  540. else dicClassStudNo.Add(tmpClassNo.GetString(), new List<(string id, string no)>() { (id, tmpNo.GetString()) });
  541. }
  542. //有給classNo才會紀錄className,classNo屬於實體教室門牌號,全校理當只會有一個。
  543. int year = 0;
  544. if (student.TryGetProperty("classYear", out var tmpClassYear) && tmpClassYear.TryGetInt32(out int syear))
  545. {
  546. year = syear;
  547. studentInfo.classYear = syear;
  548. }
  549. if (student.TryGetProperty("className", out var tmpClassName) && !string.IsNullOrWhiteSpace(tmpClassName.GetString()))
  550. {
  551. studentInfo.className = tmpClassName.GetString();
  552. if (!dicClassInfo.ContainsKey($"{studentInfo.periodId}_{year}_{tmpClassNo.GetString()}"))
  553. { dicClassInfo.Add($"{studentInfo.periodId}_{year}_{tmpClassNo.GetString()}", (tmpClassName.GetString(), studentInfo.periodId, year, tmpClassNo.GetString())); }
  554. }
  555. }
  556. ///导入的时候
  557. if (student.TryGetProperty("guardian", out var guardian) && !string.IsNullOrWhiteSpace($"{guardian}")) {
  558. studentInfo.guardian =$"{guardian}";
  559. student.TryGetProperty("gName", out var gName);
  560. student.TryGetProperty("gPhone", out var gPhone);
  561. studentInfo.gName = $"{gName}";
  562. studentInfo.gPhone = $"{gPhone}";
  563. }
  564. //更新的时候
  565. if (student.TryGetProperty("guardians", out var _guardians) && _guardians.ValueKind.Equals(JsonValueKind.Array)) {
  566. List<StudentGuardian> guardians = _guardians.Deserialize<List<StudentGuardian>>();
  567. studentInfo.guardians = guardians;
  568. }
  569. if (student.TryGetProperty("imei", out var tmpImei) && !string.IsNullOrWhiteSpace($"{tmpImei}")) studentInfo.imei = tmpImei.GetString();
  570. //將最後結果加到字典內
  571. dicStuds.Add(id, studentInfo);
  572. }
  573. }
  574. return (dicStuds, dicClassInfo, dicClassStudNo, errorYear, duplId);
  575. }
  576. /// <summary>
  577. /// 更新或是新增學生
  578. /// </summary>
  579. /// <param name="schoolId"></param>
  580. /// <param name="students"></param>
  581. /// <returns></returns>
  582. public static async Task<(List<object> studs, Dictionary<string, List<string>> classDuplNos, List<string> errorIds)> upsertStudents(AzureCosmosFactory _azureCosmos, DingDing _dingDing, Option _option,
  583. string schoolId,
  584. JsonElement.ArrayEnumerator students)
  585. {
  586. try
  587. {
  588. var sortedImpData = doSortImpStuds(schoolId, students);
  589. //var classNos = sortedImpData.classInfo.Select(o => new {key= o.Key, periodId=o.Value.periodId,index= o.Value.gradeIndex,year = o.Value.year }).ToList();
  590. //抓到教室資訊
  591. var classInfos = await getClassInfoUseNo(_azureCosmos, _dingDing, _option, schoolId, sortedImpData.classInfo);
  592. //取出已存在教室的classId,後面查座號要用。
  593. List<Task> tasks = new List<Task>();
  594. //Key:ClassNo Value:No 匯入時只有ClassNo
  595. Dictionary<string, List<(string id, string no)>> classStudNos = new Dictionary<string, List<(string id, string no)>>();
  596. //Key:ClassNo_gradeId Value:ClassId 存放教室no及id的變數
  597. Dictionary<string, (string classId, string className, string periodId, string gradeId, int year)> classNoId = new Dictionary<string, (string classId, string className, string periodId, string gradeId, int year)>();
  598. foreach (var classInfo in classInfos)
  599. {
  600. string classGradeId = classInfo.Value.gradeId;
  601. int classYear = classInfo.Value.year;
  602. classNoId.Add(classInfo.Value.no + "_" + classYear,
  603. (classInfo.Value.id, classInfo.Value.name, classInfo.Value.periodId, classGradeId, classYear));
  604. tasks.Add(
  605. Task.Run(
  606. async () =>
  607. {
  608. //(id,no)
  609. var studNo = await checkStudNo(_azureCosmos, _dingDing, _option, schoolId, classInfo.Value.id);
  610. classStudNos.Add(classInfo.Value.no + "_" + classInfo.Value.year, studNo);
  611. }));
  612. }
  613. //這邊整理出不存在的教室,之後創建新教室用(比對classNo)。
  614. //var nonexistentClassNo = classNos.Except(classInfos.Select(o => o.Key).ToList());
  615. List<string> exsitkey = new List<string>();
  616. foreach (var classInfo in classInfos)
  617. {
  618. //$"{studentInfo.periodId}_{year}_{tmpClassNo.GetString()}"
  619. var key = $"{classInfo.Value.periodId}_{classInfo.Value.year}_{classInfo.Value.no}";
  620. exsitkey.Add(key);
  621. }
  622. List<KeyValuePair<string, (string className, string periodId, int year, string no)>> nonexistentClassNo = new List<KeyValuePair<string, (string className, string periodId, int year, string no)>>();
  623. foreach (var key in sortedImpData.classInfo.Keys)
  624. {
  625. if (!exsitkey.Contains(key))
  626. {
  627. nonexistentClassNo.Add(new KeyValuePair<string, (string className, string periodId, int year, string no)>(key, sortedImpData.classInfo[key]));
  628. }
  629. }
  630. // var nonexistentClassNo = exsitkey.Except(sortedImpData.classInfo.Select(o => o.Key).ToList());
  631. if (nonexistentClassNo.Count() != 0)
  632. {
  633. var gradesInfo = await getGrades(_azureCosmos, _dingDing, _option, schoolId);
  634. foreach (var item in nonexistentClassNo)
  635. {
  636. string gradeId = string.Empty;
  637. string periodId = periodId = sortedImpData.classInfo[item.Key].periodId;
  638. int year = sortedImpData.classInfo[item.Key].year;
  639. //確認該學段存在及輸入的年級index正確(-1後大於等於0)
  640. //if (gradesInfo.ContainsKey(sortedImpData.classInfo[item].periodId) && sortedImpData.classInfo[item].gradeIndex - 1>=0)
  641. // {
  642. // periodId = sortedImpData.classInfo[item].periodId;
  643. //gradeId = gradesInfo[sortedImpData.classInfo[item].periodId][sortedImpData.classInfo[item].gradeIndex - 1].gradeId;
  644. //}
  645. //建立新教室
  646. (string classId, string classNo, string className, string periodId, string gradeId, int classYear) retCreateClassInfo =
  647. await createClassInfo(_azureCosmos, _dingDing, _option,
  648. schoolId,
  649. null,
  650. sortedImpData.classInfo[item.Key].className,
  651. sortedImpData.classInfo[item.Key].no,
  652. periodId,
  653. gradeId,
  654. year);
  655. classStudNos.Add(retCreateClassInfo.classNo + "_" + retCreateClassInfo.classYear, new List<(string id, string no)>());
  656. classNoId.Add(retCreateClassInfo.classNo + "_" + retCreateClassInfo.classYear, (retCreateClassInfo.classId, retCreateClassInfo.className, periodId, gradeId, year));
  657. }
  658. }
  659. var taskWhenAll = Task.WhenAll(tasks);
  660. taskWhenAll.Wait();
  661. //-------------------------------------------------------------------------
  662. //建立學生或是更新學生,並且要確認座號是否重複
  663. //每間教室的全部座號 欲更新的教室座號
  664. //先新建帳號若出現409則進行資料更新
  665. //紀錄有重複做號的id
  666. Dictionary<string, List<string>> duplNos = new Dictionary<string, List<string>>();
  667. List<string> errorIds = new List<string>();
  668. List<object> retStuds = new List<object>();
  669. CosmosContainer cosmosContainer = _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Student");
  670. long now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
  671. //並行處理
  672. foreach (var stud in sortedImpData.studs)
  673. {
  674. //這邊一樣要確認已存在和欲加入還有欲修改的座號。
  675. //欲修改的不會有重複
  676. string classId = null;
  677. bool isContinue = false;
  678. if (!string.IsNullOrWhiteSpace(stud.Value.no) && !string.IsNullOrWhiteSpace(stud.Value.classNo))
  679. {
  680. classId = classNoId[stud.Value.classNo + "_" + stud.Value.classYear].classId;
  681. (string id, string no) existNoInfo = (null, null);
  682. //檢查要更新的座號是否已存在於雲端座號(已被其他同學使用)
  683. classStudNos[stud.Value.classNo + "_" + stud.Value.classYear].ForEach(
  684. o =>
  685. {
  686. if (o.no.Equals(stud.Value.no) && !o.id.Equals(stud.Key))
  687. {
  688. existNoInfo = (o.id, o.no);
  689. if (duplNos.ContainsKey(stud.Value.classNo)) duplNos[stud.Value.classNo].Add(stud.Key);
  690. else duplNos.Add(stud.Value.classNo, new List<string>() { stud.Key });
  691. }
  692. });
  693. //如果不是空的代表有座號重覆到,此時要再確認這個重複座號的id,是否存在於這次的更新,而且是要更新座號的。
  694. if (!string.IsNullOrWhiteSpace(existNoInfo.id))
  695. {
  696. isContinue = true;
  697. //輪巡所有匯入的學生資料,並檢查匯入的座號。
  698. sortedImpData.classStudNo[stud.Value.classNo].ForEach(
  699. o =>
  700. {
  701. if (o.id.Equals(existNoInfo.id) && !o.no.Equals(existNoInfo.no))
  702. {
  703. //可以更新該座號
  704. isContinue = false;
  705. duplNos[stud.Value.classNo].Remove(stud.Key);
  706. return;
  707. }
  708. });
  709. }
  710. }
  711. if (isContinue) continue;
  712. (string id, string name, string picture, int year, string no, string classId, string classNo, string className, string gradeId, string periodId) tmpStudInfo
  713. = (stud.Key, stud.Value.name, null, stud.Value.year, stud.Value.no, null, stud.Value.classNo, null, null, null);
  714. using var memoryStream = new MemoryStream();
  715. using var writerNew = new Utf8JsonWriter(memoryStream);
  716. writerNew.WriteStartObject();
  717. writerNew.WriteString("id", stud.Key);
  718. writerNew.WriteString("pk", $"Base");
  719. writerNew.WriteString("code", $"Base-{schoolId}");
  720. writerNew.WriteString("schoolId", schoolId);
  721. writerNew.WriteNumber("year", stud.Value.year);
  722. writerNew.WriteNumber("createTime", now);
  723. writerNew.WriteString("salt", stud.Value.salt);
  724. writerNew.WriteString("pw", stud.Value.pw);
  725. if (string.IsNullOrWhiteSpace(stud.Value.name)) writerNew.WriteNull("name");
  726. else writerNew.WriteString("name", stud.Value.name);
  727. writerNew.WriteString("gender", "M");
  728. writerNew.WriteNull("picture");
  729. writerNew.WriteNull("mail");
  730. writerNew.WriteNull("mobile");
  731. writerNew.WriteNull("country");
  732. if (!string.IsNullOrWhiteSpace(stud.Value.periodId))
  733. {
  734. writerNew.WriteString("periodId", stud.Value.periodId);
  735. }
  736. if (string.IsNullOrWhiteSpace(stud.Value.classNo)) writerNew.WriteNull("classId");
  737. else
  738. {
  739. writerNew.WriteString("classId", classId);
  740. tmpStudInfo.classId = classId;
  741. tmpStudInfo.className = classNoId[stud.Value.classNo + "_" + stud.Value.classYear].className;
  742. tmpStudInfo.gradeId = classNoId[stud.Value.classNo + "_" + stud.Value.classYear].gradeId;
  743. tmpStudInfo.periodId = classNoId[stud.Value.classNo + "_" + stud.Value.classYear].periodId;
  744. }
  745. if (!string.IsNullOrWhiteSpace(stud.Value.imei))
  746. {
  747. writerNew.WriteString("imei", stud.Value.imei);
  748. }
  749. else {
  750. writerNew.WriteNull("imei");
  751. }
  752. if (string.IsNullOrWhiteSpace(stud.Value.no))
  753. {
  754. writerNew.WriteNull("no");
  755. writerNew.WriteNull("irs");
  756. }
  757. else
  758. {
  759. writerNew.WriteString("irs", stud.Value.no);
  760. writerNew.WriteString("no", stud.Value.no);
  761. };
  762. writerNew.WriteNull("groupId");
  763. writerNew.WriteNull("groupName");
  764. ///写入监护人
  765. if (!string.IsNullOrWhiteSpace(stud.Value.gPhone)) {
  766. writerNew.WriteStartArray("guardians");
  767. writerNew.WriteStartObject();
  768. writerNew.WriteString("relation", stud.Value.guardian);
  769. writerNew.WriteString("name", stud.Value.gName);
  770. writerNew.WriteString("mobile", stud.Value.gPhone);
  771. writerNew.WriteEndObject();
  772. writerNew.WriteEndArray();
  773. }
  774. writerNew.WriteEndObject();
  775. writerNew.Flush();
  776. var response = await cosmosContainer.CreateItemStreamAsync(memoryStream, new PartitionKey($"Base-{schoolId}"));
  777. if (response.Status == (int)HttpStatusCode.Created)
  778. {
  779. //如果是Created則啥都不做,讓他去下面進行資料的彙整。
  780. }
  781. //查到已存在的id,則進行基本資料更新。
  782. else if (response.Status == (int)HttpStatusCode.Conflict)
  783. {
  784. try
  785. {
  786. bool isUpPwDone = false;
  787. Student student = await _azureCosmos
  788. .GetCosmosClient()
  789. .GetContainer(Constant.TEAMModelOS, "Student")
  790. .ReadItemAsync<Student>(stud.Key, new PartitionKey($"Base-{schoolId}"));
  791. if (!string.IsNullOrWhiteSpace(stud.Value.name))
  792. {
  793. student.name = stud.Value.name;
  794. }
  795. if (stud.Value.year > 0)
  796. {
  797. student.year = stud.Value.year;
  798. }
  799. if (!string.IsNullOrWhiteSpace(stud.Value.no))
  800. {
  801. student.no = stud.Value.no;
  802. }
  803. if (!string.IsNullOrWhiteSpace(stud.Value.classNo))
  804. {
  805. student.classId = classId;
  806. }
  807. if (!string.IsNullOrWhiteSpace(stud.Value.periodId))
  808. {
  809. student.periodId = stud.Value.periodId;
  810. }
  811. if (!string.IsNullOrWhiteSpace(stud.Value.imei))
  812. {
  813. student.imei = stud.Value.imei;
  814. }
  815. if (!isUpPwDone)
  816. {
  817. student.pw = stud.Value.pw;
  818. student.salt = stud.Value.salt;
  819. isUpPwDone = true;
  820. }
  821. tmpStudInfo.picture = student.picture;
  822. if (!string.IsNullOrWhiteSpace(stud.Value.gPhone)) {
  823. var guardian = student.guardians.Find(x => !string.IsNullOrWhiteSpace(x.mobile) && x.mobile.Equals(stud.Value.gPhone));
  824. if (guardian != null)
  825. {
  826. guardian.name = stud.Value.gName;
  827. guardian.relation = stud.Value.guardian;
  828. }
  829. else
  830. {
  831. student.guardians.Add(new StudentGuardian { mobile = stud.Value.gPhone, name = stud.Value.gName, relation = stud.Value.guardian });
  832. }
  833. }
  834. if (stud.Value.guardians.IsNotEmpty()) {
  835. student.guardians=stud.Value.guardians;
  836. }
  837. await cosmosContainer.ReplaceItemAsync(student, stud.Key, new PartitionKey($"Base-{schoolId}"));
  838. }
  839. catch (CosmosException ex)
  840. {
  841. errorIds.Add(stud.Key);
  842. continue;
  843. }
  844. }
  845. else
  846. {
  847. errorIds.Add(stud.Key);
  848. continue;
  849. }
  850. //整理輸出用資料
  851. retStuds.Add(
  852. new
  853. {
  854. tmpStudInfo.id,
  855. tmpStudInfo.name,
  856. tmpStudInfo.picture,
  857. tmpStudInfo.year,
  858. tmpStudInfo.no,
  859. tmpStudInfo.classId,
  860. tmpStudInfo.classNo,
  861. tmpStudInfo.className,
  862. // tmpStudInfo.gradeId,
  863. // tmpStudInfo.periodId
  864. });
  865. }
  866. return (retStuds, duplNos.Where(o => o.Value.Count != 0).ToDictionary(o => o.Key, o => o.Value), errorIds);
  867. }
  868. catch (Exception ex)
  869. {
  870. await _dingDing.SendBotMsg(
  871. $"IES5,{_option.Location},StudentController/upsertStudents()\nex:{ex.Message}\n{ex.StackTrace}",
  872. GroupNames.醍摩豆服務運維群組);
  873. }
  874. return (null, null, null);
  875. }
  876. /// <summary>
  877. /// 單純建立單一學生
  878. /// </summary>
  879. /// <param name="schoolId"></param>
  880. /// <param name="studCreateInfo"></param>
  881. public static async Task<bool> createStudent(AzureCosmosFactory _azureCosmos, DingDing _dingDing, Option _option, string schoolId, studCreateInfo studCreateInfo)
  882. {
  883. try
  884. {
  885. using var stream = new MemoryStream();
  886. using var writer = new Utf8JsonWriter(stream);
  887. writer.WriteStartObject();
  888. writer.WriteString("pk", $"Base");
  889. writer.WriteString("code", $"Base-{schoolId}");
  890. writer.WriteString("id", studCreateInfo.id);
  891. if (string.IsNullOrWhiteSpace(studCreateInfo.name)) writer.WriteNull("name");
  892. else writer.WriteString("name", studCreateInfo.name);
  893. if (string.IsNullOrWhiteSpace(studCreateInfo.gender)) writer.WriteNull("gender");
  894. else writer.WriteString("gender", studCreateInfo.gender);
  895. writer.WriteString("schoolId", schoolId);
  896. //20210713 huanghb add 增加学段
  897. if (string.IsNullOrWhiteSpace(studCreateInfo.periodId)) writer.WriteNull("periodId");
  898. else writer.WriteString("periodId", studCreateInfo.periodId);
  899. writer.WriteNumber("year", studCreateInfo.year);
  900. writer.WriteNumber("createTime", DateTimeOffset.UtcNow.ToUnixTimeMilliseconds());
  901. writer.WriteNull("picture");
  902. writer.WriteNull("mail");
  903. writer.WriteNull("mobile");
  904. writer.WriteNull("country");
  905. if (string.IsNullOrWhiteSpace(studCreateInfo.imei)) writer.WriteNull("imei");
  906. else writer.WriteString("periodId", studCreateInfo.imei);
  907. //Password,若沒給則使用學號當密碼
  908. string salt = Utils.CreatSaltString(8);
  909. string hashPw = string.IsNullOrWhiteSpace(studCreateInfo.pw)
  910. ? Utils.HashedPassword(studCreateInfo.id, salt)
  911. : Utils.HashedPassword(studCreateInfo.pw, salt);
  912. writer.WriteString("salt", salt);
  913. writer.WriteString("pw", hashPw);
  914. if (string.IsNullOrWhiteSpace(studCreateInfo.classId)) writer.WriteNull("classId");
  915. else writer.WriteString("classId", studCreateInfo.classId);
  916. if (string.IsNullOrWhiteSpace(studCreateInfo.no))
  917. {
  918. writer.WriteNull("no");
  919. writer.WriteNull("irs");
  920. }
  921. else
  922. {
  923. writer.WriteString("no", studCreateInfo.no);
  924. writer.WriteString("irs", studCreateInfo.no);
  925. }
  926. writer.WriteNull("groupId");
  927. writer.WriteNull("groupName");
  928. if (studCreateInfo.guardians.IsNotEmpty())
  929. {
  930. writer.WriteStartArray("guardians");
  931. foreach (var guardian in studCreateInfo.guardians)
  932. {
  933. writer.WriteStartObject();
  934. writer.WriteString("relation", guardian.relation);
  935. writer.WriteString("name", guardian.name);
  936. writer.WriteString("mobile", guardian.mobile);
  937. writer.WriteEndObject();
  938. }
  939. writer.WriteEndArray();
  940. }
  941. else
  942. {
  943. writer.WriteStartArray("guardians");
  944. writer.WriteStartObject();
  945. writer.WriteEndObject();
  946. writer.WriteEndArray();
  947. }
  948. writer.WriteEndObject();
  949. writer.Flush();
  950. var response = await _azureCosmos
  951. .GetCosmosClient()
  952. .GetContainer(Constant.TEAMModelOS, "Student")
  953. .CreateItemStreamAsync(stream, new PartitionKey($"Base-{schoolId}"));
  954. if (response.Status == (int)HttpStatusCode.Created || response.Status == (int)HttpStatusCode.OK) return true;
  955. if (response.Status == (int)HttpStatusCode.Conflict) return false;
  956. else
  957. {
  958. await _dingDing.SendBotMsg($"IES5,{_option.Location},StudentController/createStudent()\nCosmosDB Create response status = {response.Status}\nID:{studCreateInfo.id}", GroupNames.醍摩豆服務運維群組);
  959. return false;
  960. }
  961. }
  962. catch (Exception ex)
  963. {
  964. await _dingDing.SendBotMsg($"IES5,{_option.Location},StudentController/createStudent()\n{ex.Message}\n{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
  965. return false;
  966. }
  967. }
  968. /// <summary>
  969. /// 生成Class資料
  970. /// </summary>
  971. /// <param name="schoolId"></param>
  972. /// <param name="classId"></param>
  973. /// <param name="className"></param>
  974. /// <param name="students"></param>
  975. /// <returns></returns>
  976. private static async Task<(string classId, string classNo, string className, string periodId, string gradeId, int classYear)> createClassInfo(
  977. AzureCosmosFactory _azureCosmos, DingDing _dingDing, Option _option,
  978. string schoolId, string classId, string className, string classNo, string periodId, string gradeId, int classYear)
  979. {
  980. //組Class JSON
  981. try
  982. {
  983. string cId = classId;
  984. using var memoryStream = new MemoryStream();
  985. using var writer = new Utf8JsonWriter(memoryStream);
  986. writer.WriteStartObject();
  987. writer.WriteString("pk", "Class");
  988. writer.WriteString("code", $"Class-{schoolId}");
  989. //如果classId是空的,則生成一組GUID。
  990. if (string.IsNullOrWhiteSpace(classId))
  991. {
  992. cId = Guid.NewGuid().ToString();
  993. writer.WriteString("id", cId);
  994. }
  995. else writer.WriteString("id", classId);
  996. if (string.IsNullOrWhiteSpace(classNo)) writer.WriteNull("no");
  997. else writer.WriteString("no", classNo);
  998. writer.WriteNull("x");
  999. writer.WriteNull("y");
  1000. if (string.IsNullOrWhiteSpace(className)) writer.WriteNull("name");
  1001. else writer.WriteString("name", className);
  1002. writer.WritePropertyName("teacher");
  1003. writer.WriteStartObject();
  1004. writer.WriteNull("id");
  1005. writer.WriteNull("name");
  1006. writer.WriteEndObject();
  1007. //if (string.IsNullOrWhiteSpace(gradeId)) writer.WriteNull("gradeId");
  1008. //else writer.WriteString("gradeId", gradeId);
  1009. if (string.IsNullOrWhiteSpace(periodId)) writer.WriteNull("periodId");
  1010. else writer.WriteString("periodId", periodId);
  1011. writer.WriteNumber("year", classYear);
  1012. writer.WriteNumber("createTime", DateTimeOffset.UtcNow.ToUnixTimeMilliseconds());
  1013. writer.WriteNull("sn");
  1014. writer.WriteString("style", "smart");
  1015. writer.WriteString("openType", "1");
  1016. writer.WriteString("scope", "school");
  1017. writer.WriteEndObject();
  1018. writer.Flush();
  1019. var ret = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "School").CreateItemStreamAsync(memoryStream, new PartitionKey($"Class-{schoolId}"));
  1020. if (ret.Status != (int)HttpStatusCode.Created)
  1021. {
  1022. await _dingDing.SendBotMsg($"IES5,{_option.Location},StudentController/createClassInfo()\nStatus:{ret.Status}\nSchoolId:{schoolId},ClassId:{classId}", GroupNames.醍摩豆服務運維群組);
  1023. }
  1024. return (cId, classNo, className, periodId, gradeId, classYear);
  1025. }
  1026. catch (CosmosException ex)
  1027. {
  1028. await _dingDing.SendBotMsg($"IES5,{_option.Location},StudentController/createClassInfo()\n{ex.Message}\n{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
  1029. }
  1030. catch (Exception ex)
  1031. {
  1032. await _dingDing.SendBotMsg($"IES5,{_option.Location},StudentController/createClassInfo()\n{ex.Message}\n{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
  1033. }
  1034. return (null, null, null, null, null, 0);
  1035. }
  1036. /// <summary>
  1037. /// 使用學校代碼查詢該校所有學生,並且在查詢該學生所屬的教室及座號,支援offset和limit操作已及ContinuationToken,若有ContinuationToken,則會優先使用ContinuationToken。
  1038. /// </summary>
  1039. /// <param name="schoolId"></param>
  1040. /// <param name="byNameOrId">透過Name或Id來查,所以不會管學制、學級和教室</param>
  1041. /// <param name="byPeriod"></param>
  1042. /// <param name="byGrade"></param>
  1043. /// <param name="byClassId"></param>
  1044. /// <param name="offset"></param>
  1045. /// <param name="limit"></param>
  1046. /// <param name="token"></param>
  1047. /// <returns></returns>
  1048. private async Task<(List<object> students, string continuationToken)> getStudents(
  1049. AzureCosmosFactory _azureCosmos, DingDing _dingDing, Option _option,
  1050. string schoolId, string byNameOrId = null, string byPeriod = null, string byGrade = null, string byClassId = null, int offset = -1, int limit = -1, string token = default)
  1051. {
  1052. try
  1053. {
  1054. //以學校學生角度去抓資料
  1055. List<(string id, string name, string picture, int year)> listStudent = new List<(string id, string name, string picture, int year)>();
  1056. string queryText = $"SELECT c.id, c.name, c.picture, c.year FROM c WHERE c.code = 'Base-{schoolId}'";
  1057. //如果有選擇ClassId的話,則先取得該教室內的學生。
  1058. List<string> searchId = new List<string>();
  1059. if (!string.IsNullOrWhiteSpace(byClassId))
  1060. {
  1061. var classInfos = await getClassInfoUseId(_azureCosmos, _dingDing, _option, schoolId, new List<string>() { byClassId });
  1062. foreach (var classInfo in classInfos)
  1063. {
  1064. var students = classInfo.Value.GetProperty("students").EnumerateArray();
  1065. while (students.MoveNext())
  1066. {
  1067. JsonElement stud = students.Current;
  1068. string id = stud.GetProperty("id").GetString();
  1069. searchId.Add(id);
  1070. }
  1071. }
  1072. //將使用者過濾classId所取得的學生ID加入sql字串內
  1073. if (searchId.Count != 0)
  1074. {
  1075. queryText = $"{queryText} AND c.id IN ({string.Join(",", searchId.Select(o => $"'{o}'"))})";
  1076. }
  1077. }
  1078. //檢查是否有接續token及是否要在sql語法內多增加offset及limit
  1079. if (string.IsNullOrWhiteSpace(token))
  1080. {
  1081. token = default;
  1082. if (offset != -1 && limit != -1) queryText = $"{queryText} OFFSET {offset} LIMIT {limit}";
  1083. }
  1084. //回傳用ContinuationToken
  1085. string continuationToken = string.Empty;
  1086. //進行學生資料的查詢
  1087. await foreach (var item in _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Student")
  1088. .GetItemQueryStreamIterator(
  1089. queryText: queryText,
  1090. continuationToken: token,
  1091. requestOptions: new QueryRequestOptions()
  1092. { PartitionKey = new PartitionKey($"Base-{schoolId}") }))
  1093. {
  1094. continuationToken = item.GetContinuationToken();
  1095. using var json = await JsonDocument.ParseAsync(item.ContentStream);
  1096. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  1097. {
  1098. var accounts = json.RootElement.GetProperty("Documents").EnumerateArray();
  1099. while (accounts.MoveNext())
  1100. {
  1101. JsonElement account = accounts.Current;
  1102. listStudent.Add((account.GetProperty("id").GetString(), account.GetProperty("name").GetString(), account.GetProperty("picture").GetString(), account.GetProperty("year").GetInt32()));
  1103. }
  1104. }
  1105. //單筆查詢上限為100條,所以查完一次即返回,並且給接續token。
  1106. break;
  1107. }
  1108. //查學生所屬的教室及座號
  1109. List<object> ret = new List<object>();
  1110. //查教室資訊,使用上面的學生id並透過子查詢查詢。
  1111. queryText = $"SELECT c.id, c.name, c.gradeId, c.students FROM c JOIN (SELECT VALUE t FROM t IN c.students WHERE t.id IN ({string.Join(",", listStudent.Select(o => $"'{o.id}'"))}))";
  1112. await foreach (Response item in _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "School")
  1113. .GetItemQueryStreamIterator(
  1114. queryText: queryText,
  1115. //continuationToken: token,
  1116. requestOptions: new QueryRequestOptions()
  1117. { PartitionKey = new PartitionKey($"Class-{schoolId}") }))
  1118. {
  1119. using var json = await JsonDocument.ParseAsync(item.ContentStream);
  1120. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  1121. {
  1122. var classrooms = json.RootElement.GetProperty("Documents").EnumerateArray();
  1123. while (classrooms.MoveNext())
  1124. {
  1125. JsonElement classroom = classrooms.Current;
  1126. var studs = classroom.GetProperty("students").EnumerateArray();
  1127. while (studs.MoveNext())
  1128. {
  1129. JsonElement stud = studs.Current;
  1130. string id = stud.GetProperty("id").GetString();
  1131. //整理出前端所需的資訊
  1132. var tmp = listStudent
  1133. .Where(o => o.id.Equals(id, StringComparison.Ordinal))
  1134. .Select(o =>
  1135. new
  1136. {
  1137. o.id,
  1138. o.name,
  1139. o.picture,
  1140. o.year,
  1141. no = stud.GetProperty("no").GetString(),
  1142. gradeId = classroom.GetProperty("gradeId").GetString(),
  1143. className = classroom.GetProperty("name").GetString()
  1144. });
  1145. ret.AddRange(tmp);
  1146. //刪除已整理完的ID
  1147. listStudent.RemoveAll(o => o.id.Equals(id, StringComparison.Ordinal));
  1148. }
  1149. }
  1150. }
  1151. }
  1152. var notJoinClassStuds = listStudent.Select(o =>
  1153. new
  1154. {
  1155. o.id,
  1156. o.name,
  1157. o.picture,
  1158. o.year,
  1159. no = (string)null,
  1160. gradeId = (string)null,
  1161. className = (string)null
  1162. });
  1163. ret.AddRange(notJoinClassStuds);
  1164. return (ret, continuationToken);
  1165. }
  1166. catch (CosmosException ex)
  1167. {
  1168. await _dingDing.SendBotMsg($"IES5,{_option.Location},StudentController/getStudents()\n{ex.Message}\n{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
  1169. }
  1170. catch (Exception ex)
  1171. {
  1172. await _dingDing.SendBotMsg($"IES5,{_option.Location},StudentController/getStudents()\n{ex.Message}\n{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
  1173. }
  1174. return (null, null);
  1175. }
  1176. /// <summary>
  1177. /// 取得該學校的所有學生。
  1178. /// </summary>
  1179. /// <param name="schoolId"></param>
  1180. /// <returns> [{id,name,picture,year,no,classId,classNo,className,gradeId,periodId},{id,name,picture,..}..]</returns>
  1181. public static async Task<List<object>> getAllStudent(AzureCosmosFactory _azureCosmos, DingDing _dingDing, Option _option, string schoolId)
  1182. {
  1183. try
  1184. {
  1185. //TODO : 進階查詢選項調整、部分地方可用並行處理
  1186. //以學校學生角度去抓資料
  1187. Dictionary<string, List<(string id, string name, string picture, int year, string no, string periodId, string irs,string imei,List<StudentGuardian> guardians)>> dicClassStuds =
  1188. new Dictionary<string, List<(string id, string name, string picture, int year, string no, string periodId, string irs, string imei, List<StudentGuardian> guardians)>>();
  1189. List<(string id, string name, string picture, int year, string no, string periodId, string irs, string imei, List<StudentGuardian> guardians)> notJoinClassStuds =
  1190. new List<(string id, string name, string picture, int year, string no, string periodId, string irs, string imei, List<StudentGuardian> guardians)>();
  1191. string queryText = $"SELECT * FROM c WHERE c.code = 'Base-{schoolId}'";
  1192. //回傳用ContinuationToken
  1193. string continuationToken = string.Empty;
  1194. var container = _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Student");
  1195. //進行學生資料的查詢 TEAMModelOS-Student
  1196. await foreach (var item in _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Student")
  1197. .GetItemQueryStreamIterator(
  1198. queryText: queryText,
  1199. requestOptions: new QueryRequestOptions()
  1200. { PartitionKey = new PartitionKey($"Base-{schoolId}"), MaxItemCount = -1 }))
  1201. {
  1202. continuationToken = item.GetContinuationToken();
  1203. using var json = await JsonDocument.ParseAsync(item.ContentStream);
  1204. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  1205. {
  1206. List<(string id, string name, string picture, int year, string no, List<StudentGuardian> guardians)> students = new List<(string id, string name, string picture, int year, string no, List<StudentGuardian> guardians)>();
  1207. var accounts = json.RootElement.GetProperty("Documents").EnumerateArray();
  1208. while (accounts.MoveNext())
  1209. {
  1210. JsonElement acc = accounts.Current;
  1211. string classId = acc.GetProperty("classId").GetString();
  1212. acc.TryGetProperty("irs", out JsonElement irs);
  1213. acc.TryGetProperty("guardians", out JsonElement _guardians);
  1214. List<StudentGuardian> guardians = new List<StudentGuardian>();
  1215. if (_guardians.ValueKind.Equals(JsonValueKind.Array)) {
  1216. guardians= _guardians.Deserialize<List<StudentGuardian>>();
  1217. if (guardians.Any()) {
  1218. guardians= guardians.FindAll(x => !string.IsNullOrWhiteSpace(x.mobile)) ;
  1219. }
  1220. }
  1221. if (string.IsNullOrWhiteSpace(classId))
  1222. {
  1223. notJoinClassStuds.Add(
  1224. (
  1225. acc.GetProperty("id").GetString(),
  1226. acc.GetProperty("name").GetString(),
  1227. acc.GetProperty("picture").GetString(),
  1228. acc.GetProperty("year").GetInt32(),
  1229. acc.GetProperty("no").GetString(),
  1230. acc.TryGetProperty("periodId", out JsonElement _periodId) && _periodId.ValueKind.Equals(JsonValueKind.String) ? _periodId.GetString() : null,
  1231. $"{irs}",
  1232. acc.TryGetProperty("imei", out JsonElement _imei) && _imei.ValueKind.Equals(JsonValueKind.String) ? _imei.GetString() : null,
  1233. guardians
  1234. )
  1235. );
  1236. }
  1237. else
  1238. {
  1239. if (dicClassStuds.ContainsKey(classId))
  1240. {
  1241. dicClassStuds[classId].Add(
  1242. (
  1243. acc.GetProperty("id").GetString(),
  1244. acc.GetProperty("name").GetString(),
  1245. acc.GetProperty("picture").GetString(),
  1246. acc.GetProperty("year").GetInt32(),
  1247. acc.GetProperty("no").GetString(), acc.TryGetProperty("periodId", out JsonElement _periodId) && _periodId.ValueKind.Equals(JsonValueKind.String) ? _periodId.GetString() : null,
  1248. $"{irs}",
  1249. acc.TryGetProperty("imei", out JsonElement _imei) && _imei.ValueKind.Equals(JsonValueKind.String) ? _imei.GetString() : null,
  1250. guardians
  1251. )
  1252. );
  1253. }
  1254. else
  1255. {
  1256. dicClassStuds.Add(classId,
  1257. new List<(string id, string name, string picture, int year, string no, string periodId, string irs,string imei, List<StudentGuardian> guardians)>()
  1258. {
  1259. (
  1260. acc.GetProperty("id").GetString(),
  1261. acc.GetProperty("name").GetString(),
  1262. acc.GetProperty("picture").GetString(),
  1263. acc.GetProperty("year").GetInt32(),
  1264. acc.GetProperty("no").GetString(), acc.TryGetProperty("periodId",out JsonElement _periodId)&& _periodId.ValueKind.Equals(JsonValueKind.String) ? _periodId.GetString() : null,
  1265. $"{irs}"
  1266. ,
  1267. acc.TryGetProperty("imei", out JsonElement _imei) && _imei.ValueKind.Equals(JsonValueKind.String) ? _imei.GetString() : null,
  1268. guardians
  1269. )
  1270. }
  1271. );
  1272. }
  1273. }
  1274. }
  1275. }
  1276. }
  1277. //查學生所屬的教室及座號
  1278. List<object> ret = new List<object>();
  1279. //查教室的資訊,用以取得gradeId,periodId資訊。
  1280. var classInfos = await getClassInfoUseId(_azureCosmos, _dingDing, _option, schoolId, dicClassStuds.Keys.ToList());
  1281. //輪循所有教室學生的資料
  1282. foreach (var classStud in dicClassStuds)
  1283. {
  1284. string classId = null, classNo = null, className = null, gradeId = null, periodId = null;
  1285. int classYear = -1;
  1286. if (classInfos.ContainsKey(classStud.Key))
  1287. {
  1288. classId = classInfos[classStud.Key].GetProperty("id").GetString();
  1289. classNo = classInfos[classStud.Key].GetProperty("no").GetString();
  1290. className = classInfos[classStud.Key].GetProperty("name").GetString();
  1291. periodId = classInfos[classStud.Key].TryGetProperty("periodId", out JsonElement _periodId) && _periodId.ValueKind.Equals(JsonValueKind.String) ? _periodId.GetString() : null;
  1292. if (classInfos[classStud.Key].TryGetProperty("year", out JsonElement year))
  1293. {
  1294. if (year.ValueKind.Equals(JsonValueKind.Number))
  1295. {
  1296. classYear = classInfos[classStud.Key].GetProperty("year").GetInt32();
  1297. }
  1298. }
  1299. }
  1300. var tmp = classStud.Value.Select(o =>
  1301. new
  1302. {
  1303. o.id,
  1304. o.name,
  1305. o.picture,
  1306. o.year,
  1307. o.no,
  1308. classId,
  1309. classNo,
  1310. className,
  1311. gradeId,
  1312. periodId = string.IsNullOrEmpty(periodId) ? o.periodId : periodId,
  1313. classYear,
  1314. irs = o.irs,
  1315. imie=o.imei,
  1316. guardians=o.guardians,
  1317. });
  1318. ret.AddRange(tmp);
  1319. }
  1320. //彙整沒有加入教室的學生
  1321. notJoinClassStuds.ForEach(o => ret.Add(
  1322. new
  1323. {
  1324. o.id,
  1325. o.name,
  1326. o.picture,
  1327. o.year,
  1328. o.no,
  1329. classId = (string)null,
  1330. classNo = (string)null,
  1331. className = (string)null,
  1332. gradeId = (string)null,
  1333. o.periodId,
  1334. classYear = -1,
  1335. irs = o.irs,
  1336. imie = o.imei,
  1337. guardians =o.guardians,
  1338. }));
  1339. return ret;
  1340. }
  1341. catch (Exception ex)
  1342. {
  1343. await _dingDing.SendBotMsg($"IES5,{_option.Location},StudentController/getStudents()\n{ex.Message}\n{ex.StackTrace},", GroupNames.醍摩豆服務運維群組);
  1344. }
  1345. return null;
  1346. }
  1347. /// <summary>
  1348. /// 取得該校所有教室內的名單
  1349. /// </summary>
  1350. /// <param name="schoolId"></param>
  1351. /// <returns></returns>
  1352. public async Task<Dictionary<string, JsonElement>> getClassStudent(AzureCosmosFactory _azureCosmos, DingDing _dingDing, Option _option, string schoolId, string classId = null)
  1353. {
  1354. try
  1355. {
  1356. string queryText = $"SELECT VALUE FROM c WHERE c.";
  1357. //if (!string.IsNullOrWhiteSpace(classId)) queryText += $" AND c.id = '{classId}'";
  1358. Dictionary<string, JsonElement> listStudent = new Dictionary<string, JsonElement>();
  1359. await foreach (Response item in _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "School")
  1360. .GetItemQueryStreamIterator(queryText: queryText, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Class-{schoolId}") }))
  1361. {
  1362. using var json = await JsonDocument.ParseAsync(item.ContentStream);
  1363. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  1364. {
  1365. JsonElement.ArrayEnumerator accounts = json.RootElement.GetProperty("Documents").EnumerateArray();
  1366. while (accounts.MoveNext())
  1367. {
  1368. JsonElement account = accounts.Current;
  1369. string cId = account.GetProperty("id").GetString();
  1370. var students = account.GetProperty("students").Clone();
  1371. listStudent.Add(cId, students);
  1372. }
  1373. }
  1374. }
  1375. return listStudent;
  1376. }
  1377. catch (Exception ex)
  1378. {
  1379. await _dingDing.SendBotMsg($"IES5,{_option.Location},StudentController/getClassStudentAsync()\n{ex.Message}\n{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
  1380. }
  1381. return null;
  1382. }
  1383. /// <summary>
  1384. /// 刪除學生,非透過批量刪除方法。
  1385. /// </summary>
  1386. /// <param name="schoolId"></param>
  1387. /// <param name="students"></param>
  1388. /// <returns></returns>
  1389. public static async Task<List<string>> deleteStudents(AzureCosmosFactory _azureCosmos, DingDing _dingDing, Option _option, string schoolId, JsonElement.ArrayEnumerator students)
  1390. {
  1391. List<string> sucIds = new List<string>();
  1392. try
  1393. {
  1394. var exceptions = new List<Exception>();
  1395. List<GroupList> scGroupLists = new List<GroupList>();
  1396. List<GroupList> teGroupLists = new List<GroupList>();
  1397. var container = _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Student");
  1398. while (students.MoveNext())
  1399. {
  1400. string id = string.Empty;
  1401. try
  1402. {
  1403. JsonElement student = students.Current;
  1404. id = student.GetProperty("id").GetString();
  1405. var ret = await container.DeleteItemStreamAsync(id, new PartitionKey($"Base-{schoolId}"));
  1406. if (ret.Status == (int)HttpStatusCode.NoContent) sucIds.Add(id);
  1407. await foreach (var item in _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "School").GetItemQueryIterator<GroupList>(queryText: $"select value(c) from c join A0 in c.members where A0.id = '{id}' and A0.code='{schoolId}' ", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"GroupList-{schoolId}") }))
  1408. {
  1409. scGroupLists.Add(item);
  1410. }
  1411. await foreach (var item in _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Teacher").GetItemQueryIterator<GroupList>(queryText: $"select value(c) from c join A0 in c.members where A0.id = '{id}' and A0.code='{schoolId}' ", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"GroupList") }))
  1412. {
  1413. teGroupLists.Add(item);
  1414. }
  1415. if (scGroupLists.Count > 0)
  1416. {
  1417. foreach (GroupList stuList in scGroupLists)
  1418. {
  1419. for (int j = 0; j < stuList.members.Count; j++)
  1420. {
  1421. if (id.Equals(stuList.members[j].id) && stuList.members[j].code.Equals(schoolId))
  1422. {
  1423. stuList.members.RemoveAt(j);
  1424. stuList.scount -= 1;
  1425. break;
  1426. }
  1427. }
  1428. await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "School").ReplaceItemAsync(stuList, stuList.id, new PartitionKey(stuList.code));
  1429. }
  1430. }
  1431. if (teGroupLists.Count > 0)
  1432. {
  1433. foreach (GroupList stuList in teGroupLists)
  1434. {
  1435. for (int j = 0; j < stuList.members.Count; j++)
  1436. {
  1437. if (id.Equals(stuList.members[j].id) && stuList.members[j].code.Equals(schoolId))
  1438. {
  1439. stuList.members.RemoveAt(j);
  1440. stuList.scount -= 1;
  1441. break;
  1442. }
  1443. }
  1444. await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Teacher").ReplaceItemAsync(stuList, stuList.id, new PartitionKey(stuList.code));
  1445. }
  1446. }
  1447. }
  1448. catch (CosmosException ex)
  1449. {
  1450. exceptions.Add(ex);
  1451. }
  1452. catch (Exception ex)
  1453. {
  1454. exceptions.Add(ex);
  1455. }
  1456. }
  1457. if (exceptions.Count == 0) return sucIds;
  1458. else if (exceptions.Count > 1) throw new AggregateException(exceptions);
  1459. else if (exceptions.Count == 1) throw exceptions.Single();
  1460. }
  1461. catch (CosmosException ex)
  1462. {
  1463. await _dingDing.SendBotMsg($"IES5,{_option.Location},StudentController/deleteStudents()\n{ex.Message}\n{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
  1464. }
  1465. catch (Exception ex)
  1466. {
  1467. await _dingDing.SendBotMsg($"IES5,{_option.Location},StudentController/deleteStudents()\n{ex.Message}\n{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
  1468. }
  1469. return sucIds;
  1470. }
  1471. /// <summary>
  1472. /// 將學生基本資料內的classId、no、groupId及groupName清為null。
  1473. /// </summary>
  1474. /// <param name="schoolId"></param>
  1475. /// <param name="students">["id1","id2",...]</param>
  1476. /// <returns></returns>
  1477. public static async Task<(List<string> studs, List<string> nonexistentIds, List<string> errorIds)> removeStudentClassInfo(AzureCosmosFactory _azureCosmos, DingDing _dingDing, Option _option, string schoolId, JsonElement.ArrayEnumerator students)
  1478. {
  1479. //紀錄輸入的學生
  1480. List<string> impStuds = new List<string>();
  1481. //紀錄更新成功的學生
  1482. List<string> sucStuds = new List<string>();
  1483. //記錄沒查到的學生
  1484. List<string> nonexistentIds = new List<string>();
  1485. //紀錄更新出錯的學生
  1486. List<string> errorIds = new List<string>();
  1487. //整理輸入的學生資訊
  1488. while (students.MoveNext())
  1489. {
  1490. JsonElement student = students.Current;
  1491. impStuds.Add(student.GetString());
  1492. }
  1493. if (impStuds.Count == 0) return (null, null, null);
  1494. string queryText = $"SELECT VALUE c FROM c WHERE c.id IN ({string.Join(",", impStuds.Select(o => $"'{o}'"))})";
  1495. await foreach (Response item in _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Student")
  1496. .GetItemQueryStreamIterator(
  1497. queryText: queryText,
  1498. requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Base-{schoolId}") }))
  1499. {
  1500. using var json = await JsonDocument.ParseAsync(item.ContentStream);
  1501. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  1502. {
  1503. JsonElement.ArrayEnumerator docs = json.RootElement.GetProperty("Documents").EnumerateArray();
  1504. while (docs.MoveNext())
  1505. {
  1506. JsonElement doc = docs.Current;
  1507. doc.TryGetProperty("id", out var tmpId);
  1508. var id = tmpId.GetString();
  1509. using var stream = new MemoryStream();
  1510. using var writer = new Utf8JsonWriter(stream);
  1511. writer.WriteStartObject();
  1512. foreach (var element in doc.EnumerateObject())
  1513. {
  1514. //將教室相關欄位清空
  1515. switch (true)
  1516. {
  1517. case bool _ when element.Name.Equals("classId", StringComparison.Ordinal):
  1518. writer.WriteNull("classId");
  1519. break;
  1520. case bool _ when element.Name.Equals("no", StringComparison.Ordinal):
  1521. writer.WriteNull("no");
  1522. break;
  1523. case bool _ when element.Name.Equals("groupId", StringComparison.Ordinal):
  1524. writer.WriteNull("groupId");
  1525. break;
  1526. case bool _ when element.Name.Equals("groupName", StringComparison.Ordinal):
  1527. writer.WriteNull("groupName");
  1528. break;
  1529. case bool _ when element.Name.Equals("irs", StringComparison.Ordinal):
  1530. writer.WriteNull("irs");
  1531. break;
  1532. case bool _ when element.Name.StartsWith("_", StringComparison.Ordinal):
  1533. break;
  1534. default:
  1535. element.WriteTo(writer);
  1536. break;
  1537. }
  1538. }
  1539. writer.WriteEndObject();
  1540. writer.Flush();
  1541. var ret = await _azureCosmos
  1542. .GetCosmosClient()
  1543. .GetContainer(Constant.TEAMModelOS, "Student")
  1544. .ReplaceItemStreamAsync(stream, id, new PartitionKey($"Base-{schoolId}"));
  1545. if (ret.Status == (int)HttpStatusCode.OK)
  1546. {
  1547. sucStuds.Add(id);
  1548. }
  1549. else
  1550. {
  1551. impStuds.Remove(id);
  1552. errorIds.Add(id);
  1553. await _dingDing.SendBotMsg(
  1554. $"IES5,{_option.Location},StudentController/removeStudentClassInfo(),CosmosDB response:{ret.Status}\nBase-{schoolId},id:{id}",
  1555. GroupNames.醍摩豆服務運維群組);
  1556. }
  1557. }
  1558. }
  1559. }
  1560. //將impStuds內的資料移除sucStuds及errorIds,所得的結果就是不存在於資料庫的id。
  1561. sucStuds.ForEach(o => impStuds.Remove(o));
  1562. errorIds.ForEach(o => impStuds.Remove(o));
  1563. return (sucStuds, impStuds, errorIds);
  1564. }
  1565. /// <summary>
  1566. /// 取得教室資訊,使用classId進行查詢。
  1567. /// </summary>
  1568. /// <returns></returns>
  1569. private static async Task<Dictionary<string, JsonElement>> getClassInfoUseId(AzureCosmosFactory _azureCosmos, DingDing _dingDing, Option _option, string schoolId, List<string> classIds)
  1570. {
  1571. try
  1572. {
  1573. if (!(classIds == null || classIds.Count == 0))
  1574. {
  1575. string queryText = $"SELECT * FROM c WHERE c.code = 'Class-{schoolId}' AND c.id IN ({string.Join(",", classIds.Select(o => $"'{o}'"))})";
  1576. Dictionary<string, JsonElement> dicClassInfo = new Dictionary<string, JsonElement>();
  1577. await foreach (Response item in _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "School")
  1578. .GetItemQueryStreamIterator(queryText: queryText, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Class-{schoolId}") }))
  1579. {
  1580. using var json = await JsonDocument.ParseAsync(item.ContentStream);
  1581. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  1582. {
  1583. var classInfos = json.RootElement.GetProperty("Documents").EnumerateArray();
  1584. while (classInfos.MoveNext())
  1585. {
  1586. JsonElement account = classInfos.Current;
  1587. string id = account.GetProperty("id").GetString();
  1588. dicClassInfo.Add(id, account.Clone());
  1589. }
  1590. }
  1591. }
  1592. return dicClassInfo;
  1593. }
  1594. else return null;
  1595. }
  1596. catch (CosmosException ex)
  1597. {
  1598. await _dingDing.SendBotMsg($"IES5,{_option.Location},StudentController/getClassInfoUseId()\n{ex.Message}\n{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
  1599. }
  1600. catch (Exception ex)
  1601. {
  1602. await _dingDing.SendBotMsg($"IES5,{_option.Location},StudentController/getClassInfoUseId()\n{ex.Message}\n{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
  1603. }
  1604. return null;
  1605. }
  1606. /// <summary>
  1607. /// 取得教室資訊,使用classNo進行查詢。
  1608. /// </summary>
  1609. /// <returns></returns>
  1610. private static async Task<Dictionary<string, Class>> getClassInfoUseNo(AzureCosmosFactory _azureCosmos, DingDing _dingDing, Option _option, string schoolId, Dictionary<string, (string className, string periodId, int year, string no)> classNos)
  1611. {
  1612. try
  1613. {
  1614. Dictionary<string, Class> dicClassInfo = new Dictionary<string, Class>();
  1615. if (!(classNos == null || classNos.Count == 0))
  1616. {
  1617. foreach (var key in classNos.Keys)
  1618. {
  1619. string queryText = $"SELECT * FROM c WHERE c.code = 'Class-{schoolId}' AND c.no='{classNos[key].no}' and c.year={classNos[key].year} and c.periodId='{classNos[key].periodId}' ";
  1620. await foreach (var item in _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "School")
  1621. .GetItemQueryIterator<Class>(queryText: queryText, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Class-{schoolId}") }))
  1622. {
  1623. dicClassInfo[item.id] = item;
  1624. item.name = classNos[key].className;
  1625. await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "School").ReplaceItemAsync<Class>(item, item.id, new PartitionKey(item.code));
  1626. }
  1627. }
  1628. return dicClassInfo;
  1629. }
  1630. else return null;
  1631. }
  1632. catch (CosmosException ex)
  1633. {
  1634. await _dingDing.SendBotMsg($"IES5,{_option.Location},StudentController/getClassInfoUseId()\n{ex.Message}\n{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
  1635. }
  1636. catch (Exception ex)
  1637. {
  1638. await _dingDing.SendBotMsg($"IES5,{_option.Location},StudentController/getClassInfoUseId()\n{ex.Message}\n{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
  1639. }
  1640. return null;
  1641. }
  1642. /// <summary>
  1643. /// 批量更新學生資訊,目前支持更新姓名、密碼、座號、性別及教室id,匯入時ClassId為必填。
  1644. /// </summary>
  1645. /// <param name="schoolId"></param>
  1646. /// <param name="students"></param>
  1647. /// <returns></returns>
  1648. public static async Task<(List<object> studs, Dictionary<string, List<string>> classDuplNos, List<string> nonexistentIds, List<string> errorIds, Dictionary<string, List<string>> errorNos, List<string> errorClassId)>
  1649. updateStudents(AzureCosmosFactory _azureCosmos, DingDing _dingDing, Option _option, string schoolId, JsonElement.ArrayEnumerator students)
  1650. {
  1651. try
  1652. {
  1653. //整理輸入的資料->檢查輸入資料有沒有重複座號->取得欲加入的教室資訊->查詢學生並將資料更新並寫入
  1654. //Key:id Value:學生基本資訊
  1655. var studentInfos
  1656. = new Dictionary<string, (string salt, string pw, string name, int year, string picture, string gender, string mail, string mobile, string classId, string no, string periodId, string irs,string imei, List<StudentGuardian> guardians)>();
  1657. //用於進行座號是否重複查詢時使用
  1658. var classStuds
  1659. = new Dictionary<string, List<(string id, string salt, string pw, string name, int year, string picture, string gender, string mail, string mobile, string classId, string no, string periodId)>>();
  1660. //紀錄教室"輸入"的學生座號是否有重複
  1661. var impClassDuplNo = new Dictionary<string, List<string>>();
  1662. //紀錄不存在的學生id
  1663. var nonexistentIds = new List<string>();
  1664. //紀錄跟現有雲端學生座號重複的
  1665. var errorNos = new Dictionary<string, List<string>>();
  1666. //紀錄處理錯誤的id,cosmosdb寫入時錯誤等...
  1667. var errorIds = new List<string>();
  1668. //紀錄沒找到的classId
  1669. var errorClassId = new List<string>();
  1670. //紀錄輸出結果
  1671. var retStuds = new List<object>();
  1672. //整理輸入資料
  1673. while (students.MoveNext())
  1674. {
  1675. JsonElement student = students.Current;
  1676. if (student.TryGetProperty("id", out var id))
  1677. {
  1678. //確認是否有id欄位,並且確認是否有給pw欄位,若無給或是null empty等,則使用id當密碼。
  1679. if (!string.IsNullOrWhiteSpace(id.GetString()))
  1680. {
  1681. string salt = null,
  1682. pw = null,
  1683. name = null,
  1684. gender = null,
  1685. mail = null,
  1686. mobile = null,
  1687. classId = null,
  1688. periodId = null,
  1689. irs = null,
  1690. imei = null,
  1691. no = null;
  1692. List<StudentGuardian> guardians = null;
  1693. int year = 0;
  1694. //有給pw欄位才進行處理
  1695. if (student.TryGetProperty("pw", out var tmpPw))
  1696. {
  1697. var response = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Student").ReadItemStreamAsync(id.GetString(), new PartitionKey($"Base-{schoolId}"));
  1698. if (response.Status == 200)
  1699. {
  1700. var rjson = await JsonDocument.ParseAsync(response.ContentStream);
  1701. rjson.RootElement.TryGetProperty("salt", out JsonElement _salt);
  1702. salt = $"{_salt}";
  1703. }
  1704. if (string.IsNullOrWhiteSpace(salt))
  1705. {
  1706. salt = Utils.CreatSaltString(8);
  1707. }
  1708. pw = !string.IsNullOrWhiteSpace(tmpPw.GetString())
  1709. ? Utils.HashedPassword(tmpPw.GetString(), salt)
  1710. : Utils.HashedPassword(id.GetString(), salt);
  1711. }
  1712. if (student.TryGetProperty("name", out var tmpName)) name = tmpName.GetString();
  1713. if (student.TryGetProperty("gender", out var tmpGender)) gender = tmpGender.GetString();
  1714. if (student.TryGetProperty("mail", out var tmpMail)) mail = tmpMail.GetString();
  1715. if (student.TryGetProperty("mobile", out var tmpMobile)) mobile = tmpMobile.GetString();
  1716. if (student.TryGetProperty("year", out var tmpYear)) year = tmpYear.GetInt32();
  1717. if (student.TryGetProperty("periodId", out var tmpperiodId)) periodId = tmpperiodId.GetString();
  1718. if (student.TryGetProperty("irs", out var tmpIrs)) irs = tmpIrs.GetString();
  1719. if (student.TryGetProperty("imei", out var tmpImei)) imei = tmpImei.GetString();
  1720. if (student.TryGetProperty("classId", out var tmpclassId)) classId = tmpclassId.GetString();
  1721. else
  1722. {
  1723. errorClassId.Add(id.GetString());
  1724. continue;
  1725. }
  1726. if (student.TryGetProperty("guardians", out var _guardians) && _guardians.ValueKind.Equals(JsonValueKind.Array) )
  1727. {
  1728. guardians = _guardians.Deserialize<List<StudentGuardian>>();
  1729. }
  1730. //如果有給該欄位,且是給空的,代表要清空
  1731. if (student.TryGetProperty("no", out var tmpNo)) no = tmpNo.GetString();
  1732. if (!studentInfos.ContainsKey(id.GetString()))
  1733. {
  1734. //如果有給classId且是給空的,則也將no設為空,後續才能將no欄位清空。
  1735. if (classId != null && classId.Length == 0) no = string.Empty;
  1736. //classId => 沒給欄位(null) 有給欄位("") 但更新一定得給教室?
  1737. if (classId == null)
  1738. {
  1739. classId = "";
  1740. }
  1741. if (classStuds.ContainsKey(classId))
  1742. {
  1743. classStuds[classId].Add((id.GetString(), salt, pw, name, year, null, gender, null, null, classId, no, periodId));
  1744. }
  1745. else
  1746. {
  1747. classStuds.Add(
  1748. classId,
  1749. new List<(string id, string salt, string pw, string name, int year, string picture, string gender, string mail, string mobile, string classId, string no, string periodId)>()
  1750. { (id.GetString(), salt, pw, name, year, null, gender, null, null, classId, no,periodId) });
  1751. }
  1752. //picture,mail,mobile暫不支持批量更新
  1753. studentInfos.Add(id.GetString(), (salt, pw, name, year, null, gender, null, null, classId, no, periodId, irs,imei, guardians));
  1754. //先將id加進去後面再做刪除動作
  1755. nonexistentIds.Add(id.GetString());
  1756. }
  1757. }
  1758. }
  1759. }
  1760. //檢查所有輸入的班級資料內,學生座號是否有重複。
  1761. for (int ii = 0; ii <= classStuds.Values.Count - 1; ii++)
  1762. {
  1763. var duplicateNo = classStuds.ElementAt(ii).Value.GroupBy(o => o.no).Where(o => o.Count() > 1).Select(o => o.Key).ToList();
  1764. duplicateNo.Remove("");
  1765. duplicateNo.Remove(null);
  1766. var wrongStuds = classStuds.ElementAt(ii).Value.Where(o => duplicateNo.Contains(o.no)).Select(o => o).ToList();
  1767. impClassDuplNo.Add(classStuds.ElementAt(ii).Key, wrongStuds.Select(o => o.id).ToList());
  1768. wrongStuds.ForEach(o => { classStuds.ElementAt(ii).Value.Remove(o); nonexistentIds.Remove(o.id); });
  1769. classStuds[classStuds.ElementAt(ii).Key] = classStuds.ElementAt(ii).Value;
  1770. }
  1771. //查詢欲加入的教室資訊。
  1772. var classInfo = await getClassInfoUseId(_azureCosmos, _dingDing, _option, schoolId, classStuds.Keys.ToList());
  1773. //準備查詢db資料
  1774. CosmosContainer cosmosContainer = _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Student");
  1775. //查要移除教室或是沒加入教室的學生
  1776. foreach (var item in classStuds)
  1777. {
  1778. //如果沒有任何學生要更新,則跳過該間教室。
  1779. if (item.Value.Count == 0) continue;
  1780. string classId = null, className = null, classNo = null, gradeId = null, periodId = null;
  1781. //如果教室不存在的話(填錯教室之類的狀況),則記錄教室的id及學生id。
  1782. if (classInfo.ContainsKey(item.Key))
  1783. {
  1784. classId = item.Key;
  1785. className = classInfo[item.Key].GetProperty("name").GetString();
  1786. classNo = classInfo[item.Key].GetProperty("no").GetString();
  1787. //gradeId = classInfo[item.Key].GetProperty("gradeId").GetString(); 此欄位已不使用
  1788. periodId = classInfo[item.Key].GetProperty("periodId").GetString();
  1789. }
  1790. else if (item.Key.Length == 0)
  1791. {
  1792. }
  1793. else
  1794. {
  1795. //沒查到有該間教室的資訊,故將該間教室的ID及學生資料清單記起來,並且跳過不處理該資料。
  1796. errorClassId.Add(item.Key);
  1797. item.Value.ForEach(o => nonexistentIds.Remove(o.id));
  1798. continue;
  1799. }
  1800. //檢查座號是否有重複
  1801. //若只是改基本資料,該處還是會查到相同的座號。
  1802. var sutdNos = item.Value.Select(o => o.no).ToList();
  1803. var existNos = await checkStudNo(_azureCosmos, _dingDing, _option, schoolId, item.Key, sutdNos);
  1804. //更新並寫入學生資料
  1805. if (item.Value.Count != 0)
  1806. {
  1807. //查學生的基本資料(該間教室全部的學生)
  1808. string queryText = $"SELECT * FROM c WHERE c.id IN ({string.Join(",", item.Value.Select(o => $"'{o.id}'"))})";
  1809. List<JsonElement> listStudent = new List<JsonElement>();
  1810. await foreach (Response responseItem in cosmosContainer
  1811. .GetItemQueryStreamIterator(
  1812. queryText: queryText,
  1813. requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Base-{schoolId}") }))
  1814. {
  1815. using var json = await JsonDocument.ParseAsync(responseItem.ContentStream);
  1816. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  1817. {
  1818. var accounts = json.RootElement.GetProperty("Documents").EnumerateArray();
  1819. while (accounts.MoveNext())
  1820. {
  1821. JsonElement account = accounts.Current;
  1822. string id = $"{ account.GetProperty("id")}";
  1823. nonexistentIds.Remove(id);
  1824. //舊的座號,基本上不會重複,但可能會是空的
  1825. string no = $"{account.GetProperty("no")}";
  1826. account.TryGetProperty("irs", out JsonElement irsjson);
  1827. string irs = $"{irsjson}";
  1828. //用來記錄最後更改完的資料
  1829. (string salt, string pw, string name, int year, string picture, string gender, string mail, string mobile, string classId, string no, string periodId, string irs ,string imei, List<StudentGuardian> guardians) tmpData
  1830. = (studentInfos[id].salt, studentInfos[id].pw, studentInfos[id].name, studentInfos[id].year, studentInfos[id].picture, studentInfos[id].gender, studentInfos[id].mail, studentInfos[id].mobile, studentInfos[id].classId, studentInfos[id].no, studentInfos[id].periodId,
  1831. studentInfos[id].irs, studentInfos[id].imei, studentInfos[id]. guardians);
  1832. bool isUpPwDone = false;
  1833. bool isWrong = false;
  1834. //開始組Json
  1835. using var memoryStream = new MemoryStream();
  1836. using var writer = new Utf8JsonWriter(memoryStream);
  1837. writer.WriteStartObject();
  1838. foreach (var element in account.EnumerateObject())
  1839. {
  1840. if (isWrong) break;
  1841. switch (true)
  1842. {
  1843. case bool _ when element.Name.Equals("name", StringComparison.Ordinal):
  1844. if (string.IsNullOrWhiteSpace(studentInfos[id].name))
  1845. {
  1846. element.WriteTo(writer);
  1847. tmpData.name = element.Value.GetString();
  1848. }
  1849. else
  1850. {
  1851. writer.WriteString("name", studentInfos[id].name);
  1852. }
  1853. break;
  1854. case bool _ when element.Name.Equals("pw", StringComparison.Ordinal):
  1855. case bool _ when element.Name.Equals("salt", StringComparison.Ordinal):
  1856. if (!isUpPwDone && !string.IsNullOrWhiteSpace(studentInfos[id].salt) && !string.IsNullOrWhiteSpace(studentInfos[id].pw))
  1857. {
  1858. writer.WriteString("salt", studentInfos[id].salt);
  1859. writer.WriteString("pw", studentInfos[id].pw);
  1860. isUpPwDone = true;
  1861. }
  1862. break;
  1863. case bool _ when element.Name.Equals("periodId", StringComparison.Ordinal):
  1864. if (string.IsNullOrWhiteSpace(studentInfos[id].periodId))
  1865. {
  1866. element.WriteTo(writer);
  1867. tmpData.periodId = element.Value.GetString();
  1868. }
  1869. else
  1870. {
  1871. writer.WriteString("periodId", studentInfos[id].periodId);
  1872. }
  1873. break;
  1874. case bool _ when element.Name.Equals("gender", StringComparison.Ordinal):
  1875. if (string.IsNullOrWhiteSpace(studentInfos[id].gender))
  1876. {
  1877. element.WriteTo(writer);
  1878. tmpData.gender = element.Value.GetString();
  1879. }
  1880. else
  1881. {
  1882. writer.WriteString("gender", studentInfos[id].gender);
  1883. }
  1884. break;
  1885. case bool _ when element.Name.Equals("year", StringComparison.Ordinal):
  1886. if (studentInfos[id].year == 0)
  1887. {
  1888. element.WriteTo(writer);
  1889. tmpData.year = element.Value.GetInt32();
  1890. }
  1891. else
  1892. {
  1893. writer.WriteNumber("year", studentInfos[id].year);
  1894. }
  1895. break;
  1896. case bool _ when element.Name.Equals("classId", StringComparison.Ordinal):
  1897. if (studentInfos[id].classId != null && studentInfos[id].classId.Length == 0)
  1898. {
  1899. writer.WriteNull("classId");
  1900. writer.WriteNull("groupId");
  1901. writer.WriteNull("groupName");
  1902. tmpData.classId = null;
  1903. }
  1904. else if (string.IsNullOrWhiteSpace(studentInfos[id].classId))
  1905. {
  1906. element.WriteTo(writer);
  1907. tmpData.classId = element.Value.GetString();
  1908. }
  1909. else
  1910. {
  1911. writer.WriteString("classId", studentInfos[id].classId);
  1912. }
  1913. break;
  1914. case bool _ when element.Name.Equals("no", StringComparison.Ordinal):
  1915. //移除座號的話會給空的
  1916. if (studentInfos[id].no != null && studentInfos[id].no.Length == 0)
  1917. {
  1918. writer.WriteNull("no");
  1919. tmpData.no = null;
  1920. }
  1921. else if (string.IsNullOrWhiteSpace(studentInfos[id].no))
  1922. {
  1923. element.WriteTo(writer);
  1924. tmpData.no = element.Value.GetString();
  1925. }
  1926. else
  1927. {
  1928. //如果要更新的座號,跟已存在的座號相同,則不進行更新。
  1929. //沒有設定過舊no,或舊座號與新座號不同,則要進行重複座號的檢查。 舊no=null or 舊no!=新no
  1930. //if (string.IsNullOrWhiteSpace(no) || (!string.IsNullOrWhiteSpace(no) && !no.Equals(studentInfos[id].no)))
  1931. //{
  1932. // //如果有檢查到新座號和舊座號重複
  1933. // if (existNos.Any(o => o.Item2.Contains(studentInfos[id].no))) //.Contains(studentInfos[id].no))
  1934. // {
  1935. // if (errorNos.ContainsKey(id))
  1936. // {
  1937. // errorNos[id].Add(studentInfos[id].no);
  1938. // }
  1939. // else
  1940. // {
  1941. // errorNos.Add(id, new List<string>() { studentInfos[id].no });
  1942. // }
  1943. // isWrong = true;
  1944. // break;
  1945. // }
  1946. //}
  1947. writer.WriteString("no", studentInfos[id].no);
  1948. }
  1949. break;
  1950. case bool _ when element.Name.Equals("irs", StringComparison.Ordinal):
  1951. //移除座號的話會給空的
  1952. if (studentInfos[id].irs != null && studentInfos[id].irs.Length == 0)
  1953. {
  1954. writer.WriteNull("irs");
  1955. tmpData.irs = null;
  1956. }
  1957. else if (string.IsNullOrWhiteSpace(studentInfos[id].irs))
  1958. {
  1959. element.WriteTo(writer);
  1960. tmpData.irs = element.Value.GetString();
  1961. }
  1962. else
  1963. {
  1964. writer.WriteString("irs", studentInfos[id].irs);
  1965. }
  1966. break;
  1967. case bool _ when element.Name.Equals("imei", StringComparison.Ordinal):
  1968. //移除座號的話會給空的
  1969. if (studentInfos[id].imei != null && studentInfos[id].imei.Length == 0)
  1970. {
  1971. writer.WriteNull("imei");
  1972. tmpData.imei = null;
  1973. }
  1974. else if (string.IsNullOrWhiteSpace(studentInfos[id].imei))
  1975. {
  1976. element.WriteTo(writer);
  1977. tmpData.imei = element.Value.GetString();
  1978. }
  1979. else
  1980. {
  1981. writer.WriteString("imei", studentInfos[id].imei);
  1982. }
  1983. break;
  1984. case bool _ when element.Name.StartsWith("_", StringComparison.Ordinal):
  1985. break;
  1986. default:
  1987. element.WriteTo(writer);
  1988. break;
  1989. }
  1990. }
  1991. if (studentInfos[id].guardians.IsNotEmpty())
  1992. {
  1993. writer.WriteStartArray("guardians");
  1994. foreach (var guardian in studentInfos[id].guardians)
  1995. {
  1996. writer.WriteStartObject();
  1997. writer.WriteString("relation", guardian.relation);
  1998. writer.WriteString("name", guardian.name);
  1999. writer.WriteString("mobile", guardian.mobile);
  2000. writer.WriteString("mail", guardian.mail);
  2001. writer.WriteString("picture", guardian.picture);
  2002. writer.WriteString("tmdid", guardian.tmdid);
  2003. writer.WriteEndObject();
  2004. }
  2005. writer.WriteEndArray();
  2006. }
  2007. else
  2008. {
  2009. writer.WriteStartArray("guardians");
  2010. //writer.WriteStartObject();
  2011. //writer.WriteEndObject();
  2012. writer.WriteEndArray();
  2013. }
  2014. //如果有錯誤,如座號重覆等,就會跳過該次更新。
  2015. if (isWrong)
  2016. {
  2017. await writer.DisposeAsync();
  2018. continue;
  2019. }
  2020. if (!account.TryGetProperty("irs", out JsonElement _irs) || _irs.ValueKind.Equals(JsonValueKind.Undefined))
  2021. {
  2022. writer.WriteString("irs", studentInfos[id].irs);
  2023. }
  2024. //若密碼和鹽沒有更新,就把舊的資料寫回去
  2025. if (!isUpPwDone)
  2026. {
  2027. writer.WriteString("salt", account.GetProperty("salt").GetString());
  2028. writer.WriteString("pw", account.GetProperty("pw").GetString());
  2029. }
  2030. writer.WriteEndObject();
  2031. writer.Flush();
  2032. try
  2033. {
  2034. var ret = await cosmosContainer.ReplaceItemStreamAsync(memoryStream, id, new PartitionKey($"Base-{schoolId}"));
  2035. //將更新完的id從字典內移除,保留沒查到的。
  2036. if (ret.Status == (int)HttpStatusCode.OK)
  2037. {
  2038. nonexistentIds.Remove(id);
  2039. retStuds.Add(new { id, tmpData.name, tmpData.picture, tmpData.year, tmpData.no, classId, classNo, className, gradeId, periodId, tmpData.irs });
  2040. }
  2041. else errorIds.Add(id);
  2042. }
  2043. catch (CosmosException ex)
  2044. {
  2045. await _dingDing.SendBotMsg($"IES5,{_option.Location},StudentController/updateStudents()\n{ex.Message}\n{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
  2046. errorIds.Add(id);
  2047. }
  2048. catch (Exception ex)
  2049. {
  2050. await _dingDing.SendBotMsg($"IES5,{_option.Location},StudentController/updateStudents()\n{ex.Message}\n{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
  2051. errorIds.Add(id);
  2052. }
  2053. }
  2054. }
  2055. //將輸入不存在的資料移除。
  2056. // nonexistentIds.ForEach(o => studentInfos.Remove(o));
  2057. }
  2058. }
  2059. }
  2060. errorClassId.ForEach(o => impClassDuplNo.Remove(o));
  2061. return (retStuds, impClassDuplNo.Where(o => o.Value.Count != 0).ToDictionary(o => o.Key, o => o.Value), nonexistentIds, errorIds, errorNos, errorClassId);
  2062. }
  2063. catch (CosmosException ex)
  2064. {
  2065. await _dingDing.SendBotMsg($"IES5,{_option.Location},StudentController/updateStudents()\n{ex.Message}\n{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
  2066. }
  2067. catch (Exception ex)
  2068. {
  2069. await _dingDing.SendBotMsg($"IES5,{_option.Location},StudentController/updateStudents()\n{ex.Message}\n{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
  2070. }
  2071. return (null, null, null, null, null, null);
  2072. }
  2073. /// <summary>
  2074. /// 創建學生帳號,目前SDK4.0預覽版還不支援批量創建(TransactionalBatch),待SDK正式發行時在優化此代碼。
  2075. /// </summary>
  2076. /// <param name="userStudents"></param>
  2077. /// <returns>已存在的ID</returns>
  2078. private async Task<(bool isSuc, List<string> existId)> createStudents(AzureCosmosFactory _azureCosmos, DingDing _dingDing, Option _option, List<Student> userStudents)
  2079. {
  2080. var existId = new List<string>();
  2081. var exceptions = new List<Exception>();
  2082. try
  2083. {
  2084. var container = _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Student");
  2085. Parallel.ForEach(userStudents, async item =>
  2086. {
  2087. try
  2088. {
  2089. await container.CreateItemAsync(item);
  2090. }
  2091. catch (CosmosException ex)
  2092. {
  2093. if (ex.Status == (int)HttpStatusCode.Conflict) existId.Add(item.id);
  2094. else exceptions.Add(ex);
  2095. }
  2096. catch (Exception ex)
  2097. {
  2098. exceptions.Add(ex);
  2099. }
  2100. });
  2101. if (exceptions.Count == 0) return (true, existId);
  2102. else if (exceptions.Count > 1) throw new AggregateException(exceptions);
  2103. else if (exceptions.Count == 1) throw exceptions.Single();
  2104. }
  2105. catch (AggregateException ex)
  2106. {
  2107. await _dingDing.SendBotMsg($"IES5,{_option.Location},StudentController/createStudents()\n{ex.Message}\n{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
  2108. }
  2109. catch (Exception ex)
  2110. {
  2111. await _dingDing.SendBotMsg($"IES5,{_option.Location},StudentController/createStudents()\n{ex.Message}\n{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
  2112. }
  2113. return (false, existId);
  2114. }
  2115. /// <summary>
  2116. /// 取得該教室的學生座號,若有給座號LIST,則座號存在才會被查到;反之,若沒給則會將該間教室所有座號抓出來。
  2117. /// </summary>
  2118. /// <param name="schoolId"></param>
  2119. /// <param name="classId"></param>
  2120. /// <param name="nos"></param>
  2121. /// <returns></returns>
  2122. public static async Task<List<(string id, string no)>> checkStudNo(AzureCosmosFactory _azureCosmos, DingDing _dingDing, Option _option, string schoolId, string classId, List<string> nos = null)
  2123. {
  2124. List<(string id, string no)> ret = new List<(string id, string no)>();
  2125. string queryText = $"SELECT c.id, c.no FROM c WHERE c.classId = '{classId}' AND c.code = 'Base-{schoolId}'";
  2126. if (nos != null) queryText += $"AND c.no IN ({string.Join(",", nos.Select(o => $"'{o}'"))})";
  2127. await foreach (Response item in _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Student")
  2128. .GetItemQueryStreamIterator(queryText: queryText, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Base-{schoolId}") }))
  2129. {
  2130. using var json = await JsonDocument.ParseAsync(item.ContentStream);
  2131. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  2132. {
  2133. var classInfos = json.RootElement.GetProperty("Documents").EnumerateArray();
  2134. while (classInfos.MoveNext())
  2135. {
  2136. JsonElement account = classInfos.Current;
  2137. string id = account.GetProperty("id").GetString();
  2138. string no = account.GetProperty("no").GetString();
  2139. ret.Add((id, no));
  2140. }
  2141. }
  2142. }
  2143. return ret;
  2144. }
  2145. /// <summary>
  2146. /// 取得年級資訊
  2147. /// </summary>
  2148. /// <param name="schoolId"></param>
  2149. /// <returns>Key:periodId Vaule:list gradeInfo</returns>
  2150. public static async Task<Dictionary<string, List<(int gradeId, string gradeName)>>> getGrades(AzureCosmosFactory _azureCosmos, DingDing _dingDing, Option _option, string schoolId)
  2151. {
  2152. try
  2153. {
  2154. //Key:學制 Value:年級資訊list
  2155. Dictionary<string, List<(int gradeId, string gradeName)>> dicPeriod = new Dictionary<string, List<(int gradeId, string gradeName)>>();
  2156. var response = await _azureCosmos
  2157. .GetCosmosClient()
  2158. .GetContainer(Constant.TEAMModelOS, "School")
  2159. .ReadItemStreamAsync(schoolId, new PartitionKey("Base"));
  2160. if (response.Status != (int)HttpStatusCode.OK) return null;
  2161. using Stream stream = response.ContentStream;
  2162. var jsonDoc = await JsonDocument.ParseAsync(stream);
  2163. var emumObject = jsonDoc.RootElement.EnumerateObject();
  2164. var period = jsonDoc.RootElement.GetProperty("period").EnumerateArray();
  2165. while (period.MoveNext())
  2166. {
  2167. List<(int gradeId, string gradeName)> gradeInfos = new List<(int gradeId, string gradeName)>();
  2168. JsonElement jsonPeriod = period.Current;
  2169. var periodId = jsonPeriod.GetProperty("id").GetString();
  2170. var grades = jsonPeriod.GetProperty("grades").ToObject<List<string>>();
  2171. for (int index = 0; index < grades.Count; index++)
  2172. {
  2173. gradeInfos.Add((index, grades[index]));
  2174. }
  2175. //var grades = jsonPeriod.GetProperty("grades").EnumerateArray();
  2176. //while (grades.MoveNext())
  2177. //{
  2178. // JsonElement grade = grades.Current;
  2179. // var gradeId = grade.GetInt32();
  2180. // var gradeName = grade.GetProperty("name").GetString();
  2181. // gradeInfos.Add((gradeId, gradeName));
  2182. //}
  2183. dicPeriod.Add(periodId, gradeInfos);
  2184. }
  2185. return dicPeriod;
  2186. }
  2187. catch (Exception ex)
  2188. {
  2189. await _dingDing.SendBotMsg($"IES5,{_option.Location},StudentController/getGrades()\n{ex.Message}\n{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
  2190. return null;
  2191. }
  2192. }
  2193. public struct studCreateInfo
  2194. {
  2195. public studCreateInfo(string id, string name, string gender, int year, string pw, string classId, string no, string periodId,string imei, List<StudentGuardian> guardians)
  2196. {
  2197. this.id = id;
  2198. this.name = name;
  2199. this.gender = gender;
  2200. this.year = year;
  2201. this.pw = pw;
  2202. this.classId = classId;
  2203. this.no = no;
  2204. this.periodId = periodId;
  2205. this.imei = imei;
  2206. this.guardians = guardians;
  2207. }
  2208. public string id { get; }
  2209. public string name { get; }
  2210. public string gender { get; }
  2211. public int year { get; }
  2212. public string pw { get; }
  2213. public string classId { get; }
  2214. public string no { get; }
  2215. public string periodId { get; set; }
  2216. public string imei { get; set; }
  2217. public List<StudentGuardian> guardians { get; set; }
  2218. }
  2219. }
  2220. }