StudentController.cs 48 KB

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