HiScanController.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. using Azure.Cosmos;
  2. using Microsoft.AspNetCore.Http;
  3. using Microsoft.AspNetCore.Mvc;
  4. using System;
  5. using System.Collections.Generic;
  6. using System.IdentityModel.Tokens.Jwt;
  7. using System.Linq;
  8. using System.Text;
  9. using System.Text.Json;
  10. using System.Threading.Tasks;
  11. using TEAMModelOS.Models.Dto;
  12. using TEAMModelOS.SDK.Models;
  13. using TEAMModelOS.SDK;
  14. using TEAMModelOS.SDK.Context.Constant.Common;
  15. using TEAMModelOS.SDK.DI;
  16. using TEAMModelOS.SDK.DI.AzureCosmos.Inner;
  17. using TEAMModelOS.SDK.Extension;
  18. using TEAMModelOS.SDK.Helper.Common.CollectionHelper;
  19. using TEAMModelOS.SDK.Helper.Common.StringHelper;
  20. using TEAMModelOS.Models;
  21. using Microsoft.Extensions.Options;
  22. using TEAMModelOS.SDK.Models.Cosmos;
  23. using Microsoft.AspNetCore.Authorization;
  24. using TEAMModelOS.Filter;
  25. using StackExchange.Redis;
  26. using TEAMModelOS.SDK.Models.Cosmos.Common.Inner;
  27. using TEAMModelOS.Services.Common;
  28. using System.IO;
  29. using System.Dynamic;
  30. using Azure.Storage.Blobs.Models;
  31. using Azure.Storage.Sas;
  32. using Lib.AspNetCore.ServerSentEvents;
  33. using TEAMModelFunction;
  34. using TEAMModelOS.SDK.Models.Cosmos.Common;
  35. namespace TEAMModelOS.Controllers.Core
  36. {
  37. [ProducesResponseType(StatusCodes.Status200OK)]
  38. [ProducesResponseType(StatusCodes.Status400BadRequest)]
  39. //[Authorize(Roles = "HiTool")]
  40. [Route("hiscan")]
  41. [ApiController]
  42. public class HiScanController : ControllerBase
  43. {
  44. private readonly AzureRedisFactory _azureRedis;
  45. private readonly AzureCosmosFactory _azureCosmos;
  46. private readonly SnowflakeId _snowflakeId;
  47. private readonly AzureServiceBusFactory _serviceBus;
  48. private readonly DingDing _dingDing;
  49. private readonly Option _option;
  50. private readonly AzureStorageFactory _azureStorage;
  51. private readonly ServerSentEventsService _sse;
  52. public HiScanController(AzureCosmosFactory azureCosmos, AzureServiceBusFactory serviceBus, SnowflakeId snowflakeId, DingDing dingDing, IOptionsSnapshot<Option> option,
  53. AzureRedisFactory azureRedis, AzureStorageFactory azureStorage, ServerSentEventsService sse)
  54. {
  55. _azureCosmos = azureCosmos;
  56. _serviceBus = serviceBus;
  57. _snowflakeId = snowflakeId;
  58. _dingDing = dingDing;
  59. _option = option?.Value;
  60. _azureRedis = azureRedis;
  61. _azureStorage = azureStorage;
  62. _sse = sse;
  63. }
  64. ///<summary>
  65. ///查询教师的阅卷任务列表
  66. /// </summary>
  67. /// <data>
  68. /// ! "code":"tmdid"
  69. /// </data>
  70. /// <param name="request"></param>
  71. /// <returns></returns>
  72. [ProducesDefaultResponseType]
  73. [HttpPost("verify-qrcode")]
  74. // [AuthToken(Roles = "teacher,admin")]
  75. public async Task<IActionResult> VerifyQrcode(JsonElement request)
  76. {
  77. try {
  78. if (!request.TryGetProperty("sid", out JsonElement sid)) return BadRequest();
  79. if (!request.TryGetProperty("id_token", out JsonElement id_token)) return BadRequest();
  80. IServerSentEventsClient sseClient;
  81. if (Guid.TryParse($"{sid}", out Guid guid) && (sseClient = _sse.GetClient(guid)) != null) {
  82. //var clientName = sseClient.GetProperty<string>("NAME");
  83. //var clientDID= sseClient.GetProperty<string>("DID");
  84. // var isHiTeach = clientName.Contains("HiScan", StringComparison.OrdinalIgnoreCase);
  85. var jwt = new JwtSecurityToken(id_token.GetString());
  86. //TODO 此驗證IdToken先簡單檢查,後面需向Core ID新API,驗證Token
  87. if (!jwt.Payload.Iss.Equals("account.teammodel", StringComparison.OrdinalIgnoreCase)) return BadRequest();
  88. var id = jwt.Payload.Sub;
  89. jwt.Payload.TryGetValue("name", out object name);
  90. jwt.Payload.TryGetValue("picture", out object picture);
  91. List<ScanSchool> schools = new List<ScanSchool>();
  92. //TODO 取得Teacher 個人相關數據(課程清單、虛擬教室清單、歷史紀錄清單等),學校數據另外API處理,多校切換時不同
  93. var client = _azureCosmos.GetCosmosClient();
  94. var response = await client.GetContainer("TEAMModelOS", "Teacher").ReadItemStreamAsync(id, new PartitionKey("Base"));
  95. int size = 0;
  96. //老師個人資料(含初始化)
  97. if (response.Status == 200)
  98. {
  99. var json = await JsonDocument.ParseAsync(response.ContentStream);
  100. if (json.RootElement.TryGetProperty("schools", out JsonElement value))
  101. {
  102. if (json.RootElement.TryGetProperty("size", out JsonElement _size) && _size.ValueKind.Equals(JsonValueKind.Number))
  103. {
  104. size = _size.GetInt32();
  105. }
  106. foreach (var obj in value.EnumerateArray())
  107. {
  108. string statusNow = obj.GetProperty("status").ToString();
  109. //正式加入才会有
  110. if (statusNow == "join")
  111. {
  112. //dynamic schoolExtobj = new ExpandoObject();
  113. var schoolJson = await client.GetContainer("TEAMModelOS", "School").ReadItemStreamAsync($"{obj.GetProperty("schoolId")}", new PartitionKey("Base"));
  114. var school = await JsonDocument.ParseAsync(schoolJson.ContentStream);
  115. var schoolId = obj.GetProperty("schoolId");
  116. var schoolName = obj.GetProperty("name");
  117. var schoolStatus = obj.GetProperty("status");
  118. var schoolPicture = school.RootElement.GetProperty("picture");
  119. //检查学校购买的模组是否包含阅卷模组
  120. int count = 0;
  121. string sql = $" SELECT value(count(product)) FROM c join product in c.service.product where c.id ='{schoolId}' and c.pk='Product' and product.prodCode='YMPCVCIM' ";
  122. await foreach (var item in client.GetContainer("TEAMModelOS", "School").GetItemQueryIterator<int>(sql, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("Product") })) {
  123. count = item;
  124. }
  125. if (count > 0) {
  126. //生成token
  127. var token = JwtAuthExtension.CreateAuthToken(_option.HostName, id, name?.ToString(), picture?.ToString(), _option.JwtSecretKey, roles: new[] { "teacher" }, schoolID: $"{schoolId}");
  128. //获取学校线下阅卷评测
  129. var sexams= await GetExam($"{schoolId}", "school", client);
  130. schools.Add(new ScanSchool { name = $"schoolName", picture = $"{schoolPicture}", code = $"{schoolId}", status = $"{schoolStatus}" ,token=token, datas = sexams });
  131. }
  132. }
  133. }
  134. }
  135. //获取个人线下阅卷评测
  136. var datas = await GetExam($"{id}", "private", client);
  137. //換取AuthToken,提供給前端
  138. var data = new { name, picture, id, schools , datas };
  139. await sseClient.SendEventAsync(data.ToJsonString());
  140. return Ok(data);
  141. }
  142. else
  143. {
  144. return Ok(new { status = 404 });
  145. }
  146. }
  147. } catch (Exception ex ) {
  148. await _dingDing.SendBotMsg($"IES5,{_option.Location},hiscan/verify-qrcode()\n{ex.Message}{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
  149. return BadRequest();
  150. }
  151. return Ok();
  152. }
  153. private async Task<List<ExamData>> GetExam(string code ,string scope, CosmosClient client)
  154. {
  155. List<ExamData> corrects = new List<ExamData>();
  156. if (scope.Equals("school"))
  157. {
  158. await foreach (var item in client.GetContainer("TEAMModelOS", "Common").GetItemQueryIterator<Correct>(queryText: "SELECT * FROM c where c.source='2' and c.progress='going' order by c.createTime ", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Correct-{code}") }))
  159. {
  160. ExamInfo exam = await client.GetContainer("TEAMModelOS", "Common").ReadItemAsync<ExamInfo>(item.id, new PartitionKey($"Exam-{code}"));
  161. (List<string> tmdids, List<Students> students)stulist =await TriggerStuActivity.GetStuList(client, _dingDing, exam.classes, code);
  162. corrects.Add(new ExamData { exam = exam, correct = item,tmdids=stulist.tmdids,stulist=stulist.students });
  163. }
  164. }
  165. else if(scope.Equals("private")){
  166. await foreach (var item in client.GetContainer("TEAMModelOS", "Common").GetItemQueryIterator<Correct>(queryText: "SELECT * FROM c where c.source='2' and c.progress='going' order by c.createTime ", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Correct-{code}") }))
  167. {
  168. ExamInfo exam = await client.GetContainer("TEAMModelOS", "Common").ReadItemAsync<ExamInfo>(item.id, new PartitionKey($"Exam-{code}"));
  169. (List<string> tmdids, List<Students> students) stulist = await TriggerStuActivity.GetStuList(client, _dingDing, exam.classes, code);
  170. corrects.Add(new ExamData { exam = exam, correct = item, tmdids = stulist.tmdids, stulist = stulist.students });
  171. }
  172. }
  173. return corrects;
  174. }
  175. }
  176. public class ScanSchool
  177. {
  178. public string name { get; set; }
  179. public string picture { get; set; }
  180. public string code { get; set; }
  181. public string status { get; set; }
  182. public string token { get; set; }
  183. // public List<(Correct,ExamInfo, (List<string> tmdids, List<Students> stulist))> exams{ get; set; }
  184. public List<ExamData> datas { get; set; }
  185. }
  186. public class ExamData
  187. {
  188. public Correct correct { get; set; }
  189. public ExamInfo exam { get; set; }
  190. public List<string> tmdids { get; set; }
  191. public List<Students> stulist { get; set; }
  192. }
  193. }