HiTeachController.cs 111 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862
  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. using HTEXLib.COMM.Helpers;
  24. using StackExchange.Redis;
  25. using TEAMModelOS.SDK.Models.Cosmos.Common;
  26. using TEAMModelOS.SDK;
  27. using Microsoft.Extensions.Configuration;
  28. using Azure.Messaging.ServiceBus;
  29. using TEAMModelOS.SDK.Services;
  30. using TEAMModelOS.SDK.Models.Service;
  31. namespace TEAMModelOS.Controllers.Client
  32. {
  33. [ProducesResponseType(StatusCodes.Status200OK)]
  34. [ProducesResponseType(StatusCodes.Status400BadRequest)]
  35. //[Authorize(Roles = "HiTeach")]
  36. [Route("hiteach")]
  37. [ApiController]
  38. public class HiTeachController : ControllerBase
  39. {
  40. private readonly AzureStorageFactory _azureStorage;
  41. private readonly AzureRedisFactory _azureRedis;
  42. private readonly AzureCosmosFactory _azureCosmos;
  43. private readonly DingDing _dingDing;
  44. private readonly Option _option;
  45. private readonly SnowflakeId _snowflakeId;
  46. private readonly AzureServiceBusFactory _serviceBus;
  47. private readonly IConfiguration _configuration;
  48. private readonly CoreAPIHttpService _coreAPIHttpService;
  49. private readonly IPSearcher _searcher;
  50. private readonly HttpTrigger _httpTrigger;
  51. public HiTeachController(
  52. AzureStorageFactory azureStorage,
  53. AzureRedisFactory azureRedis,
  54. AzureCosmosFactory azureCosmos,
  55. DingDing dingDing,
  56. SnowflakeId snowflakeId,
  57. IOptionsSnapshot<Option> option,
  58. AzureServiceBusFactory serviceBus,
  59. IConfiguration configuration,
  60. CoreAPIHttpService coreAPIHttpService, IPSearcher searcher, HttpTrigger httpTrigger)
  61. {
  62. _azureStorage = azureStorage;
  63. _azureRedis = azureRedis;
  64. _azureCosmos = azureCosmos;
  65. _dingDing = dingDing;
  66. _snowflakeId = snowflakeId;
  67. _option = option?.Value;
  68. _serviceBus = serviceBus;
  69. _configuration = configuration;
  70. _coreAPIHttpService = coreAPIHttpService;
  71. _searcher = searcher;
  72. _httpTrigger = httpTrigger;
  73. }
  74. /// <summary>
  75. /// 更新课堂记录
  76. /// </summary>
  77. /// <param name="request"></param>
  78. /// <returns></returns>
  79. [ProducesDefaultResponseType]
  80. [HttpPost("update-lesson-record")]
  81. public async Task<IActionResult> UpdateLessonRecord(JsonElement request)
  82. {
  83. var client = _azureCosmos.GetCosmosClient();
  84. if (!request.TryGetProperty("lesson_id", out JsonElement _lessonId)) return BadRequest();
  85. if (!request.TryGetProperty("tmdid", out JsonElement _tmdid)) return BadRequest();
  86. request.TryGetProperty("name", out JsonElement name);
  87. request.TryGetProperty("school", out JsonElement _school);
  88. if (!request.TryGetProperty("scope", out JsonElement _scope)) return BadRequest();
  89. request.TryGetProperty("grant_types", out JsonElement _grant_types);
  90. string tbname;
  91. string code;
  92. if (_scope.GetString().Equals("school") && !string.IsNullOrWhiteSpace(_school.GetString()))
  93. {
  94. code = $"LessonRecord-{_school}";
  95. tbname = "School";
  96. }
  97. else if ($"{_scope}".Equals("private"))
  98. {
  99. code = $"LessonRecord";
  100. tbname = "Teacher";
  101. }
  102. else
  103. {
  104. return BadRequest();
  105. }
  106. try
  107. {
  108. LessonRecord lessonRecord = await client.GetContainer(Constant.TEAMModelOS, tbname).ReadItemAsync<LessonRecord>($"{_lessonId}", new PartitionKey(code));
  109. List<LessonUpdate> updates = new List<LessonUpdate>() { new LessonUpdate { grant_type = "up-base" } };
  110. if (_grant_types.ValueKind.Equals(JsonValueKind.Array))
  111. {
  112. updates = _grant_types.ToObject<List<LessonUpdate>>();
  113. if (!updates.Select(x => x.grant_type).Contains("up-base"))
  114. {
  115. updates.Add(new LessonUpdate { grant_type = "up-base" });
  116. }
  117. }
  118. if (!string.IsNullOrWhiteSpace($"{name}"))
  119. {
  120. lessonRecord.name = $"{name}";
  121. await client.GetContainer(Constant.TEAMModelOS, tbname).ReplaceItemAsync<LessonRecord>(lessonRecord, $"{_lessonId}", new PartitionKey(code));
  122. }
  123. var ActiveTask = _configuration.GetValue<string>("Azure:ServiceBus:ActiveTask");
  124. string msg = null;
  125. msg = new { lesson_id = $"{_lessonId}", tmdid = $"{_tmdid}", grant_types = updates, school = $"{_school}", scope = $"{_scope}" }.ToJsonString();
  126. var messageChange = new ServiceBusMessage(msg);
  127. messageChange.ApplicationProperties.Add("name", "LessonRecordEvent");
  128. //await _dingDing.SendBotMsg($"{_option.Location},课堂id:{_lessonId} 更新事件,{msg}", GroupNames.醍摩豆服務運維群組);
  129. await _serviceBus.GetServiceBusClient().SendMessageAsync(ActiveTask, messageChange);
  130. return Ok(new { status = 200 });
  131. }
  132. catch (CosmosException ex) when (ex.Status == 404)
  133. {
  134. return BadRequest("课堂记录不存在");
  135. }
  136. catch (Exception ex)
  137. {
  138. return BadRequest();
  139. }
  140. }
  141. /// <summary>
  142. /// 创建课堂开课记录
  143. /// </summary>
  144. /// <param name="request"></param>
  145. /// <returns></returns>
  146. [ProducesDefaultResponseType]
  147. // [AuthToken(Roles = "teacher,admin")]
  148. [HttpPost("create-lesson-record")]
  149. public async Task<IActionResult> CreateLessonRecord(JsonElement request)
  150. {
  151. if (!request.TryGetProperty("lesson", out JsonElement _lesson)) return BadRequest();
  152. var client = _azureCosmos.GetCosmosClient();
  153. LessonRecord lessonRecord = _lesson.ToObject<LessonRecord>();
  154. if (!string.IsNullOrEmpty(lessonRecord.scope) && !string.IsNullOrEmpty(lessonRecord.tmdid) && !string.IsNullOrEmpty(lessonRecord.id))
  155. {
  156. RedisValue value = default;
  157. string tbname = null;
  158. if (lessonRecord.scope.Equals("school") && !string.IsNullOrEmpty(lessonRecord.school))
  159. {
  160. lessonRecord.code = $"LessonRecord-{lessonRecord.school}";
  161. tbname = "School";
  162. }
  163. if (lessonRecord.scope.Equals("private"))
  164. {
  165. lessonRecord.code = $"LessonRecord";
  166. tbname = "Teacher";
  167. }
  168. if (tbname == null)
  169. {
  170. return BadRequest("参数异常:scope应为school或private,scope=school ,school字段不能为空");
  171. }
  172. if (lessonRecord.startTime == 0)
  173. {
  174. lessonRecord.startTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
  175. }
  176. lessonRecord.pk = "LessonRecord";
  177. try
  178. {
  179. await client.GetContainer(Constant.TEAMModelOS, tbname).CreateItemAsync(lessonRecord, new PartitionKey(lessonRecord.code));
  180. var ActiveTask = _configuration.GetValue<string>("Azure:ServiceBus:ActiveTask");
  181. var messageChange = new ServiceBusMessage(lessonRecord.ToJsonString());
  182. messageChange.ApplicationProperties.Add("name", "LessonRecordEvent");
  183. await _serviceBus.GetServiceBusClient().SendMessageAsync(ActiveTask, messageChange);
  184. return Ok(new { status = 200 });
  185. }
  186. catch (Exception ex)
  187. {
  188. await _dingDing.SendBotMsg($"IES5,{_option.Location}, hiteach/create-lesson-record:()\n{ex.Message}\n{ex.StackTrace}{request.ToJsonString()}", GroupNames.醍摩豆服務運維群組);
  189. return BadRequest();
  190. }
  191. }
  192. else
  193. {
  194. return BadRequest();
  195. }
  196. }
  197. [ProducesDefaultResponseType]
  198. [HttpPost("get-teacher-info")]
  199. public async Task<IActionResult> GetTeacherInfo()
  200. {
  201. //Debug
  202. //string json = System.Text.Json.JsonSerializer.Serialize(id_token);
  203. try
  204. {
  205. string id_token = HttpContext.GetXAuth("IdToken");
  206. (string ip, string region) = await LoginService.LoginIp(HttpContext, _searcher);
  207. if (string.IsNullOrEmpty(id_token)) return BadRequest();
  208. var jwt = new JwtSecurityToken(id_token);
  209. if (!jwt.Payload.Iss.Equals("account.teammodel", StringComparison.OrdinalIgnoreCase)) return BadRequest();
  210. var id = jwt.Payload.Sub;
  211. jwt.Payload.TryGetValue("name", out object name);
  212. jwt.Payload.TryGetValue("picture", out object picture);
  213. int total = 0;
  214. List<object> schools = new List<object>();
  215. string defaultschool = null;
  216. //TODK 取得Teacher 個人相關數據(課程清單、虛擬教室清單、歷史紀錄清單等),學校數據另外API處理,多校切換時不同
  217. var client = _azureCosmos.GetCosmosClient();
  218. var response = await client.GetContainer(Constant.TEAMModelOS, "Teacher").ReadItemStreamAsync(id, new PartitionKey("Base"));
  219. if (response.Status == 200)
  220. {
  221. var jsonsc = await JsonDocument.ParseAsync(response.ContentStream);
  222. if (jsonsc.RootElement.TryGetProperty("size", out JsonElement size) && size.ValueKind.Equals(JsonValueKind.Number))
  223. {
  224. total = int.Parse($"{size}");
  225. }
  226. if (jsonsc.RootElement.TryGetProperty("schools", out JsonElement value))
  227. {
  228. foreach (var obj in value.EnumerateArray())
  229. {
  230. string statusNow = obj.GetProperty("status").ToString();
  231. if (statusNow.Equals("join")) //成為老師的才放入
  232. {
  233. dynamic schoolExtobj = new ExpandoObject();
  234. var schoolJson = await client.GetContainer(Constant.TEAMModelOS, "School").ReadItemStreamAsync($"{obj.GetProperty("schoolId")}", new PartitionKey("Base"));
  235. var school = await JsonDocument.ParseAsync(schoolJson.ContentStream);
  236. schoolExtobj.schoolId = obj.GetProperty("schoolId");
  237. schoolExtobj.name = school.RootElement.GetProperty("name");
  238. schoolExtobj.status = obj.GetProperty("status");
  239. schoolExtobj.picture = school.RootElement.GetProperty("picture");
  240. schools.Add(schoolExtobj);
  241. var sctch = await client.GetContainer(Constant.TEAMModelOS, "School").ReadItemStreamAsync(id, new PartitionKey($"Teacher-{obj.GetProperty("schoolId")}"));
  242. if (sctch.Status == 200 && sctch != null && sctch.ContentStream != null) {
  243. var jsonDoc = await JsonDocument.ParseAsync(sctch.ContentStream);
  244. SchoolTeacher schoolTeacher = jsonDoc.RootElement.ToObject<SchoolTeacher>();
  245. total += schoolTeacher.size;
  246. }
  247. }
  248. }
  249. }
  250. //預設學校ID
  251. if (jsonsc.RootElement.TryGetProperty("defaultSchool", out JsonElement valueD) && !string.IsNullOrEmpty(valueD.ToString()))
  252. {
  253. defaultschool = valueD.ToString();
  254. }
  255. }
  256. else
  257. {
  258. //如果沒有,則初始化Teacher基本資料到Cosmos
  259. Teacher teacher = new Teacher
  260. {
  261. createTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
  262. id = id,
  263. pk = "Base",
  264. code = "Base",
  265. name = name?.ToString(),
  266. picture = picture?.ToString(),
  267. //创建账号并第一次登录IES5则默认赠送1G
  268. size = 1,
  269. defaultSchool = null,
  270. schools = new List<Teacher.TeacherSchool>(),
  271. };
  272. total = 1;
  273. teacher = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Teacher").CreateItemAsync<Teacher>(teacher, new PartitionKey("Base"));
  274. }
  275. //老師個人課程清單
  276. List<object> courses = new List<object>();
  277. await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "Teacher").GetItemQueryStreamIterator(queryText: $"SELECT c.id, c.name, c.schedule, c.scope FROM c ", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Course-{id}") }))
  278. {
  279. var jsontcs = await JsonDocument.ParseAsync(item.ContentStream);
  280. if (jsontcs.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  281. {
  282. foreach (var obj in jsontcs.RootElement.GetProperty("Documents").EnumerateArray())
  283. {
  284. dynamic courseExtobj = new ExpandoObject();
  285. courseExtobj.id = Convert.ToString(obj.GetProperty("id"));
  286. courseExtobj.name = Convert.ToString(obj.GetProperty("name"));
  287. courseExtobj.scope = Convert.ToString(obj.GetProperty("scope"));
  288. List<object> classes = new List<object>();
  289. if (obj.TryGetProperty("schedule", out JsonElement schedule))
  290. {
  291. foreach (var scheduleobj in schedule.EnumerateArray())
  292. {
  293. dynamic classExtobj = new ExpandoObject();
  294. classExtobj.id = null;
  295. classExtobj.code = null;
  296. classExtobj.teacher = null;
  297. if (scheduleobj.TryGetProperty("teacherId", out JsonElement teacherId) && !string.IsNullOrWhiteSpace(Convert.ToString(teacherId)))
  298. {
  299. await foreach (var teaitem in client.GetContainer(Constant.TEAMModelOS, "Teacher").GetItemQueryStreamIterator(queryText: $"SELECT c.id, c.name FROM c WHERE c.id = '{teacherId}'", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("Base") }))
  300. {
  301. var jsontea = await JsonDocument.ParseAsync(teaitem.ContentStream);
  302. foreach (var teaobj in jsontea.RootElement.GetProperty("Documents").EnumerateArray())
  303. {
  304. classExtobj.teacher = new ExpandoObject();
  305. classExtobj.teacher = teaobj.ToObject<object>();
  306. }
  307. }
  308. }
  309. classExtobj.scope = "private";
  310. int stuCount = 0;
  311. string stuListId = Convert.ToString(scheduleobj.GetProperty("stulist"));
  312. classExtobj.stuListId = stuListId;
  313. await foreach (var stuitem in client.GetContainer(Constant.TEAMModelOS, "Teacher").GetItemQueryStreamIterator(queryText: $"SELECT c.id, c.name, c.tcount, c.scount FROM c WHERE c.id = '{stuListId}'", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("GroupList") }))
  314. {
  315. //取得學生總數
  316. var jsonstu = await JsonDocument.ParseAsync(stuitem.ContentStream);
  317. foreach (var stuobj in jsonstu.RootElement.GetProperty("Documents").EnumerateArray())
  318. {
  319. classExtobj.name = stuobj.GetProperty("name");
  320. stuCount += stuobj.GetProperty("scount").GetInt32();
  321. stuCount += stuobj.GetProperty("tcount").GetInt32();
  322. }
  323. }
  324. classExtobj.stuCnt = stuCount;
  325. classExtobj.grpCnt = 0;
  326. classExtobj.gradeId = null;
  327. classExtobj.year = 0;
  328. classes.Add(classExtobj);
  329. }
  330. }
  331. courseExtobj.classes = classes;
  332. courses.Add(courseExtobj);
  333. }
  334. }
  335. }
  336. //取得老師個人評測
  337. List<object> exams = new List<object>();
  338. //取得評測ID List (HiTeach只取source='1'(課中评量) && progress = 'going'(進行中))
  339. List<string> examIdList = new List<string>();
  340. await foreach (var exam in client.GetContainer(Constant.TEAMModelOS, "Common").GetItemQueryStreamIterator(queryText: $"SELECT c.id FROM c where (c.status<>404 or IS_DEFINED(c.status) = false ) and c.source = '1' AND c.progress = 'going'", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Exam-{id}") }))
  341. {
  342. using var json = await JsonDocument.ParseAsync(exam.ContentStream);
  343. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  344. {
  345. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  346. {
  347. examIdList.Add(obj.GetProperty("id").GetString());
  348. }
  349. }
  350. }
  351. //取得有作答的評測班級
  352. Dictionary<string, List<string>> examClassFinDic = new Dictionary<string, List<string>>();
  353. await foreach (var exam in client.GetContainer(Constant.TEAMModelOS, "Common").GetItemQueryStreamIterator(queryText: $"SELECT c.examId, c.info.id as classId FROM c where (c.status<>404 or IS_DEFINED(c.status) = false ) and ARRAY_CONTAINS({JsonSerializer.Serialize(examIdList)}, c.examId) AND c.progress=true", requestOptions: new QueryRequestOptions() { }))
  354. {
  355. var jsonecr = await JsonDocument.ParseAsync(exam.ContentStream);
  356. if (jsonecr.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  357. {
  358. foreach (var obj in jsonecr.RootElement.GetProperty("Documents").EnumerateArray())
  359. {
  360. string examId = obj.GetProperty("examId").ToString();
  361. string classId = obj.GetProperty("classId").ToString();
  362. if (examClassFinDic.ContainsKey(examId) && !examClassFinDic[examId].Contains(classId))
  363. {
  364. examClassFinDic[examId].Add(classId);
  365. }
  366. else
  367. {
  368. List<string> classIdList = new List<string>();
  369. classIdList.Add(classId);
  370. examClassFinDic.Add(examId, classIdList);
  371. }
  372. }
  373. }
  374. }
  375. //取得評測資料
  376. await foreach (var exam in client.GetContainer(Constant.TEAMModelOS, "Common").GetItemQueryStreamIterator(queryText: $"SELECT c.code, c.id, c.name, c.createTime, c.startTime, c.endTime ,c.year, c.source, c.type, c.progress, c.stuCount, c.scope, c.owner, c.period, c.grades, c.subjects, c.classes, c.stuLists, ARRAY(SELECT p.id, p.code, p.name, p.blob, p.scope FROM p IN c.papers) AS papers FROM c WHERE ARRAY_CONTAINS({JsonSerializer.Serialize(examIdList)}, c.id)", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Exam-{id}") }))
  377. {
  378. var jsonex = await JsonDocument.ParseAsync(exam.ContentStream);
  379. if (jsonex.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  380. {
  381. foreach (var obj in jsonex.RootElement.GetProperty("Documents").EnumerateArray())
  382. {
  383. dynamic examExtobj = new ExpandoObject();
  384. string examId = obj.GetProperty("id").GetString();
  385. examExtobj.code = obj.GetProperty("code");
  386. examExtobj.id = examId;
  387. examExtobj.name = obj.GetProperty("name");
  388. examExtobj.createTime = obj.GetProperty("createTime");
  389. examExtobj.startTime = obj.GetProperty("startTime");
  390. examExtobj.endTime = obj.GetProperty("endTime");
  391. examExtobj.year = obj.GetProperty("year");
  392. examExtobj.source = obj.GetProperty("source");
  393. examExtobj.type = obj.GetProperty("type");
  394. examExtobj.progress = obj.GetProperty("progress");
  395. examExtobj.stuCount = obj.GetProperty("stuCount");
  396. examExtobj.scope = obj.GetProperty("scope");
  397. examExtobj.owner = obj.GetProperty("owner");
  398. examExtobj.period = obj.GetProperty("period");
  399. examExtobj.grades = obj.GetProperty("grades");
  400. examExtobj.subjects = obj.GetProperty("subjects");
  401. examExtobj.classes = (obj.TryGetProperty("classes", out JsonElement classes)) ? classes.ToObject<List<string>>() : new List<string>();
  402. examExtobj.stuLists = (obj.TryGetProperty("stuLists", out JsonElement stuLists)) ? stuLists.ToObject<List<string>>() : new List<string>();
  403. examExtobj.finishClasses = (examClassFinDic.ContainsKey(examId)) ? examClassFinDic[examId] : new List<string>();
  404. examExtobj.papers = obj.GetProperty("papers");
  405. //examExtobj = obj.ToObject<object>();
  406. exams.Add(examExtobj);
  407. }
  408. }
  409. }
  410. //用户在线记录
  411. try
  412. {
  413. _ = _httpTrigger.RequestHttpTrigger(new { school = defaultschool, scope = $"{Constant.ScopeTeacher}", id = $"{id}", ip = $"{ip}", expire = 1 }, _option.Location, "online-record");
  414. }
  415. catch { }
  416. //取得Teacher Blob 容器位置及SAS
  417. var container = _azureStorage.GetBlobContainerClient(id);
  418. await container.CreateIfNotExistsAsync(PublicAccessType.None); //嘗試創建Teacher私有容器,如存在則不做任何事,保障容器一定存在
  419. var (blob_uri, blob_sas) = _azureStorage.GetBlobContainerSAS(id, BlobContainerSasPermissions.Write | BlobContainerSasPermissions.Read | BlobContainerSasPermissions.List | BlobContainerSasPermissions.Delete);
  420. var (blob_uri_read, blob_sas_read) = _azureStorage.GetBlobContainerSAS(id, BlobContainerSasPermissions.Read);
  421. var (blob_uri_write, blob_sas_write) = _azureStorage.GetBlobContainerSAS(id, BlobContainerSasPermissions.Write);
  422. RedisValue redisValue = default;
  423. long blobsize = 0;
  424. _azureRedis.GetRedisClient(8).HashGet($"Blob:Record", id);
  425. if (redisValue != default && !redisValue.IsNullOrEmpty)
  426. {
  427. JsonElement record = redisValue.ToString().ToObject<JsonElement>();
  428. if (record.TryGetInt64(out blobsize))
  429. {
  430. }
  431. }
  432. else
  433. {
  434. var messageBlob = new ServiceBusMessage(new { id = Guid.NewGuid().ToString(), progress = "update", name = $"{id}" }.ToJsonString()); ;
  435. messageBlob.ApplicationProperties.Add("name", "BlobRoot");
  436. var ActiveTask = _configuration.GetValue<string>("Azure:ServiceBus:ActiveTask");
  437. await _serviceBus.GetServiceBusClient().SendMessageAsync(ActiveTask, messageBlob);
  438. }
  439. double blobUsed = blobsize / 1073741824.0 ; //1073741824 1G
  440. return Ok(new { blob_uri, blob_sas, blob_sas_read, blob_sas_write, schools, defaultschool, courses, exams , blobTotal=total, blobUsed });
  441. }
  442. catch (Exception ex)
  443. {
  444. // await _dingDing.SendBotMsg($"CoreAPI2,{_option.Location},hiteach/GetTeacherInfo()\n{ex.Message}\n{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
  445. return BadRequest();
  446. }
  447. }
  448. [ProducesDefaultResponseType]
  449. [HttpPost("get-school-info")]
  450. public async Task<IActionResult> GetSchoolInfo(JsonElement requert)
  451. {
  452. try
  453. {
  454. string id_token = HttpContext.GetXAuth("IdToken");
  455. (string ip, string region) = await LoginService.LoginIp(HttpContext, _searcher);
  456. if (string.IsNullOrEmpty(id_token)) return BadRequest();
  457. if (!requert.TryGetProperty("school_code", out JsonElement school_code)) return BadRequest();
  458. var jwt = new JwtSecurityToken(id_token);
  459. if (!jwt.Payload.Iss.Equals("account.teammodel", StringComparison.Ordinal)) return BadRequest();
  460. var id = jwt.Payload.Sub;
  461. var client = _azureCosmos.GetCosmosClient();
  462. //取得學校學段、年級、科目、考試類型
  463. List<object> periods = new List<object>();
  464. List<object> grades = new List<object>();
  465. List<object> subjects = new List<object>();
  466. List<object> examTypes = new List<object>();
  467. int total = 0;
  468. var responsesch = await client.GetContainer(Constant.TEAMModelOS, "School").ReadItemStreamAsync(school_code.ToString(), new PartitionKey($"Base"));
  469. if (responsesch.Status == 200)
  470. {
  471. var jsons = await JsonDocument.ParseAsync(responsesch.ContentStream);
  472. if (jsons.RootElement.TryGetProperty("size", out JsonElement _size) && _size.ValueKind.Equals(JsonValueKind.Number)) {
  473. total = int.Parse($"{_size}");
  474. }
  475. if (jsons.RootElement.TryGetProperty("period", out JsonElement periodJobj))
  476. {
  477. foreach (var periodinfo in periodJobj.EnumerateArray())
  478. {
  479. dynamic periodExtobj = new ExpandoObject();
  480. periodExtobj.id = periodinfo.GetProperty("id");
  481. periodExtobj.name = periodinfo.GetProperty("name");
  482. periods.Add(periodExtobj);
  483. if (periodinfo.TryGetProperty("grades", out JsonElement gradesJobj))
  484. {
  485. int gradeIndex = 0;
  486. foreach (var gradeName in gradesJobj.EnumerateArray()) //2021-06-09 [式樣]grades改為array(string)
  487. {
  488. dynamic gradeExtobj = new ExpandoObject();
  489. gradeExtobj.id = gradeIndex.ToString();
  490. gradeExtobj.name = gradeName;
  491. gradeExtobj.periodId = periodinfo.GetProperty("id");
  492. grades.Add(gradeExtobj);
  493. gradeIndex++;
  494. }
  495. }
  496. if (periodinfo.TryGetProperty("subjects", out JsonElement subjectsJobj))
  497. {
  498. foreach (var subjectinfo in subjectsJobj.EnumerateArray())
  499. {
  500. dynamic subjectExtobj = new ExpandoObject();
  501. subjectExtobj.id = subjectinfo.GetProperty("id");
  502. subjectExtobj.name = subjectinfo.GetProperty("name");
  503. subjectExtobj.periodId = periodinfo.GetProperty("id");
  504. subjects.Add(subjectExtobj);
  505. }
  506. }
  507. if (periodinfo.TryGetProperty("analysis", out JsonElement periodanalysisJobj))
  508. {
  509. if (periodanalysisJobj.TryGetProperty("type", out JsonElement examTypeJobj))
  510. {
  511. foreach (var examType in examTypeJobj.EnumerateArray())
  512. {
  513. examTypes.Add(examType.ToObject<object>());
  514. }
  515. }
  516. }
  517. }
  518. }
  519. }
  520. else //無此學校資料
  521. {
  522. return BadRequest();
  523. }
  524. //該老師排定的學校課程
  525. List<object> courses = new List<object>();
  526. List<string> classIds = new List<string>(); //班級ID列表 篩選校園評測用
  527. List<string> groupIds = new List<string>(); //團體ID列表 篩選校園評測用
  528. var query = $"SELECT c.id, c.name, c.scope, c.subject, schedule.classId AS scheduleClassId, schedule.teacher AS scheduleTeacher, schedule.stulist AS scheduleStulist, schedule.time AS scheduleTime, schedule.notice AS scheduleNotice FROM c JOIN schedule IN c.schedule WHERE schedule.teacherId = '{id}'";
  529. //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}'";
  530. await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryStreamIterator(queryText: query, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Course-{school_code}") }))
  531. {
  532. var jsoncs = await JsonDocument.ParseAsync(item.ContentStream);
  533. if (jsoncs.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  534. {
  535. foreach (var obj in jsoncs.RootElement.GetProperty("Documents").EnumerateArray())
  536. {
  537. //班級
  538. string classIdNow = string.Empty;
  539. if (obj.TryGetProperty("scheduleClassId", out JsonElement scheduleClassId))
  540. {
  541. classIdNow = Convert.ToString(scheduleClassId);
  542. }
  543. dynamic classExtobj = new ExpandoObject();
  544. classExtobj.id = null;
  545. classExtobj.code = null;
  546. classExtobj.name = null;
  547. classExtobj.scope = null;
  548. classExtobj.gradeId = null;
  549. classExtobj.year = 0;
  550. classExtobj.teacher = null;
  551. classExtobj.stuListId = null;
  552. classExtobj.stuCnt = 0;
  553. classExtobj.grpCnt = 0;
  554. if (!string.IsNullOrWhiteSpace(classIdNow))
  555. {
  556. var querycl = $"SELECT c.code, c.id, c.name, c.teacher, c.gradeId, c.year FROM c WHERE c.id = '{classIdNow}'";
  557. await foreach (var itemcl in client.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryStreamIterator(queryText: querycl, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Class-{school_code}") }))
  558. {
  559. var jsoncl = await JsonDocument.ParseAsync(itemcl.ContentStream);
  560. foreach (var objcl in jsoncl.RootElement.GetProperty("Documents").EnumerateArray())
  561. {
  562. classIds.Add(Convert.ToString(objcl.GetProperty("id")));
  563. classExtobj.id = Convert.ToString(objcl.GetProperty("id"));
  564. classExtobj.code = Convert.ToString(objcl.GetProperty("code"));
  565. classExtobj.name = Convert.ToString(objcl.GetProperty("name"));
  566. //classExtobj.scope = Convert.ToString(objcl.GetProperty("scope")); //2021-07-16 廢除class.scope
  567. classExtobj.gradeId = (objcl.TryGetProperty("gradeId", out JsonElement gradeIdJson)) ? Convert.ToString(gradeIdJson) : null; //2021-06-01 [式樣]class去除gradeId,改以year代替
  568. classExtobj.year = (objcl.TryGetProperty("year", out JsonElement yearJson)) ? yearJson.GetInt32() : 0;
  569. classExtobj.teacher = objcl.GetProperty("teacher");
  570. classExtobj.stuCnt = 0;
  571. classExtobj.grpCnt = 0;
  572. var queryclstc = $"SELECT Count(1) AS stuCnt FROM c WHERE c.classId = '{classIdNow}'";
  573. await foreach (var itemclstc in client.GetContainer(Constant.TEAMModelOS, "Student").GetItemQueryStreamIterator(queryText: queryclstc, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Base-{school_code}") }))
  574. {
  575. var jsonclstc = await JsonDocument.ParseAsync(itemclstc.ContentStream);
  576. foreach (var objstc in jsonclstc.RootElement.GetProperty("Documents").EnumerateArray())
  577. {
  578. classExtobj.stuCnt = objstc.GetProperty("stuCnt").GetUInt32();
  579. }
  580. }
  581. var queryclgp = $"SELECT c.groupId FROM c WHERE c.classId = '{classIdNow}' AND IS_NULL(c.groupId)=false GROUP BY c.groupId";
  582. await foreach (var itemgp in client.GetContainer(Constant.TEAMModelOS, "Student").GetItemQueryStreamIterator(queryText: queryclgp, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Base-{school_code}") }))
  583. {
  584. var jsongp = await JsonDocument.ParseAsync(itemgp.ContentStream);
  585. if (jsongp.RootElement.TryGetProperty("_count", out JsonElement gpcount) && gpcount.GetUInt32() > 0)
  586. {
  587. classExtobj.grpCnt = gpcount.GetUInt32();
  588. }
  589. }
  590. }
  591. }
  592. }
  593. var stuListId = obj.GetProperty("scheduleStulist");
  594. if (!stuListId.ValueKind.Equals(JsonValueKind.Null) && !string.IsNullOrWhiteSpace(stuListId.GetString()))
  595. {
  596. classExtobj.stuListId = Convert.ToString(stuListId);
  597. await foreach (var stuitem in client.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryStreamIterator(queryText: $"SELECT c.code, c.id, c.name, c.year, c.tcount, c.scount FROM c WHERE c.id = '{stuListId}'", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"GroupList-{school_code}") }))
  598. {
  599. var jsonstu = await JsonDocument.ParseAsync(stuitem.ContentStream);
  600. foreach (var stuobj in jsonstu.RootElement.GetProperty("Documents").EnumerateArray())
  601. {
  602. groupIds.Add(Convert.ToString(stuobj.GetProperty("id")));
  603. classExtobj.id = Convert.ToString(stuobj.GetProperty("id"));
  604. classExtobj.code = Convert.ToString(stuobj.GetProperty("code"));
  605. classExtobj.name = Convert.ToString(stuobj.GetProperty("name"));
  606. classExtobj.gradeId = null;
  607. classExtobj.year = (stuobj.TryGetProperty("year", out JsonElement yearJson)) ? yearJson.GetInt32() : 0;
  608. classExtobj.teacher = null;
  609. classExtobj.stuCnt += stuobj.GetProperty("tcount").GetInt32();
  610. classExtobj.stuCnt += stuobj.GetProperty("scount").GetInt32();
  611. }
  612. }
  613. }
  614. //課程
  615. //var scheduleTeacherInfo = obj.GetProperty("scheduleTeacher");
  616. string courseIdNow = obj.GetProperty("id").ToString();
  617. var courseExist = courses.Where((dynamic x) => x.id == courseIdNow).FirstOrDefault();
  618. if (courseExist == null)
  619. {
  620. dynamic courseExtobj = new ExpandoObject();
  621. courseExtobj.id = courseIdNow;
  622. courseExtobj.name = obj.GetProperty("name").ToString();
  623. courseExtobj.scope = obj.GetProperty("scope").ToString();
  624. courseExtobj.classes = new List<object>();
  625. courseExtobj.subject = obj.GetProperty("subject");
  626. //classExtobj.teacher = scheduleTeacherInfo;
  627. if (!string.IsNullOrWhiteSpace(classExtobj.id))
  628. {
  629. courseExtobj.classes.Add(classExtobj);
  630. }
  631. courses.Add(courseExtobj);
  632. }
  633. else
  634. {
  635. //classExtobj.teacher = scheduleTeacherInfo;
  636. if (!string.IsNullOrWhiteSpace(classExtobj.id))
  637. {
  638. courseExist.classes.Add(classExtobj);
  639. }
  640. }
  641. }
  642. }
  643. }
  644. //取得校園評測 (HiTeach只取source='1'(課中评量) && progress = 'going'(進行中)) && 排定的學校課程班級
  645. List<object> exams = new List<object>();
  646. string classSqlString = "";
  647. if (classIds.Count > 0 || groupIds.Count > 0)
  648. {
  649. if (classIds.Count > 0)
  650. {
  651. foreach (string classId in classIds)
  652. {
  653. if (!string.IsNullOrWhiteSpace(classSqlString))
  654. {
  655. classSqlString += " OR ";
  656. }
  657. classSqlString += $"ARRAY_CONTAINS(c.classes, '{classId}')";
  658. }
  659. }
  660. if (groupIds.Count > 0)
  661. {
  662. foreach (string groupId in groupIds)
  663. {
  664. if (!string.IsNullOrWhiteSpace(classSqlString))
  665. {
  666. classSqlString += " OR ";
  667. }
  668. classSqlString += $"ARRAY_CONTAINS(c.classes, '{groupId}')";
  669. }
  670. }
  671. }
  672. else //無分配任何課程教室,校園評測不取
  673. {
  674. classSqlString += "c.id = '0'";
  675. }
  676. classSqlString = $"AND ({classSqlString})";
  677. //取得評測ID List
  678. List<string> examIdList = new List<string>();
  679. await foreach (var exam in client.GetContainer(Constant.TEAMModelOS, "Common").GetItemQueryStreamIterator(queryText: $"SELECT c.id FROM c WHERE c.source = '1' AND c.progress = 'going' {classSqlString}", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Exam-{school_code}") }))
  680. {
  681. using var json = await JsonDocument.ParseAsync(exam.ContentStream);
  682. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  683. {
  684. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  685. {
  686. examIdList.Add(obj.GetProperty("id").GetString());
  687. }
  688. }
  689. }
  690. //取得有作答的評測班級
  691. Dictionary<string, List<string>> examClassFinDic = new Dictionary<string, List<string>>();
  692. await foreach (var exam in client.GetContainer(Constant.TEAMModelOS, "Common").GetItemQueryStreamIterator(queryText: $"SELECT c.examId, c.info.id as classId FROM c WHERE ARRAY_CONTAINS({JsonSerializer.Serialize(examIdList)}, c.examId) AND c.progress=true", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"ExamClassResult-{school_code}") }))
  693. {
  694. var jsonecr = await JsonDocument.ParseAsync(exam.ContentStream);
  695. if (jsonecr.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  696. {
  697. foreach (var obj in jsonecr.RootElement.GetProperty("Documents").EnumerateArray())
  698. {
  699. string examId = obj.GetProperty("examId").ToString();
  700. string classId = obj.GetProperty("classId").ToString();
  701. if (examClassFinDic.ContainsKey(examId) && !examClassFinDic[examId].Contains(classId))
  702. {
  703. examClassFinDic[examId].Add(classId);
  704. }
  705. else
  706. {
  707. List<string> classIdList = new List<string>();
  708. classIdList.Add(classId);
  709. examClassFinDic.Add(examId, classIdList);
  710. }
  711. }
  712. }
  713. }
  714. //取得評測資料
  715. await foreach (var exam in client.GetContainer(Constant.TEAMModelOS, "Common").GetItemQueryStreamIterator(queryText: $"SELECT c.code, c.id, c.name, c.createTime, c.startTime, c.endTime ,c.year, c.source, c.type, c.progress, c.stuCount, c.scope, c.owner, c.period, c.grades, c.subjects, c.classes, c.stuLists, ARRAY(SELECT p.id, p.code, p.name, p.blob, p.scope FROM p IN c.papers) AS papers FROM c WHERE ARRAY_CONTAINS({JsonSerializer.Serialize(examIdList)}, c.id)", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Exam-{school_code}") }))
  716. {
  717. var jsonex = await JsonDocument.ParseAsync(exam.ContentStream);
  718. if (jsonex.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  719. {
  720. foreach (var obj in jsonex.RootElement.GetProperty("Documents").EnumerateArray())
  721. {
  722. dynamic examExtobj = new ExpandoObject();
  723. string examId = obj.GetProperty("id").GetString();
  724. examExtobj.code = obj.GetProperty("code");
  725. examExtobj.id = examId;
  726. examExtobj.name = obj.GetProperty("name");
  727. examExtobj.createTime = obj.GetProperty("createTime");
  728. examExtobj.startTime = obj.GetProperty("startTime");
  729. examExtobj.endTime = obj.GetProperty("endTime");
  730. examExtobj.year = obj.GetProperty("year");
  731. examExtobj.source = obj.GetProperty("source");
  732. examExtobj.type = obj.GetProperty("type");
  733. examExtobj.progress = obj.GetProperty("progress");
  734. examExtobj.stuCount = obj.GetProperty("stuCount");
  735. examExtobj.scope = obj.GetProperty("scope");
  736. examExtobj.owner = obj.GetProperty("owner");
  737. examExtobj.period = obj.GetProperty("period");
  738. examExtobj.grades = obj.GetProperty("grades");
  739. examExtobj.subjects = obj.GetProperty("subjects");
  740. examExtobj.classes = (obj.TryGetProperty("classes", out JsonElement classes)) ? classes.ToObject<List<string>>() : new List<string>();
  741. examExtobj.stuLists = (obj.TryGetProperty("stuLists", out JsonElement stuLists)) ? stuLists.ToObject<List<string>>() : new List<string>();
  742. examExtobj.finishClasses = (examClassFinDic.ContainsKey(examId)) ? examClassFinDic[examId] : new List<string>();
  743. examExtobj.papers = obj.GetProperty("papers");
  744. //examExtobj = obj.ToObject<object>();
  745. exams.Add(examExtobj);
  746. }
  747. }
  748. }
  749. //用户在线记录
  750. try
  751. {
  752. _ = _httpTrigger.RequestHttpTrigger(new { school = school_code.GetString(), scope = $"{Constant.ScopeTeacher}", id = $"{id}", ip = $"{ip}", expire = 1 }, _option.Location, "online-record");
  753. }
  754. catch { }
  755. //取得School Blob 容器位置及SAS
  756. string school_code_blob = school_code.GetString().ToLower();
  757. var (blob_uri, blob_sas) = _azureStorage.GetBlobContainerSAS(school_code_blob, BlobContainerSasPermissions.Read | BlobContainerSasPermissions.List | BlobContainerSasPermissions.Write); //讀列
  758. var (blob_uri_read, blob_sas_read) = _azureStorage.GetBlobContainerSAS(school_code_blob, BlobContainerSasPermissions.Read); //讀
  759. var (blob_uri_write, blob_sas_write) = _azureStorage.GetBlobContainerSAS(school_code_blob, BlobContainerSasPermissions.Write); //寫
  760. RedisValue redisValue = default;
  761. long teach = 0;
  762. long blobsize = 0;
  763. _azureRedis.GetRedisClient(8).HashGet($"Blob:Record", $"{school_code}");
  764. if (redisValue != default && !redisValue.IsNullOrEmpty)
  765. {
  766. JsonElement record = redisValue.ToString().ToObject<JsonElement>();
  767. if (record.TryGetInt64(out blobsize))
  768. {
  769. }
  770. }
  771. else
  772. {
  773. var messageBlob = new ServiceBusMessage(new { id = Guid.NewGuid().ToString(), progress = "update", name = $"{school_code}" }.ToJsonString()); ;
  774. messageBlob.ApplicationProperties.Add("name", "BlobRoot");
  775. var ActiveTask = _configuration.GetValue<string>("Azure:ServiceBus:ActiveTask");
  776. await _serviceBus.GetServiceBusClient().SendMessageAsync(ActiveTask, messageBlob);
  777. }
  778. //计算分配给学校教师的空间
  779. await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryStreamIterator(queryText: $"SELECT sum(c.size) as size FROM c ",
  780. requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Teacher-{school_code}") }))
  781. {
  782. var json = await JsonDocument.ParseAsync(item.ContentStream);
  783. foreach (var elmt in json.RootElement.GetProperty("Documents").EnumerateArray())
  784. {
  785. if (elmt.TryGetProperty("size", out JsonElement _size) && _size.ValueKind.Equals(JsonValueKind.Number))
  786. {
  787. teach = _size.GetInt32();
  788. break;
  789. }
  790. }
  791. }
  792. double blobUsed = blobsize / 1073741824.0 + teach; //1073741824 1G
  793. return Ok(new { blob_uri, blob_sas, blob_sas_read, blob_sas_write, periods, grades, subjects, courses, examTypes, exams, blobTotal = total, blobUsed});
  794. }
  795. catch (Exception ex)
  796. {
  797. // await _dingDing.SendBotMsg($"CoreAPI2,{_option.Location},hiteach/GetTeacherInfo()\n{ex.Message}\n{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
  798. return BadRequest();
  799. }
  800. }
  801. //取得試卷
  802. [ProducesDefaultResponseType]
  803. [HttpPost("get-paper")]
  804. public async Task<IActionResult> GetPaperList(JsonElement request)
  805. {
  806. try
  807. {
  808. //Header驗證
  809. string id_token = HttpContext.GetXAuth("IdToken");
  810. if (string.IsNullOrEmpty(id_token)) return BadRequest();
  811. var jwt = new JwtSecurityToken(id_token);
  812. if (!jwt.Payload.Iss.Equals("account.teammodel", StringComparison.Ordinal)) return BadRequest();
  813. var id = jwt.Payload.Sub;
  814. var client = _azureCosmos.GetCosmosClient();
  815. //參數
  816. if (!request.TryGetProperty("grant_type", out JsonElement grant_type)) return BadRequest();
  817. string partitionid = string.Empty;
  818. string container = string.Empty;
  819. if (grant_type.ToString().Equals("school"))
  820. {
  821. if (!request.TryGetProperty("school_code", out JsonElement school_code_json))
  822. {
  823. return BadRequest();
  824. }
  825. else
  826. {
  827. partitionid = school_code_json.ToString();
  828. container = "School";
  829. }
  830. }
  831. else
  832. {
  833. partitionid = id.ToString();
  834. container = "Teacher";
  835. }
  836. //SQL文
  837. List<object> papers = new List<object>();
  838. string queryWhere = " WHERE 1=1 ";
  839. string queryOption = string.Empty;
  840. //學段
  841. if (request.TryGetProperty("periodId", out JsonElement periodId) && container.Equals("School"))
  842. {
  843. queryWhere += $" AND c.periodId = '{periodId}'";
  844. }
  845. //年級
  846. if (request.TryGetProperty("gradeId", out JsonElement gradeIds) && gradeIds.GetArrayLength() > 0 && container.Equals("School"))
  847. {
  848. string queryOptionForGrade = string.Empty;
  849. for (int i = 0; i < gradeIds.GetArrayLength(); i++)
  850. {
  851. if (!string.IsNullOrWhiteSpace(queryOptionForGrade))
  852. {
  853. queryOptionForGrade += " OR ";
  854. }
  855. queryOptionForGrade += $" ARRAY_CONTAINS(c.gradeIds, '{gradeIds[i]}', true)";
  856. }
  857. queryWhere += $" AND ( {queryOptionForGrade} )";
  858. }
  859. //科目ID
  860. if (request.TryGetProperty("subjectId", out JsonElement subjectId) && container.Equals("School"))
  861. {
  862. queryWhere += $" AND c.subjectId = '{subjectId}'";
  863. }
  864. //科目名稱
  865. if (request.TryGetProperty("subjectName", out JsonElement subjectName))
  866. {
  867. queryWhere += $" AND c.subjectName = '{subjectName}'";
  868. }
  869. //試卷ID
  870. if (request.TryGetProperty("id", out JsonElement paperId))
  871. {
  872. queryWhere += $" AND c.id = '{paperId}'";
  873. }
  874. int perpage = 0; //每頁幾條
  875. if (request.TryGetProperty("perpage", out JsonElement perpage_json)) perpage = perpage_json.GetInt32();
  876. int page = 0; //目前第幾頁
  877. if (request.TryGetProperty("page", out JsonElement page_json))
  878. {
  879. if (page_json.GetInt32() > 0)
  880. {
  881. page = page_json.GetInt32() - 1;
  882. }
  883. }
  884. string order = "createTime"; //排序項目
  885. if (request.TryGetProperty("order", out JsonElement order_json))
  886. {
  887. if (order_json.ToString().Equals("useCount"))
  888. {
  889. order = order_json.ToString();
  890. }
  891. }
  892. queryOption += $" Order By c." + order + " DESC ";
  893. if (perpage > 0)
  894. {
  895. queryOption += $" OFFSET {perpage * page} LIMIT {perpage}";
  896. }
  897. //資料取得
  898. await foreach (var item in client.GetContainer(Constant.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, c.scope From c {queryWhere + queryOption}", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Paper-{partitionid}") }))
  899. {
  900. using var json = await JsonDocument.ParseAsync(item.ContentStream);
  901. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  902. {
  903. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  904. {
  905. papers.Add(obj.ToObject<object>());
  906. }
  907. }
  908. }
  909. //總件數
  910. int totalCount = 0;
  911. await foreach (var item in client.GetContainer(Constant.TEAMModelOS, container).GetItemQueryStreamIterator(queryText: $"SELECT VALUE COUNT(1) From c {queryWhere}", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Paper-{partitionid}") }))
  912. {
  913. using var json = await JsonDocument.ParseAsync(item.ContentStream);
  914. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  915. {
  916. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  917. {
  918. totalCount = obj.GetInt32();
  919. }
  920. }
  921. }
  922. return Ok(new { papers, totalCount });
  923. }
  924. catch (Exception ex)
  925. {
  926. return BadRequest();
  927. }
  928. }
  929. //取得試題
  930. [ProducesDefaultResponseType]
  931. [HttpPost("get-item")]
  932. public async Task<IActionResult> GetItemList(JsonElement request)
  933. {
  934. try
  935. {
  936. //Header驗證
  937. string id_token = HttpContext.GetXAuth("IdToken");
  938. if (string.IsNullOrEmpty(id_token)) return BadRequest();
  939. var jwt = new JwtSecurityToken(id_token);
  940. if (!jwt.Payload.Iss.Equals("account.teammodel", StringComparison.Ordinal)) return BadRequest();
  941. var id = jwt.Payload.Sub;
  942. var client = _azureCosmos.GetCosmosClient();
  943. //參數
  944. if (!request.TryGetProperty("grant_type", out JsonElement grant_type)) return BadRequest();
  945. string partitionid = string.Empty;
  946. string container = string.Empty;
  947. if (grant_type.ToString().Equals("school"))
  948. {
  949. if (!request.TryGetProperty("school_code", out JsonElement school_code_json))
  950. {
  951. return BadRequest();
  952. }
  953. else
  954. {
  955. partitionid = school_code_json.ToString();
  956. container = "School";
  957. }
  958. }
  959. else
  960. {
  961. partitionid = id.ToString();
  962. container = "Teacher";
  963. }
  964. //SQL文
  965. List<object> items = new List<object>();
  966. string queryWhere = " WHERE 1=1 ";
  967. string queryOption = string.Empty;
  968. //學段
  969. if (request.TryGetProperty("periodId", out JsonElement periodId) && container.Equals("School"))
  970. {
  971. queryWhere += $" AND c.periodId = '{periodId}'";
  972. }
  973. //年級
  974. if (request.TryGetProperty("gradeId", out JsonElement gradeIds) && gradeIds.GetArrayLength() > 0 && container.Equals("School"))
  975. {
  976. string queryOptionForGrade = string.Empty;
  977. for (int i = 0; i < gradeIds.GetArrayLength(); i++)
  978. {
  979. if (!string.IsNullOrWhiteSpace(queryOptionForGrade))
  980. {
  981. queryOptionForGrade += " OR ";
  982. }
  983. queryOptionForGrade += $" ARRAY_CONTAINS(c.gradeIds, '{gradeIds[i]}', true)";
  984. }
  985. queryWhere += $" AND ( {queryOptionForGrade} )";
  986. }
  987. //科目ID
  988. if (request.TryGetProperty("subjectId", out JsonElement subjectId) && container.Equals("school"))
  989. {
  990. queryWhere += $" AND c.subjectId = '{subjectId}'";
  991. }
  992. //科目名稱
  993. if (request.TryGetProperty("subjectName", out JsonElement subjectName))
  994. {
  995. queryWhere += $" AND c.subjectName = '{subjectName}'";
  996. }
  997. //題型
  998. int dummy = 0;
  999. if (request.TryGetProperty("type", out JsonElement type))
  1000. {
  1001. queryWhere += $" AND c.type = '{type}'";
  1002. }
  1003. //難度
  1004. dummy = 0;
  1005. if (request.TryGetProperty("level", out JsonElement level) && int.TryParse(level.ToString(), out dummy))
  1006. {
  1007. queryWhere += $" AND c.level = {level}";
  1008. }
  1009. //層次
  1010. if (request.TryGetProperty("field", out JsonElement field) && int.TryParse(field.ToString(), out dummy))
  1011. {
  1012. queryWhere += $" AND c.field = {field}";
  1013. }
  1014. int perpage = 0; //每頁幾條
  1015. if (request.TryGetProperty("perpage", out JsonElement perpage_json)) perpage = perpage_json.GetInt32();
  1016. int page = 0; //目前第幾頁
  1017. if (request.TryGetProperty("page", out JsonElement page_json))
  1018. {
  1019. if (page_json.GetInt32() > 0)
  1020. {
  1021. page = page_json.GetInt32() - 1;
  1022. }
  1023. }
  1024. string order = "createTime"; //排序項目
  1025. if (request.TryGetProperty("order", out JsonElement order_json))
  1026. {
  1027. if (order_json.ToString().Equals("useCount"))
  1028. {
  1029. order = order_json.ToString();
  1030. }
  1031. }
  1032. queryOption += $" Order By c." + order + " DESC ";
  1033. if (perpage > 0)
  1034. {
  1035. queryOption += $" OFFSET {perpage * page} LIMIT {perpage}";
  1036. }
  1037. //資料取得
  1038. await foreach (var item in client.GetContainer(Constant.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}") }))
  1039. {
  1040. using var json = await JsonDocument.ParseAsync(item.ContentStream);
  1041. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  1042. {
  1043. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  1044. {
  1045. items.Add(obj.ToObject<object>());
  1046. }
  1047. }
  1048. }
  1049. //總件數
  1050. int totalCount = 0;
  1051. await foreach (var item in client.GetContainer(Constant.TEAMModelOS, container).GetItemQueryStreamIterator(queryText: $"SELECT VALUE COUNT(1) From c {queryWhere}", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Item-{partitionid}") }))
  1052. {
  1053. using var json = await JsonDocument.ParseAsync(item.ContentStream);
  1054. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  1055. {
  1056. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  1057. {
  1058. totalCount = obj.GetInt32();
  1059. }
  1060. }
  1061. }
  1062. return Ok(new { items, totalCount });
  1063. }
  1064. catch (Exception ex)
  1065. {
  1066. return BadRequest();
  1067. }
  1068. }
  1069. //取得知識點
  1070. [ProducesDefaultResponseType]
  1071. [HttpPost("get-knowledge")]
  1072. public async Task<IActionResult> GetKnowledgePointList(JsonElement request)
  1073. {
  1074. var client = _azureCosmos.GetCosmosClient();
  1075. if (!request.TryGetProperty("school_code", out JsonElement school_code)) return BadRequest();
  1076. //知識點
  1077. List<object> points = new List<object>();
  1078. await foreach (var item in client.GetContainer(Constant.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}") }))
  1079. {
  1080. using var json = await JsonDocument.ParseAsync(item.ContentStream);
  1081. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  1082. {
  1083. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  1084. {
  1085. points.Add(obj.ToObject<object>());
  1086. }
  1087. }
  1088. }
  1089. return Ok(points);
  1090. }
  1091. //取得課綱
  1092. [ProducesDefaultResponseType]
  1093. [HttpPost("get-syllabus")]
  1094. public async Task<IActionResult> GetSyllabusList(JsonElement request)
  1095. {
  1096. //Header驗證
  1097. string id_token = HttpContext.GetXAuth("IdToken");
  1098. if (string.IsNullOrEmpty(id_token)) return BadRequest();
  1099. var jwt = new JwtSecurityToken(id_token);
  1100. if (!jwt.Payload.Iss.Equals("account.teammodel", StringComparison.OrdinalIgnoreCase)) return BadRequest();
  1101. var id = jwt.Payload.Sub;
  1102. //參數驗證
  1103. if (!request.TryGetProperty("grant_type", out JsonElement grant_type)) return BadRequest();
  1104. string grantType = (grant_type.GetString().Equals("school")) ? "school" : "private";
  1105. JsonElement school_code = new JsonElement();
  1106. if (grantType == "school" && !request.TryGetProperty("school_code", out school_code)) return BadRequest();
  1107. string dataId = (grantType.Equals("school")) ? school_code.GetString() : id;
  1108. string container = (grantType.Equals("school")) ? "School" : "Teacher";
  1109. string periodId = (request.TryGetProperty("periodId", out JsonElement periodIdJobj)) ? periodIdJobj.GetString() : String.Empty;
  1110. string subjectId = (request.TryGetProperty("subjectId", out JsonElement subjectIdJobj)) ? subjectIdJobj.GetString() : String.Empty;
  1111. string volumeId = (request.TryGetProperty("volumeId", out JsonElement volumeIdJobj)) ? volumeIdJobj.GetString() : String.Empty;
  1112. string syllabusId = (request.TryGetProperty("syllabusId", out JsonElement syllabusIdJobj)) ? syllabusIdJobj.GetString() : String.Empty;
  1113. int display = (request.TryGetProperty("display", out JsonElement displayJobj)) ? displayJobj.GetInt32() : 0;
  1114. var client = _azureCosmos.GetCosmosClient();
  1115. //取得卷前置作業:有給syllabusId,取得該課綱的卷ID
  1116. string volumeIdFromSyllabusId = String.Empty;
  1117. if (!string.IsNullOrWhiteSpace(syllabusId))
  1118. {
  1119. await foreach (string volid in client.GetContainer(Constant.TEAMModelOS, container).GetItemQueryIterator<string>(queryText: $"SELECT value(c.volumeId) FROM c WHERE c.id = '{syllabusId}'", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Syllabus-{dataId}") }))
  1120. {
  1121. volumeIdFromSyllabusId = volid;
  1122. }
  1123. }
  1124. //取得卷
  1125. List<object> volumes = new List<object>();
  1126. List<Volume> volumeList = new List<Volume>();
  1127. List<string> volumeIdList = new List<string>();
  1128. string queryTextVol = String.Empty;
  1129. List<string> WhereVol = new List<string>();
  1130. if (!String.IsNullOrWhiteSpace(periodId)) WhereVol.Add($" c.periodId = '{periodId}' ");
  1131. if (!String.IsNullOrWhiteSpace(subjectId)) WhereVol.Add($" c.subjectId = '{subjectId}' ");
  1132. if (!String.IsNullOrWhiteSpace(volumeId)) WhereVol.Add($" c.id = '{volumeId}' ");
  1133. if (!String.IsNullOrWhiteSpace(volumeIdFromSyllabusId)) WhereVol.Add($" c.id = '{volumeIdFromSyllabusId}' ");
  1134. foreach (string Where in WhereVol)
  1135. {
  1136. queryTextVol += (String.IsNullOrWhiteSpace(queryTextVol)) ? $" WHERE {Where} " : $" AND {Where} ";
  1137. }
  1138. queryTextVol = "SELECT * FROM c " + queryTextVol;
  1139. await foreach (var itemv in client.GetContainer(Constant.TEAMModelOS, container).GetItemQueryStreamIterator(queryText: queryTextVol, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Volume-{dataId}") }))
  1140. {
  1141. var jsons = await JsonDocument.ParseAsync(itemv.ContentStream);
  1142. if (jsons.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  1143. {
  1144. foreach (var obj in jsons.RootElement.GetProperty("Documents").EnumerateArray())
  1145. {
  1146. Volume volExtobj = obj.ToObject<Volume>();
  1147. volumeList.Add(volExtobj);
  1148. volumeIdList.Add(volExtobj.id);
  1149. }
  1150. }
  1151. }
  1152. //取得課綱 ※display=1時不取任何課綱
  1153. List<SyllabusTreeNode> treeNodes = new List<SyllabusTreeNode>();
  1154. if (!display.Equals(1))
  1155. {
  1156. string queryTextSyl = $"SELECT value(c) FROM c WHERE ARRAY_CONTAINS({JsonSerializer.Serialize(volumeIdList)}, c.volumeId, true)";
  1157. if (!string.IsNullOrWhiteSpace(syllabusId)) queryTextSyl += $" AND c.id = '{syllabusId}'";
  1158. await foreach (Syllabus items in client.GetContainer(Constant.TEAMModelOS, container).GetItemQueryIterator<Syllabus>(queryText: queryTextSyl, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Syllabus-{dataId}") }))
  1159. {
  1160. List<SyllabusTree> trees = SyllabusService.ListToTree(items.children);
  1161. SyllabusTreeNode tree = new SyllabusTreeNode() { id = items.id, scope = items.scope, trees = trees, volumeId = items.volumeId, auth = items.auth, codeval = $"{id}" };
  1162. treeNodes.Add(tree);
  1163. }
  1164. }
  1165. //輸出結果
  1166. foreach (Volume volumeRow in volumeList)
  1167. {
  1168. dynamic volumeExtobj = new ExpandoObject();
  1169. volumeExtobj.periodId = volumeRow.periodId;
  1170. volumeExtobj.subjectId = volumeRow.subjectId;
  1171. volumeExtobj.volumeId = volumeRow.id;
  1172. volumeExtobj.gradeId = volumeRow.gradeId;
  1173. volumeExtobj.semesterId = volumeRow.semesterId;
  1174. volumeExtobj.name = volumeRow.name;
  1175. volumeExtobj.creatorId = volumeRow.creatorId;
  1176. volumeExtobj.creatorName = volumeRow.creatorName;
  1177. volumeExtobj.school = volumeRow.school;
  1178. volumeExtobj.scope = volumeRow.scope;
  1179. volumeExtobj.syllabusIds = volumeRow.syllabusIds; //課綱內容ID
  1180. volumeExtobj.auth = volumeRow.auth; //授權教師
  1181. List<SyllabusTreeNode> treesInVol = treeNodes.Where(t => t.volumeId == volumeRow.id).ToList();
  1182. volumeExtobj.syllabus = treesInVol;
  1183. volumes.Add(volumeExtobj);
  1184. }
  1185. return Ok(volumes);
  1186. }
  1187. //取得某班級的學生成員
  1188. [ProducesDefaultResponseType]
  1189. [HttpPost("get-students-list")]
  1190. public async Task<IActionResult> GetStudentsList(JsonElement request)
  1191. {
  1192. string id_token = HttpContext.GetXAuth("IdToken");
  1193. if (string.IsNullOrWhiteSpace(id_token)) return BadRequest();
  1194. var jwt = new JwtSecurityToken(id_token);
  1195. if (!jwt.Payload.Iss.Equals("account.teammodel", StringComparison.Ordinal)) return BadRequest();
  1196. var id = jwt.Payload.Sub;
  1197. if (!request.TryGetProperty("grant_type", out JsonElement grant_type)) return BadRequest();
  1198. request.TryGetProperty("class_code", out JsonElement class_code);
  1199. request.TryGetProperty("stulist_id", out JsonElement stulist_id);
  1200. string classId = Convert.ToString(class_code);
  1201. string stulist = Convert.ToString(stulist_id);
  1202. if (string.IsNullOrWhiteSpace(classId) && string.IsNullOrWhiteSpace(stulist)) return BadRequest();
  1203. request.TryGetProperty("school_code", out JsonElement school_code);
  1204. if (grant_type.GetString().Equals("school") && string.IsNullOrWhiteSpace(Convert.ToString(school_code))) return BadRequest();
  1205. var client = _azureCosmos.GetCosmosClient();
  1206. List<ClassStudents> students = new List<ClassStudents>();
  1207. Dictionary<string, string> irsDic = new Dictionary<string, string>(); //key:學生ID或TMID value:irs號碼
  1208. string container = (grant_type.GetString().Equals("school")) ? "School" : "Teacher";
  1209. List<string> listids = new List<string>();
  1210. if (!string.IsNullOrWhiteSpace(stulist))
  1211. {
  1212. listids.Add(stulist);
  1213. }
  1214. else if (!string.IsNullOrWhiteSpace(classId))
  1215. {
  1216. listids.Add(classId);
  1217. }
  1218. (List<RMember> memberList, List<RGroupList> groupList) = await GroupListService.GetStutmdidListids(_coreAPIHttpService, client, _dingDing, listids, $"{school_code}");
  1219. if (memberList.IsNotEmpty())
  1220. {
  1221. foreach (RMember member in memberList)
  1222. {
  1223. ClassStudents strdentRow = new ClassStudents();
  1224. strdentRow.nickname = member.nickname;
  1225. strdentRow.id = member.id;
  1226. strdentRow.name = member.name;
  1227. strdentRow.no = member.no;
  1228. strdentRow.schoolId = member.code;
  1229. strdentRow.groupId = member.groupId;
  1230. strdentRow.groupName = member.groupName;
  1231. strdentRow.irs = member.irs;
  1232. strdentRow.type = member.type;
  1233. students.Add(strdentRow);
  1234. }
  1235. }
  1236. #region 舊代碼 不使用
  1237. ////Case 1 取得stulist成員 (有stulist_id則優先取)
  1238. //if (grant_type.GetString().Equals("private") && string.IsNullOrWhiteSpace(stulist) && !string.IsNullOrWhiteSpace(classId)) //若private,且classId不為空,stulist為空,有可能HiTeach無法判別是若classId還是stulist
  1239. //{
  1240. // stulist = classId;
  1241. //}
  1242. //if (!string.IsNullOrWhiteSpace(stulist))
  1243. //{
  1244. // string pk = (grant_type.GetString() .Equals("school")) ? $"GroupList-{school_code}" : $"GroupList";
  1245. // var querystl = $"SELECT c.members FROM c WHERE c.id = '{stulist}'";
  1246. // Dictionary<string, List<string>> stuDic = new Dictionary<string, List<string>>();
  1247. // List<string> tmidList = new List<string>();
  1248. // await foreach (var item in client.GetContainer(Constant.TEAMModelOS, container).GetItemQueryStreamIterator(queryText: querystl, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey(pk) }))
  1249. // {
  1250. // var json = await JsonDocument.ParseAsync(item.ContentStream);
  1251. // if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  1252. // {
  1253. // foreach (var stuObj in json.RootElement.GetProperty("Documents").EnumerateArray())
  1254. // {
  1255. // foreach (var studentObj in stuObj.GetProperty("members").EnumerateArray())
  1256. // {
  1257. // string stuCode = studentObj.GetProperty("code").ToString();
  1258. // string stuId = studentObj.GetProperty("id").ToString();
  1259. // int stuType = studentObj.GetProperty("type").GetInt32();
  1260. // string irs = studentObj.GetProperty("irs").ToString();
  1261. // //取得學校班級學生ID表
  1262. // if (stuType.Equals(2))
  1263. // {
  1264. // if (stuDic.ContainsKey(stuCode) && !stuDic[stuCode].Contains(stuId))
  1265. // {
  1266. // if (!stuDic[stuCode].Contains(stuId))
  1267. // {
  1268. // stuDic[stuCode].Add(stuId);
  1269. // }
  1270. // }
  1271. // else
  1272. // {
  1273. // List<string> stuIdList = new List<string>();
  1274. // stuIdList.Add(stuId);
  1275. // stuDic.Add(stuCode, stuIdList);
  1276. // }
  1277. // }
  1278. // //取得TMID加入學生ID表
  1279. // else if(stuType.Equals(1) && !tmidList.Contains(stuId))
  1280. // {
  1281. // tmidList.Add(stuId);
  1282. // }
  1283. // //IRS號碼表 (irs無值不取)
  1284. // if (!irsDic.ContainsKey(stuId) && !string.IsNullOrWhiteSpace(irs))
  1285. // {
  1286. // irsDic.Add(stuId, irs);
  1287. // }
  1288. // }
  1289. // }
  1290. // }
  1291. // }
  1292. // //取得學校班級學生資料
  1293. // if (stuDic.Count > 0)
  1294. // {
  1295. // foreach (KeyValuePair<string, List<string>> stuDicRow in stuDic)
  1296. // {
  1297. // string stuCode = $"Base-{stuDicRow.Key}";
  1298. // List<string> stuIdList = stuDicRow.Value;
  1299. // var queryst = $"SELECT c.id, c.name, null AS no, c.schoolId, c.irs FROM c WHERE ARRAY_CONTAINS({JsonSerializer.Serialize(stuIdList)}, c.id)";
  1300. // await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "Student").GetItemQueryStreamIterator(queryText: queryst, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey(stuCode) }))
  1301. // {
  1302. // using var json = await JsonDocument.ParseAsync(item.ContentStream);
  1303. // if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  1304. // {
  1305. // foreach (var stuObj in json.RootElement.GetProperty("Documents").EnumerateArray())
  1306. // {
  1307. // students.Add(stuObj.ToObject<ClassStudents>());
  1308. // }
  1309. // }
  1310. // }
  1311. // }
  1312. // }
  1313. // //取得TMID用戶資料
  1314. // List<ClassStudents> tmids = new List<ClassStudents>();
  1315. // if (tmidList.Count > 0)
  1316. // {
  1317. // var querytmct = $"SELECT c.id, c.name, null AS no, null AS schoolId, c.irs FROM c WHERE ARRAY_CONTAINS({JsonSerializer.Serialize(tmidList)}, c.id)";
  1318. // await foreach (var itemtm in client.GetContainer(Constant.TEAMModelOS, "Student").GetItemQueryStreamIterator(queryText: querytmct, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Base") }))
  1319. // {
  1320. // using var json = await JsonDocument.ParseAsync(itemtm.ContentStream);
  1321. // if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  1322. // {
  1323. // foreach (var stuObj in json.RootElement.GetProperty("Documents").EnumerateArray())
  1324. // {
  1325. // tmids.Add(stuObj.ToObject<ClassStudents>());
  1326. // }
  1327. // //排序
  1328. // tmids = tmids.OrderBy(x => tmidList.IndexOf(x.id)).ToList();
  1329. // }
  1330. // }
  1331. // }
  1332. // students = students.Concat(tmids).ToList();
  1333. //}
  1334. ////Case 2 取得班級固定成員 條件:有school_code、有classId、無stulist
  1335. //if (!string.IsNullOrWhiteSpace(Convert.ToString(school_code)) && students.Count == 0 && !string.IsNullOrWhiteSpace(classId))
  1336. //{
  1337. // var query = $"SELECT c.id, c.name, c.no, c.schoolId, c.groupId, c.groupName, c.irs FROM c WHERE c.classId = '{classId}'";
  1338. // await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "Student").GetItemQueryStreamIterator(queryText: query, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Base-{school_code}") }))
  1339. // {
  1340. // using var jsonst = await JsonDocument.ParseAsync(item.ContentStream);
  1341. // if (jsonst.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  1342. // {
  1343. // foreach (var stuObj in jsonst.RootElement.GetProperty("Documents").EnumerateArray())
  1344. // {
  1345. // students.Add(stuObj.ToObject<ClassStudents>());
  1346. // }
  1347. // }
  1348. // }
  1349. //}
  1350. ////IRS號碼補足 邏輯:若irsDic數 = students數、全放入
  1351. //if(students.Count.Equals(irsDic.Count))
  1352. //{
  1353. // for (int i = 0; i < students.Count; i++)
  1354. // {
  1355. // if (irsDic.ContainsKey(students[i].id))
  1356. // {
  1357. // students[i].irs = irsDic[students[i].id];
  1358. // }
  1359. // }
  1360. //}
  1361. #endregion
  1362. //取得分組資訊
  1363. List<ClassGroups> groups = new List<ClassGroups>();
  1364. foreach (ClassStudents studentInfo in students)
  1365. {
  1366. if (!string.IsNullOrWhiteSpace(studentInfo.groupId))
  1367. {
  1368. ClassGroups groupRow = groups.Where(g => g.id == studentInfo.groupId).FirstOrDefault();
  1369. if (groupRow != null) //已有此group資訊 => 追加學生ID
  1370. {
  1371. if (!groupRow.studentIds.Contains(studentInfo.id))
  1372. {
  1373. groupRow.studentIds.Add(studentInfo.id);
  1374. }
  1375. }
  1376. else //無此group資訊
  1377. {
  1378. ClassGroups groupAdd = new ClassGroups();
  1379. groupAdd.id = studentInfo.groupId;
  1380. groupAdd.name = studentInfo.groupName;
  1381. groupAdd.studentIds = new List<string>();
  1382. groupAdd.studentIds.Add(studentInfo.id);
  1383. groups.Add(groupAdd);
  1384. }
  1385. }
  1386. }
  1387. return Ok(new { students, groups });
  1388. }
  1389. [ProducesDefaultResponseType]
  1390. [HttpPost("start-lesson")]
  1391. public async Task<IActionResult> StartLesson(JsonElement request)
  1392. {
  1393. //醍摩豆ID驗證
  1394. string teacherId = string.Empty;
  1395. string id_token = HttpContext.GetXAuth("IdToken");
  1396. if (!string.IsNullOrWhiteSpace(id_token))
  1397. {
  1398. var jwt = new JwtSecurityToken(id_token);
  1399. if (!jwt.Payload.Iss.Equals("account.teammodel", StringComparison.OrdinalIgnoreCase)) return BadRequest();
  1400. teacherId = jwt.Payload.Sub;
  1401. }
  1402. if (string.IsNullOrWhiteSpace(teacherId)) return BadRequest(); //無醍摩豆ID,BadRequest
  1403. //取得授課ID
  1404. string lesson_code = _snowflakeId.NextId().ToString();
  1405. //bool get_lesson_id = (string.IsNullOrWhiteSpace(Convert.ToString(lesson_id)) || Convert.ToString(lesson_id) != lesson_code) ? false : true;
  1406. //string tableName = "TeacherLesson";
  1407. return Ok(new { lesson_code });
  1408. }
  1409. //上傳評測結果
  1410. //錯誤代碼:error = 1001 message = "Paper blob copy failure."
  1411. // error = 1002 message = "Student answers blob upload failure."
  1412. [ProducesDefaultResponseType]
  1413. [HttpPost("upd-exam-result")]
  1414. public async Task<IActionResult> UploadExamResult(JsonElement request)
  1415. {
  1416. try
  1417. {
  1418. string message = "";
  1419. int error = 0;
  1420. string id_token = HttpContext.GetXAuth("IdToken");
  1421. if (string.IsNullOrWhiteSpace(id_token)) return BadRequest();
  1422. var jwt = new JwtSecurityToken(id_token);
  1423. if (!jwt.Payload.Iss.Equals("account.teammodel", StringComparison.Ordinal)) return BadRequest();
  1424. var id = jwt.Payload.Sub;
  1425. if (!request.TryGetProperty("exam", out JsonElement exam)) return BadRequest();
  1426. if (!request.TryGetProperty("examClassResult", out JsonElement examClassResult)) return BadRequest();
  1427. bool blobUploaded = (request.TryGetProperty("blobUploaded", out JsonElement blobUploadedJson)) ? blobUploadedJson.GetBoolean() : false;
  1428. bool recordSwitch = (request.TryGetProperty("recordSwitch", out JsonElement recordSwitchJson)) ? recordSwitchJson.GetBoolean() : false;
  1429. //ExamInfo ExamInfoFromReq = exam.ToObject<ExamInfo>();
  1430. string strExam = JsonSerializer.Serialize(exam);
  1431. if (strExam.Contains("\"publish\":\"0\""))
  1432. {
  1433. strExam = strExam.Replace("\"publish\":\"0\"", "\"publish\":0");
  1434. }
  1435. ExamInfo ExamInfoFromReq = JsonSerializer.Deserialize<ExamInfo>(strExam);
  1436. string examId = ExamInfoFromReq.id;
  1437. string excode = ExamInfoFromReq.code;
  1438. //ExamInfo dbExamInfo = exam.ToObject<ExamInfo>();
  1439. ExamInfo dbExamInfo = JsonSerializer.Deserialize<ExamInfo>(strExam);
  1440. var queryex = $"SELECT * FROM c WHERE c.id = '{examId}'";
  1441. await foreach (var itemex in _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Common").GetItemQueryStreamIterator(queryText: queryex, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"{excode}") }))
  1442. {
  1443. var jsonex = await JsonDocument.ParseAsync(itemex.ContentStream);
  1444. if (jsonex.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  1445. {
  1446. foreach (var obj in jsonex.RootElement.GetProperty("Documents").EnumerateArray())
  1447. {
  1448. string strExamDb = JsonSerializer.Serialize(obj);
  1449. if (strExamDb.Contains("\"publish\":\"0\""))
  1450. {
  1451. strExamDb = strExamDb.Replace("\"publish\":\"0\"", "\"publish\":0");
  1452. }
  1453. dbExamInfo = JsonSerializer.Deserialize<ExamInfo>(strExamDb);
  1454. //dbExamInfo = obj.ToObject<ExamInfo>();
  1455. }
  1456. }
  1457. }
  1458. if (string.IsNullOrWhiteSpace(dbExamInfo.id))
  1459. {
  1460. dbExamInfo = ExamInfoFromReq;
  1461. }
  1462. //Boolean isTestFlg = (_option.Location.Contains("Test") || _option.Location.Contains("Dep")) ? true : false;
  1463. //ExamInfo內容取得、調整 [2021-7-13 廢除,給予HiTeach學校Blob寫入權限,API不再對Blob做搬運]
  1464. //※規則 owner:"school" => 校園評測 "teacher" => 個人評測
  1465. //※規則 scope:"school" => 校本班級 "private" => 個人班級
  1466. //※規則 BLOB容器: scope:"school" => {學校ID}下 scope:"private" => {個人ID}下
  1467. string blobContainer = (!string.IsNullOrWhiteSpace(dbExamInfo.school) && dbExamInfo.scope.Equals("school")) ? dbExamInfo.school : id; //blob容器
  1468. //dbExamInfo.source = "1"; //評測來源固定為 1:課中評量(不應由API擅自變更)
  1469. //試卷List
  1470. List<Dictionary<string, string>> recordPaperInfo = new List<Dictionary<string, string>>();
  1471. int paperIndex = 0;
  1472. foreach (PaperSimple paperInfo in ExamInfoFromReq.papers)
  1473. {
  1474. string paperBlobPath = (!paperInfo.blob.EndsWith("/")) ? paperInfo.blob + "/" : paperInfo.blob;
  1475. paperBlobPath = (paperInfo.blob.StartsWith("/")) ? paperBlobPath.Remove(0, 1) : paperBlobPath;
  1476. string subjectId = dbExamInfo.subjects[paperIndex].id;
  1477. recordPaperInfo.Add(new Dictionary<string, string>() { { "id", paperInfo.id }, { "blob", paperBlobPath }, { "subjectId", subjectId }, { "itemcount", paperInfo.point.Count.ToString() } });
  1478. paperIndex++;
  1479. }
  1480. //取得課堂紀錄下的試卷資料(blob)、複製到評測紀錄下
  1481. bool paperDataCopyErrFlg = false; //試卷資料拷貝錯誤Flag true:拷貝錯誤 [2021-7-13 (測試站)不再對Blob做搬運 永為false]
  1482. //Blob搬運 (待HiTeach完善後刪除)
  1483. if (!blobUploaded)
  1484. {
  1485. foreach (Dictionary<string, string> recordPaperInfoDic in recordPaperInfo)
  1486. {
  1487. string targetScope = dbExamInfo.scope; //評測對象 school:校本班級 private:私人課程
  1488. var blobPrivateContainer = _azureStorage.GetBlobContainerClient(id);
  1489. string sourceBlobPath = recordPaperInfoDic["blob"];
  1490. string destBlobPath = $"exam/{dbExamInfo.id}/paper/{recordPaperInfoDic["subjectId"]}/"; //拷貝對象路徑 path:exam/{評測ID}/paper/{subjectID}/ ※2022-1-6 式樣變更[原]/paper/{試卷ID}/ [新]/paper/{subjectID}/
  1491. if (targetScope.Equals("school")) //校本
  1492. {
  1493. string schoolId = dbExamInfo.school;
  1494. var blobSchoolContainer = _azureStorage.GetBlobContainerClient(schoolId);
  1495. var sourceBlobContainer = (recordSwitch) ? blobSchoolContainer : blobPrivateContainer;
  1496. Azure.Pageable<BlobItem> sourceBlobs = sourceBlobContainer.GetBlobs(prefix: sourceBlobPath);
  1497. if (sourceBlobs.Count() > 0)
  1498. {
  1499. foreach (var blob in sourceBlobs)
  1500. {
  1501. var sourceFileBlob = sourceBlobContainer.GetBlobClient(blob.Name);
  1502. if (sourceFileBlob.Exists())
  1503. {
  1504. var sourceFileUri = sourceBlobContainer.GetBlobClient(blob.Name).Uri;
  1505. string fileName = blob.Name.Replace(sourceBlobPath, "");
  1506. string destBlobFilePath = $"{destBlobPath}{fileName}";
  1507. await blobSchoolContainer.GetBlobClient(destBlobFilePath).StartCopyFromUriAsync(sourceFileUri);
  1508. }
  1509. else
  1510. {
  1511. paperDataCopyErrFlg = true;
  1512. }
  1513. }
  1514. }
  1515. }
  1516. else //私人
  1517. {
  1518. var sourceBlobContainer = blobPrivateContainer;
  1519. Azure.Pageable<BlobItem> sourceBlobs = sourceBlobContainer.GetBlobs(prefix: sourceBlobPath);
  1520. if (sourceBlobs.Count() > 0)
  1521. {
  1522. foreach (var blob in sourceBlobs)
  1523. {
  1524. var sourceFileBlob = sourceBlobContainer.GetBlobClient(blob.Name);
  1525. if (sourceFileBlob.Exists())
  1526. {
  1527. var sourceFileUri = sourceBlobContainer.GetBlobClient(blob.Name).Uri;
  1528. string fileName = blob.Name.Replace(sourceBlobPath, "");
  1529. string destBlobFilePath = $"{destBlobPath}{fileName}";
  1530. await blobPrivateContainer.GetBlobClient(destBlobFilePath).StartCopyFromUriAsync(sourceFileUri);
  1531. }
  1532. else
  1533. {
  1534. paperDataCopyErrFlg = true;
  1535. }
  1536. }
  1537. }
  1538. }
  1539. //替換 Exam.papers.blob
  1540. PaperSimple paperInfoNow = dbExamInfo.papers.Where((PaperSimple p) => p.id == recordPaperInfoDic["id"]).FirstOrDefault();
  1541. string destBlobPathForDocument = (destBlobPath.EndsWith("/")) ? destBlobPath.Remove(destBlobPath.Length - 1, 1) : destBlobPath;
  1542. destBlobPathForDocument = (!destBlobPathForDocument.StartsWith("/")) ? "/" + destBlobPathForDocument : destBlobPathForDocument;
  1543. paperInfoNow.blob = destBlobPathForDocument;
  1544. }
  1545. }
  1546. //ExamClassResult內容調整
  1547. bool studentAnswerCopyErrFlg = false; //學生作答資料拷貝錯誤Flag true:拷貝錯誤 [2021-7-13 不再對Blob做搬運 永為false]
  1548. //Blob搬運 (待HiTeach完善後刪除)
  1549. if (!blobUploaded)
  1550. {
  1551. List<ExamClassResultStudentAnswerArrayOld> dbExamClassResultList = examClassResult.ToObject<List<ExamClassResultStudentAnswerArrayOld>>();
  1552. foreach (ExamClassResultStudentAnswerArrayOld examClassResultRow in dbExamClassResultList)
  1553. {
  1554. //以subjectId及classId(info.id)取得DB中的ExamClassResult
  1555. ExamClassResult examClassResultUpd = new ExamClassResult();
  1556. string exclcode = examClassResultRow.code;
  1557. string classId = examClassResultRow.info.id;
  1558. string subjectId = examClassResultRow.subjectId;
  1559. var querycr = $"SELECT * FROM c WHERE c.examId = '{examId}' AND c.info.id = '{classId}' AND c.subjectId = '{subjectId}'";
  1560. await foreach (var itemcr in _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Common").GetItemQueryStreamIterator(queryText: querycr, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"{exclcode}") }))
  1561. {
  1562. var jsontcr = await JsonDocument.ParseAsync(itemcr.ContentStream);
  1563. if (jsontcr.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  1564. {
  1565. foreach (var obj in jsontcr.RootElement.GetProperty("Documents").EnumerateArray())
  1566. {
  1567. examClassResultUpd = obj.ToObject<ExamClassResult>();
  1568. examClassResultUpd.progress = examClassResultRow.progress;
  1569. examClassResultUpd.studentIds = examClassResultRow.studentIds;
  1570. examClassResultUpd.studentAnswers = new List<List<string>>();
  1571. examClassResultUpd.studentScores = examClassResultRow.studentScores;
  1572. examClassResultUpd.status = examClassResultRow.status;
  1573. examClassResultUpd.sum = examClassResultRow.sum;
  1574. }
  1575. }
  1576. }
  1577. //無法取得既有ExamClassResult,新建
  1578. if (string.IsNullOrWhiteSpace(examClassResultUpd.id))
  1579. {
  1580. examClassResultUpd.pk = examClassResultRow.pk;
  1581. examClassResultUpd.code = examClassResultRow.code;
  1582. examClassResultUpd.id = examClassResultRow.id;
  1583. examClassResultUpd.school = examClassResultRow.school;
  1584. examClassResultUpd.examId = examClassResultRow.examId;
  1585. examClassResultUpd.subjectId = examClassResultRow.subjectId;
  1586. examClassResultUpd.gradeId = examClassResultRow.gradeId;
  1587. examClassResultUpd.year = examClassResultRow.year;
  1588. examClassResultUpd.info = examClassResultRow.info;
  1589. examClassResultUpd.progress = examClassResultRow.progress;
  1590. examClassResultUpd.studentIds = examClassResultRow.studentIds;
  1591. examClassResultUpd.studentAnswers = new List<List<string>>(); //學生作答Blob位置
  1592. examClassResultUpd.studentScores = examClassResultRow.studentScores;
  1593. examClassResultUpd.status = examClassResultRow.status;
  1594. examClassResultUpd.scope = examClassResultRow.scope;
  1595. examClassResultUpd.sum = examClassResultRow.sum;
  1596. }
  1597. //examClassResult.studentAnswers (1)將學生答案上傳blob後轉換內容為blob路徑 //[2021-7-13 不再對Blob做搬運] (2)將學生答案放入examClassResult.ans
  1598. if (examClassResultRow.studentIds != null && examClassResultRow.studentIds.Count > 0 && examClassResultRow.studentAnswersArray != null && examClassResultRow.studentAnswersArray.Count > 0)
  1599. {
  1600. for (int i = 0; i < examClassResultRow.studentAnswersArray.Count; i++)
  1601. {
  1602. string studentId = examClassResultRow.studentIds[i];
  1603. string fileName = examId + "/" + subjectId + "/" + studentId;
  1604. string blob = fileName + "/" + "ans.json";
  1605. var uploadFileResult = await _azureStorage.GetBlobContainerClient(blobContainer).UploadFileByContainer(examClassResultRow.studentAnswersArray[i].ToJsonString(), "exam", blob, false);
  1606. if (string.IsNullOrWhiteSpace(uploadFileResult))
  1607. {
  1608. studentAnswerCopyErrFlg = true;
  1609. }
  1610. //studentAnswers
  1611. List<string> studenrAnswerRow = new List<string>();
  1612. studenrAnswerRow.Add(blob);
  1613. examClassResultUpd.studentAnswers.Add(studenrAnswerRow);
  1614. }
  1615. //examClassResult.ans 將學生答案放入
  1616. examClassResultUpd.ans = examClassResultRow.studentAnswersArray;
  1617. }
  1618. //批註欄位處理
  1619. Dictionary<string, string> paperStatusNow = recordPaperInfo.Where((Dictionary<string, string> p) => p.TryGetValue("subjectId", out string paperStatusSubjectId) && paperStatusSubjectId.Equals(subjectId)).FirstOrDefault();
  1620. int itemCount = Convert.ToInt32(paperStatusNow["itemcount"]);
  1621. if (examClassResultUpd.mark.Count != examClassResultUpd.studentIds.Count || (examClassResultUpd.mark.ElementAtOrDefault(0) != null && examClassResultUpd.mark[0].Count != itemCount)) //第一層:學生數不符 OR 第二層:第一位學生批註數!=試卷題數 => 批註格式不符
  1622. {
  1623. examClassResultUpd.mark = this.createEmptyMark(examClassResultUpd.studentIds.Count, itemCount);
  1624. }
  1625. //UPDATE ExamClassResult
  1626. var examClassResultResponse = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Common").UpsertItemAsync(examClassResultUpd, new PartitionKey(examClassResultUpd.code));
  1627. }
  1628. }
  1629. else
  1630. {
  1631. List<ExamClassResultStudentAnswerArray> dbExamClassResultList = examClassResult.ToObject<List<ExamClassResultStudentAnswerArray>>();
  1632. foreach (ExamClassResultStudentAnswerArray examClassResultRow in dbExamClassResultList)
  1633. {
  1634. //以subjectId及classId(info.id)取得DB中的ExamClassResult
  1635. ExamClassResult examClassResultUpd = new ExamClassResult();
  1636. string exclcode = examClassResultRow.code;
  1637. string classId = examClassResultRow.info.id;
  1638. string subjectId = examClassResultRow.subjectId;
  1639. var querycr = $"SELECT * FROM c WHERE c.examId = '{examId}' AND c.info.id = '{classId}' AND c.subjectId = '{subjectId}'";
  1640. await foreach (var itemcr in _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Common").GetItemQueryStreamIterator(queryText: querycr, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"{exclcode}") }))
  1641. {
  1642. var jsontcr = await JsonDocument.ParseAsync(itemcr.ContentStream);
  1643. if (jsontcr.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  1644. {
  1645. foreach (var obj in jsontcr.RootElement.GetProperty("Documents").EnumerateArray())
  1646. {
  1647. examClassResultUpd = obj.ToObject<ExamClassResult>();
  1648. examClassResultUpd.progress = examClassResultRow.progress;
  1649. examClassResultUpd.studentIds = examClassResultRow.studentIds;
  1650. examClassResultUpd.studentAnswers = new List<List<string>>();
  1651. examClassResultUpd.studentScores = examClassResultRow.studentScores;
  1652. examClassResultUpd.status = examClassResultRow.status;
  1653. examClassResultUpd.sum = examClassResultRow.sum;
  1654. }
  1655. }
  1656. }
  1657. //無法取得既有ExamClassResult,新建
  1658. if (string.IsNullOrWhiteSpace(examClassResultUpd.id))
  1659. {
  1660. examClassResultUpd.pk = examClassResultRow.pk;
  1661. examClassResultUpd.code = examClassResultRow.code;
  1662. examClassResultUpd.id = examClassResultRow.id;
  1663. examClassResultUpd.school = examClassResultRow.school;
  1664. examClassResultUpd.examId = examClassResultRow.examId;
  1665. examClassResultUpd.subjectId = examClassResultRow.subjectId;
  1666. examClassResultUpd.gradeId = examClassResultRow.gradeId;
  1667. examClassResultUpd.year = examClassResultRow.year;
  1668. examClassResultUpd.info = examClassResultRow.info;
  1669. examClassResultUpd.progress = examClassResultRow.progress;
  1670. examClassResultUpd.studentIds = examClassResultRow.studentIds;
  1671. examClassResultUpd.studentAnswers = new List<List<string>>();
  1672. examClassResultUpd.studentScores = examClassResultRow.studentScores;
  1673. examClassResultUpd.status = examClassResultRow.status;
  1674. examClassResultUpd.scope = examClassResultRow.scope;
  1675. examClassResultUpd.sum = examClassResultRow.sum;
  1676. }
  1677. //學生作答Blob位置[2021-7-13 不再對Blob做搬運 學生答案直接寫入DB]
  1678. examClassResultUpd.studentAnswers = examClassResultRow.studentAnswersArray;
  1679. //學生作答答案 (從blob取得學生答案、放入ans欄位)
  1680. string targetScope = dbExamInfo.scope; //評測對象 school:校本班級 private:私人課程
  1681. var targetBlobContainer = _azureStorage.GetBlobContainerClient(id);
  1682. string blobCntr = id;
  1683. if (targetScope.Equals("school")) //校本
  1684. {
  1685. string schoolId = dbExamInfo.school;
  1686. blobCntr = schoolId;
  1687. }
  1688. targetBlobContainer = _azureStorage.GetBlobContainerClient(blobCntr);
  1689. if (examClassResultRow.studentAnswersArray.Count > 0)
  1690. {
  1691. foreach (List<string> studentAnswerBlobPath in examClassResultRow.studentAnswersArray)
  1692. {
  1693. string stuAnswerBlobPath = studentAnswerBlobPath.FirstOrDefault();
  1694. if (stuAnswerBlobPath != null)
  1695. {
  1696. StringBuilder builder = new StringBuilder();
  1697. builder.Append("exam").Append("/").Append(stuAnswerBlobPath);
  1698. var Download = await targetBlobContainer.GetBlobClient(builder.ToString()).DownloadAsync();
  1699. var json = await JsonDocument.ParseAsync(Download.Value.Content);
  1700. var Record = json.RootElement.ToObject<List<List<string>>>();
  1701. examClassResultUpd.ans.Add(Record);
  1702. }
  1703. }
  1704. }
  1705. //批註欄位處理
  1706. Dictionary<string, string> paperStatusNow = recordPaperInfo.Where((Dictionary<string, string> p) => p.TryGetValue("subjectId", out string subjectId) && subjectId.Equals(examClassResultUpd.subjectId)).FirstOrDefault();
  1707. int itemCount = Convert.ToInt32(paperStatusNow["itemcount"]);
  1708. if (examClassResultUpd.mark.Count != examClassResultUpd.studentIds.Count || (examClassResultUpd.mark.ElementAtOrDefault(0) != null && examClassResultUpd.mark[0].Count != itemCount)) //第一層:學生數不符 OR 第二層:第一位學生批註數!=試卷題數 => 批註格式不符
  1709. {
  1710. examClassResultUpd.mark = this.createEmptyMark(examClassResultUpd.studentIds.Count, itemCount);
  1711. }
  1712. //UPDATE ExamClassResult
  1713. var examClassResultResponse = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Common").UpsertItemAsync(examClassResultUpd, new PartitionKey(examClassResultUpd.code));
  1714. }
  1715. }
  1716. //UPDATE Exam
  1717. await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Common").UpsertItemAsync(dbExamInfo, new PartitionKey(dbExamInfo.code));
  1718. //錯誤處理
  1719. if (paperDataCopyErrFlg) //試卷Blob拷貝失敗
  1720. {
  1721. error = 1001;
  1722. message = "Paper blob copy failure.";
  1723. }
  1724. else if (studentAnswerCopyErrFlg) //學生作答資料上傳blob失敗
  1725. {
  1726. error = 1002;
  1727. message = "Student answers blob upload failure.";
  1728. }
  1729. return Ok(new { error, message });
  1730. }
  1731. catch (CosmosException cex)
  1732. {
  1733. return BadRequest(cex.Message);
  1734. }
  1735. catch (StorageException bex)
  1736. {
  1737. return BadRequest(bex.Message);
  1738. }
  1739. catch (Exception ex)
  1740. {
  1741. return BadRequest(ex.Message);
  1742. }
  1743. }
  1744. private List<List<List<Details>>> createEmptyMark(int studentNum, int itemNum)
  1745. {
  1746. List<List<List<Details>>> mark = new List<List<List<Details>>>();
  1747. for (int i = 0; i < studentNum; i++)
  1748. {
  1749. List<List<Details>> markRow = new List<List<Details>>();
  1750. for (int j = 0; j < itemNum; j++)
  1751. {
  1752. markRow.Add(new List<Details>());
  1753. }
  1754. mark.Add(markRow);
  1755. }
  1756. return mark;
  1757. }
  1758. public class ClassStudents
  1759. {
  1760. public string id { get; set; }
  1761. public string name { get; set; }
  1762. public string no { get; set; }
  1763. public string schoolId { get; set; }
  1764. public string groupId { get; set; }
  1765. public string groupName { get; set; }
  1766. public string irs { get; set; }
  1767. public int type { get; set; } //类型 1 tmdid,2 student
  1768. public string nickname { get; set; }
  1769. }
  1770. public class ClassGroups
  1771. {
  1772. public ClassGroups()
  1773. {
  1774. studentIds = new List<string>();
  1775. }
  1776. public string id { get; set; }
  1777. public string name { get; set; }
  1778. public List<string> studentIds { get; set; }
  1779. }
  1780. //ExamClassResult 學生作答紀錄(舊式 正式站暫時用這個)
  1781. public class ExamClassResultStudentAnswerArrayOld : ExamClassResult
  1782. {
  1783. public List<List<List<string>>> studentAnswersArray { get; set; }
  1784. }
  1785. //ExamClassResult 學生作答紀錄
  1786. public class ExamClassResultStudentAnswerArray : ExamClassResult
  1787. {
  1788. public List<List<string>> studentAnswersArray { get; set; }
  1789. }
  1790. }
  1791. }