StudentController.cs 45 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Dynamic;
  5. using System.IO;
  6. using System.Linq;
  7. using System.Net;
  8. using System.Text;
  9. using System.Text.Json;
  10. using System.Threading.Tasks;
  11. using Azure;
  12. using Azure.Cosmos;
  13. using Azure.Messaging.ServiceBus;
  14. using Azure.Storage.Sas;
  15. using HTEXLib.COMM.Helpers;
  16. using Microsoft.AspNetCore.Authorization;
  17. using Microsoft.AspNetCore.Cryptography.KeyDerivation;
  18. using Microsoft.AspNetCore.Mvc;
  19. using Microsoft.Extensions.Configuration;
  20. using Microsoft.Extensions.Options;
  21. using TEAMModelOS.Filter;
  22. using TEAMModelOS.Models;
  23. using TEAMModelOS.SDK;
  24. using TEAMModelOS.SDK.DI;
  25. using TEAMModelOS.SDK.Extension;
  26. using TEAMModelOS.SDK.Models;
  27. using TEAMModelOS.SDK.Models.Cosmos;
  28. using TEAMModelOS.SDK.Models.Cosmos.Common;
  29. using static TEAMModelOS.SDK.StudentService;
  30. namespace TEAMModelOS.Controllers
  31. {
  32. [Route("student")]
  33. [ApiController]
  34. public class StudentController : Controller
  35. {
  36. private readonly AzureCosmosFactory _azureCosmos;
  37. private readonly AzureStorageFactory _azureStorage;
  38. private readonly DingDing _dingDing;
  39. private readonly Option _option;
  40. public IConfiguration _configuration { get; set; }
  41. private readonly AzureServiceBusFactory _serviceBus;
  42. public StudentController(
  43. AzureCosmosFactory azureCosmos,
  44. AzureStorageFactory azureStorage,
  45. DingDing dingDing,
  46. IOptionsSnapshot<Option> option, IConfiguration configuration, AzureServiceBusFactory serviceBus
  47. )
  48. {
  49. _azureCosmos = azureCosmos;
  50. _azureStorage = azureStorage;
  51. _dingDing = dingDing;
  52. _option = option?.Value;
  53. _configuration = configuration;
  54. _serviceBus = serviceBus;
  55. }
  56. /// <summary>
  57. /// 學生帳號管理
  58. /// </summary>
  59. /// <param name="request"></param>
  60. /// <returns></returns>
  61. [HttpPost("student-manage")]
  62. [Authorize(Roles = "IES")]
  63. [AuthToken(Roles = "admin,student,teacher")]
  64. public async Task<IActionResult> StudentManage(JsonElement request)
  65. {
  66. try
  67. {
  68. //TODO : 權限檢查、學校檢查。
  69. if (!request.TryGetProperty("grant_type", out JsonElement grant_type) || !request.TryGetProperty("schoolId", out JsonElement schoolId)) return BadRequest();
  70. List<Student> preStudents = null;
  71. List<Student> webStudents = null;
  72. var (stuid, _, _, school) = HttpContext.GetAuthTokenInfo();
  73. switch (grant_type.GetString())
  74. {
  75. case "create":
  76. //單人創建 創建學生->將學生加入教室 檢查學生ID是否重複,欲加入的班級存不存在,座號是否重複。
  77. //id pw name classId no year
  78. //retrun 如果有重複則回{ existNo } , 成功則{ id, name, year, classId, no }
  79. var importStuds = request.GetProperty("students").EnumerateArray();
  80. Dictionary<string, GroupChange> dictChange = new Dictionary<string, GroupChange>();
  81. while (importStuds.MoveNext())
  82. {
  83. JsonElement currStud = importStuds.Current;
  84. string id = null, name = null, pw = null, no = null, classId = null, periodId = null;
  85. int year = 0;
  86. //讀取輸入的資料
  87. if (!currStud.TryGetProperty("id", out var tmpId) || !currStud.TryGetProperty("name", out var tmpName)) continue;
  88. id = tmpId.GetString();
  89. name = tmpName.GetString();
  90. if (currStud.TryGetProperty("pw", out var tmpPw)) pw = tmpPw.GetString();
  91. if (currStud.TryGetProperty("year", out var tmpYear)) year = tmpYear.GetInt32();
  92. if (currStud.TryGetProperty("no", out var tmpNo)) no = tmpNo.GetString();
  93. if (currStud.TryGetProperty("classId", out var tmpClassId)) classId = tmpClassId.GetString();
  94. //要檢查座號使否已被使用
  95. var existNo = await StudentService.checkStudNo( _azureCosmos, _dingDing, _option, schoolId.GetString(), classId, new List<string>() { id });
  96. if (existNo.Count != 0) return this.Ok(new { code = $"Base-{schoolId.GetString()}", existNo = existNo.Select(o => o.id).ToList() });
  97. if (currStud.TryGetProperty("periodId", out var tempPeriodId)) periodId = tempPeriodId.GetString();
  98. //建立學生
  99. studCreateInfo studCreateInfo = new studCreateInfo(id, name, "M", year, pw, classId, no, periodId);
  100. var isCreateSuc = await createStudent( _azureCosmos, _dingDing, _option, schoolId.GetString(), studCreateInfo);
  101. if (isCreateSuc) {
  102. if (dictChange.ContainsKey(classId))
  103. {
  104. dictChange[classId].stujoin.Add(
  105. new Member
  106. {
  107. id = id,
  108. code = $"{schoolId.GetString()}",
  109. type=2,
  110. });
  111. }
  112. else {
  113. GroupChange change = new GroupChange
  114. {
  115. scope = "school",
  116. school = schoolId.GetString(),
  117. type = "student",
  118. originCode = schoolId.GetString(),
  119. listid = classId,
  120. stujoin = new List<Member>
  121. {
  122. new Member
  123. {
  124. id= id,
  125. code=schoolId.GetString(),
  126. type=2,
  127. }
  128. }
  129. };
  130. dictChange.Add(classId, change);
  131. }
  132. foreach (var changed in dictChange.Keys)
  133. {
  134. var change = dictChange[changed];
  135. if (change.stujoin.Count != 0 || change.stuleave.Count != 0)
  136. {
  137. var messageChange = new ServiceBusMessage(change.ToJsonString());
  138. messageChange.ApplicationProperties.Add("name", "GroupChange");
  139. var ActiveTask = _configuration.GetValue<string>("Azure:ServiceBus:ActiveTask");
  140. await _serviceBus.GetServiceBusClient().SendMessageAsync(ActiveTask, messageChange);
  141. }
  142. }
  143. return this.Ok(new { code = $"Base-{schoolId.GetString()}", id, name, year, classId, no, periodId });
  144. }
  145. else return this.Ok(new { code = $"Base-{schoolId.GetString()}", errorId = id });
  146. }
  147. break;
  148. case "import":
  149. //只有ClassNo可以比對
  150. webStudents = request.GetProperty("students").ToObject<List<Student>>();
  151. preStudents= await StudentService.GeStudentData(_azureCosmos, schoolId.GetString(), webStudents?.Select(x => x.id));
  152. var retUpsert = await StudentService.upsertStudents( _azureCosmos, _dingDing, _option, schoolId.GetString(), request.GetProperty("students").EnumerateArray());
  153. await CheckStudent( _serviceBus, _configuration, _azureCosmos, schoolId.GetString(), webStudents, preStudents);
  154. return this.Ok(new { code = $"Base-{schoolId.GetString()}", students = retUpsert.studs, retUpsert.classDuplNos, retUpsert.errorIds });
  155. case "read":
  156. //讀取該間學校所有的學生資訊
  157. var students = await StudentService.getAllStudent( _azureCosmos, _dingDing, _option,schoolId.GetString());
  158. return this.Ok(new { code = $"Base-{schoolId.GetString()}", students });
  159. case "update":
  160. //更新學生資料,批量密碼重置,基本資訊更新(姓名、教室ID、性別、學年及座號)
  161. webStudents = request.GetProperty("students").ToObject<List<Student>>();
  162. preStudents = await StudentService.GeStudentData(_azureCosmos, schoolId.GetString(), webStudents?.Select(x => x.id));
  163. var retUpdate = await StudentService.updateStudents( _azureCosmos, _dingDing, _option, schoolId.GetString(), request.GetProperty("students").EnumerateArray());
  164. await StudentService.CheckStudent( _serviceBus, _configuration, _azureCosmos, schoolId.GetString(), webStudents, preStudents);
  165. return this.Ok(new { code = $"Base-{schoolId.GetString()}", students = retUpdate.studs, retUpdate.classDuplNos, retUpdate.nonexistentIds, retUpdate.errorNos, retUpdate.errorClassId });
  166. case "delete":
  167. //刪除學生資料及從教室學生名單內移除該學生
  168. webStudents = request.GetProperty("students").ToObject<List<Student>>();
  169. preStudents = await StudentService.GeStudentData(_azureCosmos, schoolId.GetString(), webStudents?.Select(x => x.id));
  170. var sucDelIds = await StudentService.deleteStudents( _azureCosmos, _dingDing, _option, schoolId.GetString(), request.GetProperty("students").EnumerateArray());
  171. await StudentService.CheckStudent( _serviceBus, _configuration, _azureCosmos,schoolId.GetString(), webStudents, preStudents);
  172. return this.Ok(new { code = $"Base-{schoolId.GetString()}", ids = sucDelIds });
  173. case "remove":
  174. //將學生基本資料內的classId、no、groupId及groupName寫入null
  175. List<string> stus = request.GetProperty("students").ToObject<List<string>>();
  176. webStudents = new List<Student>();
  177. foreach (string idstu in stus) {
  178. webStudents.Add(new Student { id= idstu, code=$"Base-{schoolId}"});
  179. }
  180. preStudents = await StudentService.GeStudentData(_azureCosmos, schoolId.GetString(), webStudents?.Select(x => x.id));
  181. (List<string> studs, List<string> nonexistentIds, List<string> errorIds) retRemove = await StudentService.removeStudentClassInfo(
  182. _azureCosmos, _dingDing, _option,
  183. schoolId.GetString(), request.GetProperty("students").EnumerateArray());
  184. await StudentService.CheckStudent( _serviceBus, _configuration, _azureCosmos, schoolId.GetString(), webStudents, preStudents);
  185. return Ok(new { code = $"Base-{schoolId.GetString()}", ids = retRemove.studs, retRemove.nonexistentIds, retRemove.errorIds });
  186. case "avatar":
  187. if (request.TryGetProperty("avatar", out JsonElement _avatar) && _avatar.ValueKind.Equals(JsonValueKind.Array))
  188. {
  189. List<StudentInfo> avatars = _avatar.ToObject<List<StudentInfo>>();
  190. if (avatars.IsNotEmpty())
  191. {
  192. List<Student> studentsp = new List<Student>();
  193. string insql = string.Join(',', avatars.Select(x => $"'{x.studentId}'"));
  194. string sql = $"select value(c) from c where c.id in ({insql}) ";
  195. await foreach (var item in _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Student")
  196. .GetItemQueryIterator<Student>(sql, requestOptions: new QueryRequestOptions { PartitionKey = new PartitionKey($"Base-{schoolId}") }))
  197. {
  198. studentsp.Add(item);
  199. }
  200. (string url, string sas) = _azureStorage.GetBlobContainerSAS99Year($"{schoolId}", BlobContainerSasPermissions.Read);
  201. foreach (Student student in studentsp)
  202. {
  203. StudentInfo avatar = avatars.Find(x => x.studentId.Equals(student.id));
  204. student.picture = avatar != null ? $"{avatar.picture}?{sas}" : null;
  205. await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Student").ReplaceItemAsync<Student>(student, student.id, new PartitionKey(student.code));
  206. }
  207. return Ok(new { students = studentsp.Select(x => new { x.id, x.picture, x.code, x.name }) });
  208. }
  209. else
  210. {
  211. return BadRequest();
  212. }
  213. }
  214. else
  215. {
  216. return BadRequest();
  217. }
  218. case "update-self-info":
  219. if (request.TryGetProperty("studentInfo", out JsonElement _studentInfo))
  220. {
  221. var studentInfo = _studentInfo.ToObject<StudentInfo>();
  222. if (studentInfo.studentId.Equals(stuid) && school .Equals($"{schoolId}")) {
  223. Student student = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Student")
  224. .ReadItemAsync<Student>(studentInfo.studentId, new PartitionKey($"Base-{schoolId}"));
  225. student.mail = string.IsNullOrEmpty(studentInfo.mail) ? student.mail : studentInfo.mail;
  226. student.mobile = string.IsNullOrEmpty(studentInfo.mobile) ? student.mobile : studentInfo.mobile;
  227. student.name = string.IsNullOrEmpty(studentInfo.name) ? student.name : studentInfo.name;
  228. (string url, string sas) = _azureStorage.GetBlobContainerSAS99Year($"{schoolId}", BlobContainerSasPermissions.Read);
  229. student.picture = string.IsNullOrEmpty(studentInfo.picture) ? student.picture : $"{studentInfo.picture}?{sas}";
  230. student.gender = string.IsNullOrEmpty(studentInfo.gender) ? student.gender : studentInfo.gender;
  231. await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Student")
  232. .ReplaceItemAsync<Student>(student, studentInfo.studentId, new PartitionKey($"Base-{schoolId}"));
  233. return Ok(new { studentInfo });
  234. }
  235. else {
  236. return Ok(new { error = false, status = false, msg = "修改的不是自己的信息" });
  237. }
  238. }
  239. else {
  240. return BadRequest();
  241. }
  242. case "update-self-password":
  243. if (request.TryGetProperty("newpwd", out JsonElement _newpwd)&&
  244. request.TryGetProperty("oldpwd", out JsonElement _oldpwd) &&
  245. request.TryGetProperty("studentId", out JsonElement _studentId)) {
  246. if ($"{_studentId}".Equals(stuid) && school.Equals($"{schoolId}")) {
  247. try {
  248. Student student = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Student")
  249. .ReadItemAsync<Student>($"{_studentId}", new PartitionKey($"Base-{schoolId}"));
  250. var HashedPW = Utils.HashedPassword($"{_oldpwd}", student.salt);
  251. if (HashedPW.Equals(student.pw))
  252. {
  253. student.pw = Utils.HashedPassword($"{_newpwd}", student.salt);
  254. await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Student")
  255. .ReplaceItemAsync<Student>(student, student.id, new PartitionKey($"Base-{schoolId}"));
  256. return Ok(new { status = true });
  257. }
  258. else
  259. {
  260. return Ok(new { error = false, status = false, msg = "密码不一致" });
  261. }
  262. } catch (Exception ex) {
  263. await _dingDing.SendBotMsg($"IES5,{_option.Location},StudentController/StudentManage()\n{ex.Message}{ex.StackTrace}\n{request.ToJsonString()}", GroupNames.醍摩豆服務運維群組);
  264. return Ok(new { error = false, status = false, msg = "账号不存在" });
  265. }
  266. }
  267. else
  268. {
  269. return Ok(new { error = false, status = false, msg = "修改的不是自己的密码" });
  270. }
  271. }
  272. else
  273. {
  274. return BadRequest();
  275. }
  276. case "read-self-info":
  277. if (request.TryGetProperty("studentId", out JsonElement __studentId))
  278. {
  279. try {
  280. Student student = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Student")
  281. .ReadItemAsync<Student>($"{__studentId}", new PartitionKey($"Base-{schoolId}"));
  282. return Ok(new { student.mail, student.mobile, student.name, student.picture, student.gender, student.id, student.schoolId, student.year, student.no, student.classId, student.periodId, irs = student.irs });
  283. }
  284. catch (Exception ex) {
  285. await _dingDing.SendBotMsg($"IES5,{_option.Location},StudentController/StudentManage()\n{ex.Message}{ex.StackTrace}\n{request.ToJsonString()}", GroupNames.醍摩豆服務運維群組);
  286. }
  287. return Ok();
  288. }
  289. else
  290. {
  291. return BadRequest();
  292. }
  293. default:
  294. return BadRequest();
  295. }
  296. }
  297. catch (Exception ex)
  298. {
  299. await _dingDing.SendBotMsg($"IES5,{_option.Location},StudentController/StudentManage()\n{ex.Message}{ex.StackTrace}\n{request.ToJsonString()}", GroupNames.醍摩豆服務運維群組);
  300. }
  301. return BadRequest();
  302. }
  303. /// <summary>
  304. /// 學生登入
  305. /// </summary>
  306. /// <param name = "request" ></ param >
  307. [AllowAnonymous]
  308. [HttpPost("login")]
  309. public async Task<IActionResult> Login(JsonElement request)
  310. {
  311. try
  312. {
  313. var client = _azureCosmos.GetCosmosClient();
  314. var schoolClient = client.GetContainer(Constant.TEAMModelOS, "School");
  315. var teacherClient = client.GetContainer(Constant.TEAMModelOS, "Teacher");
  316. var studentClient = client.GetContainer(Constant.TEAMModelOS, "Student");
  317. //參數取得
  318. if (!request.TryGetProperty("school_code", out JsonElement school_code)) return BadRequest();
  319. if (!request.TryGetProperty("id", out JsonElement id)) return BadRequest();
  320. if (!request.TryGetProperty("pw", out JsonElement pw)) return BadRequest();
  321. var response = await studentClient.ReadItemStreamAsync(id.GetString(), new PartitionKey($"Base-{school_code.GetString().ToLower()}"));
  322. if (response.Status == 200)
  323. {
  324. var rjson = await JsonDocument.ParseAsync(response.ContentStream);
  325. rjson.RootElement.TryGetProperty("salt", out JsonElement salt);
  326. rjson.RootElement.TryGetProperty("pw", out JsonElement dbpw);
  327. rjson.RootElement.TryGetProperty("name", out JsonElement name);
  328. rjson.RootElement.TryGetProperty("picture", out JsonElement picture);
  329. rjson.RootElement.TryGetProperty("classId", out JsonElement classId);
  330. rjson.RootElement.TryGetProperty("no", out JsonElement no);
  331. rjson.RootElement.TryGetProperty("groupId", out JsonElement groupId);
  332. rjson.RootElement.TryGetProperty("groupName", out JsonElement groupName);
  333. var HashedPW = Utils.HashedPassword(pw.ToString(), salt.ToString());
  334. if (HashedPW.Equals(dbpw.GetString()))
  335. {
  336. //班級課程
  337. object classinfo = null;
  338. List<object> courses = new List<object>();
  339. ////校本
  340. //取得所屬預設班級信息
  341. if (!classId.ValueKind.Equals(JsonValueKind.Null) && classId.ValueKind.Equals(JsonValueKind.String))
  342. {
  343. var query = $"SELECT c.code, c.id, c.name, c.periodId, c.gradeId FROM c WHERE c.id = '{classId.GetString()}'";
  344. await foreach (var item in schoolClient.GetItemQueryStreamIterator(queryText: query, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Class-{school_code}") }))
  345. {
  346. using var json = await JsonDocument.ParseAsync(item.ContentStream);
  347. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  348. {
  349. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  350. {
  351. classinfo = obj.ToObject<object>();
  352. }
  353. }
  354. }
  355. }
  356. //取得該學生跑班課名單ID
  357. List<string> stulistidsSch = new List<string>();
  358. var querysl = $"SELECT c.id FROM c JOIN members IN c.members WHERE members.id = '{id.GetString()}' AND members.code = '{school_code}'";
  359. await foreach (var item in schoolClient.GetItemQueryStreamIterator(queryText: querysl, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"GroupList-{school_code}") }))
  360. {
  361. using var json = await JsonDocument.ParseAsync(item.ContentStream);
  362. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  363. {
  364. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  365. {
  366. stulistidsSch.Add(obj.GetProperty("id").ToString());
  367. }
  368. }
  369. }
  370. //取得該學生的學校課程名單
  371. var queryc = $"SELECT DISTINCT c.id, c.name, schedule.class, schedule.time, schedule.notice, c.scope FROM c JOIN schedule IN c.schedule WHERE (schedule.class.id = '{classId}' AND schedule.stulist = null) OR (ARRAY_CONTAINS({JsonSerializer.Serialize(stulistidsSch)}, schedule.stulist, true))";
  372. await foreach (var item in schoolClient.GetItemQueryStreamIterator(queryText: queryc, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Course-{school_code}") }))
  373. {
  374. using var json = await JsonDocument.ParseAsync(item.ContentStream);
  375. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  376. {
  377. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  378. {
  379. courses.Add(obj.ToObject<object>());
  380. }
  381. }
  382. }
  383. ////個人
  384. //取得該學生跑班課名單ID
  385. Dictionary<string, Dictionary<string, string>> stulistidsTea = new Dictionary<string, Dictionary<string, string>>();
  386. var queryslt = $"SELECT c.id, c.course.id as courseId, c.course.code as courseCode FROM c JOIN members IN c.members WHERE members.id = '{id.GetString()}' AND members.code = '{school_code}'";
  387. await foreach (var item in teacherClient.GetItemQueryStreamIterator(queryText: queryslt, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("GroupList") }))
  388. {
  389. using var json = await JsonDocument.ParseAsync(item.ContentStream);
  390. var js = json.RootElement.ToJsonString();
  391. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  392. {
  393. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  394. {
  395. string courseCode = "";
  396. if (obj.TryGetProperty("courseCode", out var code))
  397. {
  398. courseCode = code.GetString();
  399. }
  400. string courseId = "";
  401. if (obj.TryGetProperty("courseId", out var cosid))
  402. {
  403. courseId = cosid.GetString();
  404. }
  405. string stulistId = "";
  406. if (obj.TryGetProperty("id", out var listId))
  407. {
  408. stulistId = listId.GetString();
  409. }
  410. if (!string.IsNullOrEmpty(courseCode))
  411. {
  412. if (!stulistidsTea.ContainsKey(courseCode))
  413. {
  414. Dictionary<string, string> pCourseIdDic = new Dictionary<string, string>();
  415. pCourseIdDic.Add(courseId, stulistId);
  416. stulistidsTea.Add(courseCode, pCourseIdDic);
  417. }
  418. else
  419. {
  420. if (!stulistidsTea[courseCode].ContainsKey(courseId))
  421. {
  422. stulistidsTea[courseCode].Add(courseId, stulistId);
  423. }
  424. }
  425. }
  426. }
  427. }
  428. }
  429. //取得該學生的老師個人課程名單
  430. foreach (KeyValuePair<string, Dictionary<string, string>> item in stulistidsTea)
  431. {
  432. string courseCode = item.Key;
  433. Dictionary<string, string> courseIdDic = item.Value;
  434. string stucourseWhere = string.Empty;
  435. foreach (KeyValuePair<string, string> itemDic in courseIdDic)
  436. {
  437. string courseId = itemDic.Key;
  438. string stuListId = itemDic.Value;
  439. if (!string.IsNullOrWhiteSpace(stucourseWhere))
  440. {
  441. stucourseWhere += " OR ";
  442. }
  443. stucourseWhere += $"( c.id = '{courseId}' AND schedule.stulist = '{stuListId}' )";
  444. }
  445. var querycst = $"SELECT DISTINCT c.id, c.name, schedule.class, schedule.time, schedule.notice, c.scope FROM c JOIN schedule IN c.schedule WHERE {stucourseWhere}";
  446. await foreach (var itemcs in teacherClient.GetItemQueryStreamIterator(queryText: querycst, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"{courseCode}") }))
  447. {
  448. using var json = await JsonDocument.ParseAsync(itemcs.ContentStream);
  449. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  450. {
  451. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  452. {
  453. courses.Add(obj.ToObject<object>());
  454. }
  455. }
  456. }
  457. }
  458. // BLOB(學校,唯讀)
  459. var (blob_uri, blob_sas) = _azureStorage.GetBlobContainerSAS(school_code.GetString().ToLower(), BlobContainerSasPermissions.Read);
  460. //換取AuthToken,提供給前端
  461. var auth_token = JwtAuthExtension.CreateAuthToken(_option.HostName, id.GetString(), name.GetString(), picture.GetString(), _option.JwtSecretKey,scope: Constant.ScopeStudent, Website: "IES", schoolID: school_code.GetString(), roles: new[] { "student" });
  462. var clientID = _configuration.GetValue<string>("HaBookAuth:CoreService:clientID");
  463. var clientSecret = _configuration.GetValue<string>("HaBookAuth:CoreService:clientSecret");
  464. var token = await CoreTokenExtensions.CreateAccessToken(clientID, clientSecret, _option.Location.Replace("-Dep","").Replace("-Test",""));
  465. return Ok(new { location = _option.Location, error = 0, auth_token, blob_uri, blob_sas, classinfo, courses,token =new { access_token = token.AccessToken, expires_in = token.ExpiresOn, id_token = auth_token, token_type = token.TokenType } });
  466. }
  467. else
  468. {
  469. return Ok(new { error = 1, message = "账号或密码错误" });
  470. }
  471. }
  472. else
  473. {
  474. return Ok(new { error = 2, message = "無此帳號存在" });
  475. }
  476. }
  477. catch (Exception ex)
  478. {
  479. await _dingDing.SendBotMsg($"IES5,{_option.Location},StudentController/login()\n{ex.Message}{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
  480. return BadRequest();
  481. }
  482. }
  483. //查询学生名单详情
  484. [ProducesDefaultResponseType]
  485. //[AuthToken(Roles = "teacher")]
  486. [HttpPost("get-summary-student")]
  487. [Authorize(Roles = "IES")]
  488. public async Task<IActionResult> getSummary(JsonElement requert)
  489. {
  490. try
  491. {
  492. requert.TryGetProperty("students", out JsonElement students);
  493. requert.TryGetProperty("tmdIds", out JsonElement tmdIds);
  494. List<TmdInfo> tmdinfos = new List<TmdInfo>();
  495. List<object> stus = new List<object>();
  496. var client = _azureCosmos.GetCosmosClient();
  497. if (students.ValueKind.Equals(JsonValueKind.Array))
  498. {
  499. List<Students> stuList = students.ToObject<List<Students>>();
  500. if (stuList.IsNotEmpty())
  501. {
  502. foreach (Students stu in stuList)
  503. {
  504. var query = $"select c.id,c.name,c.picture,c.classId,c.code,c.groupId,c.groupName,c.no from c where c.id = '{stu.id}'";
  505. await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "Student").GetItemQueryStreamIterator(queryText: query, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"{stu.code}") }))
  506. {
  507. using var json = await JsonDocument.ParseAsync(item.ContentStream);
  508. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  509. {
  510. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  511. {
  512. stus.Add(obj.ToObject<object>());
  513. }
  514. }
  515. }
  516. }
  517. }
  518. }
  519. if (tmdIds.ValueKind.Equals(JsonValueKind.Array))
  520. {
  521. List<string> tmdids = tmdIds.ToObject<List<string>>();
  522. if (tmdids.IsNotEmpty())
  523. {
  524. List<string> inids = new List<string>();
  525. tmdids.ForEach(x => { inids.Add($"'{x}'"); });
  526. var insql = string.Join(",", inids);
  527. var queryslt = $"SELECT c.id,c.name,c.picture FROM c where c.id in ({insql})";
  528. await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "Student").GetItemQueryIterator<TmdInfo>(queryText: queryslt, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Base") }))
  529. {
  530. tmdinfos.Add(item);
  531. }
  532. }
  533. }
  534. return Ok(new { stus, tmdinfos });
  535. }
  536. catch (Exception ex)
  537. {
  538. await _dingDing.SendBotMsg($"OS,{_option.Location},student/get-summary-student()\n{ex.Message}", GroupNames.醍摩豆服務運維群組);
  539. return BadRequest();
  540. }
  541. }
  542. /// <summary>
  543. /// 學生簡易登入
  544. /// </summary>
  545. /// <param name = "request" ></ param >
  546. [AllowAnonymous]
  547. [HttpPost("login-simple")]
  548. public async Task<IActionResult> LoginSimple(JsonElement request)
  549. {
  550. try
  551. {
  552. var client = _azureCosmos.GetCosmosClient();
  553. var schoolClient = client.GetContainer(Constant.TEAMModelOS, "School");
  554. var studentClient = client.GetContainer(Constant.TEAMModelOS, "Student");
  555. //參數取得
  556. if (!request.TryGetProperty("school_code", out JsonElement school_code)) return BadRequest();
  557. if (!request.TryGetProperty("id", out JsonElement id)) return BadRequest();
  558. if (!request.TryGetProperty("pw", out JsonElement pw)) return BadRequest();
  559. var response = await studentClient.ReadItemStreamAsync(id.GetString(), new PartitionKey($"Base-{school_code.GetString().ToLower()}"));
  560. if (response.Status == 200)
  561. {
  562. var rjson = await JsonDocument.ParseAsync(response.ContentStream);
  563. rjson.RootElement.TryGetProperty("salt", out JsonElement salt);
  564. rjson.RootElement.TryGetProperty("pw", out JsonElement dbpw);
  565. rjson.RootElement.TryGetProperty("name", out JsonElement name);
  566. rjson.RootElement.TryGetProperty("picture", out JsonElement picture);
  567. rjson.RootElement.TryGetProperty("classId", out JsonElement classId);
  568. rjson.RootElement.TryGetProperty("no", out JsonElement no);
  569. rjson.RootElement.TryGetProperty("groupId", out JsonElement groupId);
  570. rjson.RootElement.TryGetProperty("groupName", out JsonElement groupName);
  571. dynamic user = new ExpandoObject();
  572. user.no = no;
  573. user.groupId = groupId;
  574. user.groupName = groupName;
  575. var HashedPW = Utils.HashedPassword(pw.ToString(), salt.ToString());
  576. if (HashedPW.Equals(dbpw.GetString()))
  577. {
  578. //取得所屬預設班級信息
  579. object classinfo = null;
  580. if (!classId.ValueKind.Equals(JsonValueKind.Null) && classId.ValueKind.Equals(JsonValueKind.String))
  581. {
  582. var query = $"SELECT c.id, c.no, c.name FROM c WHERE c.id = '{classId.GetString()}'";
  583. await foreach (var item in schoolClient.GetItemQueryStreamIterator(queryText: query, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Class-{school_code}") }))
  584. {
  585. using var json = await JsonDocument.ParseAsync(item.ContentStream);
  586. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  587. {
  588. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  589. {
  590. classinfo = obj.ToObject<object>();
  591. }
  592. }
  593. }
  594. }
  595. //換取AuthToken,提供給前端
  596. var auth_token = JwtAuthExtension.CreateAuthToken(_option.HostName, id.GetString(), name.GetString(), picture.GetString(), _option.JwtSecretKey, Website: "IES", scope: Constant.ScopeStudent, schoolID: school_code.GetString(), roles: new[] { "student" });
  597. //其他訊息
  598. dynamic school = new ExpandoObject();
  599. //回傳
  600. return Ok(new { error = 0, auth_token, classinfo, user });
  601. }
  602. else
  603. {
  604. return Ok(new { error = 1, message = "Invalid account or password" });
  605. }
  606. }
  607. else
  608. {
  609. return Ok(new { error = 2, message = "Invalid account" });
  610. }
  611. }
  612. catch (Exception ex)
  613. {
  614. await _dingDing.SendBotMsg($"IES5,{_option.Location},StudentController/login-simple()\n{ex.Message}", GroupNames.醍摩豆服務運維群組);
  615. return BadRequest();
  616. }
  617. }
  618. //TODO 此API需處理對應前端返回的相關數據
  619. [ProducesDefaultResponseType]
  620. [AuthToken(Roles = "student,teacher")]
  621. [HttpPost("get-school-info")]
  622. public async Task<IActionResult> GetSchoolInfo(JsonElement request)
  623. {
  624. try
  625. {
  626. var (id, _, _, school) = HttpContext.GetAuthTokenInfo();
  627. var client = _azureCosmos.GetCosmosClient();
  628. /// tmdid, schoolid
  629. var userType = "schoolid";
  630. if (request.TryGetProperty("userType", out JsonElement usertype))
  631. {
  632. if (!usertype.ValueKind.Equals(JsonValueKind.Undefined) && !usertype.ValueKind.Equals(JsonValueKind.Null) && usertype.ValueKind.Equals(JsonValueKind.String))
  633. {
  634. userType = usertype.GetString();
  635. }
  636. }
  637. if (string.IsNullOrEmpty(school))
  638. {
  639. if (userType.Equals("tmdid"))
  640. {
  641. Teacher teacher = await client.GetContainer(Constant.TEAMModelOS, "School").ReadItemAsync<Teacher>(id, new PartitionKey("Base"));
  642. if (teacher.schools.IsNotEmpty())
  643. {
  644. var tech = teacher.schools.Find(x => x.status.Equals("join"));
  645. if (tech == null)
  646. {
  647. school = teacher.schools[0].schoolId;
  648. }
  649. else
  650. {
  651. school = tech.schoolId;
  652. }
  653. }
  654. }
  655. }
  656. if (!string.IsNullOrEmpty(school))
  657. {
  658. object school_base = null;
  659. var response = await client.GetContainer(Constant.TEAMModelOS, "School").ReadItemStreamAsync(school, new PartitionKey("Base"));
  660. if (response.Status == 200)
  661. {
  662. using var json = await JsonDocument.ParseAsync(response.ContentStream);
  663. school_base = json.RootElement.ToObject<object>();
  664. }
  665. //取得班级
  666. List<object> school_classes = new List<object>();
  667. await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryStreamIterator(queryText: $"SELECT c.id,c.x,c.y,c.name,c.year,c.teacher,c.periodId,c.gradeId,c.room,c.sn,c.no,c.style,c.status,c.openType,c.scope, ARRAY_LENGTH(c.students) AS studCount FROM c", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Class-{school}") }))
  668. {
  669. var jsonc = await JsonDocument.ParseAsync(item.ContentStream);
  670. foreach (var classeinfo in jsonc.RootElement.GetProperty("Documents").EnumerateArray())
  671. {
  672. school_classes.Add(classeinfo.ToObject<object>());
  673. }
  674. }
  675. //取得教室
  676. List<Room> school_rooms = new List<Room>();
  677. await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryIterator<Room>(queryText: $"select value(c) from c ",
  678. requestOptions: new QueryRequestOptions() { PartitionKey = new Azure.Cosmos.PartitionKey($"Room-{school}") }))
  679. {
  680. school_rooms.Add(item);
  681. }
  682. return Ok(new { school_base, school_classes, school_rooms, status = 200 });
  683. }
  684. else
  685. {
  686. return Ok(new { status = 404 }); ;
  687. }
  688. }
  689. catch (CosmosException ex)
  690. {
  691. return Ok(new { status = ex.Status }); ;
  692. }
  693. catch (Exception ex)
  694. {
  695. await _dingDing.SendBotMsg($"IES5,{_option.Location},Student/get-school-info()\n{ex.Message}{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
  696. return BadRequest();
  697. }
  698. }
  699. }
  700. }