HiTeachController.cs 44 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863
  1. using Azure.Cosmos;
  2. using Azure.Storage.Blobs.Models;
  3. using Azure.Storage.Sas;
  4. using Microsoft.AspNetCore.Authorization;
  5. using Microsoft.AspNetCore.Http;
  6. using Microsoft.AspNetCore.Mvc;
  7. using Microsoft.Extensions.Options;
  8. using System;
  9. using System.Collections.Generic;
  10. using System.IdentityModel.Tokens.Jwt;
  11. using System.IO;
  12. using System.Linq;
  13. using System.Text;
  14. using System.Text.Json;
  15. using System.Threading.Tasks;
  16. using TEAMModelOS.Models;
  17. using TEAMModelOS.Filter;
  18. using TEAMModelOS.SDK.DI;
  19. using TEAMModelOS.SDK.Extension;
  20. using Microsoft.Azure.Cosmos.Table;
  21. using TEAMModelOS.SDK.Models;
  22. using System.Dynamic;
  23. namespace TEAMModelOS.Controllers.Client
  24. {
  25. [ProducesResponseType(StatusCodes.Status200OK)]
  26. [ProducesResponseType(StatusCodes.Status400BadRequest)]
  27. //[Authorize(Roles = "HiTeach")]
  28. [Route("hiteach")]
  29. [ApiController]
  30. public class HiTeachController : ControllerBase
  31. {
  32. private readonly AzureStorageFactory _azureStorage;
  33. private readonly AzureRedisFactory _azureRedis;
  34. private readonly AzureCosmosFactory _azureCosmos;
  35. private readonly DingDing _dingDing;
  36. private readonly Option _option;
  37. private readonly SnowflakeId _snowflakeId;
  38. public HiTeachController(
  39. AzureStorageFactory azureStorage,
  40. AzureRedisFactory azureRedis,
  41. AzureCosmosFactory azureCosmos,
  42. DingDing dingDing,
  43. SnowflakeId snowflakeId,
  44. IOptionsSnapshot<Option> option)
  45. {
  46. _azureStorage = azureStorage;
  47. _azureRedis = azureRedis;
  48. _azureCosmos = azureCosmos;
  49. _dingDing = dingDing;
  50. _snowflakeId = snowflakeId;
  51. _option = option?.Value;
  52. }
  53. [ProducesDefaultResponseType]
  54. [HttpPost("get-teacher-info")]
  55. public async Task<IActionResult> GetTeacherInfo()
  56. {
  57. //Debug
  58. //string json = System.Text.Json.JsonSerializer.Serialize(id_token);
  59. try
  60. {
  61. string id_token = HttpContext.GetXAuth("IdToken");
  62. if(string.IsNullOrEmpty(id_token)) return BadRequest();
  63. var jwt = new JwtSecurityToken(id_token);
  64. if (!jwt.Payload.Iss.Equals("account.teammodel", StringComparison.OrdinalIgnoreCase)) return BadRequest();
  65. var id = jwt.Payload.Sub;
  66. jwt.Payload.TryGetValue("name", out object name);
  67. jwt.Payload.TryGetValue("picture", out object picture);
  68. List<object> schools = new List<object>();
  69. string defaultschool = null;
  70. //TODK 取得Teacher 個人相關數據(課程清單、虛擬教室清單、歷史紀錄清單等),學校數據另外API處理,多校切換時不同
  71. var client = _azureCosmos.GetCosmosClient();
  72. var response = await client.GetContainer("TEAMModelOS", "Teacher").ReadItemStreamAsync(id, new PartitionKey("Base"));
  73. if (response.Status == 200)
  74. {
  75. var jsonsc = await JsonDocument.ParseAsync(response.ContentStream);
  76. if (jsonsc.RootElement.TryGetProperty("schools", out JsonElement value))
  77. {
  78. foreach (var obj in value.EnumerateArray())
  79. {
  80. string statusNow = obj.GetProperty("status").ToString();
  81. if(statusNow == "join") //成為老師的才放入
  82. {
  83. dynamic schoolExtobj = new ExpandoObject();
  84. schoolExtobj.schoolId = obj.GetProperty("schoolId");
  85. schoolExtobj.name = obj.GetProperty("name");
  86. schoolExtobj.status = obj.GetProperty("status");
  87. schools.Add(schoolExtobj);
  88. }
  89. }
  90. }
  91. //預設學校ID
  92. if (jsonsc.RootElement.TryGetProperty("defaultSchool", out JsonElement valueD) && !string.IsNullOrEmpty(valueD.ToString()))
  93. {
  94. defaultschool = valueD.ToString();
  95. }
  96. }
  97. else
  98. {
  99. //如果沒有,則初始化Teacher基本資料到Cosmos
  100. using var stream = new MemoryStream();
  101. using var writer = new Utf8JsonWriter(stream); //new JsonWriterOptions() { Indented = true }
  102. writer.WriteStartObject();
  103. writer.WriteString("code", "Base");
  104. writer.WriteString("id", id);
  105. writer.WriteString("name", name?.ToString());
  106. writer.WriteString("picture", picture?.ToString());
  107. writer.WriteStartArray("schools");
  108. writer.WriteEndArray();
  109. writer.WriteEndObject();
  110. writer.Flush();
  111. //Debug
  112. //string teacher = Encoding.UTF8.GetString(stream.ToArray());
  113. response = await _azureCosmos.GetCosmosClient().GetContainer("TEAMModelOS", "Teacher").CreateItemStreamAsync(stream, new PartitionKey("Base"));
  114. }
  115. //老師個人課程清單
  116. List<object> courses = new List<object>();
  117. await foreach (var item in client.GetContainer("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}") }))
  118. {
  119. using var json = await JsonDocument.ParseAsync(item.ContentStream);
  120. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  121. {
  122. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  123. {
  124. courses.Add(obj.ToObject<object>());
  125. }
  126. }
  127. }
  128. //取得老師個人評測 (HiTeach只取source='1'(課中评量))
  129. List<object> exams = new List<object>();
  130. await foreach (var exam in client.GetContainer("TEAMModelOS", "Common").GetItemQueryStreamIterator(queryText: $"SELECT c.code, c.id, c.name, c.startTime, c.endTime ,c.year, c.source, c.type, c.progress, c.stuCount, c.scope FROM c WHERE c.source = '1'", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Exam-{id}") }))
  131. {
  132. using var json = await JsonDocument.ParseAsync(exam.ContentStream);
  133. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  134. {
  135. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  136. {
  137. exams.Add(obj.ToObject<object>());
  138. }
  139. }
  140. }
  141. //取得Teacher Blob 容器位置及SAS
  142. var container = _azureStorage.GetBlobContainerClient(id);
  143. await container.CreateIfNotExistsAsync(PublicAccessType.None); //嘗試創建Teacher私有容器,如存在則不做任何事,保障容器一定存在
  144. var (blob_uri, blob_sas) = _azureStorage.GetBlobContainerSAS(id, BlobContainerSasPermissions.Write | BlobContainerSasPermissions.Read | BlobContainerSasPermissions.List | BlobContainerSasPermissions.Delete);
  145. return Ok(new { blob_uri, blob_sas, schools, defaultschool, courses, exams });
  146. }
  147. catch (Exception ex)
  148. {
  149. // await _dingDing.SendBotMsg($"CoreAPI2,{_option.Location},hiteach/GetTeacherInfo()\n{ex.Message}", GroupNames.醍摩豆服務運維群組);
  150. return BadRequest();
  151. }
  152. }
  153. [ProducesDefaultResponseType]
  154. [HttpPost("get-school-info")]
  155. public async Task<IActionResult> GetSchoolInfo(JsonElement requert)
  156. {
  157. try
  158. {
  159. string id_token = HttpContext.GetXAuth("IdToken");
  160. if (string.IsNullOrEmpty(id_token)) return BadRequest();
  161. if (!requert.TryGetProperty("school_code", out JsonElement school_code)) return BadRequest();
  162. var jwt = new JwtSecurityToken(id_token);
  163. if (!jwt.Payload.Iss.Equals("account.teammodel", StringComparison.Ordinal)) return BadRequest();
  164. var id = jwt.Payload.Sub;
  165. var client = _azureCosmos.GetCosmosClient();
  166. //取得學校學段、年級、科目、考試類型
  167. List<object> periods = new List<object>();
  168. List<object> grades = new List<object>();
  169. List<object> subjects = new List<object>();
  170. List<object> examTypes = new List<object>();
  171. var responsesch = await client.GetContainer("TEAMModelOS", "School").ReadItemStreamAsync(school_code.ToString(), new PartitionKey($"Base"));
  172. if (responsesch.Status == 200)
  173. {
  174. var jsons = await JsonDocument.ParseAsync(responsesch.ContentStream);
  175. if (jsons.RootElement.TryGetProperty("period", out JsonElement periodJobj))
  176. {
  177. foreach (var periodinfo in periodJobj.EnumerateArray())
  178. {
  179. dynamic periodExtobj = new ExpandoObject();
  180. periodExtobj.id = periodinfo.GetProperty("id");
  181. periodExtobj.name = periodinfo.GetProperty("name");
  182. periods.Add(periodExtobj);
  183. if (periodinfo.TryGetProperty("grades", out JsonElement gradesJobj))
  184. {
  185. foreach (var gradeinfo in gradesJobj.EnumerateArray())
  186. {
  187. dynamic gradeExtobj = new ExpandoObject();
  188. gradeExtobj.id = gradeinfo.GetProperty("id");
  189. gradeExtobj.name = gradeinfo.GetProperty("name");
  190. gradeExtobj.periodId = periodinfo.GetProperty("id");
  191. grades.Add(gradeExtobj);
  192. }
  193. }
  194. if (periodinfo.TryGetProperty("subjects", out JsonElement subjectsJobj))
  195. {
  196. foreach (var subjectinfo in subjectsJobj.EnumerateArray())
  197. {
  198. dynamic subjectExtobj = new ExpandoObject();
  199. subjectExtobj.id = subjectinfo.GetProperty("id");
  200. subjectExtobj.name = subjectinfo.GetProperty("name");
  201. subjectExtobj.periodId = periodinfo.GetProperty("id");
  202. subjects.Add(subjectExtobj);
  203. }
  204. }
  205. if (periodinfo.TryGetProperty("analysis", out JsonElement periodanalysisJobj))
  206. {
  207. if (periodanalysisJobj.TryGetProperty("type", out JsonElement examTypeJobj))
  208. {
  209. foreach (var examType in examTypeJobj.EnumerateArray())
  210. {
  211. examTypes.Add(examType.ToObject<object>());
  212. }
  213. }
  214. }
  215. }
  216. }
  217. }
  218. else //無此學校資料
  219. {
  220. return BadRequest();
  221. }
  222. //該老師排定的學校課程
  223. Dictionary<string, int> classStuDic = new Dictionary<string, int>(); //所有班級學生數
  224. var querycst = $"SELECT (IS_ARRAY(c.students)) ? ARRAY_LENGTH(c.students) : 0 as stuCnt, c.id FROM c";
  225. await foreach (var item in client.GetContainer("TEAMModelOS", "School").GetItemQueryStreamIterator(queryText: querycst, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Class-{school_code}") }))
  226. {
  227. var jsoncst = await JsonDocument.ParseAsync(item.ContentStream);
  228. foreach (var obj in jsoncst.RootElement.GetProperty("Documents").EnumerateArray())
  229. {
  230. string classId = obj.GetProperty("id").GetString();
  231. int stuCnt = obj.GetProperty("stuCnt").GetInt32();
  232. classStuDic.Add(classId, stuCnt);
  233. }
  234. }
  235. Dictionary<string, int> classGrpDic = new Dictionary<string, int>(); //所有班級分組數
  236. var querycgp = $"SELECT Count(1) as grpCnt, c.id FROM c JOIN(SELECT DISTINCT t.groupId FROM t IN c.students WHERE t.groupId != null) GROUP BY c.id";
  237. await foreach (var item in client.GetContainer("TEAMModelOS", "School").GetItemQueryStreamIterator(queryText: querycgp, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Class-{school_code}") }))
  238. {
  239. var jsoncgp = await JsonDocument.ParseAsync(item.ContentStream);
  240. foreach (var obj in jsoncgp.RootElement.GetProperty("Documents").EnumerateArray())
  241. {
  242. string classId = obj.GetProperty("id").GetString();
  243. int grpCnt = obj.GetProperty("grpCnt").GetInt32();
  244. classGrpDic.Add(classId, grpCnt);
  245. }
  246. }
  247. Dictionary<string, object> schoolCoursesDic = new Dictionary<string, object>(); //課程與科目對應表
  248. var querysc = $"SELECT c.id, c.name, c.subject FROM c";
  249. await foreach (var item in client.GetContainer("TEAMModelOS", "School").GetItemQueryStreamIterator(queryText: querysc, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Course-{school_code}") }))
  250. {
  251. var jsonsc = await JsonDocument.ParseAsync(item.ContentStream);
  252. if (jsonsc.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  253. {
  254. foreach (var obj in jsonsc.RootElement.GetProperty("Documents").EnumerateArray())
  255. {
  256. string courseIdNow = obj.GetProperty("id").ToString();
  257. object subjectNow = obj.GetProperty("subject");
  258. schoolCoursesDic.Add(courseIdNow, subjectNow);
  259. }
  260. }
  261. }
  262. List<object> courses = new List<object>(); //老師被安排的課程列表
  263. 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}'";
  264. await foreach (var item in client.GetContainer("TEAMModelOS", "School").GetItemQueryStreamIterator(queryText: query, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"CourseManagement-{school_code}") }))
  265. {
  266. var jsoncm = await JsonDocument.ParseAsync(item.ContentStream);
  267. if (jsoncm.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  268. {
  269. foreach (var obj in jsoncm.RootElement.GetProperty("Documents").EnumerateArray())
  270. {
  271. //班級
  272. string classIdNow = obj.GetProperty("id").ToString();
  273. dynamic classExtobj = new ExpandoObject();
  274. classExtobj.code = $"Class-{school_code}";
  275. classExtobj.id = classIdNow;
  276. classExtobj.name = obj.GetProperty("name").ToString();
  277. classExtobj.teacher = obj.GetProperty("teacher").ToObject<object>();
  278. classExtobj.scope = obj.GetProperty("scope").ToString();
  279. classExtobj.stuCnt = (classStuDic.ContainsKey(classIdNow)) ? classStuDic[classIdNow] : 0;
  280. classExtobj.grpCnt = (classGrpDic.ContainsKey(classIdNow)) ? classGrpDic[classIdNow] : 0;
  281. //課程
  282. obj.TryGetProperty("course", out JsonElement courseNow);
  283. string courseIdNow = courseNow.GetProperty("id").ToString();
  284. var courseExist = courses.Where((dynamic x) => x.id == courseIdNow).FirstOrDefault();
  285. if (courseExist == null)
  286. {
  287. dynamic courseExtobj = new ExpandoObject();
  288. courseExtobj.id = courseIdNow;
  289. courseExtobj.name = courseNow.GetProperty("name").ToString();
  290. courseExtobj.scope = courseNow.GetProperty("scope").ToString();
  291. courseExtobj.classes = new List<object>();
  292. courseExtobj.subject = schoolCoursesDic[courseIdNow];
  293. courseExtobj.classes.Add(classExtobj);
  294. courses.Add(courseExtobj);
  295. }
  296. else
  297. {
  298. courseExist.classes.Add(classExtobj);
  299. }
  300. }
  301. }
  302. }
  303. //取得校園評測 (HiTeach只取source='1'(課中评量))
  304. List<object> exams = new List<object>();
  305. await foreach (var exam in client.GetContainer("TEAMModelOS", "Common").GetItemQueryStreamIterator(queryText: $"SELECT c.code, c.id, c.name, c.startTime, c.endTime ,c.year, c.source, c.type, c.progress, c.stuCount, c.scope FROM c WHERE c.source = '1'", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Exam-{school_code}") }))
  306. {
  307. using var json = await JsonDocument.ParseAsync(exam.ContentStream);
  308. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  309. {
  310. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  311. {
  312. exams.Add(obj.ToObject<object>());
  313. }
  314. }
  315. }
  316. //取得School Blob 容器位置及SAS
  317. string school_code_blob = school_code.GetString().ToLower();
  318. var (blob_uri, blob_sas) = _azureStorage.GetBlobContainerSAS(school_code_blob, BlobContainerSasPermissions.Read | BlobContainerSasPermissions.List);
  319. return Ok(new { blob_uri, blob_sas, periods, grades, subjects, courses, examTypes, exams });
  320. }
  321. catch (Exception ex)
  322. {
  323. // await _dingDing.SendBotMsg($"CoreAPI2,{_option.Location},hiteach/GetTeacherInfo()\n{ex.Message}", GroupNames.醍摩豆服務運維群組);
  324. return BadRequest();
  325. }
  326. }
  327. //取得試卷
  328. [ProducesDefaultResponseType]
  329. [HttpPost("get-paper")]
  330. public async Task<IActionResult> GetPaperList(JsonElement request)
  331. {
  332. try
  333. {
  334. //Header驗證
  335. string id_token = HttpContext.GetXAuth("IdToken");
  336. if (string.IsNullOrEmpty(id_token)) return BadRequest();
  337. var jwt = new JwtSecurityToken(id_token);
  338. if (!jwt.Payload.Iss.Equals("account.teammodel", StringComparison.Ordinal)) return BadRequest();
  339. var id = jwt.Payload.Sub;
  340. var client = _azureCosmos.GetCosmosClient();
  341. //參數
  342. if (!request.TryGetProperty("grant_type", out JsonElement grant_type)) return BadRequest();
  343. string partitionid = string.Empty;
  344. string container = string.Empty;
  345. if (grant_type.ToString() == "school")
  346. {
  347. if (!request.TryGetProperty("school_code", out JsonElement school_code_json))
  348. {
  349. return BadRequest();
  350. }
  351. else
  352. {
  353. partitionid = school_code_json.ToString();
  354. container = "School";
  355. }
  356. }
  357. else
  358. {
  359. partitionid = id.ToString();
  360. container = "Teacher";
  361. }
  362. //SQL文
  363. List<object> papers = new List<object>();
  364. string queryWhere = " WHERE 1=1 ";
  365. string queryOption = string.Empty;
  366. //學段
  367. if (request.TryGetProperty("periodId", out JsonElement periodId) && container == "School")
  368. {
  369. queryWhere += $" AND c.periodId = '{periodId}'";
  370. }
  371. //年級
  372. if (request.TryGetProperty("gradeId", out JsonElement gradeIds) && gradeIds.GetArrayLength() > 0 && container == "School")
  373. {
  374. string queryOptionForGrade = string.Empty;
  375. for (int i = 0; i < gradeIds.GetArrayLength(); i++)
  376. {
  377. if(!string.IsNullOrWhiteSpace(queryOptionForGrade))
  378. {
  379. queryOptionForGrade += " OR ";
  380. }
  381. queryOptionForGrade += $" ARRAY_CONTAINS(c.gradeIds, '{gradeIds[i]}', true)";
  382. }
  383. queryWhere += $" AND ( {queryOptionForGrade} )";
  384. }
  385. //科目ID
  386. if (request.TryGetProperty("subjectId", out JsonElement subjectId) && container == "School")
  387. {
  388. queryWhere += $" AND c.subjectId = '{subjectId}'";
  389. }
  390. //科目名稱
  391. if (request.TryGetProperty("subjectName", out JsonElement subjectName))
  392. {
  393. queryWhere += $" AND c.subjectName = '{subjectName}'";
  394. }
  395. int perpage = 0; //每頁幾條
  396. if (request.TryGetProperty("perpage", out JsonElement perpage_json)) perpage = perpage_json.GetInt32();
  397. int page = 0; //目前第幾頁
  398. if (request.TryGetProperty("page", out JsonElement page_json))
  399. {
  400. if(page_json.GetInt32() > 0)
  401. {
  402. page = page_json.GetInt32() - 1;
  403. }
  404. }
  405. string order = "createTime"; //排序項目
  406. if (request.TryGetProperty("order", out JsonElement order_json))
  407. {
  408. if(order_json.ToString() == "useCount")
  409. {
  410. order = order_json.ToString();
  411. }
  412. }
  413. queryOption += $" Order By c." + order + " DESC ";
  414. if(perpage > 0 )
  415. {
  416. queryOption += $" OFFSET {perpage * page} LIMIT {perpage}";
  417. }
  418. //資料取得
  419. await foreach (var item in client.GetContainer("TEAMModelOS", container).GetItemQueryStreamIterator(queryText: $"SELECT c.code, c.id, c.periodId, c.gradeIds, c.subjectId, c.subjectName, c.name, REPLACE(c.blob, 'index.json', '') AS blob, c.score, c.useCount, ARRAY_LENGTH(c.scoring) AS itemCount, c.createTime From c {queryWhere + queryOption}", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Paper-{partitionid}") }))
  420. {
  421. using var json = await JsonDocument.ParseAsync(item.ContentStream);
  422. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  423. {
  424. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  425. {
  426. papers.Add(obj.ToObject<object>());
  427. }
  428. }
  429. }
  430. //總件數
  431. int totalCount = 0;
  432. await foreach (var item in client.GetContainer("TEAMModelOS", container).GetItemQueryStreamIterator(queryText: $"SELECT VALUE COUNT(1) From c {queryWhere}", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Paper-{partitionid}") }))
  433. {
  434. using var json = await JsonDocument.ParseAsync(item.ContentStream);
  435. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  436. {
  437. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  438. {
  439. totalCount = obj.GetInt32();
  440. }
  441. }
  442. }
  443. return Ok(new { papers, totalCount });
  444. }
  445. catch (Exception ex)
  446. {
  447. return BadRequest();
  448. }
  449. }
  450. //取得試題
  451. [ProducesDefaultResponseType]
  452. [HttpPost("get-item")]
  453. public async Task<IActionResult> GetItemList(JsonElement request)
  454. {
  455. try
  456. {
  457. //Header驗證
  458. string id_token = HttpContext.GetXAuth("IdToken");
  459. if (string.IsNullOrEmpty(id_token)) return BadRequest();
  460. var jwt = new JwtSecurityToken(id_token);
  461. if (!jwt.Payload.Iss.Equals("account.teammodel", StringComparison.Ordinal)) return BadRequest();
  462. var id = jwt.Payload.Sub;
  463. var client = _azureCosmos.GetCosmosClient();
  464. //參數
  465. if (!request.TryGetProperty("grant_type", out JsonElement grant_type)) return BadRequest();
  466. string partitionid = string.Empty;
  467. string container = string.Empty;
  468. if (grant_type.ToString() == "school")
  469. {
  470. if (!request.TryGetProperty("school_code", out JsonElement school_code_json))
  471. {
  472. return BadRequest();
  473. }
  474. else
  475. {
  476. partitionid = school_code_json.ToString();
  477. container = "School";
  478. }
  479. }
  480. else
  481. {
  482. partitionid = id.ToString();
  483. container = "Teacher";
  484. }
  485. //SQL文
  486. List<object> items = new List<object>();
  487. string queryWhere = " WHERE 1=1 ";
  488. string queryOption = string.Empty;
  489. //學段
  490. if (request.TryGetProperty("periodId", out JsonElement periodId) && container == "School")
  491. {
  492. queryWhere += $" AND c.periodId = '{periodId}'";
  493. }
  494. //年級
  495. if (request.TryGetProperty("gradeId", out JsonElement gradeIds) && gradeIds.GetArrayLength() > 0 && container == "School")
  496. {
  497. string queryOptionForGrade = string.Empty;
  498. for (int i = 0; i < gradeIds.GetArrayLength(); i++)
  499. {
  500. if (!string.IsNullOrWhiteSpace(queryOptionForGrade))
  501. {
  502. queryOptionForGrade += " OR ";
  503. }
  504. queryOptionForGrade += $" ARRAY_CONTAINS(c.gradeIds, '{gradeIds[i]}', true)";
  505. }
  506. queryWhere += $" AND ( {queryOptionForGrade} )";
  507. }
  508. //科目ID
  509. if (request.TryGetProperty("subjectId", out JsonElement subjectId) && container == "School")
  510. {
  511. queryWhere += $" AND c.subjectId = '{subjectId}'";
  512. }
  513. //科目名稱
  514. if (request.TryGetProperty("subjectName", out JsonElement subjectName))
  515. {
  516. queryWhere += $" AND c.subjectName = '{subjectName}'";
  517. }
  518. //題型
  519. int dummy = 0;
  520. if (request.TryGetProperty("type", out JsonElement type))
  521. {
  522. queryWhere += $" AND c.type = '{type}'";
  523. }
  524. //難度
  525. dummy = 0;
  526. if (request.TryGetProperty("level", out JsonElement level) && int.TryParse(level.ToString(), out dummy))
  527. {
  528. queryWhere += $" AND c.level = {level}";
  529. }
  530. //層次
  531. if (request.TryGetProperty("field", out JsonElement field) && int.TryParse(field.ToString(), out dummy))
  532. {
  533. queryWhere += $" AND c.field = {field}";
  534. }
  535. int perpage = 0; //每頁幾條
  536. if (request.TryGetProperty("perpage", out JsonElement perpage_json)) perpage = perpage_json.GetInt32();
  537. int page = 0; //目前第幾頁
  538. if (request.TryGetProperty("page", out JsonElement page_json))
  539. {
  540. if (page_json.GetInt32() > 0)
  541. {
  542. page = page_json.GetInt32() - 1;
  543. }
  544. }
  545. string order = "createTime"; //排序項目
  546. if (request.TryGetProperty("order", out JsonElement order_json))
  547. {
  548. if (order_json.ToString() == "useCount")
  549. {
  550. order = order_json.ToString();
  551. }
  552. }
  553. queryOption += $" Order By c." + order + " DESC ";
  554. if (perpage > 0)
  555. {
  556. queryOption += $" OFFSET {perpage * page} LIMIT {perpage}";
  557. }
  558. //資料取得
  559. await foreach (var item in client.GetContainer("TEAMModelOS", container).GetItemQueryStreamIterator(queryText: $"SELECT c.id, c.periodId, c.gradeIds, c.subjectId, c.subjectName, c.blob, c.field, c.level, c.type, c.useCount, c.createTime From c {queryWhere + queryOption}", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Item-{partitionid}") }))
  560. {
  561. using var json = await JsonDocument.ParseAsync(item.ContentStream);
  562. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  563. {
  564. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  565. {
  566. items.Add(obj.ToObject<object>());
  567. }
  568. }
  569. }
  570. //總件數
  571. int totalCount = 0;
  572. await foreach (var item in client.GetContainer("TEAMModelOS", container).GetItemQueryStreamIterator(queryText: $"SELECT VALUE COUNT(1) From c {queryWhere}", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Item-{partitionid}") }))
  573. {
  574. using var json = await JsonDocument.ParseAsync(item.ContentStream);
  575. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  576. {
  577. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  578. {
  579. totalCount = obj.GetInt32();
  580. }
  581. }
  582. }
  583. return Ok(new { items, totalCount });
  584. }
  585. catch (Exception ex)
  586. {
  587. return BadRequest();
  588. }
  589. }
  590. //取得知識點
  591. [ProducesDefaultResponseType]
  592. [HttpPost("get-knowledge")]
  593. public async Task<IActionResult> GetKnowledgePointList(JsonElement request)
  594. {
  595. var client = _azureCosmos.GetCosmosClient();
  596. if (!request.TryGetProperty("school_code", out JsonElement school_code)) return BadRequest();
  597. //知識點
  598. List<object> points = new List<object>();
  599. await foreach (var item in client.GetContainer("TEAMModelOS", "School").GetItemQueryStreamIterator(queryText: $"SELECT c.id, c.name, c.subjectId FROM c WHERE c.type = 'point'", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Knowledge-{school_code}") }))
  600. {
  601. using var json = await JsonDocument.ParseAsync(item.ContentStream);
  602. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  603. {
  604. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  605. {
  606. points.Add(obj.ToObject<object>());
  607. }
  608. }
  609. }
  610. return Ok(points);
  611. }
  612. //取得課綱
  613. [ProducesDefaultResponseType]
  614. [HttpPost("get-syllabus")]
  615. public async Task<IActionResult> GetSyllabusList(JsonElement request)
  616. {
  617. var client = _azureCosmos.GetCosmosClient();
  618. if (!request.TryGetProperty("school_code", out JsonElement school_code)) return BadRequest();
  619. //校本課綱
  620. List<SyllabusRole> syllabus = new List<SyllabusRole>();
  621. await foreach (var item in client.GetContainer("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}") }))
  622. {
  623. var jsons = await JsonDocument.ParseAsync(item.ContentStream);
  624. if (jsons.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  625. {
  626. foreach (var obj in jsons.RootElement.GetProperty("Documents").EnumerateArray())
  627. {
  628. SyllabusRole syllabusRole = new SyllabusRole();
  629. syllabusRole.id = obj.GetProperty("id").ToString();
  630. syllabusRole.name = obj.GetProperty("name").ToString();
  631. syllabusRole.period = obj.GetProperty("period");
  632. syllabusRole.grade = obj.GetProperty("grade");
  633. syllabusRole.semester = obj.GetProperty("semester");
  634. syllabusRole.subject = obj.GetProperty("subject");
  635. syllabusRole.resourceCount = obj.GetProperty("resourceCount").GetUInt16();
  636. syllabusRole.itemCount = obj.GetProperty("itemCount").GetUInt16();
  637. List<Syllabus> syllabusList = obj.GetProperty("children").ToObject<List<Syllabus>>();
  638. syllabusList.Insert(0, new Syllabus { id = syllabusRole.id, name = syllabusRole.name, pid = "", order = 0 });
  639. syllabusList = syllabusList.OrderBy(x => x.order).ToList();
  640. syllabusRole.structure = CreateSyllabusTree(syllabusList);
  641. syllabus.Add(syllabusRole);
  642. }
  643. //[DEBUG] string jsonString = System.Text.Json.JsonSerializer.Serialize(syllabusRoles);
  644. }
  645. }
  646. return Ok(syllabus);
  647. }
  648. [ProducesDefaultResponseType]
  649. [HttpPost("get-students-list")]
  650. public async Task<IActionResult> GetStudentsList(JsonElement requert)
  651. {
  652. string id_token = HttpContext.GetXAuth("IdToken");
  653. if (string.IsNullOrWhiteSpace(id_token)) return BadRequest();
  654. var jwt = new JwtSecurityToken(id_token);
  655. if (!jwt.Payload.Iss.Equals("account.teammodel", StringComparison.Ordinal)) return BadRequest();
  656. var id = jwt.Payload.Sub;
  657. if (!requert.TryGetProperty("grant_type", out JsonElement grant_type)) return BadRequest();
  658. if (!requert.TryGetProperty("class_code", out JsonElement class_code)) return BadRequest();
  659. requert.TryGetProperty("school_code", out JsonElement school_code);
  660. if (grant_type.GetString() == "school" && string.IsNullOrEmpty(school_code.GetString())) return BadRequest();
  661. var client = _azureCosmos.GetCosmosClient();
  662. List<object> students = new List<object>();
  663. var query = $"SELECT c.students FROM c WHERE c.id = '{class_code}' AND c.scope = '{grant_type}'";
  664. string pk = (grant_type.GetString() == "school") ? $"Class-{school_code}" : $"Class-{id}";
  665. string container = (grant_type.GetString() == "school") ? "School" : "Teacher";
  666. await foreach (var item in client.GetContainer("TEAMModelOS", container).GetItemQueryStreamIterator(queryText: query, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey(pk) }))
  667. {
  668. var json = await JsonDocument.ParseAsync(item.ContentStream);
  669. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  670. {
  671. foreach (var classObj in json.RootElement.GetProperty("Documents").EnumerateArray())
  672. {
  673. foreach (var studentObj in classObj.GetProperty("students").EnumerateArray())
  674. {
  675. dynamic studentsExtobj = new ExpandoObject();
  676. studentsExtobj.id = studentObj.GetProperty("id");
  677. studentsExtobj.name = studentObj.GetProperty("name");
  678. studentsExtobj.no = studentObj.GetProperty("no");
  679. students.Add(studentsExtobj);
  680. }
  681. }
  682. }
  683. else
  684. {
  685. return Ok(new { error = 1, message = "No class found!" });
  686. }
  687. }
  688. return Ok(new { students });
  689. }
  690. [ProducesDefaultResponseType]
  691. [HttpPost("start-lesson")]
  692. public async Task<IActionResult> StartLesson(JsonElement request)
  693. {
  694. //取得授課ID
  695. string lesson_code = (request.TryGetProperty("lesson_id", out JsonElement lesson_id) && !string.IsNullOrWhiteSpace(Convert.ToString(lesson_id))) ? Convert.ToString(lesson_id) : _snowflakeId.NextId().ToString();
  696. bool get_lesson_id = (string.IsNullOrWhiteSpace(Convert.ToString(lesson_id)) || Convert.ToString(lesson_id) != lesson_code) ? false : true;
  697. //取得醍摩豆ID
  698. string teacherId = string.Empty;
  699. string id_token = HttpContext.GetXAuth("IdToken");
  700. if (!string.IsNullOrWhiteSpace(id_token))
  701. {
  702. var jwt = new JwtSecurityToken(id_token);
  703. if (!jwt.Payload.Iss.Equals("account.teammodel", StringComparison.OrdinalIgnoreCase)) return BadRequest();
  704. teacherId = jwt.Payload.Sub;
  705. }
  706. if (string.IsNullOrWhiteSpace(teacherId) && get_lesson_id) return BadRequest(); //有授課ID無醍摩豆ID,BadRequest
  707. //DB操作
  708. if (!string.IsNullOrWhiteSpace(teacherId))
  709. {
  710. string tableName = "TeacherLesson";
  711. CloudTable table = _azureStorage.GetCloudTableClient().GetTableReference(tableName);
  712. Lesson lessonEntity = new Lesson(teacherId, lesson_code);
  713. TableOperation operation = TableOperation.InsertOrReplace(lessonEntity);
  714. TableResult result = await table.ExecuteAsync(operation);
  715. }
  716. return Ok(new { lesson_code });
  717. }
  718. [ProducesDefaultResponseType]
  719. [HttpPost("upd-exam-result")]
  720. public async Task<IActionResult> UploadExamResult(JsonElement request)
  721. {
  722. try
  723. {
  724. string message = "";
  725. int error = 0;
  726. string id_token = HttpContext.GetXAuth("IdToken");
  727. if (string.IsNullOrWhiteSpace(id_token)) return BadRequest();
  728. var jwt = new JwtSecurityToken(id_token);
  729. if (!jwt.Payload.Iss.Equals("account.teammodel", StringComparison.Ordinal)) return BadRequest();
  730. var id = jwt.Payload.Sub;
  731. if (!request.TryGetProperty("exam", out JsonElement exam)) return BadRequest();
  732. //if (!request.TryGetProperty("examResult", out JsonElement examResult)) return BadRequest();
  733. if (!request.TryGetProperty("examClassResult", out JsonElement examClassResult)) return BadRequest();
  734. ExamInfo dbExamInfo = exam.ToObject<ExamInfo>();
  735. //ExamResult dbExamResult = examResult.ToObject<ExamResult>();
  736. List<ExamClassResult> dbExamClassResultList = examClassResult.ToObject<List<ExamClassResult>>();
  737. var examResponse = _azureCosmos.GetCosmosClient().GetContainer("TEAMModelOS", "Common").UpsertItemAsync(dbExamInfo, new PartitionKey(dbExamInfo.code)).GetAwaiter().GetResult();
  738. //var examResultResponse = await _azureCosmos.GetCosmosClient().GetContainer("TEAMModelOS", "Common").UpsertItemAsync(dbExamResult, new PartitionKey(dbExamResult.code));
  739. foreach (ExamClassResult examClassResultRow in dbExamClassResultList)
  740. {
  741. var examClassResultResponse = await _azureCosmos.GetCosmosClient().GetContainer("TEAMModelOS", "Common").UpsertItemAsync(examClassResultRow, new PartitionKey(examClassResultRow.code));
  742. }
  743. return Ok(new { error, message });
  744. }
  745. catch (Exception ex)
  746. {
  747. return BadRequest();
  748. }
  749. }
  750. //課綱的model先記在下面,待式樣確定後再轉換
  751. private List<SyllabusNode> CreateSyllabusTree(List<Syllabus> syllabuses)
  752. {
  753. List<SyllabusNode> nodes = new List<SyllabusNode>();
  754. foreach (var syllabus in syllabuses)
  755. {
  756. if (syllabus.pid == "")
  757. nodes.Add(new SyllabusNode { id = syllabus.id, name = syllabus.name });
  758. else
  759. {
  760. CreateNode(nodes, syllabus);
  761. }
  762. }
  763. return nodes;
  764. }
  765. private void CreateNode(List<SyllabusNode> nodes, Syllabus parent)
  766. {
  767. foreach (var node in nodes)
  768. {
  769. if (node.id == parent.pid)
  770. {
  771. node.children.Add(new SyllabusNode { id = parent.id, name = parent.name });
  772. }
  773. else
  774. {
  775. CreateNode(node.children, parent);
  776. }
  777. }
  778. }
  779. public class SyllabusRole
  780. {
  781. public string id { get; set; }
  782. public string name { get; set; }
  783. public object period { get; set; }
  784. public object semester { get; set; }
  785. public object grade { get; set; }
  786. public object subject { get; set; }
  787. public int resourceCount { get; set; }
  788. public int itemCount { get; set; }
  789. public object structure { get; set; }
  790. }
  791. public class Syllabus
  792. {
  793. public string id { get; set; }
  794. public string name { get; set; }
  795. public string pid { get; set; }
  796. public int order { get; set; }
  797. }
  798. public class SyllabusNode
  799. {
  800. public string id { get; set; }
  801. public string name { get; set; }
  802. public List<SyllabusNode> children { get; set; }
  803. public SyllabusNode()
  804. {
  805. children = new List<SyllabusNode>();
  806. }
  807. }
  808. }
  809. }