InitController.cs 46 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780
  1. using Azure.Cosmos;
  2. using Azure.Storage.Blobs.Models;
  3. using Azure.Storage.Sas;
  4. using Microsoft.AspNetCore.Http;
  5. using Microsoft.AspNetCore.Mvc;
  6. using Microsoft.Extensions.Options;
  7. using System;
  8. using System.Collections.Generic;
  9. using System.Dynamic;
  10. using System.IdentityModel.Tokens.Jwt;
  11. using System.IO;
  12. using System.Linq;
  13. using System.Text.Json;
  14. using System.Threading.Tasks;
  15. using TEAMModelOS.Models;
  16. using TEAMModelOS.SDK.Models;
  17. using TEAMModelOS.SDK.DI;
  18. using TEAMModelOS.SDK.Extension;
  19. using TEAMModelOS.Filter;
  20. using TEAMModelOS.SDK.Models.Cosmos;
  21. using HTEXLib.COMM.Helpers;
  22. using TEAMModelOS.SDK.Models.Service;
  23. using Microsoft.Extensions.Configuration;
  24. using System.Net.Http;
  25. using TEAMModelOS.SDK;
  26. namespace TEAMModelOS.Controllers
  27. {
  28. [ProducesResponseType(StatusCodes.Status200OK)]
  29. [ProducesResponseType(StatusCodes.Status400BadRequest)]
  30. //[Authorize(Roles = "IES5")]
  31. [Route("teacher/init")]
  32. [ApiController]
  33. public class InitController : ControllerBase
  34. {
  35. private readonly AzureCosmosFactory _azureCosmos;
  36. private readonly AzureStorageFactory _azureStorage;
  37. private readonly DingDing _dingDing;
  38. private readonly Option _option;
  39. private readonly IConfiguration _configuration;
  40. private readonly NotificationService _notificationService;
  41. public InitController(AzureCosmosFactory azureCosmos, AzureStorageFactory azureStorage, DingDing dingDing, IOptionsSnapshot<Option> option, IConfiguration configuration, NotificationService notificationService)
  42. {
  43. _azureCosmos = azureCosmos;
  44. _azureStorage = azureStorage;
  45. _dingDing = dingDing;
  46. _option = option?.Value;
  47. _configuration = configuration;
  48. _notificationService = notificationService;
  49. }
  50. /// <summary>
  51. /// 修改教师信息
  52. /// </summary>
  53. /// <param name="request"></param>
  54. /// <returns></returns>
  55. [ProducesDefaultResponseType]
  56. [HttpPost("set-teacher-info")]
  57. [AuthToken(Roles = "admin,teacher,student")]
  58. public async Task<IActionResult> SetTeacherInfo(JsonElement request) {
  59. var client = _azureCosmos.GetCosmosClient();
  60. if (!request.TryGetProperty("defaultSchool", out JsonElement _defaultSchool))
  61. {
  62. return BadRequest();
  63. }
  64. if (!request.TryGetProperty("opt", out JsonElement _opt))
  65. {
  66. return BadRequest();
  67. }
  68. var (userid, _, _, _) = HttpContext.GetAuthTokenInfo();
  69. try {
  70. Teacher teacher = await client.GetContainer(Constant.TEAMModelOS, "Teacher").ReadItemAsync<Teacher>(userid, new PartitionKey("Base"));
  71. switch (true) {
  72. //修改默认学校
  73. case bool when $"{_opt}".Equals("UpdateDefaultSchool", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrEmpty($"{_defaultSchool}"):
  74. if (teacher.schools.Select(x => x.schoolId).Contains($"{_defaultSchool}"))
  75. {
  76. teacher.defaultSchool = $"{_defaultSchool}";
  77. await client.GetContainer(Constant.TEAMModelOS, "Teacher").ReplaceItemAsync<Teacher>(teacher, userid, new PartitionKey("Base"));
  78. return Ok(new { teacher, status = 1 });
  79. }
  80. else {
  81. return BadRequest(new { status=-1,msg="您未加入该学校!"});
  82. }
  83. }
  84. }
  85. catch(Exception ex)
  86. {
  87. return BadRequest("teacher not exist");
  88. }
  89. return Ok();
  90. }
  91. //TODO 此API需處理對應前端返回的相關數據
  92. [ProducesDefaultResponseType]
  93. [HttpPost("get-teacher-info")]
  94. public async Task<IActionResult> GetTeacherInfo(JsonElement request)
  95. {
  96. try
  97. {
  98. if (!request.TryGetProperty("id_token", out JsonElement id_token)) return BadRequest();
  99. var jwt = new JwtSecurityToken(id_token.GetString());
  100. //TODO 此驗證IdToken先簡單檢查,後面需向Core ID新API,驗證Token
  101. if (!jwt.Payload.Iss.Equals("account.teammodel", StringComparison.OrdinalIgnoreCase)) return BadRequest();
  102. var id = jwt.Payload.Sub;
  103. jwt.Payload.TryGetValue("name", out object name);
  104. jwt.Payload.TryGetValue("picture", out object picture);
  105. List<object> schools = new List<object>();
  106. string defaultschool = null;
  107. //TODO 取得Teacher 個人相關數據(課程清單、虛擬教室清單、歷史紀錄清單等),學校數據另外API處理,多校切換時不同
  108. var client = _azureCosmos.GetCosmosClient();
  109. int total = 0;
  110. int tsize = 0;
  111. try {
  112. Teacher teacher = await client.GetContainer(Constant.TEAMModelOS, "Teacher").ReadItemAsync<Teacher>(id, new PartitionKey("Base"));
  113. teacher.name = $"{name}";
  114. teacher.picture = $"{picture}";
  115. ///教师的个人空间
  116. tsize = teacher.size;
  117. ///教师的总空间 包含 个人空间和学校赠送的空间累加
  118. total = teacher.size;
  119. //检查是否有加入学校,如果加入学校,则当个人空间size是0G的时候,则免费获得一个G空间,但无论加入多少个学校,只能获取一次 1G的免费空间。没有加入学校则默认0G空间,除非自己购买空间
  120. if (teacher.schools.IsNotEmpty()) {
  121. foreach (var sc in teacher.schools) {
  122. string statusNow = sc.status!=null?sc.status:"";
  123. if (statusNow .Equals("join") || statusNow .Equals("invite") || statusNow.Equals("request")) {
  124. dynamic schoolExtobj = new ExpandoObject();
  125. var schoolJson = await client.GetContainer(Constant.TEAMModelOS, "School").ReadItemStreamAsync($"{sc.schoolId}", new PartitionKey("Base"));
  126. var school = await JsonDocument.ParseAsync(schoolJson.ContentStream);
  127. schoolExtobj.schoolId = sc.schoolId;
  128. schoolExtobj.name = school.RootElement.GetProperty("name") ;
  129. schoolExtobj.status =sc.status;
  130. schoolExtobj.time = sc.time;
  131. schoolExtobj.picture = school.RootElement.GetProperty("picture");
  132. var sctch = await client.GetContainer(Constant.TEAMModelOS, "School").ReadItemStreamAsync(id, new PartitionKey($"Teacher-{sc.schoolId}"));
  133. if (sctch.Status == 200)
  134. {
  135. var jsonDoc = await JsonDocument.ParseAsync(sctch.ContentStream);
  136. SchoolTeacher schoolTeacher = jsonDoc.RootElement.ToObject<SchoolTeacher>();
  137. schoolTeacher.name = $"{name}";
  138. schoolTeacher.picture = $"{picture}";
  139. await client.GetContainer(Constant.TEAMModelOS, "School").ReplaceItemAsync(schoolTeacher, id, new PartitionKey($"Teacher-{sc.schoolId}"));
  140. if (jsonDoc.RootElement.TryGetProperty("size", out JsonElement _size) && _size.ValueKind.Equals(JsonValueKind.Number))
  141. {
  142. total += _size.GetInt32();
  143. schoolExtobj.size = _size.GetInt32();
  144. }
  145. else { schoolExtobj.size = 0; }
  146. }
  147. else {
  148. schoolExtobj.size=0;
  149. }
  150. if (statusNow.Equals("join")) {
  151. //初始化
  152. await TmdUserService.JoinSchool(client, teacher.id, teacher.picture, teacher.name, sc.schoolId, sc.name);
  153. }
  154. schools.Add(schoolExtobj);
  155. }
  156. }
  157. //如果包含任何申请,邀请,加入学校的记录 且个人空间未分配2G 则默认分配个人空间至2G.
  158. if ( teacher.size<2 && teacher.schools.Count > 0)
  159. {
  160. teacher.size = 2;
  161. }
  162. //如果未包含任何申请,邀请,加入学校的记录 且 个人空间没有分配1G 则默认赠送一个G
  163. if (teacher.schools.Count == 0 && teacher.size<1) {
  164. teacher.size = 1;
  165. }
  166. }
  167. await client.GetContainer(Constant.TEAMModelOS, "Teacher").ReplaceItemAsync<Teacher>(teacher, id, new PartitionKey("Base"));
  168. //預設學校ID
  169. defaultschool = teacher.defaultSchool;
  170. } catch(CosmosException ex)
  171. {
  172. if (ex.Status == 404) {
  173. //如果沒有,則初始化Teacher基本資料到Cosmos
  174. Teacher teacher = new Teacher
  175. {
  176. id = id,
  177. pk = "Base",
  178. code = "Base",
  179. name = name?.ToString(),
  180. picture = picture?.ToString(),
  181. //创建账号并第一次登录IES5则默认赠送1G
  182. size = 1,
  183. defaultSchool = null,
  184. schools = new List<Teacher.School>(),
  185. };
  186. var container = _azureStorage.GetBlobContainerClient(id);
  187. await container.CreateIfNotExistsAsync(PublicAccessType.None); //嘗試創建Teacher私有容器,如存在則不做任何事,保障容器一定存在
  188. teacher = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Teacher").CreateItemAsync<Teacher>(teacher, new PartitionKey("Base"));
  189. total = teacher.size;
  190. tsize = teacher.size;
  191. }
  192. }
  193. //私人課程
  194. List<object> courses = new List<object>();
  195. await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "Teacher").GetItemQueryStreamIterator(queryText: $"select c.id, c.name, c.classes, c.notice ,c.scope from c ", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Course-{id}") }))
  196. {
  197. using var json = await JsonDocument.ParseAsync(item.ContentStream);
  198. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  199. {
  200. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  201. {
  202. courses.Add(obj.ToObject<object>());
  203. }
  204. }
  205. }
  206. //老師個人課綱 TODO 暂时不取
  207. List<SyllabusRole> syllabus = new List<SyllabusRole>();
  208. //await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "Teacher").GetItemQueryStreamIterator(queryText: $"SELECT c.id, c.name, c.scope, c.resourceCount, c.itemCount, c.children from c ", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Syllabus-{id}") }))
  209. //{
  210. // var jsons = await JsonDocument.ParseAsync(item.ContentStream);
  211. // if (jsons.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  212. // {
  213. // foreach (var obj in jsons.RootElement.GetProperty("Documents").EnumerateArray())
  214. // {
  215. // SyllabusRole syllabusRole = new SyllabusRole();
  216. // syllabusRole.id = obj.GetProperty("id").ToString();
  217. // syllabusRole.name = obj.GetProperty("name").ToString();
  218. // syllabusRole.resourceCount = obj.GetProperty("resourceCount").GetUInt16();
  219. // syllabusRole.itemCount = obj.GetProperty("itemCount").GetUInt16();
  220. // List<Syllabus> syllabusList = obj.GetProperty("children").ToObject<List<Syllabus>>();
  221. // syllabusList.Insert(0, new Syllabus { id = syllabusRole.id, name = syllabusRole.name, pid = "", order = 0 });
  222. // syllabusList = syllabusList.OrderBy(x => x.order).ToList();
  223. // syllabusRole.structure = CreateSyllabusTree(syllabusList);
  224. // syllabus.Add(syllabusRole);
  225. // }
  226. // //[DEBUG] string jsonString = System.Text.Json.JsonSerializer.Serialize(syllabusRoles);
  227. // }
  228. //}
  229. //換取AuthToken,提供給前端
  230. var auth_token = JwtAuthExtension.CreateAuthToken(_option.HostName, id, name?.ToString(), picture?.ToString(), _option.JwtSecretKey, roles: new[] { "teacher" });
  231. //取得Teacher Blob 容器位置及SAS
  232. var (blob_uri, blob_sas) = _azureStorage.GetBlobContainerSAS(id, BlobContainerSasPermissions.Write | BlobContainerSasPermissions.Read | BlobContainerSasPermissions.List | BlobContainerSasPermissions.Delete);
  233. return Ok(new { auth_token, blob_uri, blob_sas, schools, defaultschool, courses, total, tsize });
  234. }
  235. catch (CosmosException ex) {
  236. await _dingDing.SendBotMsg($"IES5,{_option.Location},Teacher/GetTeacherInfo()\n{ex.Message}{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
  237. return BadRequest();
  238. }
  239. catch (Exception ex)
  240. {
  241. await _dingDing.SendBotMsg($"IES5,{_option.Location},Teacher/GetTeacherInfo()\n{ex.Message}{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
  242. return BadRequest();
  243. }
  244. }
  245. //TODO 此API需處理對應前端返回的相關數據
  246. [ProducesDefaultResponseType]
  247. [HttpPost("get-school-info")]
  248. public async Task<IActionResult> GetSchoolInfo(JsonElement requert)
  249. {
  250. try
  251. {
  252. if (!requert.TryGetProperty("id_token", out JsonElement id_token)) return BadRequest();
  253. if (!requert.TryGetProperty("school_code", out JsonElement school_code)) return BadRequest();
  254. var jwt = new JwtSecurityToken(id_token.GetString());
  255. if (!jwt.Payload.Iss.Equals("account.teammodel", StringComparison.Ordinal)) return BadRequest();
  256. var id = jwt.Payload.Sub;
  257. var client = _azureCosmos.GetCosmosClient();
  258. //權限token
  259. jwt.Payload.TryGetValue("name", out object name);
  260. jwt.Payload.TryGetValue("picture", out object picture);
  261. List<string> roles = new List<string>();
  262. List<string> permissions = new List<string>();
  263. int size = 0;
  264. var response = await client.GetContainer(Constant.TEAMModelOS, "School").ReadItemStreamAsync(id, new PartitionKey($"Teacher-{school_code}"));
  265. if (response.Status == 200)
  266. {
  267. using var json = await JsonDocument.ParseAsync(response.ContentStream);
  268. if (json.RootElement.TryGetProperty("size", out JsonElement _size) && _size.ValueKind == JsonValueKind.Number)
  269. {
  270. size = _size.GetInt32();
  271. }
  272. if (json.RootElement.TryGetProperty("roles", out JsonElement _roles) && _roles.ValueKind != JsonValueKind.Null)
  273. {
  274. foreach (var obj in _roles.EnumerateArray())
  275. {
  276. roles.Add(obj.GetString());
  277. }
  278. }
  279. if (json.RootElement.TryGetProperty("permissions", out JsonElement _permissions) && _permissions.ValueKind != JsonValueKind.Null)
  280. {
  281. foreach (var obj in _permissions.EnumerateArray())
  282. {
  283. permissions.Add(obj.GetString());
  284. }
  285. }
  286. }
  287. else //無此學校資料
  288. {
  289. return Ok(new { status=404});
  290. }
  291. if (roles.Count == 0)
  292. {
  293. roles.Add("teacher");
  294. roles.Add("student");
  295. }
  296. //TODO JJ,调整为取得学校基础设置数据,取代下方學校學制、年級
  297. School school_base = await client.GetContainer(Constant.TEAMModelOS, "School").ReadItemAsync<School>($"{school_code}", new PartitionKey("Base"));
  298. //TODO JJ,更新Token时,在取得学校资讯时,没有传入schoolId
  299. var auth_token = JwtAuthExtension.CreateAuthToken(_option.HostName, id, name?.ToString(), picture?.ToString(), _option.JwtSecretKey, schoolID: school_code.ToString(),standard:school_base.standard, roles: roles.ToArray(), permissions: permissions.ToArray());
  300. //取得班级
  301. List<object> school_classes = new List<object>();
  302. 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.school, ARRAY_LENGTH(c.students) AS studCount FROM c", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Class-{school_code}") }))
  303. {
  304. var jsonc = await JsonDocument.ParseAsync(item.ContentStream);
  305. foreach (var classeinfo in jsonc.RootElement.GetProperty("Documents").EnumerateArray())
  306. {
  307. school_classes.Add(classeinfo.ToObject<object>());
  308. }
  309. }
  310. //取得教室
  311. List<Room> school_rooms = new List<Room>();
  312. await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryIterator<Room>(queryText: $"select value(c) from c ",
  313. requestOptions: new QueryRequestOptions() { PartitionKey = new Azure.Cosmos.PartitionKey($"Room-{school_code}") }))
  314. {
  315. school_rooms.Add(item);
  316. }
  317. //List<object> periods = new List<object>();
  318. //List<object> grades = new List<object>();
  319. //var responsesch = await client.GetContainer(Constant.TEAMModelOS, "School").ReadItemStreamAsync(school_code.ToString(), new PartitionKey($"Base"));
  320. //if (responsesch.Status == 200)
  321. //{
  322. // var jsons = await JsonDocument.ParseAsync(responsesch.ContentStream);
  323. // if (jsons.RootElement.TryGetProperty("period", out JsonElement periodJobj))
  324. // {
  325. // foreach (var periodinfo in periodJobj.EnumerateArray())
  326. // {
  327. // dynamic periodExtobj = new ExpandoObject();
  328. // periodExtobj.id = periodinfo.GetProperty("id");
  329. // periodExtobj.name = periodinfo.GetProperty("name");
  330. // periods.Add(periodExtobj);
  331. // if (periodinfo.TryGetProperty("grades", out JsonElement gradesJobj))
  332. // {
  333. // foreach (var gradeinfo in gradesJobj.EnumerateArray())
  334. // {
  335. // dynamic gradeExtobj = new ExpandoObject();
  336. // gradeExtobj.id = gradeinfo.GetProperty("id");
  337. // gradeExtobj.name = gradeinfo.GetProperty("name");
  338. // gradeExtobj.periodId = periodinfo.GetProperty("id");
  339. // grades.Add(gradeExtobj);
  340. // }
  341. // }
  342. // }
  343. // }
  344. //}
  345. //該老師排定的學校課程
  346. List<object> school_courses = new List<object>();
  347. var query = $"SELECT c.id, c.name, c.teacher, cc.course, c.scope FROM c JOIN cc IN c.courses JOIN cct IN cc.teachers WHERE cct.id = '{id}'";
  348. await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryStreamIterator(queryText: query, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"CourseManagement-{school_code}") }))
  349. {
  350. var jsoncm = await JsonDocument.ParseAsync(item.ContentStream);
  351. if (jsoncm.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  352. {
  353. foreach (var obj in jsoncm.RootElement.GetProperty("Documents").EnumerateArray())
  354. {
  355. //班級
  356. string classIdNow = obj.GetProperty("id").ToString();
  357. dynamic classExtobj = new ExpandoObject();
  358. classExtobj.code = $"Class-{school_code}";
  359. classExtobj.id = classIdNow;
  360. classExtobj.name = obj.GetProperty("name").ToString();
  361. classExtobj.teacher = obj.GetProperty("teacher").ToObject<object>();
  362. classExtobj.scope = obj.GetProperty("scope").ToString();
  363. //課程
  364. obj.TryGetProperty("course", out JsonElement courseNow);
  365. string courseIdNow = courseNow.GetProperty("id").ToString();
  366. var courseExist = school_courses.Where((dynamic x) => x.id == courseIdNow).FirstOrDefault();
  367. if (courseExist == null)
  368. {
  369. dynamic courseExtobj = new ExpandoObject();
  370. courseExtobj.id = courseIdNow;
  371. courseExtobj.name = courseNow.GetProperty("name").ToString();
  372. courseExtobj.scope = courseNow.GetProperty("scope").ToString();
  373. courseExtobj.classes = new List<object>();
  374. courseExtobj.classes.Add(classExtobj);
  375. school_courses.Add(courseExtobj);
  376. }
  377. else
  378. {
  379. courseExist.classes.Add(classExtobj);
  380. }
  381. }
  382. }
  383. }
  384. //校本課綱 [式樣未定 先不取]
  385. List<SyllabusRole> school_syllabus = new List<SyllabusRole>();
  386. //await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryStreamIterator(queryText: $"SELECT c.id, c.name, c.period, c.grade, c.semester, c.subject, c.scope, c.resourceCount, c.itemCount, c.children from c ", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Syllabus-{school_code}") }))
  387. //{
  388. // var jsons = await JsonDocument.ParseAsync(item.ContentStream);
  389. // if (jsons.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  390. // {
  391. // foreach (var obj in jsons.RootElement.GetProperty("Documents").EnumerateArray())
  392. // {
  393. // SyllabusRole syllabusRole = new SyllabusRole();
  394. // syllabusRole.id = obj.GetProperty("id").ToString();
  395. // syllabusRole.name = obj.GetProperty("name").ToString();
  396. // syllabusRole.period = obj.GetProperty("period");
  397. // syllabusRole.grade = obj.GetProperty("grade");
  398. // syllabusRole.semester = obj.GetProperty("semester");
  399. // syllabusRole.subject = obj.GetProperty("subject");
  400. // syllabusRole.resourceCount = obj.GetProperty("resourceCount").GetUInt16();
  401. // syllabusRole.itemCount = obj.GetProperty("itemCount").GetUInt16();
  402. // List<Syllabus> syllabusList = obj.GetProperty("children").ToObject<List<Syllabus>>();
  403. // syllabusList.Insert(0, new Syllabus { id = syllabusRole.id, name = syllabusRole.name, pid = "", order = 0 });
  404. // syllabusList = syllabusList.OrderBy(x => x.order).ToList();
  405. // syllabusRole.structure = CreateSyllabusTree(syllabusList);
  406. // syllabus.Add(syllabusRole);
  407. // }
  408. // //[DEBUG] string jsonString = System.Text.Json.JsonSerializer.Serialize(syllabusRoles);
  409. // }
  410. //}
  411. //取得School Blob 容器位置及SAS
  412. string school_code_blob = school_code.GetString().ToLower();
  413. var container = _azureStorage.GetBlobContainerClient(school_code_blob);
  414. await container.CreateIfNotExistsAsync(PublicAccessType.None); //嘗試創建School容器,如存在則不做任何事,保障容器一定存在
  415. var (blob_uri, blob_sas) = (roles.Contains("admin") || permissions.Contains("schoolAc-upd")) ? _azureStorage.GetBlobContainerSAS(school_code_blob, BlobContainerSasPermissions.Write | BlobContainerSasPermissions.Read | BlobContainerSasPermissions.List | BlobContainerSasPermissions.Delete) : _azureStorage.GetBlobContainerSAS(school_code_blob, BlobContainerSasPermissions.Read | BlobContainerSasPermissions.List | BlobContainerSasPermissions.Write);
  416. ///https://teammodelstorage.blob.core.chinacloudapi.cn/teammodelos
  417. var (osblob_uri, osblob_sas) = roles.Contains("area") ? _azureStorage.GetBlobContainerSAS("teammodelos", BlobContainerSasPermissions.Write | BlobContainerSasPermissions.Read | BlobContainerSasPermissions.List | BlobContainerSasPermissions.Delete) : _azureStorage.GetBlobContainerSAS("teammodelos", BlobContainerSasPermissions.Read | BlobContainerSasPermissions.List);
  418. return Ok(new { auth_token, blob_uri, blob_sas, school_base, school_courses, school_syllabus, school_classes, school_rooms, size, osblob_uri, osblob_sas });
  419. }
  420. catch (Exception ex) {
  421. await _dingDing.SendBotMsg($"IES5,{_option.Location},Teacher/init/get-school-info()\n{ex.Message}{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
  422. return BadRequest();
  423. }
  424. }
  425. /// <summary>
  426. /// 取得學校所有列表
  427. /// </summary>
  428. /// <param name="request"></param>
  429. /// <returns></returns>
  430. [ProducesDefaultResponseType]
  431. //[AuthToken(Roles = "teacher")]
  432. [HttpPost("get-school-list")]
  433. public async Task<IActionResult> GetSchoolList(JsonElement request)
  434. {
  435. try
  436. {
  437. //輸入值
  438. string sqlSchoolId = (request.TryGetProperty("schoolId", out JsonElement reqSchoolId)) ? reqSchoolId.GetString() : string.Empty;
  439. bool sqlHasManager = (request.TryGetProperty("manager", out JsonElement reqHasManager)) ? reqHasManager.GetBoolean() : false;
  440. bool sqlHasMBaseModule = (request.TryGetProperty("base", out JsonElement reqHasBaseModule)) ? reqHasBaseModule.GetBoolean() : false;
  441. var client = _azureCosmos.GetCosmosClient();
  442. //有管理者的學校
  443. Dictionary<string, List<Dictionary<string, string>>> managerSchDic = new Dictionary<string, List<Dictionary<string, string>>>();
  444. string managerWhereOption = (!string.IsNullOrWhiteSpace(sqlSchoolId)) ? $" AND c.code = 'Teacher-{sqlSchoolId}'" : string.Empty;
  445. string managerSql = $"SELECT DISTINCT REPLACE(c.code, 'Teacher-', '') AS schoolId, c.id, c.name FROM c WHERE ARRAY_CONTAINS(c.roles, 'admin', true) AND c.pk = 'Teacher' AND c.status = 'join'{managerWhereOption}";
  446. await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryStreamIterator(queryText: managerSql, requestOptions: new QueryRequestOptions() {} ))
  447. {
  448. using var json = await JsonDocument.ParseAsync(item.ContentStream);
  449. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  450. {
  451. string id = obj.GetProperty("id").GetString(); //管理者ID
  452. string name = obj.GetProperty("name").GetString(); //管理者姓名
  453. string schoolId = obj.GetProperty("schoolId").GetString(); //學校ID
  454. Dictionary<string, string> managerDic = new Dictionary<string, string>();
  455. managerDic.Add("id", id);
  456. managerDic.Add("name", name);
  457. if (managerSchDic.ContainsKey(schoolId))
  458. {
  459. managerSchDic[schoolId].Add(managerDic);
  460. }
  461. else
  462. {
  463. List<Dictionary<string, string>> managerList = new List<Dictionary<string, string>>();
  464. managerList.Add(managerDic);
  465. managerSchDic.Add(schoolId, managerList);
  466. }
  467. }
  468. }
  469. //有管理模組的學校
  470. List<string> baseModuleSchList = new List<string>();
  471. string baseModuleWhereOption = (!string.IsNullOrWhiteSpace(sqlSchoolId)) ? $" AND c.id = '{sqlSchoolId}'" : string.Empty;
  472. await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryStreamIterator(queryText: $"SELECT c.id FROM c JOIN serviceProduct IN c.service.product WHERE serviceProduct.prodCode = 'IPDYZYLC'{baseModuleWhereOption}", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("Product") }))
  473. {
  474. using var json = await JsonDocument.ParseAsync(item.ContentStream);
  475. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  476. {
  477. string schoolId = obj.GetProperty("id").GetString(); //學校ID
  478. baseModuleSchList.Add(schoolId);
  479. }
  480. }
  481. //學校資料
  482. List<object> schools = new List<object>();
  483. string schoolWhereOption = (!string.IsNullOrWhiteSpace(sqlSchoolId)) ? $" WHERE c.id = '{sqlSchoolId}'" : string.Empty;
  484. await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryStreamIterator(queryText: $"SELECT c.id, c.name, c.region, c.province, c.city, c.picture FROM c {schoolWhereOption}", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("Base") }))
  485. {
  486. var jsons = await JsonDocument.ParseAsync(item.ContentStream);
  487. if (jsons.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  488. {
  489. foreach (var obj in jsons.RootElement.GetProperty("Documents").EnumerateArray())
  490. {
  491. dynamic schoolExtobj = new ExpandoObject();
  492. schoolExtobj.id = obj.GetProperty("id").GetString(); //學校ID
  493. schoolExtobj.name = obj.GetProperty("name");
  494. schoolExtobj.region = obj.GetProperty("region");
  495. schoolExtobj.province = obj.GetProperty("province");
  496. schoolExtobj.city = obj.GetProperty("city");
  497. schoolExtobj.picture = obj.GetProperty("picture");
  498. if (managerSchDic.ContainsKey(schoolExtobj.id))
  499. {
  500. schoolExtobj.hasManager = true;
  501. schoolExtobj.managers = managerSchDic[schoolExtobj.id];
  502. }
  503. else
  504. {
  505. schoolExtobj.hasManager = false;
  506. schoolExtobj.managers = new List<object>();
  507. }
  508. schoolExtobj.hasBaseModule = (baseModuleSchList.Contains(schoolExtobj.id)) ? true : false;
  509. //學校輸出結果加入篩選
  510. if( (sqlHasManager && sqlHasMBaseModule) && (schoolExtobj.hasManager && schoolExtobj.hasBaseModule) )
  511. {
  512. schools.Add(schoolExtobj);
  513. }
  514. else if(sqlHasManager && schoolExtobj.hasManager)
  515. {
  516. schools.Add(schoolExtobj);
  517. }
  518. else if(sqlHasMBaseModule && schoolExtobj.hasBaseModule)
  519. {
  520. schools.Add(schoolExtobj);
  521. }
  522. else if(!sqlHasManager && !sqlHasMBaseModule)
  523. {
  524. schools.Add(schoolExtobj);
  525. }
  526. }
  527. }
  528. }
  529. return Ok(new { schools });
  530. }
  531. catch (Exception ex)
  532. {
  533. return BadRequest();
  534. }
  535. }
  536. /// <summary>
  537. /// 申請或同意邀請加入學校
  538. /// </summary>
  539. ///
  540. /// <param name="requert"></param>
  541. /// <returns></returns>
  542. [ProducesDefaultResponseType]
  543. [AuthToken(Roles = "teacher")]
  544. [HttpPost("join-school")]
  545. public async Task<IActionResult> JoinSchool(JsonElement requert)
  546. {
  547. try
  548. {
  549. if (!requert.TryGetProperty("grant_type", out JsonElement grant_type)) return BadRequest(); //"invite":學校邀請 "request":老師申請 "join":"成為學校老師",leave 离开,cancel 取消。
  550. if (!requert.TryGetProperty("school_code", out JsonElement school_code)) return BadRequest();
  551. if (!requert.TryGetProperty("school_name", out JsonElement school_name)) return BadRequest();
  552. ///当邀请某个老师加入学校则需要知道是谁邀请的
  553. //if (!requert.TryGetProperty("school_admin", out JsonElement school_admin)) return BadRequest();
  554. string authtoken = HttpContext.GetXAuth("AuthToken");
  555. if (string.IsNullOrEmpty(authtoken)) return BadRequest();
  556. var jwt = new JwtSecurityToken(authtoken);
  557. var id = jwt.Payload.Sub;
  558. var schoolcode = jwt.Payload.Azp;
  559. var Claims = jwt.Payload.Claims;
  560. jwt.Payload.TryGetValue("name", out object name);
  561. jwt.Payload.TryGetValue("picture", out object picture);
  562. var client = _azureCosmos.GetCosmosClient();
  563. //在老師表找出老師,處理該學校狀態 (老師基本資料應該要存在)
  564. Teacher teacher = await client.GetContainer(Constant.TEAMModelOS, "Teacher").ReadItemAsync<Teacher>(id, new PartitionKey("Base"));
  565. if (teacher.schools == null)
  566. teacher.schools = new List<Teacher.School>();
  567. var school = teacher.schools?.FirstOrDefault(x => x.schoolId.Equals(school_code.GetString(), StringComparison.OrdinalIgnoreCase));
  568. if (school != null)
  569. school.status = grant_type.GetString();
  570. else
  571. {
  572. long now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
  573. teacher.schools.Add(new Teacher.School() { schoolId = school_code.GetString(), name = school_name.GetString(), status = grant_type.GetString() ,time= now });
  574. }
  575. if (grant_type.GetString() .Equals("leave") || grant_type.GetString() .Equals("cancel"))
  576. {
  577. if (teacher.schools.IsNotEmpty())
  578. {
  579. //获取之前已经加入的学校或者申请的学校
  580. var inSchools = teacher.schools.Where(x => x.schoolId == school_code.GetString()).ToList();
  581. if (inSchools.IsNotEmpty())
  582. {
  583. inSchools.ForEach(x => teacher.schools.Remove(x));
  584. }
  585. }
  586. if (teacher.schools.Count > 0 && teacher.size <= 1)
  587. {
  588. teacher.size = 2;
  589. }
  590. await client.GetContainer(Constant.TEAMModelOS, "Teacher").ReplaceItemAsync<Teacher>(teacher, id, new PartitionKey("Base"));
  591. var sresponse = await client.GetContainer(Constant.TEAMModelOS, "School").DeleteItemStreamAsync(id, new PartitionKey($"Teacher-{school_code}"));
  592. //await TmdUserService.LeaveSchool(client, teacher.id, school.schoolId);
  593. return Ok(new { stauts = 1 });
  594. }
  595. else {
  596. var dft = new List<string>() { "content-read", "exercise-read", "knowledge-read", "syllabus-read" };
  597. if (teacher.schools.Count > 0 && teacher.size <= 1)
  598. {
  599. teacher.size = 2;
  600. }
  601. await client.GetContainer(Constant.TEAMModelOS, "Teacher").ReplaceItemAsync<Teacher>(teacher, id, new PartitionKey("Base"));
  602. //在學校表處理該學校教師帳號的狀態
  603. var sresponse = await client.GetContainer(Constant.TEAMModelOS, "School").ReadItemStreamAsync(id, new PartitionKey($"Teacher-{school_code}"));
  604. if (sresponse.Status == 200)
  605. {
  606. using var json = await JsonDocument.ParseAsync(sresponse.ContentStream);
  607. SchoolTeacher steacher = json.ToObject<SchoolTeacher>();
  608. steacher.status = grant_type.GetString();
  609. if (grant_type.ToString().Equals("join")) {
  610. if (steacher.permissions.IsNotEmpty())
  611. {
  612. foreach (var d in dft) {
  613. if (!steacher.permissions.Contains(d))
  614. {
  615. steacher.permissions.Add(d);
  616. }
  617. }
  618. }
  619. else {
  620. steacher.permissions = dft;
  621. }
  622. }
  623. var response = await client.GetContainer(Constant.TEAMModelOS, "School").ReplaceItemAsync(steacher, id, new PartitionKey($"Teacher-{school_code}"));
  624. }
  625. else
  626. {
  627. SchoolTeacher st = new SchoolTeacher()
  628. {
  629. pk = "Teacher",
  630. code = $"Teacher-{school_code}",
  631. createTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
  632. id = id,
  633. name = name.ToString(),
  634. picture = picture?.ToString(),
  635. permissions = grant_type.ToString().Equals("join")? dft : null,
  636. roles = new List<string>() { "teacher" },
  637. size = 0,
  638. status = grant_type.GetString()
  639. };
  640. var response = await client.GetContainer(Constant.TEAMModelOS, "School").CreateItemAsync(st, new PartitionKey($"Teacher-{school_code}"));
  641. }
  642. if (grant_type.ToString().Equals("join")) {
  643. await TmdUserService.JoinSchool(client, teacher.id, teacher.picture, teacher.name, school.schoolId, school.name);
  644. }
  645. Notification notification = null;
  646. List<SchoolTeacher> teachers = new List<SchoolTeacher>();
  647. var queryslt = $"SELECT value(c) FROM c join A1 in c.roles where A1 in ('admin')";
  648. await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryIterator<SchoolTeacher>(queryText: queryslt, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Teacher-{school_code}") }))
  649. {
  650. teachers.Add(item);
  651. }
  652. if (teachers.IsNotEmpty()) {
  653. string code = grant_type.GetString();
  654. if (grant_type.GetString() .Equals("join")) {
  655. code = "invite-join";
  656. }
  657. notification = new Notification
  658. {
  659. hubName = "hita",
  660. type = "msg",
  661. from = $"ies5:private",
  662. to = teachers.Select(x => x.id).ToList(),
  663. label = $"{code}_school",
  664. body = new {biz= code, tmdid= id ,tmdname= name.ToString(), schoolcode=$"{school_code}", schoolname=$"{school_name}", status =1,time= DateTimeOffset.UtcNow .ToUnixTimeMilliseconds()}.ToJsonString(),
  665. expires= DateTimeOffset.UtcNow.AddDays(7).ToUnixTimeSeconds()
  666. };
  667. }
  668. if (notification != null) {
  669. var url= _configuration.GetValue<string>("HaBookAuth:CoreService:sendnotification");
  670. var clientID= _configuration.GetValue<string>("HaBookAuth:CoreService:clientID");
  671. var clientSecret = _configuration.GetValue<string>("HaBookAuth:CoreService:clientSecret");
  672. var location = _option.Location;
  673. var code = await _notificationService .SendNotification(clientID, clientSecret, location, url, notification);
  674. }
  675. return Ok(new { stauts = 1 });
  676. }
  677. }
  678. catch (Exception ex)
  679. {
  680. await _dingDing.SendBotMsg($"TEAMModel,{_option.Location},Init/JoinSchool()\n{ex.Message}{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
  681. return BadRequest();
  682. }
  683. }
  684. //課綱的model先記在下面,待式樣確定後再轉換
  685. private List<SyllabusNode> CreateSyllabusTree(List<Syllabus> syllabuses)
  686. {
  687. List<SyllabusNode> nodes = new List<SyllabusNode>();
  688. foreach (var syllabus in syllabuses)
  689. {
  690. if (syllabus.pid .Equals(""))
  691. nodes.Add(new SyllabusNode { id = syllabus.id, name = syllabus.name });
  692. else
  693. {
  694. CreateNode(nodes, syllabus);
  695. }
  696. }
  697. return nodes;
  698. }
  699. private void CreateNode(List<SyllabusNode> nodes, Syllabus parent)
  700. {
  701. foreach (var node in nodes)
  702. {
  703. if (node.id == parent.pid)
  704. {
  705. node.children.Add(new SyllabusNode { id = parent.id, name = parent.name });
  706. }
  707. else
  708. {
  709. CreateNode(node.children, parent);
  710. }
  711. }
  712. }
  713. public class SyllabusRole
  714. {
  715. public string id { get; set; }
  716. public string name { get; set; }
  717. public object period { get; set; }
  718. public object semester { get; set; }
  719. public object grade { get; set; }
  720. public object subject { get; set; }
  721. public int resourceCount { get; set; }
  722. public int itemCount { get; set; }
  723. public object structure { get; set; }
  724. }
  725. public class Syllabus
  726. {
  727. public string id { get; set; }
  728. public string name { get; set; }
  729. public string pid { get; set; }
  730. public int order { get; set; }
  731. }
  732. public class SyllabusNode
  733. {
  734. public string id { get; set; }
  735. public string name { get; set; }
  736. public List<SyllabusNode> children { get; set; }
  737. public SyllabusNode()
  738. {
  739. children = new List<SyllabusNode>();
  740. }
  741. }
  742. }
  743. }