InitController.cs 40 KB

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