ClassController.cs 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507
  1. using Microsoft.Azure.Cosmos;
  2. using Azure.Messaging.ServiceBus;
  3. using Microsoft.AspNetCore.Authorization;
  4. using Microsoft.AspNetCore.Http;
  5. using Microsoft.AspNetCore.Mvc;
  6. using Microsoft.Extensions.Configuration;
  7. using Microsoft.Extensions.Options;
  8. using System;
  9. using System.Collections.Generic;
  10. using System.Dynamic;
  11. using System.Linq;
  12. using System.Text;
  13. using System.Text.Json;
  14. using System.Threading.Tasks;
  15. using TEAMModelOS.Filter;
  16. using TEAMModelOS.Models;
  17. using TEAMModelOS.SDK;
  18. using TEAMModelOS.SDK.DI;
  19. using TEAMModelOS.SDK.DI;
  20. using TEAMModelOS.SDK.Extension;
  21. using TEAMModelOS.SDK.Models;
  22. using TEAMModelOS.SDK.Models.Cosmos;
  23. namespace TEAMModelOS.Controllers
  24. {
  25. [ProducesResponseType(StatusCodes.Status200OK)]
  26. [ProducesResponseType(StatusCodes.Status400BadRequest)]
  27. [Route("school/classroom")]
  28. [ApiController]
  29. public class ClassController : ControllerBase
  30. {
  31. public readonly AzureCosmosFactory _azureCosmos;
  32. private readonly Option _option;
  33. private readonly DingDing _dingDing;
  34. public IConfiguration _configuration { get; set; }
  35. private readonly AzureServiceBusFactory _serviceBus;
  36. public ClassController(AzureCosmosFactory azureCosmos, DingDing dingDing, AzureServiceBusFactory serviceBus, IConfiguration configuration,
  37. IOptionsSnapshot<Option> option)
  38. {
  39. _azureCosmos = azureCosmos;
  40. _dingDing = dingDing;
  41. _option = option?.Value;
  42. _serviceBus = serviceBus;
  43. _configuration = configuration;
  44. }
  45. [ProducesDefaultResponseType]
  46. //[AuthToken(Roles = "teacher")]
  47. [HttpPost("upsert")]
  48. [AuthToken(Roles = "teacher,admin,student")]
  49. #if !DEBUG
  50. [Authorize(Roles = "IES")]
  51. #endif
  52. public async ValueTask<IActionResult> Upsert(JsonElement request)
  53. {
  54. try
  55. {
  56. if (!request.TryGetProperty("classroom", out JsonElement room)) return BadRequest();
  57. Class classroom = room.ToObject<Class>();
  58. var client = _azureCosmos.GetCosmosClient();
  59. classroom.code = "Class-" + classroom.school;
  60. if (string.IsNullOrEmpty(classroom.id))
  61. {
  62. if (classroom.graduate != 0) {
  63. return Ok(new { error = 409, V = "已毕业的班级不能被编辑!" });
  64. }
  65. List<string> resultIds = new List<string>();
  66. await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryStreamIteratorSql(queryText: $"select c.id from c where c.periodId = '{classroom.periodId}' and c.no = '{classroom.no}' and c.year = '{classroom.year}' ", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey(classroom.code) }))
  67. {
  68. using var json = await JsonDocument.ParseAsync(item.Content);
  69. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  70. {
  71. var accounts = json.RootElement.GetProperty("Documents").EnumerateArray();
  72. while (accounts.MoveNext())
  73. {
  74. JsonElement account = accounts.Current;
  75. resultIds.Add(account.GetProperty("id").GetString());
  76. }
  77. }
  78. }
  79. if (resultIds.Count > 0)
  80. {
  81. return Ok(new { error = ResponseCode.DATA_EXIST, V = "班级编码已经存在!" });
  82. }
  83. classroom.id = Guid.NewGuid().ToString();
  84. classroom = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "School").CreateItemAsync(classroom, new PartitionKey(classroom.code));
  85. }
  86. else
  87. {
  88. var response = await client.GetContainer(Constant.TEAMModelOS, "School").ReadItemStreamAsync(classroom.id, new PartitionKey(classroom.code));
  89. if (response.StatusCode==System.Net.HttpStatusCode.OK)
  90. {
  91. using var json = await JsonDocument.ParseAsync(response.Content);
  92. Class @class = json.ToObject<Class>();
  93. if (classroom.graduate != 0)
  94. {
  95. return Ok(new { error = 409, V = "已毕业的班级不能被编辑!" });
  96. }
  97. if (!@class.no.Equals(classroom.no))
  98. {
  99. List<string> resultIds = new List<string>();
  100. await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryStreamIteratorSql(queryText: $"select c.id from c where c.year = '{classroom.year}' and c.no = '{classroom.no}' and c.periodId = '{classroom.periodId}' ", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey(classroom.code) }))
  101. {
  102. using var document = await JsonDocument.ParseAsync(item.Content);
  103. if (document.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  104. {
  105. var accounts = document.RootElement.GetProperty("Documents").EnumerateArray();
  106. while (accounts.MoveNext())
  107. {
  108. JsonElement account = accounts.Current;
  109. resultIds.Add(account.GetProperty("id").GetString());
  110. }
  111. }
  112. }
  113. if (resultIds.Count > 0)
  114. {
  115. return Ok(new { error = ResponseCode.DATA_EXIST, V = "班级编码已经存在!" });
  116. }
  117. }
  118. classroom = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "School").ReplaceItemAsync(classroom, classroom.id, new PartitionKey(classroom.code));
  119. }
  120. else
  121. {
  122. classroom = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "School").CreateItemAsync(classroom, new PartitionKey(classroom.code));
  123. }
  124. }
  125. return Ok(new { classroom });
  126. }
  127. catch (Exception ex)
  128. {
  129. await _dingDing.SendBotMsg($"OS,{_option.Location},class/Upsert()\n{ex.Message}\n{ex.StackTrace}\n", GroupNames.醍摩豆服務運維群組);
  130. return BadRequest();
  131. }
  132. }
  133. [ProducesDefaultResponseType]
  134. //[AuthToken(Roles = "teacher")]
  135. [HttpPost("upsert-group")]
  136. [AuthToken(Roles = "teacher,admin")]
  137. #if !DEBUG
  138. [Authorize(Roles = "IES")]
  139. #endif
  140. public async ValueTask<IActionResult> UpsertGroup(JsonElement request)
  141. {
  142. try
  143. {
  144. List<Student> students = new List<Student>();
  145. if (!request.TryGetProperty("students", out JsonElement stus)) return BadRequest();
  146. students = stus.ToObject<List<Student>>();
  147. var client = _azureCosmos.GetCosmosClient();
  148. foreach (Student stu in students)
  149. {
  150. var response = await client.GetContainer(Constant.TEAMModelOS, "Student").ReadItemStreamAsync(stu.id, new PartitionKey($"Base-{stu.code}"));
  151. if (response.StatusCode==System.Net.HttpStatusCode.OK)
  152. {
  153. using var json = await JsonDocument.ParseAsync(response.Content);
  154. Student stuInfo = json.ToObject<Student>();
  155. stuInfo.groupId = stu.groupId;
  156. stuInfo.groupName = stu.groupName;
  157. await client.GetContainer(Constant.TEAMModelOS, "Student").ReplaceItemAsync(stuInfo, stuInfo.id, new PartitionKey($"{stuInfo.code}"));
  158. }
  159. }
  160. return Ok(new { students });
  161. }
  162. catch (Exception ex)
  163. {
  164. await _dingDing.SendBotMsg($"OS,{_option.Location},class/UpsertGroup()\n{ex.Message}\n{ex.StackTrace}\n", GroupNames.醍摩豆服務運維群組);
  165. return BadRequest();
  166. }
  167. }
  168. /// <summary>
  169. /// 已弃用
  170. /// </summary>
  171. /// <param name="request"></param>
  172. /// <returns></returns>
  173. [ProducesDefaultResponseType]
  174. [HttpPost("find")]
  175. [AuthToken(Roles = "teacher,admin,student")]
  176. #if !DEBUG
  177. [Authorize(Roles = "IES")]
  178. #endif
  179. public async Task<IActionResult> Find(JsonElement request)
  180. {
  181. if (!request.TryGetProperty("school_code", out JsonElement school_code)) return BadRequest();
  182. if (!request.TryGetProperty("periodId", out JsonElement _periodId)) return BadRequest();
  183. try
  184. {
  185. //增加毕业查询。当毕业查询字段1时,则需要传入入学年份。
  186. int graduate = 0;
  187. int inyear = 0;
  188. //讀取該間學校所有的學生資訊
  189. if (request.TryGetProperty("graduate", out JsonElement _graduate) && $"{_graduate}".Equals("1"))
  190. {
  191. if (request.TryGetProperty("year", out JsonElement _year) && _year.ValueKind.Equals(JsonValueKind.Number))
  192. {
  193. if (int.TryParse($"{_year}", out inyear) && inyear > 0)
  194. {
  195. graduate = 1;
  196. }
  197. else
  198. {
  199. return BadRequest("入学年份大于0");
  200. }
  201. }
  202. else
  203. {
  204. return BadRequest("请输入毕业学生的入学年份");
  205. }
  206. }
  207. string sql = "";
  208. if (graduate == 0) {
  209. sql = $"SELECT value c FROM c where c.periodId='{_periodId}'and (c.graduate = 0 or IS_DEFINED(c.graduate) = false) ";
  210. }
  211. else {
  212. sql = $"SELECT value c FROM c where c.periodId='{_periodId}'and c.graduate =1 and c.year ={inyear}";
  213. }
  214. List<Class> school_classes = new List<Class>();
  215. var client = _azureCosmos.GetCosmosClient();
  216. await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryIteratorSql<Class>
  217. (queryText:sql,
  218. requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Class-{school_code}") }))
  219. {
  220. school_classes.Add(item);
  221. }
  222. return Ok(new { school_classes });
  223. }
  224. catch (Exception ex)
  225. {
  226. await _dingDing.SendBotMsg($"OS,{_option.Location},class/Find()\n{ex.Message}\n{ex.StackTrace}\n", GroupNames.醍摩豆服務運維群組);
  227. return BadRequest();
  228. }
  229. }
  230. [ProducesDefaultResponseType]
  231. //[AuthToken(Roles = "teacher")]
  232. [HttpPost("delete")]
  233. [AuthToken(Roles = "teacher,admin")]
  234. #if !DEBUG
  235. [Authorize(Roles = "IES")]
  236. #endif
  237. public async Task<IActionResult> Delete(JsonElement request)
  238. {
  239. if (!request.TryGetProperty("school_code", out JsonElement code)) return BadRequest();
  240. if (!request.TryGetProperty("id", out JsonElement id)) return BadRequest();
  241. try
  242. {
  243. Class clssz = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.School).ReadItemAsync<Class>($"{id}", new PartitionKey($"Class-{code}"));
  244. if (clssz.graduate == 1) {
  245. return Ok(new { error = 409, V = "已毕业的班级不能被编辑!" });
  246. }
  247. var client = _azureCosmos.GetCosmosClient();
  248. List<Student> students = new List<Student>();
  249. await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "Student").GetItemQueryIteratorSql<Student>(queryText: $"select * from c where c.classId='{id}' ", requestOptions: new QueryRequestOptions { PartitionKey = new PartitionKey($"Base-{code}") }))
  250. {
  251. item.classId = null ;
  252. students.Add(item);
  253. }
  254. List<Task<ItemResponse<Student>>> tasks = new List<Task<ItemResponse<Student>>>();
  255. foreach (var stu in students)
  256. {
  257. tasks.Add(client.GetContainer(Constant.TEAMModelOS, "Student").ReplaceItemAsync(stu, stu.id, new PartitionKey(($"Base-{code}"))));
  258. }
  259. GroupChange change = new GroupChange() { client="web",name =clssz .name};
  260. if (students.IsNotEmpty()) {
  261. change.stuleave.AddRange(students.Select(x=> new Member {
  262. id = x.id,
  263. type = 2,
  264. nickname = x.name,
  265. irs = x.irs,
  266. no = x.no,
  267. groupId = x.groupId,
  268. groupName = x.groupName,
  269. code = $"{code}"
  270. }));
  271. }
  272. change.listid = $"{id}";
  273. change.scope = "school";
  274. change.originCode = $"{code}";
  275. change.school = $"{code}";
  276. change.type = "class";
  277. change.status = "delete";
  278. var messageChange = new ServiceBusMessage(change.ToJsonString());
  279. messageChange.ApplicationProperties.Add("name", "GroupChange");
  280. var ActiveTask = _configuration.GetValue<string>("Azure:ServiceBus:ActiveTask");
  281. await _serviceBus.GetServiceBusClient().SendMessageAsync(ActiveTask, messageChange);
  282. await Task.WhenAll(tasks);
  283. Class classroom= await client.GetContainer(Constant.TEAMModelOS, "School").DeleteItemAsync<Class>(id.ToString(), new PartitionKey($"Class-{code}"));
  284. return Ok(new { classroom });
  285. }
  286. catch (Exception ex)
  287. {
  288. await _dingDing.SendBotMsg($"OS,{_option.Location},class/Delete()\n{ex.Message}\n{ex.StackTrace}\n", GroupNames.醍摩豆服務運維群組);
  289. return BadRequest();
  290. }
  291. }
  292. [ProducesDefaultResponseType]
  293. //[AuthToken(Roles = "teacher")]
  294. [HttpPost("hiteach-link-unlink")]
  295. [AuthToken(Roles = "teacher,admin,student")]
  296. #if !DEBUG
  297. [Authorize(Roles = "IES")]
  298. #endif
  299. public async Task<IActionResult> HiteachLink(JsonElement request)
  300. {
  301. // 必要檢查
  302. if (!request.TryGetProperty("school_code", out JsonElement school_code)) return BadRequest();
  303. if (!request.TryGetProperty("serial_id", out JsonElement serial_id)) return BadRequest();
  304. if (!request.TryGetProperty("opt", out JsonElement opt)) return BadRequest();
  305. request.TryGetProperty("uuid", out JsonElement uuid);
  306. request.TryGetProperty("uuid2", out JsonElement uuid2);
  307. if (!request.TryGetProperty("classId", out JsonElement classId)) return BadRequest();
  308. // 如果是link classId 必填
  309. if (!$"{opt}".Equals("link") &&!$"{opt}".Equals("unlink")) return BadRequest();
  310. try
  311. {
  312. // [取得DB資料]
  313. var client = _azureCosmos.GetCosmosClient();
  314. var response = await client.GetContainer(Constant.TEAMModelOS, "School").ReadItemStreamAsync($"{serial_id}", new PartitionKey($"Product-{school_code}"));
  315. int status = 200;
  316. if (response.StatusCode==System.Net.HttpStatusCode.OK)
  317. {
  318. deviceBound bound = null;
  319. var serial = JsonDocument.Parse(response.Content).RootElement.Deserialize<SchoolProductSerial>();
  320. if ($"{opt}".Equals("link"))
  321. {
  322. if (!string.IsNullOrWhiteSpace($"{uuid}") && !string.IsNullOrWhiteSpace($"{uuid2}"))
  323. {
  324. bound = serial.deviceBound.Find(x => $"{uuid}".Equals(x.uuid) && $"{uuid2}".Equals(x.uuid2));
  325. if (bound != null)
  326. {
  327. bound.classId = $"{classId}";
  328. }
  329. }
  330. else if (!string.IsNullOrWhiteSpace($"{uuid}") && string.IsNullOrWhiteSpace($"{uuid2}"))
  331. {
  332. bound = serial.deviceBound.Find(x => $"{uuid}".Equals(x.uuid));
  333. if (bound != null)
  334. {
  335. bound.classId = $"{classId}";
  336. }
  337. }
  338. else if (string.IsNullOrWhiteSpace($"{uuid}") && !string.IsNullOrWhiteSpace($"{uuid2}"))
  339. {
  340. bound = serial.deviceBound.Find(x => $"{uuid2}".Equals(x.uuid2));
  341. if (bound != null)
  342. {
  343. bound.classId = $"{classId}";
  344. }
  345. }
  346. }
  347. else if ($"{opt}".Equals("unlink")) {
  348. if (!string.IsNullOrWhiteSpace($"{uuid}") && !string.IsNullOrWhiteSpace($"{uuid2}"))
  349. {
  350. bound = serial.deviceBound.Find(x => $"{uuid}".Equals(x.uuid) && $"{uuid2}".Equals(x.uuid2) && $"{classId}".Equals(x.classId));
  351. if (bound != null)
  352. {
  353. bound.classId = null;
  354. }
  355. }
  356. else if (!string.IsNullOrWhiteSpace($"{uuid}") && string.IsNullOrWhiteSpace($"{uuid2}") )
  357. {
  358. bound = serial.deviceBound.Find(x => $"{uuid}".Equals(x.uuid) && $"{classId}".Equals(x.classId));
  359. if (bound != null)
  360. {
  361. bound.classId = null;
  362. }
  363. }
  364. else if (string.IsNullOrWhiteSpace($"{uuid}") && !string.IsNullOrWhiteSpace($"{uuid2}") )
  365. {
  366. bound = serial.deviceBound.Find(x => $"{uuid2}".Equals(x.uuid2) && $"{classId}".Equals(x.classId));
  367. if (bound != null)
  368. {
  369. bound.classId = null;
  370. }
  371. }
  372. }
  373. if (bound == null) {
  374. status = 404;
  375. }
  376. await client.GetContainer(Constant.TEAMModelOS, "School").ReplaceItemAsync<SchoolProductSerial>(serial, $"{serial_id}", new PartitionKey($"Product-{school_code}"));
  377. }
  378. else {
  379. status = 404;
  380. }
  381. return Ok(new { status });
  382. }
  383. catch (Exception ex)
  384. {
  385. await _dingDing.SendBotMsg($"IES5,{_option.Location},hiteach-link()\n{ex.Message}\n{ex.StackTrace}{request.ToJsonString()}", GroupNames.醍摩豆服務運維群組);
  386. return BadRequest();
  387. }
  388. }
  389. [ProducesDefaultResponseType]
  390. //[AuthToken(Roles = "teacher")]
  391. [HttpPost("hiteach-unlink-classId")]
  392. [AuthToken(Roles = "teacher,admin,student")]
  393. #if !DEBUG
  394. [Authorize(Roles = "IES")]
  395. #endif
  396. public async Task<IActionResult> HiteachunlinkByClassId(JsonElement request)
  397. {
  398. // 必要檢查
  399. if (!request.TryGetProperty("school_code", out JsonElement code)) return BadRequest();
  400. if (!request.TryGetProperty("classId", out JsonElement id)) return BadRequest();
  401. try
  402. {
  403. // [變數宣告]
  404. string school_code = code.ToString(); // 學校簡碼
  405. string classId = id.ToString(); // 教室ID
  406. // [取得DB資料]
  407. var client = _azureCosmos.GetCosmosClient();
  408. var response = await client.GetContainer(Constant.TEAMModelOS, "School").ReadItemStreamAsync(school_code, new PartitionKey("Product"));
  409. if (response.StatusCode==System.Net.HttpStatusCode.OK)
  410. {
  411. var json = await JsonDocument.ParseAsync(response.Content);
  412. //軟體
  413. SchoolProduct schoolProductItem = json.ToObject<SchoolProduct>();
  414. foreach (SerialInfoBaseWithdeviceBound updSerialInfo in schoolProductItem.serial)
  415. {
  416. if (updSerialInfo.deviceBound != null)
  417. {
  418. deviceBound updDeviceBound = updSerialInfo.deviceBound.Where(d => d.classId == classId).FirstOrDefault();
  419. if (updDeviceBound != null)
  420. {
  421. updDeviceBound.classId = null;
  422. }
  423. }
  424. }
  425. await client.GetContainer(Constant.TEAMModelOS, "School").ReplaceItemAsync<SchoolProduct>(schoolProductItem, school_code, new PartitionKey("Product"));
  426. }
  427. return Ok(new { error = 0 });
  428. }
  429. catch (Exception ex)
  430. {
  431. await _dingDing.SendBotMsg($"IES5,{_option.Location},hiteach-unlink-classId()\n{ex.Message}\n{ex.StackTrace}{request.ToJsonString()}", GroupNames.醍摩豆服務運維群組);
  432. return BadRequest();
  433. }
  434. }
  435. [ProducesDefaultResponseType]
  436. //[AuthToken(Roles = "teacher")]
  437. [HttpPost("name")]
  438. [AuthToken(Roles = "teacher,admin,student")]
  439. #if !DEBUG
  440. [Authorize(Roles = "IES")]
  441. #endif
  442. public async Task<IActionResult> GetNameList(JsonElement request)
  443. {
  444. if (!request.TryGetProperty("ids", out JsonElement ids)) return BadRequest();
  445. if (!request.TryGetProperty("code", out JsonElement code)) return BadRequest();
  446. try
  447. {
  448. var client = _azureCosmos.GetCosmosClient();
  449. //List<string> id = ids.ToObject<List<string>>();
  450. string info = "";
  451. for (int i = 0; i < ids.GetArrayLength(); i++)
  452. {
  453. //ids.Add(id[i].ToJsonString());
  454. info += ids[i].ToJsonString() + ",";
  455. }
  456. List<object> ClassName = new List<object>();
  457. await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "Teacher").GetItemQueryStreamIteratorSql(
  458. queryText: $"select c.id,c.name from c where c.id in ({info[0..^1]})", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Class-{code}") }))
  459. {
  460. using var json = await JsonDocument.ParseAsync(item.Content);
  461. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  462. {
  463. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  464. {
  465. ClassName.Add(obj.ToObject<object>());
  466. }
  467. }
  468. }
  469. return Ok(new { ClassName });
  470. }
  471. catch (Exception ex)
  472. {
  473. await _dingDing.SendBotMsg($"OS,{_option.Location},class/name()\n{ex.Message}\n{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
  474. return BadRequest();
  475. }
  476. }
  477. }
  478. }