HiTeachController.cs 249 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320
  1. using Microsoft.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 TEAMModelOS.SDK.Models.Table;
  21. using TEAMModelOS.SDK.Models;
  22. using System.Dynamic;
  23. using StackExchange.Redis;
  24. using TEAMModelOS.SDK.Models.Cosmos.Common;
  25. using TEAMModelOS.SDK;
  26. using Microsoft.Extensions.Configuration;
  27. using Azure.Messaging.ServiceBus;
  28. using TEAMModelOS.SDK.Services;
  29. using TEAMModelOS.SDK.Models.Service;
  30. using TEAMModelOS.Models.Request;
  31. using Azure;
  32. using TEAMModelOS.Controllers.Both;
  33. using TEAMModelOS.SDK.Models.Cosmos.School;
  34. using Azure.Storage.Blobs;
  35. using HtmlAgilityPack;
  36. using System.Diagnostics;
  37. using TEAMModelOS.Models.Service;
  38. namespace TEAMModelOS.Controllers.Client
  39. {
  40. [ProducesResponseType(StatusCodes.Status200OK)]
  41. [ProducesResponseType(StatusCodes.Status400BadRequest)]
  42. //[Authorize(Roles = "HiTeach")]
  43. [Route("hiteach")]
  44. [ApiController]
  45. public class HiTeachController : ControllerBase
  46. {
  47. private readonly AzureStorageFactory _azureStorage;
  48. private readonly AzureRedisFactory _azureRedis;
  49. private readonly AzureCosmosFactory _azureCosmos;
  50. private readonly DingDing _dingDing;
  51. private readonly Option _option;
  52. private readonly SnowflakeId _snowflakeId;
  53. private readonly AzureServiceBusFactory _serviceBus;
  54. private readonly IConfiguration _configuration;
  55. private readonly CoreAPIHttpService _coreAPIHttpService;
  56. private readonly IPSearcher _searcher;
  57. private readonly HttpTrigger _httpTrigger;
  58. public HiTeachController(
  59. AzureStorageFactory azureStorage,
  60. AzureRedisFactory azureRedis,
  61. AzureCosmosFactory azureCosmos,
  62. DingDing dingDing,
  63. SnowflakeId snowflakeId,
  64. IOptionsSnapshot<Option> option,
  65. AzureServiceBusFactory serviceBus,
  66. IConfiguration configuration,
  67. CoreAPIHttpService coreAPIHttpService, IPSearcher searcher, HttpTrigger httpTrigger)
  68. {
  69. _azureStorage = azureStorage;
  70. _azureRedis = azureRedis;
  71. _azureCosmos = azureCosmos;
  72. _dingDing = dingDing;
  73. _snowflakeId = snowflakeId;
  74. _option = option?.Value;
  75. _serviceBus = serviceBus;
  76. _configuration = configuration;
  77. _coreAPIHttpService = coreAPIHttpService;
  78. _searcher = searcher;
  79. _httpTrigger = httpTrigger;
  80. }
  81. /// <summary>
  82. /// 更新课堂记录
  83. /// </summary>
  84. /// <param name="request"></param>
  85. /// <returns></returns>
  86. //[Authorize(Roles = "HiTeach")]
  87. [ProducesDefaultResponseType]
  88. [HttpPost("update-lesson-record")]
  89. public async Task<IActionResult> UpdateLessonRecord(JsonElement request)
  90. {
  91. var client = _azureCosmos.GetCosmosClient();
  92. if (!request.TryGetProperty("lesson_id", out JsonElement _lessonId)) return BadRequest();
  93. if (!request.TryGetProperty("tmdid", out JsonElement _tmdid)) return BadRequest();
  94. request.TryGetProperty("name", out JsonElement name);
  95. request.TryGetProperty("school", out JsonElement _school);
  96. if (!request.TryGetProperty("scope", out JsonElement _scope)) return BadRequest();
  97. request.TryGetProperty("grant_types", out JsonElement _grant_types);
  98. string tbname;
  99. string code;
  100. if (_scope.GetString().Equals("school") && !string.IsNullOrWhiteSpace(_school.GetString()))
  101. {
  102. code = $"LessonRecord-{_school}";
  103. tbname = "School";
  104. }
  105. else if ($"{_scope}".Equals("private"))
  106. {
  107. code = $"LessonRecord";
  108. tbname = "Teacher";
  109. }
  110. else
  111. {
  112. return BadRequest();
  113. }
  114. try
  115. {
  116. LessonRecord lessonRecord = await client.GetContainer(Constant.TEAMModelOS, tbname).ReadItemAsync<LessonRecord>($"{_lessonId}", new PartitionKey(code));
  117. List<LessonUpdate> updates = new List<LessonUpdate>() { new LessonUpdate { grant_type = "up-base" } };
  118. if (_grant_types.ValueKind.Equals(JsonValueKind.Array))
  119. {
  120. updates = _grant_types.ToObject<List<LessonUpdate>>();
  121. if (!updates.Select(x => x.grant_type).Contains("up-base"))
  122. {
  123. updates.Add(new LessonUpdate { grant_type = "up-base" });
  124. }
  125. }
  126. if (!string.IsNullOrWhiteSpace($"{name}"))
  127. {
  128. lessonRecord.name = $"{name}";
  129. await client.GetContainer(Constant.TEAMModelOS, tbname).ReplaceItemAsync<LessonRecord>(lessonRecord, $"{_lessonId}", new PartitionKey(code));
  130. }
  131. var ActiveTask = _configuration.GetValue<string>("Azure:ServiceBus:ActiveTask");
  132. string msg = null;
  133. msg = new { lesson_id = $"{_lessonId}", tmdid = $"{_tmdid}", grant_types = updates, school = $"{_school}", scope = $"{_scope}" }.ToJsonString();
  134. var messageChange = new ServiceBusMessage(msg);
  135. messageChange.ApplicationProperties.Add("name", "LessonRecordEvent");
  136. //await _dingDing.SendBotMsg($"{_option.Location},课堂id:{_lessonId} 更新事件,{msg}", GroupNames.醍摩豆服務運維群組);
  137. await _serviceBus.GetServiceBusClient().SendMessageAsync(ActiveTask, messageChange);
  138. await LessonServiceIES.SetLearnRecordContent(_school, _tmdid, _lessonId, _scope, _azureCosmos, _azureStorage);
  139. return Ok(new { status = 200 });
  140. }
  141. catch (CosmosException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound)
  142. {
  143. await _dingDing.SendBotMsg($"更新课堂记录出错\n{ex.StackTrace}{ex.Message}", GroupNames.成都开发測試群組);
  144. return BadRequest("课堂记录不存在");
  145. }
  146. catch (Exception ex)
  147. {
  148. await _dingDing.SendBotMsg($"更新课堂记录出错\n{ex.StackTrace}{ex.Message}", GroupNames.成都开发測試群組);
  149. return BadRequest();
  150. }
  151. }
  152. /// <summary>
  153. /// 產生指定日期區間的學習紀錄(包含學校課程、個人課程、評量)
  154. /// </summary>
  155. /// <param name="request"></param>
  156. /// <returns></returns>
  157. [Authorize(Roles = "HiTeach")]
  158. [ProducesDefaultResponseType]
  159. [HttpPost("run-lesson-LearnRecord")]
  160. public async Task<IActionResult> RunLessonLearnRecord(JsonElement request)
  161. {
  162. try
  163. {
  164. if (!request.TryGetProperty("startdate", out JsonElement _startdate)) return BadRequest();
  165. if (!request.TryGetProperty("enddate", out JsonElement _enddate)) return BadRequest();
  166. var client = _azureCosmos.GetCosmosClient();
  167. #region 學校課堂
  168. string sql = $"SELECT c.id, c.tmdid, c.school, c.scope FROM c WHERE CONTAINS(c.code,'LessonRecord-') and c.startTime > {_startdate} and c.startTime < {_enddate}";
  169. List<LessonRecordItemPart> LessonRecordItemPartList = new List<LessonRecordItemPart>();
  170. await foreach (var item in client.GetContainer(Constant.TEAMModelOS, Constant.School).GetItemQueryIteratorSql<LessonRecordItemPart>(queryText: sql))
  171. {
  172. LessonRecordItemPartList.Add(item);
  173. }
  174. foreach (LessonRecordItemPart lrip in LessonRecordItemPartList)
  175. {
  176. await SetLearnRecordContentForBatch(lrip.school, lrip.tmdid, lrip.id, lrip.scope, "0");
  177. }
  178. #endregion
  179. #region 個人課堂
  180. string sql2 = $"SELECT c.id, c.tmdid, c.school, c.scope FROM c WHERE c.code = 'LessonRecord' and c.startTime > {_startdate} and c.startTime < {_enddate}";
  181. List<LessonRecordItemPart> LessonRecordItemPartList2 = new List<LessonRecordItemPart>();
  182. await foreach (var item in client.GetContainer(Constant.TEAMModelOS, Constant.Teacher).GetItemQueryIteratorSql<LessonRecordItemPart>(queryText: sql2))
  183. {
  184. LessonRecordItemPartList2.Add(item);
  185. }
  186. foreach (LessonRecordItemPart lrip in LessonRecordItemPartList2)
  187. {
  188. await SetLearnRecordContentForBatch("", lrip.tmdid, lrip.id, lrip.scope, "1");
  189. }
  190. #endregion
  191. #region 評量
  192. string sql3 = $"SELECT * FROM c WHERE CONTAINS(c.code,'Exam-') and c.endTime > {_startdate} and c.endTime < {_enddate}";
  193. await foreach (var item in client.GetContainer(Constant.TEAMModelOS, Constant.Common).GetItemQueryIteratorSql<ExamInfo>(queryText: sql3))
  194. {
  195. await SetLearnRecordContent(item, _azureStorage, _azureCosmos);
  196. }
  197. #endregion
  198. return Ok(new { status = 200 });
  199. }
  200. catch (Exception ex)
  201. {
  202. return BadRequest();
  203. }
  204. }
  205. /// <summary>
  206. ///
  207. /// </summary>
  208. /// <param name="info"></param>
  209. /// <param name="data"></param>
  210. /// <param name="_azureStorage"></param>
  211. /// <param name="_azureCosmos"></param>
  212. /// <returns></returns>
  213. private static async Task SetLearnRecordContent(ExamInfo info, AzureStorageFactory _azureStorage, AzureCosmosFactory _azureCosmos)
  214. {
  215. try
  216. {
  217. if (info.papers.Count > 0)
  218. {
  219. // Unix 时间戳的起始时间是 1970 年 1 月 1 日 00:00:00 UTC
  220. DateTime unixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
  221. // 将 Unix 时间戳转换为 TimeSpan,并加到 Unix 起始时间上
  222. // 将时间戳转换为 DateTime
  223. DateTime dateTime = unixEpoch.AddMilliseconds(info.endTime);
  224. // 将 DateTime 格式化为 yyyyMMdd 格式的日期字符串
  225. string date_path = dateTime.ToString("yyyyMMdd");
  226. for (int i = 0; i < info.papers.Count; i++)
  227. {// 每一個科目的試卷
  228. string rootName = "";
  229. //if (info.school == "SYSTEM_NO_SCHOOL")
  230. if (info.scope == "private")
  231. {// 未入校老師的評量
  232. rootName = info.creatorId;
  233. }
  234. else
  235. {// 如果有學校代碼
  236. rootName = info.school;
  237. }
  238. if (_azureStorage.GetBlobContainerClient(rootName).GetBlobClient($"{info.papers[i].blob}/index.json").Exists())
  239. {// 如果試卷的blob存在,再開始
  240. BlobDownloadResult paperblobDownload = await _azureStorage.GetBlobContainerClient(rootName).GetBlobClient($"{info.papers[i].blob}/index.json").DownloadContentAsync();
  241. PaperIndex paperIndex = paperblobDownload.Content.ToObjectFromJson<PaperIndex>();
  242. List<LearnRecordItem> learnRecordItems = new();
  243. StringBuilder sbsql = new StringBuilder($" SELECT * FROM c WHERE c.examId = '{info.id}' and c.pk = 'ExamClassResult' and c.subjectId = '{info.subjects[i].id}' ");
  244. #region === 開始拼裝資料 ===
  245. //Debug.WriteLine("開始拼裝資料start ----" + DateTime.Now.ToLongTimeString());
  246. if (info.classes.Count > 0 || info.stuLists.Count > 0)
  247. {
  248. // 組合SQL
  249. if (info.classes.Count > 0)
  250. {// 按照classes取ans.json
  251. sbsql.Append($" and ARRAY_CONTAINS({System.Text.Json.JsonSerializer.Serialize(info.classes)}, c.info.id)");
  252. }
  253. else if (info.stuLists.Count > 0)
  254. {
  255. sbsql.Append($" and ARRAY_CONTAINS({System.Text.Json.JsonSerializer.Serialize(info.stuLists)}, c.info.id)");
  256. }
  257. // 取學生答案及學生名單
  258. var client = _azureCosmos.GetCosmosClient();
  259. Dictionary<string, List<string>> stuDic = new Dictionary<string, List<string>>();
  260. if (!string.IsNullOrWhiteSpace(info.school) && !stuDic.ContainsKey(info.school))
  261. {
  262. stuDic.Add(info.school, new List<string>());
  263. }
  264. List<StudentAnswers> studentAnswersList = new List<StudentAnswers>();
  265. List<string> stuListForSql = new List<string>();
  266. await foreach (var item in client.GetContainer(Constant.TEAMModelOS, Constant.Common).GetItemQueryIteratorSql<StudentAnswers>(queryText: sbsql.ToString(), requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"ExamClassResult-{rootName}") }))
  267. {
  268. studentAnswersList.Add(item);
  269. stuListForSql = stuListForSql.Union(item.studentIds).ToList();
  270. }
  271. //取得學校的學生名單
  272. List<string> stuOpenIdList = new List<string>();
  273. if (!string.IsNullOrWhiteSpace(info.school))
  274. {
  275. StringBuilder stusql = new StringBuilder($"SELECT VALUE c.id FROM c WHERE c.pk = 'Base' AND ARRAY_CONTAINS({System.Text.Json.JsonSerializer.Serialize(stuListForSql)}, c.id)");
  276. await foreach (string item in client.GetContainer(Constant.TEAMModelOS, Constant.Student).GetItemQueryIteratorSql<string>(queryText: stusql.ToString(), requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Base-{info.school}") }))
  277. {
  278. stuDic[info.school].Add(item);
  279. }
  280. }
  281. // 按照取出的學生答案blob 對答案 組合資料
  282. foreach (var studentAnswers in studentAnswersList)
  283. {// 每一個班級
  284. foreach (var studentAnswersBlob in studentAnswers.studentAnswers)
  285. {// 每一個學生
  286. if (studentAnswersBlob.Count > 0)
  287. {// 有學生的答案才開始
  288. if (_azureStorage.GetBlobContainerClient(rootName).GetBlobClient($"/exam/{studentAnswersBlob[0]}").Exists())
  289. {// 如果blob存在才開始
  290. BlobDownloadResult ansblobDownload = await _azureStorage.GetBlobContainerClient(rootName).GetBlobClient($"/exam/{studentAnswersBlob[0]}").DownloadContentAsync();
  291. List<List<string>> ansList = Newtonsoft.Json.JsonConvert.DeserializeObject<List<List<string>>>(ansblobDownload.Content.ToString());
  292. // 從blob路徑取出學生id
  293. string[] arr = studentAnswersBlob[0].Split('/');
  294. string stuid = arr[arr.Length - 2];
  295. #region ===StartExam 開始評量===
  296. LearnRecordItem LRItem_StartExam = new();
  297. LRItem_StartExam.verb = "StartExam";
  298. LRItem_StartExam.actor = getStuId(info.school, stuid, stuDic);
  299. LRItem_StartExam.time = info.createTime;
  300. LRItem_StartExam.ID = info.id;
  301. LRItem_StartExam.Desc = info.name;
  302. LRItem_StartExam.Correct = null;
  303. LRItem_StartExam.Choices = null;
  304. LRItem_StartExam.ExamQuesQty = info.papers[i].point.Count;
  305. LRItem_StartExam.TotalScore = 0;
  306. foreach (var pointItem in info.papers[i].point)
  307. {
  308. LRItem_StartExam.TotalScore = LRItem_StartExam.TotalScore + pointItem;
  309. }
  310. LRItem_StartExam.Success = null;
  311. learnRecordItems.Add(LRItem_StartExam);
  312. #endregion
  313. //Debug.WriteLine("開始評量題目start ----" + DateTime.Now.ToLongTimeString());
  314. #region ===評量題目===
  315. for (int j = 0; j < ansList.Count; j++)
  316. {
  317. #region === 判斷題型 ===
  318. // 這五種題型才記錄
  319. if (paperIndex.slides[j].type == "single" ||
  320. paperIndex.slides[j].type == "multiple" ||
  321. paperIndex.slides[j].type == "judge" ||
  322. paperIndex.slides[j].type == "complete" ||
  323. paperIndex.slides[j].type == "subjective")
  324. {
  325. LearnRecordItem learnRecordItem = new();
  326. // 單選
  327. if (paperIndex.slides[j].type == "single") { learnRecordItem.verb = "AnsSingle"; }
  328. // 複選
  329. if (paperIndex.slides[j].type == "multiple") { learnRecordItem.verb = "AnsMultiple"; }
  330. // 是非
  331. if (paperIndex.slides[j].type == "judge") { learnRecordItem.verb = "AnsJudge"; }
  332. // 填充
  333. if (paperIndex.slides[j].type == "complete") { learnRecordItem.verb = "AnsComplete"; }
  334. // 問答
  335. if (paperIndex.slides[j].type == "subjective") { learnRecordItem.verb = "AnsSubjective"; }
  336. learnRecordItem.actor = getStuId(info.school, stuid, stuDic);
  337. learnRecordItem.time = info.endTime;
  338. string[] arrurlsingle = paperIndex.slides[j].url.Split('.');
  339. learnRecordItem.ID = arrurlsingle[0];
  340. //Debug.WriteLine("設定Correct Choices Desc start ----" + DateTime.Now.ToLongTimeString());
  341. #region === 設定Correct Choices Desc===
  342. if (_azureStorage.GetBlobContainerClient(rootName).GetBlobClient($"{info.papers[i].blob}/{paperIndex.slides[j].url}").Exists())
  343. {
  344. BlobDownloadResult itemblobDownload = await _azureStorage.GetBlobContainerClient(rootName).GetBlobClient($"{info.papers[i].blob}/{paperIndex.slides[j].url}").DownloadContentAsync();
  345. QuestionData questionData = itemblobDownload.Content.ToObjectFromJson<QuestionData>();
  346. setCorrectChoicesExam(questionData, learnRecordItem);
  347. // 使用HtmlAgilityPack解析HTML
  348. HtmlDocument doc = new HtmlDocument();
  349. doc.LoadHtml(questionData.item[0].question);
  350. // 先刪除所有<img>標籤
  351. foreach (var imgNode in doc.DocumentNode.Descendants("img").ToArray())
  352. {
  353. imgNode.Remove();
  354. }
  355. // 再取純文字
  356. learnRecordItem.Desc = doc.DocumentNode.InnerText;
  357. }
  358. #endregion
  359. //Debug.WriteLine("設定Correct Choices Desc end ----" + DateTime.Now.ToLongTimeString());
  360. learnRecordItem.ExamQuesQty = null;
  361. learnRecordItem.TotalScore = null;
  362. learnRecordItem.Points = paperIndex.slides[j].scoring == null ? new List<string>() : paperIndex.slides[j].scoring.knowledge;
  363. //比對答案
  364. learnRecordItem.Success = paperIndex.slides[j].scoring == null ? null : (paperIndex.slides[j].scoring.ans.SequenceEqual(ansList[j]) ? true : false);
  365. learnRecordItems.Add(learnRecordItem);
  366. }
  367. #endregion
  368. }
  369. #endregion
  370. //Debug.WriteLine("開始評量題目end ----" + DateTime.Now.ToLongTimeString());
  371. #region ===EndExam 結束評量===
  372. LearnRecordItem LRItem_EndExam = new();
  373. LRItem_EndExam.verb = "EndExam";
  374. LRItem_EndExam.actor = getStuId(info.school, stuid, stuDic);
  375. LRItem_EndExam.time = info.endTime;
  376. LRItem_EndExam.ID = info.id;
  377. LRItem_EndExam.Desc = info.name;
  378. LRItem_EndExam.Correct = null;
  379. LRItem_EndExam.Choices = null;
  380. LRItem_EndExam.ExamQuesQty = info.papers[i].point.Count;
  381. LRItem_EndExam.TotalScore = 0;
  382. foreach (var pointItem in info.papers[i].point)
  383. {
  384. LRItem_EndExam.TotalScore = LRItem_EndExam.TotalScore + pointItem;
  385. }
  386. LRItem_EndExam.Success = null;
  387. learnRecordItems.Add(LRItem_EndExam);
  388. #endregion
  389. }
  390. }
  391. }
  392. }
  393. }
  394. //Debug.WriteLine("開始拼裝資料end ----" + DateTime.Now.ToLongTimeString());
  395. #endregion
  396. if (learnRecordItems.Count > 0)
  397. {
  398. #region 寫入blob
  399. //// 容器名称
  400. //string containerName = "twmoeld";
  401. //// 要上传的文件内容
  402. //string jsonString = Newtonsoft.Json.JsonConvert.SerializeObject(learnRecordItems);
  403. //// 指定目录和文件名
  404. //string directoryPath = @"D:\twmoeld\exam";
  405. //string fileName = $"{info.id}.json";
  406. //string filePath = Path.Combine(directoryPath, fileName);
  407. //try
  408. //{
  409. // // 将 JSON 字符串写入文件
  410. // System.IO.File.WriteAllText(filePath, jsonString);
  411. // Console.WriteLine($"JSON 数据已成功写入文件: {filePath}");
  412. //}
  413. //catch (Exception ex)
  414. //{
  415. // Console.WriteLine($"写入文件时发生错误: {ex.Message}");
  416. //}
  417. // 容器名称
  418. string containerName = "twmoeld";
  419. // 新文件的名称
  420. //string blobName = $"/{DateTime.Now.ToString("yyyyMMdd")}/{info.id}.json";
  421. string blobName = $"/{date_path}/{info.id}.json";
  422. // 要上传的文件内容
  423. string fileContent = Newtonsoft.Json.JsonConvert.SerializeObject(learnRecordItems);
  424. // 获取容器引用
  425. BlobContainerClient containerClient = _azureStorage.GetBlobContainerClient(containerName);
  426. // 获取 Blob 客户端
  427. BlobClient blobClient = containerClient.GetBlobClient(blobName);
  428. // 将文件内容上传到 Blob
  429. using (MemoryStream stream = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(fileContent)))
  430. {
  431. await blobClient.UploadAsync(stream, true);
  432. }
  433. #endregion
  434. }
  435. }
  436. }
  437. }
  438. }
  439. catch (Exception ex)
  440. {
  441. }
  442. }
  443. /// <summary>
  444. /// 取得學習紀錄的actor
  445. /// </summary>
  446. /// <param name="learnRecordItem"></param>
  447. /// <param name="school"></param>
  448. /// <param name="stuid"></param>
  449. /// <returns></returns>
  450. private static string getStuId(string school, string stuid, Dictionary<string, List<string>> stuDic)
  451. {
  452. if (!string.IsNullOrWhiteSpace(school) && stuDic.ContainsKey(school) && stuDic[school].Contains(stuid))
  453. {// 校內帳號用組合的 "Base-hbgl,473891247381"
  454. return $"Base-{school.Trim()},{stuid}";
  455. }
  456. else
  457. {// 未入校老師的評量
  458. return stuid;
  459. }
  460. }
  461. /// <summary>
  462. /// 評量 - 對答案
  463. /// </summary>
  464. /// <param name="questionData"></param>
  465. /// <param name="learnRecordItem"></param>
  466. private static void setCorrectChoicesExam(QuestionData questionData, LearnRecordItem learnRecordItem)
  467. {
  468. #region === Correct Choices ===
  469. #region === 是非題邏輯 ===
  470. if (questionData.exercise.type == "judge" && questionData.exercise.answer.Count > 0)
  471. {// 如果是是非題 正確答案要用true false的方式設定
  472. if (questionData.exercise.answer[0] == "A")
  473. {
  474. learnRecordItem.Correct = new string[] { "true" };
  475. }
  476. else
  477. {
  478. learnRecordItem.Correct = new string[] { "false" };
  479. }
  480. }
  481. else
  482. {// 防呆 去html標籤
  483. if (questionData.exercise.answer.Count > 0)
  484. {
  485. List<string> anslist = new();
  486. foreach (var answer in questionData.exercise.answer)
  487. {
  488. // 使用HtmlAgilityPack解析HTML
  489. HtmlDocument doc = new HtmlDocument();
  490. doc.LoadHtml(answer);
  491. // 再取純文字
  492. anslist.Add(doc.DocumentNode.InnerText);
  493. }
  494. learnRecordItem.Correct = anslist;
  495. }
  496. else
  497. {
  498. learnRecordItem.Correct = questionData.exercise.answer;
  499. }
  500. }
  501. #endregion
  502. if (questionData.item[0].option.Count > 0)
  503. {// 如果有選項資料 記錄起來
  504. foreach (var option in questionData.item[0].option)
  505. {
  506. ChoicesItem item = new();
  507. item.id = option.code;
  508. if (questionData.exercise.type == "judge")
  509. {// 如果是是非題 固定選項
  510. if (option.code == "A")
  511. {
  512. item.description.zhTW = "是";
  513. }
  514. else
  515. {
  516. item.description.zhTW = "否";
  517. }
  518. }
  519. else
  520. {
  521. if (string.IsNullOrWhiteSpace(option.value))
  522. {
  523. item.description.zhTW = $"選項{option.code}";
  524. }
  525. else
  526. {
  527. // 使用HtmlAgilityPack解析HTML
  528. HtmlDocument doc = new HtmlDocument();
  529. doc.LoadHtml(option.value);
  530. // 再取純文字
  531. item.description.zhTW = doc.DocumentNode.InnerText;
  532. }
  533. }
  534. learnRecordItem.Choices.Add(item);
  535. }
  536. }
  537. #endregion
  538. }
  539. /// <summary>
  540. /// 更新课堂记录
  541. /// </summary>
  542. /// <param name="request"></param>
  543. /// <returns></returns>
  544. [Authorize(Roles = "HiTeach")]
  545. [ProducesDefaultResponseType]
  546. [HttpPost("update-lesson-recordLearnRecord")]
  547. public async Task<IActionResult> UpdateLessonRecordLearnRecord(JsonElement request)
  548. {
  549. try
  550. {
  551. if (!request.TryGetProperty("lesson_id", out JsonElement _lessonId)) return BadRequest();
  552. if (!request.TryGetProperty("scope", out JsonElement _scope)) return BadRequest();
  553. if (!request.TryGetProperty("tmdid", out JsonElement _tmdid)) return BadRequest();
  554. request.TryGetProperty("school", out JsonElement _school);
  555. await LessonServiceIES.SetLearnRecordContent(_school, _tmdid, _lessonId, _scope, _azureCosmos, _azureStorage);
  556. return Ok(new { status = 200 });
  557. }
  558. catch (Exception ex)
  559. {
  560. return BadRequest();
  561. }
  562. }
  563. /// <summary>
  564. /// 本機使用教育雲-學習紀錄
  565. /// </summary>
  566. /// <param name="request"></param>
  567. /// <returns></returns>
  568. [Authorize(Roles = "HiTeach")]
  569. [ProducesDefaultResponseType]
  570. [HttpPost("local-lesson-learnrecord")]
  571. public async Task<IActionResult> LocalLessonLearnRecord(JsonElement request)
  572. {
  573. try
  574. {
  575. if (!request.TryGetProperty("lesson_id", out JsonElement _lessonId)) return BadRequest();
  576. if (!request.TryGetProperty("tmdid", out JsonElement _tmdid)) return BadRequest();
  577. request.TryGetProperty("school", out JsonElement _school);
  578. request.TryGetProperty("scope", out JsonElement _scope);
  579. await LessonServiceIES.SetLearnRecordContent(_school, _tmdid, _lessonId, _scope, _azureCosmos, _azureStorage, false);
  580. return Ok(new { status = 200 });
  581. }
  582. catch (Exception ex)
  583. {
  584. return BadRequest();
  585. }
  586. }
  587. private async Task SetLearnRecordContentForBatch(string _school, string _tmdid, string _lessonId, string _scope, string htype, bool _isIES = true)
  588. {
  589. try
  590. {
  591. var client = _azureCosmos.GetCosmosClient();
  592. string school = $"{_school}";
  593. string tmdid = _tmdid;
  594. string lessonId = _lessonId;
  595. string rootName = "";
  596. string date_path = "";
  597. List<LearnRecordItem> learnRecordItems = new List<LearnRecordItem>();
  598. // 判斷個人或是學校課堂以及是否為本地課程 來取blob的容器位置
  599. if (_scope == null)
  600. {// 本地課堂
  601. rootName = tmdid;
  602. }
  603. else
  604. {
  605. if (_scope.Equals("school") && !string.IsNullOrWhiteSpace(_school))
  606. {// 學校課堂
  607. rootName = school;
  608. }
  609. else if ($"{_scope}".Equals("private"))
  610. {// 個人課堂
  611. rootName = tmdid;
  612. }
  613. }
  614. // 本地路徑 1524738018 / temp / records / 1334aa30868520170580732778614850743
  615. string blobTimeLineUrl = "";
  616. string blobIRSUrl = "";
  617. string blobbaseUrl = "";
  618. string blobTaskUrl = "";
  619. // 處理資料是由IES5或是hiteach本地來的邏輯
  620. if (_isIES)
  621. {
  622. blobTimeLineUrl = $"/records/{lessonId}/IES/TimeLine.json";
  623. blobIRSUrl = $"/records/{lessonId}/IES/IRS.json";
  624. blobbaseUrl = $"/records/{lessonId}/IES/base.json";
  625. blobTaskUrl = $"/records/{lessonId}/IES/Task.json";
  626. }
  627. else
  628. {
  629. blobTimeLineUrl = $"/temp/records/{lessonId}/IES/TimeLine.json";
  630. blobIRSUrl = $"/temp/records/{lessonId}/IES/IRS.json";
  631. blobbaseUrl = $"/temp/records/{lessonId}/IES/base.json";
  632. blobTaskUrl = $"/temp/records/{lessonId}/IES/Task.json";
  633. }
  634. // 檢查 TimeLine 是否存在blob
  635. if (_azureStorage.GetBlobContainerClient(rootName).GetBlobClient(blobTimeLineUrl).Exists() &&
  636. _azureStorage.GetBlobContainerClient(rootName).GetBlobClient(blobIRSUrl).Exists() &&
  637. _azureStorage.GetBlobContainerClient(rootName).GetBlobClient(blobbaseUrl).Exists())
  638. {
  639. // 開啟 base.json
  640. BlobDownloadResult baseblobDownload = await _azureStorage.GetBlobContainerClient(rootName).GetBlobClient(blobbaseUrl).DownloadContentAsync();
  641. ScoreLessonBase lessonBase = baseblobDownload.Content.ToObjectFromJson<ScoreLessonBase>();
  642. date_path = lessonBase.summary.date.Replace(".", "");
  643. //取得學校學生名單
  644. List<string> stuIdForSql = new List<string>();
  645. if (lessonBase.student.Count > 0)
  646. {
  647. foreach (LessonStudent stu in lessonBase.student)
  648. {
  649. if (!string.IsNullOrWhiteSpace(stu.id) && !stuIdForSql.Contains(stu.id))
  650. {
  651. stuIdForSql.Add(stu.id);
  652. }
  653. }
  654. }
  655. GroupList groupList = new ();
  656. List<LessonStudent> student = lessonBase.student.FindAll(x => x.type.Equals(2));
  657. if (student.Count > 0 && string.IsNullOrWhiteSpace(school)) // 如果有學生事校內帳號而且沒有schoolid 先取出班級id 下面設定schoolid的時候可以用
  658. {
  659. var clientTeacher = _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher);
  660. GroupIdsFromLesson groupIdsFromLesson = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher).ReadItemAsync<GroupIdsFromLesson>(lessonId, new PartitionKey($"LessonRecord"));
  661. groupList = await clientTeacher.ReadItemAsync<GroupList>(groupIdsFromLesson.groupIds[0], new PartitionKey($"GroupList"));
  662. }
  663. // 開啟 TimeLine.json
  664. BlobDownloadResult TimeLineblobDownload = await _azureStorage.GetBlobContainerClient(rootName).GetBlobClient(blobTimeLineUrl).DownloadContentAsync();
  665. TimeLineEvents timeLineEvents = TimeLineblobDownload.Content.ToObjectFromJson<TimeLineEvents>();
  666. // 開啟 IRS.json
  667. BlobDownloadResult IRSblobDownload = await _azureStorage.GetBlobContainerClient(rootName).GetBlobClient(blobIRSUrl).DownloadContentAsync();
  668. List<IRSItem> iRSItems = IRSblobDownload.Content.ToObjectFromJson<List<IRSItem>>();
  669. // 開啟 Task.json
  670. BlobDownloadResult taskblobDownload = await _azureStorage.GetBlobContainerClient(rootName).GetBlobClient(blobTaskUrl).DownloadContentAsync();
  671. List<TaskItem> taskItems = taskblobDownload.Content.ToObjectFromJson<List<TaskItem>>();
  672. if (timeLineEvents.events.Count > 0)
  673. {
  674. for (int i = 0; i < timeLineEvents.events.Count; i++)
  675. {
  676. switch (timeLineEvents.events[i].Event)
  677. {
  678. // 互動
  679. case "PopQuesLoad":
  680. case "QuesLoad":
  681. for (int j = 0; j < iRSItems.Count; j++)
  682. {
  683. if (timeLineEvents.events[i].Pgid == iRSItems[j].pageID) // 比對 Pgid 找到 IRS.json 該活動的詳細資料
  684. {
  685. switch (iRSItems[j].question.exercise.type)
  686. {
  687. // 單選
  688. case "single":
  689. await SetPopQuesLoadContent(lessonBase, school, lessonId, iRSItems[j], timeLineEvents.events[i], "AnsSingle", learnRecordItems, groupList);
  690. break;
  691. // 複選
  692. case "multiple":
  693. await SetPopQuesLoadContent(lessonBase, school, lessonId, iRSItems[j], timeLineEvents.events[i], "AnsMultiple", learnRecordItems, groupList);
  694. break;
  695. // 是非
  696. case "judge":
  697. await SetPopQuesLoadContent(lessonBase, school, lessonId, iRSItems[j], timeLineEvents.events[i], "AnsJudge", learnRecordItems, groupList);
  698. break;
  699. // 填充
  700. case "complete":
  701. await SetPopQuesLoadContent(lessonBase, school, lessonId, iRSItems[j], timeLineEvents.events[i], "AnsComplete", learnRecordItems, groupList);
  702. break;
  703. // 問答
  704. case "subjective":
  705. await SetPopQuesLoadContent(lessonBase, school, lessonId, iRSItems[j], timeLineEvents.events[i], "AnsSubjective", learnRecordItems, groupList);
  706. break;
  707. }
  708. }
  709. }
  710. break;
  711. // 搶權
  712. case "BuzrAns":
  713. for (int j = 0; j < iRSItems.Count; j++)
  714. {
  715. if (timeLineEvents.events[i].Pgid == iRSItems[j].pageID && iRSItems[j].isBuzz) // 比對 Pgid 找到 IRS.json 該活動的詳細資料
  716. {
  717. for (int k = 0; k < iRSItems[j].buzzClients.Count; k++)
  718. {
  719. LearnRecordItem learnRecordItem = new();
  720. for (int m = 0; m < lessonBase.student.Count; m++)
  721. {// 比對學生座號 取學號
  722. if (lessonBase.student[m].seatID.ToString() == iRSItems[j].buzzClients[k])
  723. {
  724. if (lessonBase.student[k].type == 2)
  725. {// 校內帳號
  726. await getSchoolCode(school, lessonId, lessonBase.student[k].id, learnRecordItem, groupList);
  727. }
  728. else
  729. {// 非校內帳號
  730. learnRecordItem.actor = setActor(school, lessonBase.student[k].id);
  731. }
  732. }
  733. }
  734. string[] arrymd = lessonBase.summary.date.Split('.');
  735. string[] arrhms = lessonBase.summary.startTime.Split(':');
  736. DateTime dt = new DateTime(int.Parse(arrymd[0]), int.Parse(arrymd[1]), int.Parse(arrymd[2]), int.Parse(arrhms[0]), int.Parse(arrhms[1]), int.Parse(arrhms[2]));
  737. dt = dt.AddSeconds(timeLineEvents.events[i].Time);
  738. learnRecordItem.verb = "AnsBuzzin";
  739. learnRecordItem.time = dt.ToUnixTimestamp();
  740. learnRecordItem.ID = timeLineEvents.events[i].Pgid;
  741. if (iRSItems[j].question.item[0].question == "_popquiz_" || iRSItems[j].question.item[0].question == "")
  742. {
  743. learnRecordItem.Desc = "隨堂問答";
  744. }
  745. else
  746. {
  747. learnRecordItem.Desc = iRSItems[j].question.item[0].question;
  748. }
  749. learnRecordItem.Points = iRSItems[j].question.exercise.knowledges;
  750. await setCorrectChoices(iRSItems[j], learnRecordItem);
  751. learnRecordItem.ExamQuesQty = null;
  752. learnRecordItem.TotalScore = null;
  753. learnRecordItem.Success = null;
  754. learnRecordItems.Add(learnRecordItem);
  755. }
  756. }
  757. }
  758. break;
  759. // 上傳作品
  760. case "WrkSpaceLoad":
  761. for (int j = 0; j < taskItems.Count; j++)
  762. {
  763. if (timeLineEvents.events[i].Pgid == taskItems[j].pageID) // 比對 Pgid 找到 IRS.json 該活動的詳細資料
  764. {
  765. for (int k = 0; k < taskItems[j].clientWorks.Count; k++)
  766. {
  767. LearnRecordItem learnRecordItem = new();
  768. for (int m = 0; m < lessonBase.student.Count; m++)
  769. {// 比對學生座號 取學號
  770. if (lessonBase.student[m].seatID == taskItems[j].clientWorks[k].seatID)
  771. {
  772. if (lessonBase.student[k].type == 2)
  773. {// 校內帳號
  774. await getSchoolCode(school, lessonId, lessonBase.student[k].id, learnRecordItem, groupList);
  775. }
  776. else
  777. {// 非校內帳號
  778. learnRecordItem.actor = setActor(school, lessonBase.student[k].id);
  779. }
  780. }
  781. }
  782. string[] arrymd = lessonBase.summary.date.Split('.');
  783. string[] arrhms = taskItems[j].clientWorks[k].reciveTime.Split(':');
  784. DateTime dt = new DateTime(int.Parse(arrymd[0]), int.Parse(arrymd[1]), int.Parse(arrymd[2]), int.Parse(arrhms[0]), int.Parse(arrhms[1]), int.Parse(arrhms[2]));
  785. dt = dt.AddSeconds(timeLineEvents.events[i].Time);
  786. learnRecordItem.verb = "SubmitTask";
  787. learnRecordItem.time = dt.ToUnixTimestamp();
  788. learnRecordItem.ID = timeLineEvents.events[i].Pgid;
  789. learnRecordItem.Desc = "隨堂作品";
  790. learnRecordItem.Correct = null;
  791. learnRecordItem.Choices = null;
  792. learnRecordItem.ExamQuesQty = null;
  793. learnRecordItem.TotalScore = null;
  794. learnRecordItem.Success = null;
  795. learnRecordItems.Add(learnRecordItem);
  796. }
  797. }
  798. }
  799. break;
  800. case "FastPgPush":
  801. for (int k = 0; k < lessonBase.student.Count; k++)
  802. {
  803. LearnRecordItem learnRecordItem = new();
  804. for (int m = 0; m < lessonBase.student.Count; m++)
  805. {// 比對學生座號 取學號
  806. if (lessonBase.student[k].type == 2)
  807. {
  808. await getSchoolCode(school, lessonId, lessonBase.student[k].id, learnRecordItem, groupList);
  809. }
  810. else
  811. {// 非校內帳號
  812. learnRecordItem.actor = setActor(school, lessonBase.student[k].id);
  813. }
  814. }
  815. string[] arrymd = lessonBase.summary.date.Split('.');
  816. string[] arrhms = lessonBase.summary.startTime.Split(':');
  817. DateTime dt = new DateTime(int.Parse(arrymd[0]), int.Parse(arrymd[1]), int.Parse(arrymd[2]), int.Parse(arrhms[0]), int.Parse(arrhms[1]), int.Parse(arrhms[2]));
  818. dt = dt.AddSeconds(timeLineEvents.events[i].Time);
  819. learnRecordItem.verb = "ViewPage";
  820. learnRecordItem.time = dt.ToUnixTimestamp();
  821. learnRecordItem.ID = timeLineEvents.events[i].Pgid;
  822. learnRecordItem.Desc = $"https://www.teammodel.net/viewpage?id={timeLineEvents.events[i].Pgid}";
  823. learnRecordItem.Correct = null;
  824. learnRecordItem.Choices = null;
  825. learnRecordItem.ExamQuesQty = null;
  826. learnRecordItem.TotalScore = null;
  827. learnRecordItem.Success = null;
  828. learnRecordItems.Add(learnRecordItem);
  829. }
  830. break;
  831. case "ActStart":
  832. case "ActEnd":
  833. for (int k = 0; k < lessonBase.student.Count; k++)
  834. {
  835. LearnRecordItem learnRecordItem = new();
  836. for (int m = 0; m < lessonBase.student.Count; m++)
  837. {// 比對學生座號 取學號
  838. if (lessonBase.student[k].type == 2)
  839. {
  840. await getSchoolCode(school, lessonId, lessonBase.student[k].id, learnRecordItem, groupList);
  841. }
  842. else
  843. {// 非校內帳號
  844. learnRecordItem.actor = setActor(school, lessonBase.student[k].id);
  845. }
  846. }
  847. string[] arrymd = lessonBase.summary.date.Split('.');
  848. string[] arrhms = lessonBase.summary.startTime.Split(':');
  849. DateTime dt = new DateTime(int.Parse(arrymd[0]), int.Parse(arrymd[1]), int.Parse(arrymd[2]), int.Parse(arrhms[0]), int.Parse(arrhms[1]), int.Parse(arrhms[2]));
  850. dt = dt.AddSeconds(timeLineEvents.events[i].Time);
  851. if (timeLineEvents.events[i].Event == "ActStart") { learnRecordItem.verb = "StartExam"; }
  852. if (timeLineEvents.events[i].Event == "ActEnd") { learnRecordItem.verb = "EndExam"; }
  853. learnRecordItem.time = dt.ToUnixTimestamp();
  854. learnRecordItem.ID = lessonId;
  855. learnRecordItem.Desc = lessonBase.summary.activityName;
  856. learnRecordItem.Correct = null;
  857. learnRecordItem.Choices = null;
  858. learnRecordItem.ExamQuesQty = 0;
  859. foreach (var item in timeLineEvents.events)
  860. {
  861. if (item.Event == "PopQuesLoad" || item.Event == "QuesLoad" || item.Event == "BuzrAns")
  862. {
  863. learnRecordItem.ExamQuesQty = learnRecordItem.ExamQuesQty + 1;
  864. }
  865. }
  866. learnRecordItem.TotalScore = learnRecordItem.ExamQuesQty * 10;
  867. learnRecordItem.Success = null;
  868. learnRecordItems.Add(learnRecordItem);
  869. }
  870. break;
  871. // 測驗
  872. case "SPQStrt":
  873. //// 開啟 .json
  874. //BlobDownloadResult taskblobDownload = await _azureStorage.GetBlobContainerClient(school).GetBlobClient($"/records/{lessonId}/IES/Task.json").DownloadContentAsync();
  875. //List<TaskItem> taskItems = taskblobDownload.Content.ToObjectFromJson<List<TaskItem>>();
  876. break;
  877. }
  878. }
  879. }
  880. #region 寫入blob
  881. //// 容器名称
  882. //string containerName = "twmoeld";
  883. //// 要上传的文件内容
  884. //string jsonString = Newtonsoft.Json.JsonConvert.SerializeObject(learnRecordItems);
  885. //// 指定目录和文件名
  886. //string directoryPath = @"D:\twmoeld";
  887. //if (htype == "0") { directoryPath = @"D:\twmoeld\school"; }
  888. //if (htype == "1") { directoryPath = @"D:\twmoeld\private"; }
  889. //if (htype == "2") { directoryPath = @"D:\twmoeld\exam"; }
  890. //directoryPath = $"{directoryPath}\\{date_path}";
  891. //string fileName = $"{_lessonId}.json";
  892. //string filePath = Path.Combine(directoryPath, fileName);
  893. // 容器名称
  894. string containerName = "twmoeld";
  895. // 新文件的名称
  896. string blobName = $"/{date_path}/{_lessonId}.json";
  897. // 要上传的文件内容
  898. string fileContent = Newtonsoft.Json.JsonConvert.SerializeObject(learnRecordItems);
  899. // 获取容器引用
  900. BlobContainerClient containerClient = _azureStorage.GetBlobContainerClient(containerName);
  901. // 获取 Blob 客户端
  902. BlobClient blobClient = containerClient.GetBlobClient(blobName);
  903. // 将文件内容上传到 Blob
  904. using (MemoryStream stream = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(fileContent)))
  905. {
  906. await blobClient.UploadAsync(stream, true);
  907. }
  908. //try
  909. //{
  910. // // 将 JSON 字符串写入文件
  911. // System.IO.File.WriteAllText(filePath, jsonString);
  912. // Console.WriteLine($"JSON 数据已成功写入文件: {filePath}");
  913. //}
  914. //catch (Exception ex)
  915. //{
  916. // Console.WriteLine($"写入文件时发生错误: {ex.Message}");
  917. //}
  918. #endregion
  919. }
  920. }
  921. catch (Exception ex)
  922. {
  923. // throw;
  924. }
  925. }
  926. #region ===雲端課程專區===
  927. /// <summary>
  928. /// 新增課程API
  929. /// </summary>
  930. /// <param name="request"></param>
  931. /// <returns></returns>
  932. [Authorize(Roles = "HiTeach")]
  933. [ProducesDefaultResponseType]
  934. [HttpPost("hiteach-create-course")]
  935. public async Task<IActionResult> Manage(JsonElement request)
  936. {
  937. try
  938. {
  939. #region === 驗證身分 ===
  940. string id_token = HttpContext.GetXAuth("IdToken");
  941. if (string.IsNullOrEmpty(id_token)) return BadRequest();
  942. var jwt = new JwtSecurityToken(id_token);
  943. if (!jwt.Payload.Iss.Equals("account.teammodel", StringComparison.OrdinalIgnoreCase)) return BadRequest();
  944. var tmdid = jwt.Payload.Sub;
  945. #endregion
  946. if (!request.TryGetProperty("courseName", out JsonElement courseName)) return BadRequest();
  947. if (!request.TryGetProperty("groupListName", out JsonElement groupListName)) return BadRequest();
  948. if (!request.TryGetProperty("creatorId", out JsonElement creatorId)) return BadRequest();
  949. if (!request.TryGetProperty("teacherName", out JsonElement tName)) return BadRequest();
  950. if (!request.TryGetProperty("limitCount", out JsonElement limitCount)) return BadRequest();
  951. // 未來會用到 是否允許自選座號
  952. request.TryGetProperty("isOptionalSeat", out JsonElement isOptionalSeat);
  953. request.TryGetProperty("courseId", out JsonElement courseId);
  954. var client = _azureCosmos.GetCosmosClient();
  955. CourseBase courseBase = new();
  956. GroupListWithCourseTaskId groupListWithCourseTaskId = new();
  957. if (string.IsNullOrWhiteSpace(courseId.GetString()))
  958. {// 如果沒有給課程Id 則為新增課程及名單
  959. #region === 新增課程 ===
  960. courseBase.name = courseName.GetString();
  961. courseBase.id = "";
  962. courseBase.subject = null;
  963. courseBase.period = null;
  964. courseBase.major = null;
  965. courseBase.desc = "";
  966. courseBase.no = "";
  967. courseBase.school = null;
  968. courseBase.color = null;
  969. courseBase.status = 1;
  970. courseBase.pk = "CourseBase";
  971. courseBase.scope = "private";
  972. string tbname = Constant.Teacher;
  973. StringBuilder sqlCourse = new StringBuilder(" select value c from c ");
  974. sqlCourse.Append($" where c.name ='{courseBase.name}' and c.creatorId='{tmdid}' ");
  975. courseBase.creatorId = tmdid;
  976. courseBase.code = $"CourseBase";
  977. List<CourseBase> courseBases = new List<CourseBase>();
  978. try
  979. {
  980. await foreach (var item in _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, tbname)
  981. .GetItemQueryIteratorSql<CourseBase>(queryText: sqlCourse.ToString(), requestOptions: new QueryRequestOptions { PartitionKey = new PartitionKey(courseBase.code) }))
  982. {
  983. if (courseBase.scope.Equals(item.scope))
  984. {// 只取個人課程
  985. if (!item.id.Equals(courseBase.id) && item.name.Equals(courseBase.name))
  986. {// id不同 但是名稱相同
  987. return Ok(new { code = 500, msg = "名字重复" });
  988. }
  989. }
  990. }
  991. }
  992. catch (Exception ex)
  993. {
  994. await _dingDing.SendBotMsg($"{_option.Location},执行课程更新异常{sqlCourse}\n{ex.Message},{ex.StackTrace}", GroupNames.成都开发測試群組);
  995. }
  996. if (string.IsNullOrWhiteSpace(courseBase.id))
  997. {// 新增課程 所以建一個新的id
  998. courseBase.id = Guid.NewGuid().ToString();
  999. }
  1000. // 新增課程
  1001. await client.GetContainer(Constant.TEAMModelOS, tbname).UpsertItemAsync(courseBase, new PartitionKey(courseBase.code));
  1002. #endregion
  1003. //新增名單
  1004. groupListWithCourseTaskId = await SaveGroupList(creatorId.GetString(), groupListName.GetString(), courseBase.id, isOptionalSeat, limitCount.GetInt32());
  1005. }
  1006. else
  1007. {// 如果有給課程Id 則只有新增名單
  1008. groupListWithCourseTaskId = await SaveGroupList(creatorId.GetString(), groupListName.GetString(), courseId.GetString(), isOptionalSeat, limitCount.GetInt32());
  1009. }
  1010. string apiUrl = this.Request.GetRequestUrlAddress();
  1011. // 解析URL
  1012. Uri uri = new Uri(apiUrl);
  1013. // 获取包含协议的主机部分
  1014. string hostWithProtocol = uri.GetLeftPart(UriPartial.Authority);
  1015. //組合完整網止
  1016. string longUrl = $"{hostWithProtocol}/joinclass?listName={groupListWithCourseTaskId.groupList.name}&cusDesc=暫無資料&cusId={groupListWithCourseTaskId.CourseTaskId}" +
  1017. $"&tName={tName}&listNo={groupListWithCourseTaskId.groupList.no}&cusName={courseName.GetString()}&m=前往加入課程:{courseName.GetString()}{groupListWithCourseTaskId.groupList.name}&o=1";
  1018. // 轉換為短網址
  1019. string shortUrl = await GetShortUrl(Uri.EscapeUriString(longUrl));
  1020. return Ok(new
  1021. {
  1022. code = 200,
  1023. result = shortUrl,
  1024. longUrl = $"{hostWithProtocol}/joinclass?listName={groupListWithCourseTaskId.groupList.name}&cusDesc=暫無資料&cusId={groupListWithCourseTaskId.CourseTaskId}&tName={tName}&listNo={groupListWithCourseTaskId.groupList.no}&cusName={courseName.GetString()}&m=前往加入課程:{courseName.GetString()}{groupListWithCourseTaskId.groupList.name}&o=1",
  1025. groupListId = groupListWithCourseTaskId.groupList.id
  1026. });
  1027. }
  1028. catch (Exception ex)
  1029. {
  1030. await _dingDing.SendBotMsg($"{_option.Location},课程处理异常,{ex.Message}\n{ex.StackTrace}", GroupNames.成都开发測試群組);
  1031. return Ok(new { code = 500, msg = ex.Message });
  1032. }
  1033. }
  1034. /// <summary>
  1035. /// 新增課程更新名單
  1036. /// </summary>
  1037. /// <param name="creatorId"></param>
  1038. /// <param name="groupListName"></param>
  1039. /// <param name="courseId"></param>
  1040. /// <param name="limitCount"></param>
  1041. /// <param name="joinLock"></param>
  1042. /// <returns></returns>
  1043. private async Task<GroupListWithCourseTaskId> SaveGroupList(string creatorId, string groupListName, string courseId, JsonElement isOptionalSeat, int limitCount = 200, int joinLock = 1)
  1044. {
  1045. try
  1046. {
  1047. //var (userid, _, _, school) = HttpContext.GetAuthTokenInfo();
  1048. //if (string.IsNullOrEmpty(list.type))
  1049. //{
  1050. // return BadRequest();
  1051. //}
  1052. int optNo = 0;
  1053. if (isOptionalSeat.ValueKind.Equals(JsonValueKind.True))
  1054. {
  1055. optNo = 1;
  1056. }
  1057. GroupListWithCourseTaskId groupListWithCourseTaskId = new();
  1058. GroupList list = new();
  1059. list.year = list.year > 0 ? list.year : DateTimeOffset.UtcNow.Year;
  1060. list.ttl = -1;
  1061. list.creatorId = creatorId;
  1062. list.leader = creatorId;
  1063. list.name = groupListName;
  1064. list.pk = "GroupList";
  1065. list.code = "GroupList";
  1066. list.school = null;
  1067. list.scope = "private";
  1068. list.joinLock = joinLock;
  1069. list.limitCount = limitCount;
  1070. list.optNo = optNo;
  1071. list.type = "teach";
  1072. GroupList dblist = null;
  1073. if (!string.IsNullOrWhiteSpace(list.id))
  1074. {
  1075. var response = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher).ReadItemStreamAsync(list.id, new PartitionKey(list.code));
  1076. if (response.StatusCode==System.Net.HttpStatusCode.OK)
  1077. {
  1078. dblist = JsonDocument.Parse(response.Content).RootElement.ToObject<GroupList>();
  1079. }
  1080. }
  1081. list = await GroupListService.CheckListNo(list, _azureCosmos, _dingDing, _option);
  1082. list = await GroupListService.UpsertList(list, _azureCosmos, _configuration, _serviceBus, _client: "hita");
  1083. #region === 更新DB課程名單 ===
  1084. //if (!request.TryGetProperty("datas", out JsonElement _datas))
  1085. //{
  1086. // return BadRequest();
  1087. //}
  1088. //string tbname = $"{scope}".Equals("school", StringComparison.OrdinalIgnoreCase) ? Constant.School : Constant.Teacher;
  1089. List<CourseTaskChanged> datas = new();
  1090. CourseTaskChanged courseTaskChanged = new();
  1091. courseTaskChanged.courseId = courseId;
  1092. courseTaskChanged.type = "teach";
  1093. courseTaskChanged.groupId = list.id;
  1094. courseTaskChanged.teacherId = creatorId;
  1095. courseTaskChanged.startTime = 0;
  1096. datas.Add(courseTaskChanged);
  1097. var client = _azureCosmos.GetCosmosClient();
  1098. string grant_type = "insert-scheduleTask";
  1099. string school = null;
  1100. //获取相关的名单
  1101. List<GroupListDto> groupListDtos = await GroupListService.GetGroupListByListids(client, _dingDing, datas.Select(z => z.groupId).ToHashSet().ToList(), school);
  1102. //获取相关的教室
  1103. var roomIds = datas.Where(x => !string.IsNullOrWhiteSpace(x.roomId)).Select(c => $"'{c.roomId}'");
  1104. List<Room> rooms = new List<Room>();
  1105. //获取教师
  1106. List<Teacher> teachers = new List<Teacher>();
  1107. var teacherIds = datas.Where(x => !string.IsNullOrWhiteSpace(x.teacherId)).Select(c => $"'{c.teacherId}'");
  1108. if (teacherIds.Any())
  1109. {
  1110. string sqlTeacher = $"select value c from c where c.id in ({string.Join(",", teacherIds)}) ";
  1111. var result = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher).GetList<Teacher>(sqlTeacher, $"Base");
  1112. if (result.list.IsNotEmpty())
  1113. {
  1114. teachers.AddRange(result.list);
  1115. }
  1116. }
  1117. //获取课程
  1118. List<CourseBase> courseBases = new List<CourseBase>();
  1119. var courseIds = datas.Where(x => !string.IsNullOrWhiteSpace(x.courseId)).Select(c => $"'{c.courseId}'");
  1120. if (courseIds.Any())
  1121. {
  1122. string sqlCourse = $"select value c from c where c.id in ({string.Join(",", courseIds)}) and c.creatorId='{creatorId}' ";
  1123. var result = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher).GetList<CourseBase>(sqlCourse, $"CourseBase");
  1124. if (result.list.IsNotEmpty())
  1125. {
  1126. courseBases.AddRange(result.list);
  1127. }
  1128. }
  1129. HashSet<CourseTask> courseTasks = new HashSet<CourseTask>();
  1130. List<CourseTaskChanged> invalidCourseTaskInsert = new List<CourseTaskChanged>();
  1131. foreach (var data in datas)
  1132. {
  1133. var courseTaskInsert = SchoolService.CheckCourseTaskInsertOrChanged($"{grant_type}", "private", data, null, null, courseBases, groupListDtos, rooms, null, teachers);
  1134. if (courseTaskInsert.invalidCode == 0)
  1135. {
  1136. string taskCode = $"CourseTask";
  1137. string taskId = courseTaskInsert.courseId;
  1138. CourseTask courseTask = courseTasks.Where(z => z.id.Equals(taskId) && z.code.Equals(taskCode)).FirstOrDefault();
  1139. if (courseTask == null)
  1140. {
  1141. ResponseMessage response = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher).ReadItemStreamAsync(taskId, new PartitionKey(taskCode));
  1142. if (response.StatusCode==System.Net.HttpStatusCode.OK)
  1143. {
  1144. courseTask = JsonDocument.Parse(response.Content).RootElement.ToObject<CourseTask>();
  1145. }
  1146. else
  1147. {
  1148. courseTask = new CourseTask
  1149. {
  1150. id = taskId,
  1151. code = taskCode,
  1152. pk = "CourseTask",
  1153. ttl = -1,
  1154. courseId = courseTaskInsert.courseId,
  1155. schedules = new List<ScheduleTask> { new ScheduleTask {
  1156. roomId = courseTaskInsert.roomId,
  1157. groupId = courseTaskInsert.groupId,
  1158. type = courseTaskInsert.type,
  1159. teacherId = courseTaskInsert.teacherId,
  1160. notice=courseTaskInsert.notice,
  1161. startTime = courseTaskInsert.startTime,
  1162. times= new List<ScheduleTime>(),
  1163. school=courseTaskInsert.school,
  1164. } }
  1165. };
  1166. }
  1167. }
  1168. ScheduleTask scheduleTask = null;
  1169. var scheduleTasks = courseTask.schedules.FindAll(z => z.type.Equals(courseTaskInsert.type) && z.groupId.Equals(courseTaskInsert.groupId));
  1170. if (scheduleTasks.IsNotEmpty())
  1171. {
  1172. if (scheduleTasks.Count > 2)
  1173. {
  1174. courseTask.schedules.RemoveRange(1, scheduleTasks.Count - 1);
  1175. }
  1176. scheduleTask = scheduleTasks.First();
  1177. scheduleTask.roomId = string.IsNullOrWhiteSpace(courseTaskInsert.roomId) ? scheduleTask.roomId : courseTaskInsert.roomId;
  1178. scheduleTask.school = school;
  1179. scheduleTask.teacherId = courseTaskInsert.teacherId;
  1180. scheduleTask.startTime = courseTaskInsert.startTime > 0 ? courseTaskInsert.startTime : scheduleTask.startTime;
  1181. scheduleTask.notice = string.IsNullOrWhiteSpace(courseTaskInsert.notice) ? scheduleTask.notice : courseTaskInsert.notice;
  1182. scheduleTask.assistants = courseTaskInsert.assistants != null ? courseTaskInsert.assistants : courseTaskInsert.assistants;
  1183. }
  1184. else
  1185. {
  1186. scheduleTask = new ScheduleTask()
  1187. {
  1188. roomId = courseTaskInsert.roomId,
  1189. groupId = courseTaskInsert.groupId,
  1190. type = courseTaskInsert.type,
  1191. teacherId = courseTaskInsert.teacherId,
  1192. times = new List<ScheduleTime>(),
  1193. school = school,
  1194. notice = courseTaskInsert.notice,
  1195. };
  1196. courseTask.schedules.Add(scheduleTask);
  1197. }
  1198. //修改教师或名单
  1199. if (grant_type.ToString().Equals("change-scheduleTask", StringComparison.OrdinalIgnoreCase))
  1200. {
  1201. if (!string.IsNullOrWhiteSpace(courseTaskInsert.teacherIdChanged))
  1202. {
  1203. scheduleTask.teacherId = courseTaskInsert.teacherIdChanged;
  1204. }
  1205. if (!string.IsNullOrWhiteSpace(courseTaskInsert.typeChanged) && !string.IsNullOrWhiteSpace(courseTaskInsert.groupIdChanged))
  1206. {
  1207. scheduleTask.groupId = courseTaskInsert.groupIdChanged;
  1208. scheduleTask.type = courseTaskInsert.typeChanged;
  1209. }
  1210. }
  1211. //await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher).UpsertItemAsync(courseTask, new PartitionKey(taskCode));
  1212. //删除教师及名单的排课信息
  1213. if (grant_type.ToString().Equals("delete-scheduleTask", StringComparison.OrdinalIgnoreCase))
  1214. {
  1215. courseTask.schedules.Remove(scheduleTask);
  1216. }
  1217. //如果被删除完了,就删除该条记录。
  1218. if (courseTask.schedules.Count <= 0)
  1219. {
  1220. courseTasks.Remove(courseTask);
  1221. await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher).DeleteItemStreamAsync(courseTask.id, new PartitionKey(courseTask.code));
  1222. }
  1223. else
  1224. {
  1225. courseTasks.Add(courseTask);
  1226. await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher).UpsertItemAsync(courseTask, new PartitionKey(taskCode));
  1227. }
  1228. groupListWithCourseTaskId.CourseTaskId = courseTask.id;
  1229. }
  1230. else
  1231. {
  1232. invalidCourseTaskInsert.Add(courseTaskInsert);
  1233. }
  1234. }
  1235. #endregion
  1236. groupListWithCourseTaskId.groupList = list;
  1237. return groupListWithCourseTaskId;
  1238. }
  1239. catch (Exception ex)
  1240. {
  1241. throw ex;
  1242. }
  1243. }
  1244. /// <summary>
  1245. /// 將完整網址轉換為短網址
  1246. /// </summary>
  1247. /// <param name="url"></param>
  1248. /// <returns></returns>
  1249. public async Task<string> GetShortUrl(string url)
  1250. {
  1251. try
  1252. {
  1253. var uri = new Uri(url);
  1254. string md5url = Md5Hash.GetMd5String(uri.OriginalString);
  1255. char[] chars = new char[]
  1256. {
  1257. 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
  1258. 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
  1259. 'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
  1260. 'y', 'z', '0', '1', '2', '3', '4', '5',
  1261. '6', '7', '8', '9', 'A', 'B', 'C', 'D',
  1262. 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
  1263. 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
  1264. 'U', 'V', 'W', 'X', 'Y', 'Z'
  1265. };
  1266. //把加密字元按照8位一組16進位制與0x3FFFFFFF進行位與運算
  1267. int hexint = 0x3FFFFFFF & Convert.ToInt32(string.Concat("0x", md5url.AsSpan(0, 8)), 16);
  1268. StringBuilder outShort = new();
  1269. for (int j = 0; j < 6; j++)
  1270. {
  1271. //把得到的值與0x0000003D進行位與運算,取得字元陣列chars索引
  1272. int index = 0x0000003D & hexint;
  1273. //把取得的字元相加
  1274. outShort.Append(chars[index]);
  1275. //每次迴圈按位右移5位
  1276. hexint >>= 5;
  1277. }
  1278. var shortid = outShort.ToString();
  1279. var table = _azureStorage.GetCloudTableClient().GetTableReference("ShortUrl");
  1280. ShortUrl su = new() { PartitionKey = uri.Host, RowKey = shortid, Url = uri.AbsoluteUri };
  1281. await table.SaveOrUpdate<ShortUrl>(su);
  1282. var result = new UriBuilder() { Scheme = uri.Scheme, Host = uri.Host, Path = shortid }.ToString();
  1283. return result;
  1284. }
  1285. catch (Exception ex)
  1286. {
  1287. throw ex;
  1288. }
  1289. }
  1290. /// <summary>
  1291. /// 取得課程列表 API
  1292. /// </summary>
  1293. /// <param name="request"></param>
  1294. /// <returns></returns>
  1295. [Authorize(Roles = "HiTeach")]
  1296. [ProducesDefaultResponseType]
  1297. [HttpPost("hiteach-get-courselist")]
  1298. public async Task<IActionResult> GetCourseList(JsonElement request)
  1299. {
  1300. try
  1301. {
  1302. string id_token = HttpContext.GetXAuth("IdToken");
  1303. if (string.IsNullOrEmpty(id_token)) return BadRequest();
  1304. if (!request.TryGetProperty("tmdid", out JsonElement _tmdid)) return BadRequest();
  1305. string tmdid = _tmdid.GetString();
  1306. var jwt = new JwtSecurityToken(id_token);
  1307. if (!jwt.Payload.Iss.Equals("account.teammodel", StringComparison.OrdinalIgnoreCase)) return BadRequest();
  1308. #region 取得資料邏輯
  1309. List<IdName> idNames = new();
  1310. HashSet<string> courseIds = new HashSet<string>();
  1311. // 1. 取CourseBase的資料 取全部的課程Id
  1312. string sqlCoursePrivate = $"select value c.id from c where c.creatorId='{tmdid}'";
  1313. var resultCourseBasePrivate = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher).GetList<string>(sqlCoursePrivate, $"CourseBase");
  1314. if (resultCourseBasePrivate.list.IsNotEmpty())
  1315. {
  1316. courseIds = new HashSet<string>(resultCourseBasePrivate.list);
  1317. }
  1318. // 2.取CourseTask的資料
  1319. string sqlprivate = $"SELECT distinct value c FROM c join b in c.schedules where c.pk='CourseTask' and (ARRAY_CONTAINS(b.assistants,'{tmdid}')or b.teacherId ='{tmdid}' )";
  1320. var resultTeacher = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher).GetList<CourseTask>(sqlprivate, $"CourseTask");
  1321. if (resultTeacher.list.IsNotEmpty())
  1322. {
  1323. resultTeacher.list.ForEach(x =>
  1324. {
  1325. var schedulesTeacherOrAssistant = x.schedules.Where(z => (!string.IsNullOrWhiteSpace(z.teacherId) && z.teacherId.Equals(tmdid)) || z.assistants.Contains(tmdid));
  1326. if (schedulesTeacherOrAssistant.Any())
  1327. {// 收集符合條件的課程 符合老師id 或是 符合助理人選
  1328. courseIds.Add(x.courseId);
  1329. }
  1330. });
  1331. }
  1332. // 3. 整合出最後資料
  1333. if (courseIds.Any())
  1334. {
  1335. string sqlCourse = $"select distinct c.id, c.name from c where c.code='CourseBase' and ( c.creatorId='{tmdid}' or c.id in ({string.Join(",", courseIds.Select(b => $"'{b}'"))}))";
  1336. var result = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher).GetList<CourseBase>(sqlCourse, $"CourseBase");
  1337. if (result.list.IsNotEmpty())
  1338. {
  1339. foreach (var item in result.list)
  1340. {
  1341. idNames.Add(new IdName { name = item.name, id = item.id });
  1342. }
  1343. }
  1344. }
  1345. #endregion
  1346. return Ok(new { code = 200, result = idNames });
  1347. }
  1348. catch (Exception ex)
  1349. {
  1350. await _dingDing.SendBotMsg($"{_option.Location},课程处理异常,{ex.Message}\n{ex.StackTrace}", GroupNames.成都开发測試群組);
  1351. return Ok(new { code = 500, msg = ex.Message });
  1352. }
  1353. }
  1354. /// <summary>
  1355. /// 更新課程名單 API
  1356. /// </summary>
  1357. /// <param name="request"></param>
  1358. /// <returns></returns>
  1359. [Authorize(Roles = "HiTeach")]
  1360. [ProducesDefaultResponseType]
  1361. [HttpPost("hiteach-upsert-grouplist")]
  1362. public async Task<IActionResult> UpsertGroupList(JsonElement request)
  1363. {
  1364. try
  1365. {
  1366. string id_token = HttpContext.GetXAuth("IdToken");
  1367. if (string.IsNullOrEmpty(id_token)) return BadRequest();
  1368. var jwt = new JwtSecurityToken(id_token);
  1369. if (!jwt.Payload.Iss.Equals("account.teammodel", StringComparison.OrdinalIgnoreCase)) return BadRequest();
  1370. if (!request.TryGetProperty("id", out JsonElement _id)) return BadRequest();
  1371. if (!request.TryGetProperty("limitCount", out JsonElement _limitCount)) return BadRequest();
  1372. if (!request.TryGetProperty("isOptionalSeat", out JsonElement _isOptionalSeat)) return BadRequest();
  1373. if (!request.TryGetProperty("joinLock", out JsonElement _joinLock)) return BadRequest();
  1374. if (!request.TryGetProperty("review", out JsonElement _review)) return BadRequest();
  1375. var clientTeacher = _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher);
  1376. // 先撈指定id的資料
  1377. GroupList groupList = await clientTeacher.ReadItemAsync<GroupList>(_id.GetString(), new PartitionKey($"GroupList"));
  1378. //設定需更新的欄位值
  1379. groupList.limitCount = _limitCount.GetInt32();
  1380. //groupList.isOptionalSeat = isOptionalSeat.GetString();
  1381. int optNo = 0;
  1382. if (_isOptionalSeat.ValueKind.Equals(JsonValueKind.True))
  1383. {
  1384. optNo = 1;
  1385. }
  1386. groupList.optNo = optNo;
  1387. groupList.joinLock = _joinLock.GetInt32();
  1388. groupList.review = _review.GetInt32();
  1389. //更新
  1390. groupList = await clientTeacher.ReplaceItemAsync(groupList, $"{groupList.id}", new PartitionKey(groupList.code));
  1391. return Ok(new { code = 200 });
  1392. }
  1393. catch (Exception ex)
  1394. {
  1395. await _dingDing.SendBotMsg($"{_option.Location},课程处理异常,{ex.Message}\n{ex.StackTrace}", GroupNames.成都开发測試群組);
  1396. return Ok(new { code = 500, msg = ex.Message });
  1397. }
  1398. }
  1399. /// <summary>
  1400. /// 取得課程名單QRcode API
  1401. /// </summary>
  1402. /// <param name="request"></param>
  1403. /// <returns></returns>
  1404. [Authorize(Roles = "HiTeach")]
  1405. [ProducesDefaultResponseType]
  1406. [HttpPost("hiteach-get-courseqrcode")]
  1407. public async Task<IActionResult> GetCourseQrcode(JsonElement request)
  1408. {
  1409. try
  1410. {
  1411. #region === 驗證身分 ===
  1412. string id_token = HttpContext.GetXAuth("IdToken");
  1413. if (string.IsNullOrEmpty(id_token)) return BadRequest();
  1414. var jwt = new JwtSecurityToken(id_token);
  1415. if (!jwt.Payload.Iss.Equals("account.teammodel", StringComparison.OrdinalIgnoreCase)) return BadRequest();
  1416. #endregion
  1417. if (!request.TryGetProperty("courseName", out JsonElement _courseName)) return BadRequest();
  1418. if (!request.TryGetProperty("teacherName", out JsonElement _teacherName)) return BadRequest();
  1419. if (!request.TryGetProperty("groupListId", out JsonElement _groupListId)) return BadRequest();
  1420. if (!request.TryGetProperty("courseTaskId", out JsonElement _courseTaskId)) return BadRequest();
  1421. var clientTeacher = _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher);
  1422. GroupList groupList = await clientTeacher.ReadItemAsync<GroupList>(_groupListId.GetString(), new PartitionKey($"GroupList"));
  1423. #region === 網址轉換 ===
  1424. string apiUrl = this.Request.GetRequestUrlAddress();
  1425. // 解析URL
  1426. Uri uri = new Uri(apiUrl);
  1427. // 获取包含协议的主机部分
  1428. string hostWithProtocol = uri.GetLeftPart(UriPartial.Authority);
  1429. string longUrl = $"{hostWithProtocol}/joinclass?listName={groupList.name}&cusDesc=暫無資料&cusId={_courseTaskId.GetString()}&tName={_teacherName.GetString()}" +
  1430. $"&listNo={groupList.no}&cusName={_courseName.GetString()}&m=前往加入課程:{_courseName.GetString()}{groupList.name}&o=1";
  1431. string shortUrl = await GetShortUrl(Uri.EscapeUriString(longUrl));
  1432. #endregion
  1433. return Ok(new
  1434. {
  1435. code = 200,
  1436. shortUrl = shortUrl,
  1437. longUrl = $"{hostWithProtocol}/joinclass?listName={groupList.name}&cusId={_courseTaskId.GetString()}&tName={_teacherName.GetString()}&listNo={groupList.no}&cusName={_courseName.GetString()}&m=前往加入課程:{_courseName.GetString()}{groupList.name}&o=1",
  1438. joinLock = groupList.joinLock,
  1439. limitCount = groupList.limitCount,
  1440. review = groupList.review,
  1441. isOptionalSeat = groupList.optNo == 1 ? true : false,
  1442. });
  1443. }
  1444. catch (Exception ex)
  1445. {
  1446. await _dingDing.SendBotMsg($"{_option.Location},API异常,{ex.Message}\n{ex.StackTrace}", GroupNames.成都开发測試群組);
  1447. return Ok(new { code = 500, msg = ex.Message });
  1448. }
  1449. }
  1450. #endregion
  1451. private string setActor(string school, string id)
  1452. {
  1453. if (!string.IsNullOrWhiteSpace(school))
  1454. {
  1455. return $"Local-{school.Trim()},{id}";
  1456. }
  1457. else
  1458. {
  1459. return id;
  1460. }
  1461. }
  1462. /// <summary>
  1463. /// 設定學習記錄互動的內容
  1464. /// </summary>
  1465. /// <param name="lessonBase"></param>
  1466. /// <param name="school"></param>
  1467. /// <param name="lessonId"></param>
  1468. /// <param name="iRSItem"></param>
  1469. /// <param name="timeLineEvent"></param>
  1470. /// <param name="verb"></param>
  1471. /// <param name="learnRecordItems"></param>
  1472. /// <returns></returns>
  1473. private async Task SetPopQuesLoadContent(ScoreLessonBase lessonBase, string school, string lessonId, IRSItem iRSItem, TimeLineEventPg timeLineEvent, string verb, List<LearnRecordItem> learnRecordItems, GroupList groupList)
  1474. {
  1475. for (int k = 0; k < lessonBase.student.Count; k++)
  1476. {
  1477. LearnRecordItem learnRecordItem = new();
  1478. if (lessonBase.student[k].type == 2)
  1479. {
  1480. await getSchoolCode(school, lessonId, lessonBase.student[k].id, learnRecordItem, groupList);
  1481. }
  1482. else
  1483. {// 非校內帳號
  1484. learnRecordItem.actor = setActor(school, lessonBase.student[k].id);
  1485. }
  1486. string[] arrymd = lessonBase.summary.date.Split('.');
  1487. string[] arrhms = lessonBase.summary.startTime.Split(':');
  1488. DateTime dt = new DateTime(int.Parse(arrymd[0]), int.Parse(arrymd[1]), int.Parse(arrymd[2]), int.Parse(arrhms[0]), int.Parse(arrhms[1]), int.Parse(arrhms[2]));
  1489. dt = dt.AddSeconds(timeLineEvent.Time);
  1490. learnRecordItem.verb = verb;
  1491. learnRecordItem.time = dt.ToUnixTimestamp();
  1492. learnRecordItem.ID = timeLineEvent.Pgid;
  1493. if (iRSItem.question.item[0].question == "_popquiz_" || iRSItem.question.item[0].question == "")
  1494. {
  1495. learnRecordItem.Desc = "隨堂問答";
  1496. }
  1497. else
  1498. {
  1499. learnRecordItem.Desc = iRSItem.question.item[0].question;
  1500. }
  1501. learnRecordItem.Points = iRSItem.question.exercise.knowledges;
  1502. await setCorrectChoices(iRSItem, learnRecordItem);
  1503. learnRecordItem.ExamQuesQty = null;
  1504. learnRecordItem.TotalScore = null;
  1505. iRSItem.clientAnswers.TryGetProperty("0", out JsonElement anwersArr);
  1506. if (anwersArr.ValueKind == JsonValueKind.Undefined)
  1507. {// 沒有作答
  1508. return;
  1509. }
  1510. else
  1511. {
  1512. List<List<string>> clientAnswersarr = anwersArr.ToJsonString().ToObject<List<List<string>>>();
  1513. if (iRSItem.question.exercise.answer.SequenceEqual(clientAnswersarr[k]))
  1514. {
  1515. learnRecordItem.Success = true;
  1516. }
  1517. else
  1518. {
  1519. learnRecordItem.Success = false;
  1520. }
  1521. learnRecordItems.Add(learnRecordItem);
  1522. }
  1523. }
  1524. }
  1525. /// <summary>
  1526. /// 取得學校簡碼
  1527. /// </summary>
  1528. /// <param name="school"></param>
  1529. /// <param name="lessonId"></param>
  1530. /// <param name="studentId"></param>
  1531. /// <param name="learnRecordItem"></param>
  1532. /// <returns></returns>
  1533. private async Task getSchoolCode(string school, string lessonId, string studentId, LearnRecordItem learnRecordItem, GroupList groupList)
  1534. {
  1535. if (string.IsNullOrWhiteSpace(school))
  1536. {// 如果沒有學校代碼 需要用lessonId去DB取學校代碼
  1537. if (!string.IsNullOrWhiteSpace(groupList.school))// 如果沒有撈到schoolid防呆
  1538. {
  1539. learnRecordItem.actor = $"Base-{groupList.school.Trim()},{studentId}";
  1540. }
  1541. else
  1542. {
  1543. learnRecordItem.actor = studentId;
  1544. }
  1545. }
  1546. else
  1547. {// 如果有學校代碼 校內帳號用組合的 "Base-hbgl,473891247381"
  1548. learnRecordItem.actor = $"Base-{school.Trim()},{studentId}";
  1549. }
  1550. }
  1551. /// <summary>
  1552. /// 設定CorrectChoices的邏輯
  1553. /// </summary>
  1554. /// <param name="iRSItems"></param>
  1555. /// <param name="learnRecordItem"></param>
  1556. /// <returns></returns>
  1557. private async Task setCorrectChoices(IRSItem iRSItem, LearnRecordItem learnRecordItem)
  1558. {
  1559. #region === Correct Choices ===
  1560. #region === 是非題邏輯 ===
  1561. if (iRSItem.question.exercise.type == "judge" && iRSItem.question.exercise.answer.Count > 0)
  1562. {// 如果是是非題 正確答案要用true false的方式設定
  1563. if (iRSItem.question.exercise.answer[0] == "A")
  1564. {
  1565. learnRecordItem.Correct = new string[] { "true" };
  1566. }
  1567. else
  1568. {
  1569. learnRecordItem.Correct = new string[] { "false" };
  1570. }
  1571. }
  1572. else
  1573. {
  1574. learnRecordItem.Correct = iRSItem.question.exercise.answer;
  1575. }
  1576. #endregion
  1577. if (iRSItem.question.item[0].option.Count > 0)
  1578. {// 如果有選項資料 記錄起來
  1579. foreach (var option in iRSItem.question.item[0].option)
  1580. {
  1581. ChoicesItem item = new();
  1582. item.id = option.code;
  1583. if (iRSItem.question.exercise.type == "judge")
  1584. {// 如果是是非題 固定選項
  1585. if (option.code == "A")
  1586. {
  1587. item.description.zhTW = "是";
  1588. }
  1589. else
  1590. {
  1591. item.description.zhTW = "否";
  1592. }
  1593. }
  1594. else
  1595. {
  1596. if (string.IsNullOrWhiteSpace(option.value))
  1597. {
  1598. item.description.zhTW = $"選項{option.code}";
  1599. }
  1600. else
  1601. {
  1602. item.description.zhTW = option.value;
  1603. }
  1604. }
  1605. learnRecordItem.Choices.Add(item);
  1606. }
  1607. }
  1608. #endregion
  1609. }
  1610. /// <summary>
  1611. /// 创建课堂开课记录(新版)
  1612. /// </summary>
  1613. /// <param name="request"></param>
  1614. /// <returns></returns>
  1615. #if !DEBUG
  1616. [Authorize(Roles = "HiTeach")]
  1617. #endif
  1618. [ProducesDefaultResponseType]
  1619. [HttpPost("create-lesson")]
  1620. public async Task<IActionResult> CreateLesson(CreateLessondRequest request)
  1621. {
  1622. try
  1623. {
  1624. string id_token = HttpContext.GetXAuth("IdToken");
  1625. //判斷ID Token
  1626. if (string.IsNullOrWhiteSpace(id_token)) return BadRequest();
  1627. var jwt = new JwtSecurityToken(id_token);
  1628. if (!jwt.Payload.Iss.Equals("account.teammodel", StringComparison.OrdinalIgnoreCase)) return BadRequest();
  1629. var tid = jwt.Payload.Sub;
  1630. //初始化
  1631. var r8 = _azureRedis.GetRedisClient(8);
  1632. var db = _azureCosmos.GetCosmosClient();
  1633. var sbm = new List<ServiceBusMessage>();
  1634. var sp = request.sp.Equals("school", StringComparison.OrdinalIgnoreCase);
  1635. int size = 0; //學校或個人總空間
  1636. int tsize = 0; //學校分配給老師的總空間
  1637. double usize = 0; //學校或個人已使用空間
  1638. string timezone = string.Empty;
  1639. string schoolArea = string.Empty;
  1640. if (sp)
  1641. {
  1642. //取得學校資訊
  1643. if (string.IsNullOrWhiteSpace(request.school)) return BadRequest();
  1644. await foreach (var item in db.GetContainer(Constant.TEAMModelOS, Constant.School).GetItemQueryStreamIteratorSql(
  1645. queryText: $"SELECT TOP 1 c.id, c.size, c.tsize, c.timeZone FROM c WHERE c.id = '{request.school}'",
  1646. requestOptions: new() { PartitionKey = new("Base") }))
  1647. {
  1648. using var json = await JsonDocument.ParseAsync(item.Content);
  1649. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  1650. {
  1651. var school = json.RootElement.GetProperty("Documents").EnumerateArray().First();
  1652. timezone = school.GetProperty("timeZone").GetProperty("value").GetString();
  1653. size = school.GetProperty("size").GetInt32();
  1654. tsize = school.TryGetProperty("tsize", out var tsvalue) ? tsvalue.GetInt32() : 0;
  1655. schoolArea=school.TryGetProperty("areaId", out var areaId) ? $"{areaId}" : string.Empty;
  1656. }
  1657. else return BadRequest();
  1658. }
  1659. }
  1660. else
  1661. {
  1662. Teacher teacher = await db.GetContainer(Constant.TEAMModelOS, Constant.Teacher).ReadItemAsync<Teacher>(tid, new PartitionKey("Base"));
  1663. size = teacher.size;
  1664. foreach (var school in teacher.schools)
  1665. {
  1666. try
  1667. {
  1668. SchoolTeacher st = await db.GetContainer(Constant.TEAMModelOS, Constant.School).ReadItemAsync<SchoolTeacher>(tid, new PartitionKey($"Teacher-{school.schoolId}"));
  1669. size += st.size;
  1670. }
  1671. catch (CosmosException)
  1672. {
  1673. }
  1674. }
  1675. }
  1676. (long usedSize, long teach, long total, long surplus, Dictionary<string, double?> catalog) space = await BlobService.GetSurplusSpace($"{(sp ? request.school : tid)}", request.sp, _option.Location, _azureCosmos, _azureRedis, _azureStorage, _dingDing, _httpTrigger);
  1677. //計算學校或個人的使用空間
  1678. // RedisValue redisValue = r8.HashGet($"Blob:Record", $"{(sp ? request.school : tid)}");
  1679. if (space.usedSize > 0)
  1680. {
  1681. usize = Math.Round(space.usedSize / 1073741824.0 - (sp ? tsize : 0), 2, MidpointRounding.AwayFromZero); //1073741824 1G
  1682. }
  1683. else //如果檢測不到緩存,觸發刷新計算空間
  1684. {
  1685. await BlobService.RefreshBlobRoot(new BlobRefreshMessage { progress = "update", root = "records", name = $"{(sp ? request.school : tid)}" }, _serviceBus, _configuration, _azureRedis);
  1686. }
  1687. //取得學校或個人名單
  1688. (List<RMember> students, _) = await GroupListService.GetMemberByListids(_coreAPIHttpService, db, _dingDing, new List<string>() { request.sid }, request.school);
  1689. string imeiType = string.Empty;
  1690. if (_option.Location.Contains("China", StringComparison.OrdinalIgnoreCase))
  1691. {
  1692. //觸發IMEI更新消息
  1693. imeiType = "tianbo";
  1694. if (ThirdIRSController.areaSchools.TryGetValue(request.sid, out (string scope, string imeiType) valg) && valg.scope.Equals("grouplist"))
  1695. {
  1696. imeiType=valg.imeiType;
  1697. }
  1698. else {
  1699. if(!string.IsNullOrWhiteSpace(request.school))
  1700. {
  1701. if (ThirdIRSController.areaSchools.TryGetValue(request.school, out (string scope, string imeiType) vals) && vals.scope.Equals("school"))
  1702. {
  1703. imeiType=vals.imeiType;
  1704. }
  1705. else
  1706. {
  1707. if (ThirdIRSController.areaSchools.TryGetValue(schoolArea, out (string scope, string imeiType) vala) && vala.scope.Equals("area"))
  1708. {
  1709. imeiType=vala.imeiType;
  1710. }
  1711. }
  1712. }
  1713. }
  1714. var stus = students.Select(x => x.id).ToList();
  1715. if (stus.IsNotEmpty() && !string.IsNullOrWhiteSpace(request.school))
  1716. {
  1717. string sql = $"select value count(1) from c where c.stuid in({string.Join (",",stus.Select(x=>$"'{x}'"))}) and c.school='{request.school}' ";
  1718. var result = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Student).GetList<int>(sql, "Imei");
  1719. if (result.list.IsNotEmpty() && result.list[0]>0)
  1720. {
  1721. var imeimsg = new ServiceBusMessage(new { request.channel, userid = request.did, request.school, stus, imeiType }.ToJsonString());
  1722. imeimsg.ApplicationProperties.Add("name", "Imei");
  1723. sbm.Add(imeimsg);
  1724. }
  1725. else {
  1726. imeiType=string.Empty;
  1727. }
  1728. }
  1729. }
  1730. //開課記錄保存
  1731. LessonRecord lr = new()
  1732. {
  1733. school = request.sp.Equals("school") ? request?.school : null,
  1734. code = request.sp.Equals("school") ? $"LessonRecord-{request.school}" : $"LessonRecord",
  1735. id = _snowflakeId.NextId().ToString(), //取得授課ID
  1736. courseId = request.cid,
  1737. groupIds = new() { request.sid },
  1738. tmdid = tid,
  1739. scope = request.sp,
  1740. pk = "LessonRecord",
  1741. name = request.cname,
  1742. startTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()
  1743. };
  1744. await db.GetContainer(Constant.TEAMModelOS, request.sp.Equals("school") ? "School" : "Teacher").CreateItemAsync(lr, new PartitionKey(lr.code));
  1745. //觸發開課統計
  1746. var messageChange = new ServiceBusMessage(lr.ToJsonString());
  1747. messageChange.ApplicationProperties.Add("name", "LessonRecordEvent");
  1748. sbm.Add(messageChange);
  1749. //批量發送消息
  1750. await _serviceBus.GetServiceBusClient().SendBatchMessageAsync(_configuration.GetValue<string>("Azure:ServiceBus:ActiveTask"), sbm);
  1751. if (sp && usize > size)
  1752. {
  1753. ////处理学校开课,空间不足时。检查是否有 当前教师tid,强制保存save<>1,没有标记未删除status<>404,没有被收藏favorite<=0 ,时间最旧的一条记录startTime
  1754. LessonRecord lessonRecord = null;
  1755. string sql = $"SELECT top 1 value(c) FROM c where ( c.expire<=0 or IS_DEFINED(c.expire) = false ) and c.tmdid='{tid}' and c.save<>1 and c.status<>404 and c.favorite<=0 order by c.startTime ";
  1756. await foreach (var item in db.GetContainer(Constant.TEAMModelOS, Constant.School).GetItemQueryIteratorSql<LessonRecord>(
  1757. queryText: sql, requestOptions: new QueryRequestOptions { PartitionKey = new PartitionKey($"LessonRecord-{request.school}") }))
  1758. {
  1759. lessonRecord = item;
  1760. break;
  1761. }
  1762. if (lessonRecord != null)
  1763. {
  1764. lessonRecord.status = 404;
  1765. await db.GetContainer(Constant.TEAMModelOS, Constant.School).ReplaceItemAsync(lessonRecord, lessonRecord.id, new PartitionKey(lessonRecord.code));
  1766. var ActiveTask = _configuration.GetValue<string>("Azure:ServiceBus:ActiveTask");
  1767. var messageChangeEvent = new ServiceBusMessage(request.ToJsonString());
  1768. messageChangeEvent.ApplicationProperties.Add("name", "LessonRecordEvent");
  1769. await _serviceBus.GetServiceBusClient().SendMessageAsync(ActiveTask, messageChangeEvent);
  1770. //保证客户端可以正常开课。
  1771. usize -= 1;
  1772. }
  1773. else
  1774. { //没有找到匹配当前 教师tid,save<>1,status<>404,没有被收藏,时间最旧的一条记录。无法手动再继续 usize -= 1;,则不能继续开课。
  1775. }
  1776. }
  1777. return Ok(new { status = 200, lr.id, students, size, usize, imeiType });
  1778. }
  1779. catch (Exception ex)
  1780. {
  1781. await _dingDing.SendBotMsg($"IES5,{_option.Location}, hiteach/create-lesson:()\n{ex.Message}\n{ex.StackTrace}{request.ToJsonString()}", GroupNames.醍摩豆服務運維群組);
  1782. return BadRequest();
  1783. }
  1784. }
  1785. /// <summary>
  1786. /// 创建课堂开课记录(舊版,已被CreateLesson取代)
  1787. /// </summary>
  1788. /// <param name="request"></param>
  1789. /// <returns></returns>
  1790. [Authorize(Roles = "HiTeach")]
  1791. [ProducesDefaultResponseType]
  1792. [HttpPost("create-lesson-record")]
  1793. public async Task<IActionResult> CreateLessonRecord(JsonElement request)
  1794. {
  1795. if (!request.TryGetProperty("lesson", out JsonElement _lesson)) return BadRequest();
  1796. var client = _azureCosmos.GetCosmosClient();
  1797. LessonRecord lessonRecord = _lesson.ToObject<LessonRecord>();
  1798. int blobTotal = 0;
  1799. long teach = 0;
  1800. string blobName = "";
  1801. if (!string.IsNullOrEmpty(lessonRecord.scope) && !string.IsNullOrEmpty(lessonRecord.tmdid) && !string.IsNullOrEmpty(lessonRecord.id))
  1802. {
  1803. string tbname = null;
  1804. if (lessonRecord.scope.Equals("school") && !string.IsNullOrEmpty(lessonRecord.school))
  1805. {
  1806. School school = await client.GetContainer(Constant.TEAMModelOS, Constant.School).ReadItemAsync<School>(lessonRecord.school, new PartitionKey("Base"));
  1807. lessonRecord.code = $"LessonRecord-{lessonRecord.school}";
  1808. tbname = "School";
  1809. blobTotal = school.size;
  1810. //计算分配给学校教师的空间
  1811. await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryStreamIteratorSql(queryText: $"SELECT sum(c.size) as size FROM c ",
  1812. requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Teacher-{lessonRecord.school}") }))
  1813. {
  1814. var json = await JsonDocument.ParseAsync(item.Content);
  1815. foreach (var elmt in json.RootElement.GetProperty("Documents").EnumerateArray())
  1816. {
  1817. if (elmt.TryGetProperty("size", out JsonElement _size) && _size.ValueKind.Equals(JsonValueKind.Number))
  1818. {
  1819. teach = _size.GetInt32();
  1820. break;
  1821. }
  1822. }
  1823. }
  1824. blobName = lessonRecord.school;
  1825. }
  1826. if (lessonRecord.scope.Equals("private"))
  1827. {
  1828. lessonRecord.code = $"LessonRecord";
  1829. tbname = "Teacher";
  1830. Teacher teacher = await client.GetContainer(Constant.TEAMModelOS, Constant.Teacher).ReadItemAsync<Teacher>(lessonRecord.tmdid, new PartitionKey("Base"));
  1831. blobTotal = teacher.size;
  1832. foreach (var school in teacher.schools)
  1833. {
  1834. SchoolTeacher st = await client.GetContainer(Constant.TEAMModelOS, "School").ReadItemAsync<SchoolTeacher>(lessonRecord.tmdid, new PartitionKey($"Teacher-{school.schoolId}"));
  1835. blobTotal += st.size;
  1836. }
  1837. blobName = lessonRecord.tmdid;
  1838. }
  1839. RedisValue redisValue = _azureRedis.GetRedisClient(8).HashGet($"Blob:Record", $"{blobName}");
  1840. long blobsize = 0;
  1841. if (redisValue != default && !redisValue.IsNullOrEmpty)
  1842. {
  1843. JsonElement record = redisValue.ToString().ToObject<JsonElement>();
  1844. if (record.TryGetInt64(out blobsize))
  1845. {
  1846. }
  1847. }
  1848. else
  1849. {
  1850. await BlobService.RefreshBlobRoot(new BlobRefreshMessage { progress = "update", root = "records", name = $"{blobName}" }, _serviceBus, _configuration, _azureRedis);
  1851. }
  1852. double blobUsed = blobsize / 1073741824.0 + teach; //1073741824 1G
  1853. if (tbname == null)
  1854. {
  1855. return BadRequest("参数异常:scope应为school或private,scope=school ,school字段不能为空");
  1856. }
  1857. if (lessonRecord.startTime == 0)
  1858. {
  1859. lessonRecord.startTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
  1860. }
  1861. lessonRecord.pk = "LessonRecord";
  1862. try
  1863. {
  1864. await client.GetContainer(Constant.TEAMModelOS, tbname).CreateItemAsync(lessonRecord, new PartitionKey(lessonRecord.code));
  1865. var ActiveTask = _configuration.GetValue<string>("Azure:ServiceBus:ActiveTask");
  1866. var messageChange = new ServiceBusMessage(lessonRecord.ToJsonString());
  1867. messageChange.ApplicationProperties.Add("name", "LessonRecordEvent");
  1868. await _serviceBus.GetServiceBusClient().SendMessageAsync(ActiveTask, messageChange);
  1869. return Ok(new { status = 200, blobTotal, blobUsed });
  1870. }
  1871. catch (Exception ex)
  1872. {
  1873. await _dingDing.SendBotMsg($"IES5,{_option.Location}, hiteach/create-lesson-record:()\n{ex.Message}\n{ex.StackTrace}{request.ToJsonString()}", GroupNames.醍摩豆服務運維群組);
  1874. return BadRequest();
  1875. }
  1876. }
  1877. else
  1878. {
  1879. return BadRequest();
  1880. }
  1881. }
  1882. [Authorize(Roles = "HiTeach")]
  1883. [ProducesDefaultResponseType]
  1884. [HttpPost("get-teacher-info")]
  1885. public async Task<IActionResult> GetTeacherInfo()
  1886. {
  1887. //Debug
  1888. //string json = System.Text.Json.JsonSerializer.Serialize(id_token);
  1889. try
  1890. {
  1891. string id_token = HttpContext.GetXAuth("IdToken");
  1892. (string ip, string region) = await LoginService.LoginIp(HttpContext, _searcher);
  1893. if (string.IsNullOrEmpty(id_token)) return BadRequest();
  1894. var jwt = new JwtSecurityToken(id_token);
  1895. if (!jwt.Payload.Iss.Equals("account.teammodel", StringComparison.OrdinalIgnoreCase)) return BadRequest();
  1896. var id = jwt.Payload.Sub;
  1897. jwt.Payload.TryGetValue("name", out object name);
  1898. jwt.Payload.TryGetValue("picture", out object picture);
  1899. List<object> schools = new List<object>();
  1900. string defaultschool = null;
  1901. //TODK 取得Teacher 個人相關數據(課程清單、虛擬教室清單、歷史紀錄清單等),學校數據另外API處理,多校切換時不同
  1902. var client = _azureCosmos.GetCosmosClient();
  1903. var response = await client.GetContainer(Constant.TEAMModelOS, "Teacher").ReadItemStreamAsync(id, new PartitionKey("Base"));
  1904. if (response.StatusCode==System.Net.HttpStatusCode.OK)
  1905. {
  1906. var jsonsc = await JsonDocument.ParseAsync(response.Content);
  1907. if (jsonsc.RootElement.TryGetProperty("schools", out JsonElement value))
  1908. {
  1909. GetTeacherInfoApiSchool schoolExtobj;
  1910. foreach (var obj in value.EnumerateArray())
  1911. {
  1912. string statusNow = obj.GetProperty("status").ToString();
  1913. if (statusNow.Equals("join")) //成為老師的才放入
  1914. {
  1915. var schoolJson = await client.GetContainer(Constant.TEAMModelOS, "School").ReadItemStreamAsync($"{obj.GetProperty("schoolId")}", new PartitionKey("Base"));
  1916. if (schoolJson.StatusCode==System.Net.HttpStatusCode.OK)
  1917. {
  1918. var school = await JsonDocument.ParseAsync(schoolJson.Content);
  1919. schoolExtobj = new GetTeacherInfoApiSchool();
  1920. schoolExtobj.schoolId = Convert.ToString(obj.GetProperty("schoolId"));
  1921. schoolExtobj.name = Convert.ToString(school.RootElement.GetProperty("name"));
  1922. schoolExtobj.status = Convert.ToString(obj.GetProperty("status"));
  1923. schoolExtobj.picture = Convert.ToString(school.RootElement.GetProperty("picture"));
  1924. schools.Add(schoolExtobj);
  1925. var sctch = await client.GetContainer(Constant.TEAMModelOS, "School").ReadItemStreamAsync(id, new PartitionKey($"Teacher-{obj.GetProperty("schoolId")}"));
  1926. if (sctch.StatusCode==System.Net.HttpStatusCode.OK && sctch != null && sctch.Content != null)
  1927. {
  1928. var jsonDoc = await JsonDocument.ParseAsync(sctch.Content);
  1929. SchoolTeacher schoolTeacher = jsonDoc.RootElement.ToObject<SchoolTeacher>();
  1930. }
  1931. }
  1932. }
  1933. }
  1934. }
  1935. //預設學校ID
  1936. if (jsonsc.RootElement.TryGetProperty("defaultSchool", out JsonElement valueD) && !string.IsNullOrEmpty(valueD.ToString()))
  1937. {
  1938. defaultschool = valueD.ToString();
  1939. }
  1940. }
  1941. else
  1942. {
  1943. //如果沒有,則初始化Teacher基本資料到Cosmos
  1944. Teacher teacher = new Teacher
  1945. {
  1946. createTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
  1947. id = id,
  1948. pk = "Base",
  1949. code = "Base",
  1950. name = name?.ToString(),
  1951. picture = picture?.ToString(),
  1952. //创建账号并第一次登录IES5则默认赠送1G
  1953. size = 1,
  1954. defaultSchool = null,
  1955. schools = new List<Teacher.TeacherSchool>(),
  1956. };
  1957. teacher = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Teacher").CreateItemAsync<Teacher>(teacher, new PartitionKey("Base"));
  1958. }
  1959. //老師個人課程清單 [新架構 Todo]
  1960. List<GetTeacherInfoApiCourse> courses = new List<GetTeacherInfoApiCourse>();
  1961. List<KeyValuePair<string, CourseTask>> privateTeacherTask = new List<KeyValuePair<string, CourseTask>>(); //老師的課程安排Dictionary key:courseId val:CourseTask
  1962. List<KeyValuePair<string, CourseTask>> privateAssistantTask = new List<KeyValuePair<string, CourseTask>>();
  1963. HashSet<string> courseIds = new HashSet<string>(); //有被安排的課程ID
  1964. List<string> groupIds = new List<string>(); //班級團體ID
  1965. List<CourseDto> teahcerCourses = new List<CourseDto>();
  1966. string sqlCoursePrivate = $"select value c.id from c where c.creatorId='{id}'";
  1967. var resultCourseBasePrivate = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher).GetList<string>(sqlCoursePrivate, $"CourseBase"); //CourseID列表
  1968. string sqlprivate = $"SELECT distinct value c FROM c join b in c.schedules where c.pk='CourseTask' and (ARRAY_CONTAINS(b.assistants,'{id}') or b.teacherId ='{id}' )";
  1969. var resultTeacher = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher).GetList<CourseTask>(sqlprivate, $"CourseTask");
  1970. if (resultTeacher.list.IsNotEmpty())
  1971. {
  1972. resultTeacher.list.ForEach(x =>
  1973. {
  1974. var schedulesTeacher = x.schedules.Where(z => !string.IsNullOrWhiteSpace(z.teacherId) && z.teacherId.Equals(id));
  1975. if (schedulesTeacher.Any())
  1976. {
  1977. courseIds.Add(x.courseId);
  1978. CourseTask courseTask = x.ToJsonString().ToObject<CourseTask>();
  1979. courseTask.schedules = schedulesTeacher.ToList();
  1980. privateTeacherTask.Add(new KeyValuePair<string, CourseTask>(x.courseId, courseTask));
  1981. groupIds.AddRange(schedulesTeacher.Where(z => !string.IsNullOrWhiteSpace(z.groupId)).Select(x => x.groupId));
  1982. }
  1983. var schedulesAssistant = x.schedules.Where(z => z.assistants.Contains(id));
  1984. if (schedulesAssistant.Any())
  1985. {
  1986. courseIds.Add(x.courseId);
  1987. CourseTask courseTask = x.ToJsonString().ToObject<CourseTask>();
  1988. courseTask.schedules = schedulesAssistant.ToList();
  1989. privateAssistantTask.Add(new KeyValuePair<string, CourseTask>(x.courseId, courseTask));
  1990. groupIds.AddRange(schedulesAssistant.Where(z => !string.IsNullOrWhiteSpace(z.groupId)).Select(x => x.groupId));
  1991. }
  1992. });
  1993. }
  1994. //班級團體人數Dic
  1995. Dictionary<string, GroupListMemberCnt> groupCntDic = new Dictionary<string, GroupListMemberCnt>();
  1996. if (groupIds.Any())
  1997. {
  1998. string sqlGroup = $"SELECT c.code, c.id, c.name, c.scope, ARRAY_LENGTH(c.members) as memberCount FROM c WHERE c.id in ({string.Join(",", groupIds.Select(b => $"'{b}'"))})";
  1999. var resultGroup = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher).GetList<GroupListMemberCnt>(sqlGroup, $"GroupList");
  2000. if (resultGroup.list.IsNotEmpty())
  2001. {
  2002. foreach (GroupListMemberCnt groupData in resultGroup.list)
  2003. {
  2004. groupCntDic.Add(groupData.id, groupData);
  2005. }
  2006. }
  2007. }
  2008. if (courseIds.Any())
  2009. {
  2010. GetTeacherInfoApiCourse courseExtobj;
  2011. List<GetTeacherInfoApiCourseClass> classes;
  2012. string sqlCourse = $"select value c from c where c.id in ({string.Join(",", courseIds.Select(b => $"'{b}'"))})";
  2013. //string sqlCourse = $"select value c from c where c.creatorId='{id}' and c.id in ({string.Join(",", courseIds.Select(b => $"'{b}'"))})";
  2014. var result = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher).GetList<CourseBase>(sqlCourse, $"CourseBase");
  2015. if (result.list.IsNotEmpty())
  2016. {
  2017. foreach (var item in result.list)
  2018. {
  2019. courseExtobj = new GetTeacherInfoApiCourse();
  2020. courseExtobj.id = item.id;
  2021. courseExtobj.name = item.name;
  2022. courseExtobj.scope = item.scope;
  2023. classes = new List<GetTeacherInfoApiCourseClass>();
  2024. //任教教師
  2025. //List<CourseTaskDto> courseTaskDtos = new List<CourseTaskDto>();
  2026. var teacher = privateTeacherTask.Where(x => x.Key.Equals(item.id)).Select(z => new CourseTaskDto { courseTask = z.Value, type = "teacher" });
  2027. if (teacher.Any())
  2028. {
  2029. List<CourseTaskDto> teacherList = teacher.ToList();
  2030. foreach (CourseTaskDto teacherTaskDto in teacherList)
  2031. {
  2032. List<ScheduleTask> schedules = teacherTaskDto.courseTask.schedules;
  2033. foreach (ScheduleTask schedule in schedules)
  2034. {
  2035. GetTeacherInfoApiCourseClass classExtobj = new GetTeacherInfoApiCourseClass();
  2036. classExtobj.code = null;
  2037. classExtobj.scope = (groupCntDic.ContainsKey(schedule.groupId)) ? groupCntDic[schedule.groupId].scope : string.Empty; ;
  2038. classExtobj.name = (groupCntDic.ContainsKey(schedule.groupId)) ? groupCntDic[schedule.groupId].name : string.Empty;
  2039. classExtobj.stuListId = schedule.groupId;
  2040. classExtobj.teacher = new GetTeacherInfoApiSimlpeBase() { id = id, name = name?.ToString() };
  2041. classExtobj.stuCnt = (groupCntDic.ContainsKey(schedule.groupId)) ? groupCntDic[schedule.groupId].memberCount : 0;
  2042. classExtobj.grpCnt = 0;
  2043. classExtobj.gradeId = null;
  2044. classExtobj.year = 0;
  2045. classes.Add(classExtobj);
  2046. }
  2047. }
  2048. }
  2049. //協同教師
  2050. var assistant = privateAssistantTask.Where(x => x.Key.Equals(item.id)).Select(z => new CourseTaskDto { courseTask = z.Value, type = "assistant" });
  2051. if (assistant.Any())
  2052. {
  2053. List<CourseTaskDto> assistantList = assistant.ToList();
  2054. foreach (CourseTaskDto assistantTaskDto in assistantList)
  2055. {
  2056. List<ScheduleTask> schedules = assistantTaskDto.courseTask.schedules;
  2057. foreach (ScheduleTask schedule in schedules)
  2058. {
  2059. GetTeacherInfoApiCourseClass classExtobj = new GetTeacherInfoApiCourseClass();
  2060. classExtobj.code = null;
  2061. classExtobj.scope = (groupCntDic.ContainsKey(schedule.groupId)) ? groupCntDic[schedule.groupId].scope : string.Empty; ;
  2062. classExtobj.name = (groupCntDic.ContainsKey(schedule.groupId)) ? groupCntDic[schedule.groupId].name : string.Empty;
  2063. classExtobj.stuListId = schedule.groupId;
  2064. classExtobj.teacher = new GetTeacherInfoApiSimlpeBase() { id = id, name = name?.ToString() };
  2065. classExtobj.stuCnt = (groupCntDic.ContainsKey(schedule.groupId)) ? groupCntDic[schedule.groupId].memberCount : 0;
  2066. classExtobj.grpCnt = 0;
  2067. classExtobj.gradeId = null;
  2068. classExtobj.year = 0;
  2069. classes.Add(classExtobj);
  2070. }
  2071. }
  2072. }
  2073. courseExtobj.classes = classes;
  2074. courses.Add(courseExtobj);
  2075. }
  2076. }
  2077. }
  2078. //老師個人課程清單 [舊架構]
  2079. //await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "Teacher").GetItemQueryStreamIteratorSql(queryText: $"SELECT c.id, c.name, c.schedule, c.scope FROM c ", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Course-{id}") }))
  2080. //{
  2081. // var jsontcs = await JsonDocument.ParseAsync(item.Content);
  2082. // if (jsontcs.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  2083. // {
  2084. // GetTeacherInfoApiCourse courseExtobj;
  2085. // List<GetTeacherInfoApiCourseClass> classes;
  2086. // foreach (var obj in jsontcs.RootElement.GetProperty("Documents").EnumerateArray())
  2087. // {
  2088. // courseExtobj = new GetTeacherInfoApiCourse();
  2089. // courseExtobj.id = Convert.ToString(obj.GetProperty("id"));
  2090. // courseExtobj.name = Convert.ToString(obj.GetProperty("name"));
  2091. // courseExtobj.scope = Convert.ToString(obj.GetProperty("scope"));
  2092. // classes = new List<GetTeacherInfoApiCourseClass>();
  2093. // if (obj.TryGetProperty("schedule", out JsonElement schedule))
  2094. // {
  2095. // GetTeacherInfoApiCourseClass classExtobj;
  2096. // foreach (var scheduleobj in schedule.EnumerateArray())
  2097. // {
  2098. // classExtobj = new GetTeacherInfoApiCourseClass();
  2099. // classExtobj.id = null;
  2100. // classExtobj.code = null;
  2101. // classExtobj.teacher = null;
  2102. // if (scheduleobj.TryGetProperty("teacherId", out JsonElement teacherId) && !string.IsNullOrWhiteSpace(Convert.ToString(teacherId)))
  2103. // {
  2104. // await foreach (var teaitem in client.GetContainer(Constant.TEAMModelOS, "Teacher").GetItemQueryStreamIteratorSql(queryText: $"SELECT c.id, c.name FROM c WHERE c.id = '{teacherId}'", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("Base") }))
  2105. // {
  2106. // var jsontea = await JsonDocument.ParseAsync(teaitem.Content);
  2107. // foreach (var teaobj in jsontea.RootElement.GetProperty("Documents").EnumerateArray())
  2108. // {
  2109. // classExtobj.teacher = new GetTeacherInfoApiSimlpeBase();
  2110. // classExtobj.teacher = teaobj.ToObject<GetTeacherInfoApiSimlpeBase>();
  2111. // }
  2112. // }
  2113. // }
  2114. // classExtobj.scope = "private";
  2115. // int stuCount = 0;
  2116. // string stuListId = Convert.ToString(scheduleobj.GetProperty("stulist"));
  2117. // classExtobj.stuListId = stuListId;
  2118. // await foreach (var stuitem in client.GetContainer(Constant.TEAMModelOS, "Teacher").GetItemQueryStreamIteratorSql(queryText: $"SELECT c.id, c.name, c.tcount, c.scount FROM c WHERE c.id = '{stuListId}'", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("GroupList") }))
  2119. // {
  2120. // //取得學生總數
  2121. // var jsonstu = await JsonDocument.ParseAsync(stuitem.Content);
  2122. // foreach (var stuobj in jsonstu.RootElement.GetProperty("Documents").EnumerateArray())
  2123. // {
  2124. // classExtobj.name = Convert.ToString(stuobj.GetProperty("name"));
  2125. // stuCount += stuobj.GetProperty("scount").GetInt32();
  2126. // stuCount += stuobj.GetProperty("tcount").GetInt32();
  2127. // }
  2128. // }
  2129. // classExtobj.stuCnt = stuCount;
  2130. // classExtobj.grpCnt = 0;
  2131. // classExtobj.gradeId = null;
  2132. // classExtobj.year = 0;
  2133. // classes.Add(classExtobj);
  2134. // }
  2135. // }
  2136. // courseExtobj.classes = classes;
  2137. // courses.Add(courseExtobj);
  2138. // }
  2139. // }
  2140. //}
  2141. //取得老師個人評測
  2142. List<object> exams = new List<object>();
  2143. //取得評測ID List (HiTeach只取source='1'(課中评量) && progress = 'going'(進行中))
  2144. List<string> examIdList = new List<string>();
  2145. await foreach (var exam in client.GetContainer(Constant.TEAMModelOS, "Common").GetItemQueryStreamIteratorSql(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}") }))
  2146. {
  2147. using var json = await JsonDocument.ParseAsync(exam.Content);
  2148. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  2149. {
  2150. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  2151. {
  2152. examIdList.Add(obj.GetProperty("id").GetString());
  2153. }
  2154. }
  2155. }
  2156. //取得有作答的評測班級
  2157. Dictionary<string, List<string>> examClassFinDic = new Dictionary<string, List<string>>();
  2158. await foreach (var exam in client.GetContainer(Constant.TEAMModelOS, "Common").GetItemQueryStreamIteratorSql(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() { }))
  2159. {
  2160. var jsonecr = await JsonDocument.ParseAsync(exam.Content);
  2161. if (jsonecr.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  2162. {
  2163. foreach (var obj in jsonecr.RootElement.GetProperty("Documents").EnumerateArray())
  2164. {
  2165. string examId = obj.GetProperty("examId").ToString();
  2166. string classId = obj.GetProperty("classId").ToString();
  2167. if (examClassFinDic.ContainsKey(examId) && !examClassFinDic[examId].Contains(classId))
  2168. {
  2169. examClassFinDic[examId].Add(classId);
  2170. }
  2171. else
  2172. {
  2173. List<string> classIdList = new List<string>();
  2174. classIdList.Add(classId);
  2175. examClassFinDic.Add(examId, classIdList);
  2176. }
  2177. }
  2178. }
  2179. }
  2180. //取得評測資料
  2181. await foreach (var exam in client.GetContainer(Constant.TEAMModelOS, "Common").GetItemQueryStreamIteratorSql(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}") }))
  2182. {
  2183. var jsonex = await JsonDocument.ParseAsync(exam.Content);
  2184. if (jsonex.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  2185. {
  2186. dynamic examExtobj;
  2187. foreach (var obj in jsonex.RootElement.GetProperty("Documents").EnumerateArray())
  2188. {
  2189. examExtobj = new ExpandoObject();
  2190. string examId = obj.GetProperty("id").GetString();
  2191. examExtobj.code = obj.GetProperty("code");
  2192. examExtobj.id = examId;
  2193. examExtobj.name = obj.GetProperty("name");
  2194. examExtobj.createTime = obj.GetProperty("createTime");
  2195. examExtobj.startTime = obj.GetProperty("startTime");
  2196. examExtobj.endTime = obj.GetProperty("endTime");
  2197. examExtobj.year = obj.GetProperty("year");
  2198. examExtobj.source = obj.GetProperty("source");
  2199. examExtobj.type = obj.GetProperty("type");
  2200. examExtobj.progress = obj.GetProperty("progress");
  2201. examExtobj.stuCount = obj.GetProperty("stuCount");
  2202. examExtobj.scope = obj.GetProperty("scope");
  2203. examExtobj.owner = obj.GetProperty("owner");
  2204. examExtobj.period = obj.GetProperty("period");
  2205. examExtobj.grades = obj.GetProperty("grades");
  2206. examExtobj.subjects = obj.GetProperty("subjects");
  2207. examExtobj.classes = (obj.TryGetProperty("classes", out JsonElement classes)) ? classes.ToObject<List<string>>() : new List<string>();
  2208. examExtobj.stuLists = (obj.TryGetProperty("stuLists", out JsonElement stuLists)) ? stuLists.ToObject<List<string>>() : new List<string>();
  2209. examExtobj.finishClasses = (examClassFinDic.ContainsKey(examId)) ? examClassFinDic[examId] : new List<string>();
  2210. examExtobj.papers = obj.GetProperty("papers");
  2211. //examExtobj = obj.ToObject<object>();
  2212. exams.Add(examExtobj);
  2213. }
  2214. }
  2215. }
  2216. //用户在线记录
  2217. //try
  2218. //{
  2219. // _ = _httpTrigger.RequestHttpTrigger(new { school = defaultschool, scope = $"{Constant.ScopeTeacher}", id = $"{id}", ip = $"{ip}", expire = 1 }, _option.Location, "online-record");
  2220. //}
  2221. //catch { }
  2222. //取得Teacher Blob 容器位置及SAS
  2223. var container = _azureStorage.GetBlobContainerClient(id);
  2224. await container.CreateIfNotExistsAsync(PublicAccessType.None); //嘗試創建Teacher私有容器,如存在則不做任何事,保障容器一定存在
  2225. var (blob_uri, blob_sas) = _azureStorage.GetBlobContainerSAS(id, BlobContainerSasPermissions.Write | BlobContainerSasPermissions.Read | BlobContainerSasPermissions.List | BlobContainerSasPermissions.Delete);
  2226. var (blob_uri_read, blob_sas_read) = _azureStorage.GetBlobContainerSAS(id, BlobContainerSasPermissions.Read);
  2227. var (blob_uri_write, blob_sas_write) = _azureStorage.GetBlobContainerSAS(id, BlobContainerSasPermissions.Write);
  2228. await SystemService.RecordAccumulateData(_azureRedis, _dingDing, new SDK.Models.Dtos.Accumulate { client = "hiteach", count = 1, id = "ies", key = "tmd_login", name = "醍摩豆账号登录", scope = "ies", target = "ies" });
  2229. return Ok(new { blob_uri, blob_sas, blob_sas_read, blob_sas_write, schools, defaultschool, courses, exams });
  2230. }
  2231. catch (Exception ex)
  2232. {
  2233. await _dingDing.SendBotMsg($"IES5,{_option.Location},hiteach/GetTeacherInfo()\n{ex.Message}\n{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
  2234. return BadRequest();
  2235. }
  2236. }
  2237. [Authorize(Roles = "HiTeach")]
  2238. [ProducesDefaultResponseType]
  2239. [HttpPost("get-school-info")]
  2240. public async Task<IActionResult> GetSchoolInfo(JsonElement request)
  2241. {
  2242. try
  2243. {
  2244. DateTime nowDate = DateTime.Now;
  2245. string id_token = HttpContext.GetXAuth("IdToken");
  2246. (string ip, string region) = await LoginService.LoginIp(HttpContext, _searcher);
  2247. if (string.IsNullOrEmpty(id_token)) return BadRequest();
  2248. if (!request.TryGetProperty("school_code", out JsonElement school_code)) return BadRequest();
  2249. var jwt = new JwtSecurityToken(id_token);
  2250. if (!jwt.Payload.Iss.Equals("account.teammodel", StringComparison.Ordinal)) return BadRequest();
  2251. var id = jwt.Payload.Sub;
  2252. string inputPeriodId = (request.TryGetProperty("periodId", out JsonElement _periodId)) ? _periodId.ToString() : string.Empty; //若未輸入periodId,則所有的學段課程都取
  2253. var client = _azureCosmos.GetCosmosClient();
  2254. //取得學校學段、年級、科目、考試類型
  2255. List<object> periods = new List<object>();
  2256. List<object> grades = new List<object>();
  2257. List<object> subjects = new List<object>();
  2258. List<object> examTypes = new List<object>();
  2259. List<string> periodIds = new List<string>(); //學段ID 取得課程時限制用
  2260. string lang = "en-us";
  2261. School school_base = await client.GetContainer(Constant.TEAMModelOS, "School").ReadItemAsync<School>($"{school_code}", new PartitionKey("Base"));
  2262. if (!string.IsNullOrWhiteSpace(school_base.id))
  2263. {
  2264. //語系取得
  2265. if (!string.IsNullOrEmpty(school_base.region))
  2266. {
  2267. if (school_base.region.Equals("中国")) lang = "zh-cn";
  2268. else if (school_base.region.Equals("臺灣")) lang = "zh-tw";
  2269. }
  2270. else
  2271. {
  2272. if (_option.Location.Equals("China")) lang = "zh-cn";
  2273. }
  2274. //年級、科目、評測類型取得
  2275. foreach (Period periodinfo in school_base.period)
  2276. {
  2277. if (!string.IsNullOrWhiteSpace(inputPeriodId))
  2278. {
  2279. if (!periodinfo.id.Equals(inputPeriodId)) continue; //若有給periodId,則過濾學段;未給則全取
  2280. }
  2281. periods.Add(new { id = periodinfo.id, name = periodinfo.name, subjects = periodinfo.subjects });
  2282. periodIds.Add(periodinfo.id);
  2283. int gradeIndex = 0;
  2284. foreach (string gradeName in periodinfo.grades)
  2285. {
  2286. grades.Add(new { id = gradeIndex.ToString(), name = gradeName, periodId = periodinfo.id });
  2287. gradeIndex++;
  2288. }
  2289. foreach (Subject subjectinfo in periodinfo.subjects)
  2290. {
  2291. subjects.Add(new { id = subjectinfo.id, name = subjectinfo.name, periodId = periodinfo.id });
  2292. }
  2293. foreach (var examType in periodinfo.analysis.type)
  2294. {
  2295. examTypes.Add(examType);
  2296. }
  2297. }
  2298. }
  2299. else //無此學校資料
  2300. {
  2301. return BadRequest();
  2302. }
  2303. //該老師排定的學校課程
  2304. List<object> courses = new List<object>();
  2305. List<string> classIds = new List<string>(); //班級ID列表 篩選校園評測用
  2306. List<string> groupIds = new List<string>(); //團體ID列表 篩選校園評測用
  2307. //取得所有班级
  2308. (List<Class> school_classes, _) = await SchoolService.DoGraduateClasses(_httpTrigger, _azureCosmos, null, school_base, _option, _dingDing);
  2309. //取得學校安排老師課程
  2310. HashSet<string> courseIds = new HashSet<string>();
  2311. List<KeyValuePair<string, CourseTask>> schoolTeacherTask = new List<KeyValuePair<string, CourseTask>>(); //key:courseId value:CourseTask
  2312. List<KeyValuePair<string, CourseTask>> schoolAssistantTask = new List<KeyValuePair<string, CourseTask>>();
  2313. List<string> periodIdList = new List<string>();
  2314. if (string.IsNullOrWhiteSpace(inputPeriodId)) periodIdList = periodIds;
  2315. else periodIdList.Add(inputPeriodId);
  2316. Dictionary<string, GroupListMemberCnt> groupCntDic = new Dictionary<string, GroupListMemberCnt>(); //班級團體+人數Dic
  2317. foreach (string periodId in periodIdList)
  2318. {
  2319. var period = school_base.period.Find(x => x.id.Equals(periodId));
  2320. (Semester currSemester, int studyYear, DateTimeOffset currSemesterDate, DateTimeOffset date, DateTimeOffset nextSemester) info = SchoolService.GetSemester(period, 0, nowDate.ToString());
  2321. int studyYear = (request.TryGetProperty("year", out JsonElement _year)) ? _year.GetInt32() : info.studyYear;
  2322. string semesterId = (request.TryGetProperty("semesterId", out JsonElement _semesterId)) ? _semesterId.GetString() : info.currSemester.id;
  2323. //string date = SchoolService.GetOpensByStudyYearAndSemester(period.semesters, int.Parse($"{_year}"), $"{_semesterId}");
  2324. string sql = $"SELECT distinct value c FROM c join b in c.schedules where c.pk='CourseTask' and ( b.teacherId ='{id}' OR ARRAY_CONTAINS(b.assistants,'{id}'))";
  2325. sql = $"{sql} and c.year={studyYear} and c.semesterId='{semesterId}'";
  2326. var resultSchool = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.School).GetList<CourseTask>(sql, $"CourseTask-{school_code}");
  2327. if (resultSchool.list.IsNotEmpty())
  2328. {
  2329. resultSchool.list.ForEach(x =>
  2330. {
  2331. var schedulesTeacher = x.schedules.Where(z => !string.IsNullOrWhiteSpace(z.teacherId) && z.teacherId.Equals(id));
  2332. if (schedulesTeacher.Any())
  2333. {
  2334. courseIds.Add(x.courseId);
  2335. CourseTask courseTask = x.ToJsonString().ToObject<CourseTask>();
  2336. courseTask.schedules = schedulesTeacher.ToList();
  2337. schoolTeacherTask.Add(new KeyValuePair<string, CourseTask>(x.courseId, courseTask));
  2338. groupIds.AddRange(schedulesTeacher.Where(z => !string.IsNullOrWhiteSpace(z.groupId)).Select(x => x.groupId));
  2339. }
  2340. var schedulesAssistant = x.schedules.Where(z => z.assistants.Contains(id));
  2341. if (schedulesAssistant.Any())
  2342. {
  2343. courseIds.Add(x.courseId);
  2344. CourseTask courseTask = x.ToJsonString().ToObject<CourseTask>();
  2345. courseTask.schedules = schedulesAssistant.ToList();
  2346. schoolAssistantTask.Add(new KeyValuePair<string, CourseTask>(x.courseId, courseTask));
  2347. groupIds.AddRange(schedulesAssistant.Where(z => !string.IsNullOrWhiteSpace(z.groupId)).Select(x => x.groupId));
  2348. }
  2349. });
  2350. }
  2351. //班級團體人數Dic
  2352. if (groupIds.Any())
  2353. {
  2354. string sqlGroup = $"SELECT c.code, c.id, c.name, c.scope, c.year, 'teach' as type, ARRAY_LENGTH(c.members) as memberCount FROM c WHERE c.id in ({string.Join(",", groupIds.Select(b => $"'{b}'"))})";
  2355. var resultGroup = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.School).GetList<GroupListMemberCnt>(sqlGroup, $"GroupList-{school_code}");
  2356. if (resultGroup.list.IsNotEmpty())
  2357. {
  2358. foreach (GroupListMemberCnt groupData in resultGroup.list)
  2359. {
  2360. if (!groupCntDic.ContainsKey(groupData.id))
  2361. {
  2362. groupCntDic.Add(groupData.id, groupData);
  2363. }
  2364. }
  2365. }
  2366. string sqlClass = $"SELECT c.code, c.id, c.name, c.scope, c.year, 'class' as type, 0 as memberCount FROM c WHERE c.id in ({string.Join(",", groupIds.Select(b => $"'{b}'"))})";
  2367. var resultClass = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.School).GetList<GroupListMemberCnt>(sqlClass, $"Class-{school_code}");
  2368. if (resultClass.list.IsNotEmpty())
  2369. {
  2370. foreach (GroupListMemberCnt groupData in resultClass.list)
  2371. {
  2372. if (!groupCntDic.ContainsKey(groupData.id))
  2373. {
  2374. groupCntDic.Add(groupData.id, groupData);
  2375. }
  2376. }
  2377. }
  2378. //取得學生數
  2379. foreach (KeyValuePair<string, GroupListMemberCnt> groupCntItem in groupCntDic)
  2380. {
  2381. string classId = groupCntItem.Key;
  2382. var sqlStudent = $"SELECT VALUE Count(1) FROM c WHERE c.classId = '{classId}'";
  2383. await foreach (int itemclstc in client.GetContainer(Constant.TEAMModelOS, Constant.Student).GetItemQueryIteratorSql<int>(queryText: sqlStudent, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Base-{school_code}") }))
  2384. {
  2385. if (itemclstc > 0)
  2386. {
  2387. groupCntDic[classId].memberCount = itemclstc;
  2388. }
  2389. }
  2390. }
  2391. }
  2392. }
  2393. if (courseIds.Any())
  2394. {
  2395. GetSchoolInfoApiCourse courseExtobj;
  2396. List<GetTeacherInfoApiCourseClass> classes;
  2397. string sqlCourse = $"select value c from c where c.id in ({string.Join(",", courseIds.Select(b => $"'{b}'"))})";
  2398. var result = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.School).GetList<CourseBase>(sqlCourse, $"CourseBase-{school_code}");
  2399. if (result.list.IsNotEmpty())
  2400. {
  2401. foreach (var item in result.list)
  2402. {
  2403. courseExtobj = new GetSchoolInfoApiCourse();
  2404. courseExtobj.id = item.id;
  2405. courseExtobj.name = item.name;
  2406. courseExtobj.scope = item.scope;
  2407. courseExtobj.subject = new GetTeacherInfoApiSimlpeBase();
  2408. courseExtobj.subject.id = item.subject.id;
  2409. courseExtobj.subject.name = item.subject.name;
  2410. var period = school_base.period.Find(p => p.subjects.Exists(s => s.id.Equals(item.subject.id)));
  2411. classes = new List<GetTeacherInfoApiCourseClass>();
  2412. //任教教師
  2413. //List<CourseTaskDto> courseTaskDtos = new List<CourseTaskDto>();
  2414. var teacher = schoolTeacherTask.Where(x => x.Key.Equals(item.id)).Select(z => new CourseTaskDto { courseTask = z.Value, type = "teacher" });
  2415. if (teacher.Any() && period != null)
  2416. {
  2417. List<CourseTaskDto> teacherList = teacher.ToList();
  2418. foreach (CourseTaskDto teacherTaskDto in teacherList)
  2419. {
  2420. List<ScheduleTask> schedules = teacherTaskDto.courseTask.schedules;
  2421. foreach (ScheduleTask schedule in schedules)
  2422. {
  2423. Class classInfo = school_classes.Where((Class x) => x.id == schedule.groupId).FirstOrDefault();
  2424. GetTeacherInfoApiCourseClass classExtobj = new GetTeacherInfoApiCourseClass();
  2425. classExtobj.code = (groupCntDic.ContainsKey(schedule.groupId)) ? groupCntDic[schedule.groupId].code : string.Empty;
  2426. classExtobj.id = schedule.groupId;
  2427. classExtobj.scope = (groupCntDic.ContainsKey(schedule.groupId)) ? groupCntDic[schedule.groupId].scope : string.Empty;
  2428. classExtobj.name = (groupCntDic.ContainsKey(schedule.groupId)) ? groupCntDic[schedule.groupId].name : string.Empty;
  2429. string classType = (groupCntDic.ContainsKey(schedule.groupId)) ? groupCntDic[schedule.groupId].type : schedule.type; /// class:編制班 teach:選課班
  2430. classExtobj.stuListId = (classType.Equals("teach")) ? schedule.groupId : string.Empty; ///選課班 stuListId才填值
  2431. classExtobj.teacher = new GetTeacherInfoApiSimlpeBase();
  2432. if (classInfo != null && !string.IsNullOrWhiteSpace(classInfo.teacher.id)) classExtobj.teacher.id = classInfo.teacher.id;
  2433. if (classInfo != null && !string.IsNullOrWhiteSpace(classInfo.teacher.name)) classExtobj.teacher.name = classInfo.teacher.name;
  2434. classExtobj.stuCnt = (groupCntDic.ContainsKey(schedule.groupId)) ? groupCntDic[schedule.groupId].memberCount : 0;
  2435. classExtobj.gradeId = null;
  2436. classExtobj.gradeName = null;
  2437. classExtobj.year = (groupCntDic.ContainsKey(schedule.groupId)) ? groupCntDic[schedule.groupId].year : teacherTaskDto.courseTask.year;
  2438. var grpCnt = schedules.GroupBy(c => c.groupId).Select(x => new { Count = x.Count() });
  2439. classExtobj.grpCnt = grpCnt.FirstOrDefault().Count;
  2440. var gradeInfo = getGradeInfoByYear(classExtobj.year, period);
  2441. if (!string.IsNullOrWhiteSpace(gradeInfo.id))
  2442. {
  2443. classExtobj.gradeId = gradeInfo.id;
  2444. if (gradeInfo.name.Equals("graduated")) classExtobj.gradeName = (lang.Equals("zh-cn")) ? "已毕业" : (lang.Equals("zh-tw")) ? "已畢業" : "Graduated";
  2445. else if (gradeInfo.name.Equals("not-enrollment")) classExtobj.gradeName = (lang.Equals("zh-cn")) ? "未到入学时间" : (lang.Equals("zh-tw")) ? "未到入學時間" : "It is not time for enrollment yet";
  2446. else classExtobj.gradeName = gradeInfo.name;
  2447. }
  2448. classes.Add(classExtobj);
  2449. }
  2450. }
  2451. }
  2452. //協同教師
  2453. var assistant = schoolAssistantTask.Where(x => x.Key.Equals(item.id)).Select(z => new CourseTaskDto { courseTask = z.Value, type = "assistant" });
  2454. if (assistant.Any() && period != null)
  2455. {
  2456. List<CourseTaskDto> assistantList = assistant.ToList();
  2457. foreach (CourseTaskDto assistantTaskDto in assistantList)
  2458. {
  2459. List<ScheduleTask> schedules = assistantTaskDto.courseTask.schedules;
  2460. foreach (ScheduleTask schedule in schedules)
  2461. {
  2462. Class classInfo = school_classes.Where((Class x) => x.id == schedule.groupId).FirstOrDefault();
  2463. GetTeacherInfoApiCourseClass classExtobj = new GetTeacherInfoApiCourseClass();
  2464. classExtobj.code = (groupCntDic.ContainsKey(schedule.groupId)) ? groupCntDic[schedule.groupId].code : string.Empty;
  2465. classExtobj.id = schedule.groupId;
  2466. classExtobj.scope = (groupCntDic.ContainsKey(schedule.groupId)) ? groupCntDic[schedule.groupId].scope : string.Empty;
  2467. classExtobj.name = (groupCntDic.ContainsKey(schedule.groupId)) ? groupCntDic[schedule.groupId].name : string.Empty;
  2468. string classType = (groupCntDic.ContainsKey(schedule.groupId)) ? groupCntDic[schedule.groupId].type : schedule.type; /// class:編制班 teach:選課班
  2469. classExtobj.stuListId = (classType.Equals("teach")) ? schedule.groupId : string.Empty; ///選課班 stuListId才填值
  2470. classExtobj.teacher = new GetTeacherInfoApiSimlpeBase();
  2471. if (classInfo != null && !string.IsNullOrWhiteSpace(classInfo.teacher.id)) classExtobj.teacher.id = classInfo.teacher.id;
  2472. if (classInfo != null && !string.IsNullOrWhiteSpace(classInfo.teacher.name)) classExtobj.teacher.name = classInfo.teacher.name;
  2473. classExtobj.stuCnt = (groupCntDic.ContainsKey(schedule.groupId)) ? groupCntDic[schedule.groupId].memberCount : 0;
  2474. classExtobj.gradeId = null;
  2475. classExtobj.gradeName = null;
  2476. classExtobj.year = (groupCntDic.ContainsKey(schedule.groupId)) ? groupCntDic[schedule.groupId].year : assistantTaskDto.courseTask.year;
  2477. var grpCnt = schedules.GroupBy(c => c.groupId).Select(x => new { Count = x.Count() });
  2478. classExtobj.grpCnt = grpCnt.FirstOrDefault().Count;
  2479. var gradeInfo = getGradeInfoByYear(classExtobj.year, period);
  2480. if (!string.IsNullOrWhiteSpace(gradeInfo.id))
  2481. {
  2482. classExtobj.gradeId = gradeInfo.id;
  2483. if (gradeInfo.name.Equals("graduated")) classExtobj.gradeName = (lang.Equals("zh-cn")) ? "已毕业" : (lang.Equals("zh-tw")) ? "已畢業" : "Graduated";
  2484. else if (gradeInfo.name.Equals("not-enrollment")) classExtobj.gradeName = (lang.Equals("zh-cn")) ? "未到入学时间" : (lang.Equals("zh-tw")) ? "未到入學時間" : "It is not time for enrollment yet";
  2485. else classExtobj.gradeName = gradeInfo.name;
  2486. }
  2487. classes.Add(classExtobj);
  2488. }
  2489. }
  2490. }
  2491. courseExtobj.classes = classes;
  2492. courses.Add(courseExtobj);
  2493. }
  2494. }
  2495. }
  2496. #region ====舊課程架構===
  2497. //如果没传,则以当前时间获取学年和学期信息
  2498. //[取得當前學年方法 備用] (Semester currSemester, int studyYear, DateTimeOffset date, DateTimeOffset nextSemester) info = SchoolService.GetSemester(period);
  2499. //var gradeInfo = getGradeInfoByYear(classInfo.year, curPeriod);
  2500. //var query = $"SELECT DISTINCT c.id, c.name, c.scope, c.subject, c.period.id AS periodId, schedule.classId AS scheduleClassId, schedule.stulist AS scheduleStulist, schedule.notice AS scheduleNotice FROM c JOIN schedule IN c.schedule WHERE schedule.teacherId = '{id}'";
  2501. //if(periodIds.Count > 0)
  2502. //{
  2503. // string periodIdsJsonStr = JsonSerializer.Serialize(periodIds);
  2504. // query += $" AND ARRAY_CONTAINS({periodIdsJsonStr}, c.period.id, true)";
  2505. //}
  2506. ////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}'";
  2507. //await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryStreamIteratorSql(queryText: query, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Course-{school_code}") }))
  2508. //{
  2509. // var jsoncs = await JsonDocument.ParseAsync(item.Content);
  2510. // if (jsoncs.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  2511. // {
  2512. // foreach (var obj in jsoncs.RootElement.GetProperty("Documents").EnumerateArray())
  2513. // {
  2514. // dynamic classExtobj = new ExpandoObject();
  2515. // classExtobj.id = null;
  2516. // classExtobj.code = null;
  2517. // classExtobj.name = null;
  2518. // classExtobj.scope = null;
  2519. // classExtobj.gradeId = null;
  2520. // classExtobj.gradeName = null;
  2521. // classExtobj.year = 0;
  2522. // classExtobj.teacher = null;
  2523. // classExtobj.stuListId = null;
  2524. // classExtobj.stuCnt = 0;
  2525. // classExtobj.grpCnt = 0;
  2526. // //編制班
  2527. // string classIdNow = string.Empty;
  2528. // if (obj.TryGetProperty("scheduleClassId", out JsonElement scheduleClassId))
  2529. // {
  2530. // classIdNow = Convert.ToString(scheduleClassId);
  2531. // }
  2532. // if (!string.IsNullOrWhiteSpace(classIdNow))
  2533. // {
  2534. // Class classInfo = school_classes.Where((Class x) => x.id == classIdNow).FirstOrDefault();
  2535. // if(classInfo != null && !string.IsNullOrWhiteSpace(classInfo.id))
  2536. // {
  2537. // classIds.Add(classInfo.id);
  2538. // classExtobj.id = classInfo.id;
  2539. // classExtobj.code = classInfo.code;
  2540. // classExtobj.name = classInfo.name;
  2541. // classExtobj.year = classInfo.year;
  2542. // classExtobj.teacher = classInfo.teacher;
  2543. // //取得學生數
  2544. // var queryclstc = $"SELECT Count(1) AS stuCnt FROM c WHERE c.classId = '{classIdNow}'";
  2545. // await foreach (var itemclstc in client.GetContainer(Constant.TEAMModelOS, "Student").GetItemQueryStreamIteratorSql(queryText: queryclstc, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Base-{school_code}") }))
  2546. // {
  2547. // var jsonclstc = await JsonDocument.ParseAsync(itemclstc.Content);
  2548. // foreach (var objstc in jsonclstc.RootElement.GetProperty("Documents").EnumerateArray())
  2549. // {
  2550. // classExtobj.stuCnt = objstc.GetProperty("stuCnt").GetInt32();
  2551. // }
  2552. // }
  2553. // //取得分組數
  2554. // var queryclgp = $"SELECT c.groupId FROM c WHERE c.classId = '{classIdNow}' AND IS_NULL(c.groupId)=false GROUP BY c.groupId";
  2555. // await foreach (var itemgp in client.GetContainer(Constant.TEAMModelOS, "Student").GetItemQueryStreamIteratorSql(queryText: queryclgp, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Base-{school_code}") }))
  2556. // {
  2557. // var jsongp = await JsonDocument.ParseAsync(itemgp.Content);
  2558. // if (jsongp.RootElement.TryGetProperty("_count", out JsonElement gpcount) && gpcount.GetInt32() > 0)
  2559. // {
  2560. // classExtobj.grpCnt = gpcount.GetInt32();
  2561. // }
  2562. // }
  2563. // //取得年級
  2564. // if (obj.TryGetProperty("periodId", out JsonElement curPeriodIdJson))
  2565. // {
  2566. // string curPeriodId = curPeriodIdJson.GetString();
  2567. // Period curPeriod = school_base.period.Where((Period x) => x.id == curPeriodId).FirstOrDefault();
  2568. // if(curPeriodId != null)
  2569. // {
  2570. // var gradeInfo = getGradeInfoByYear(classInfo.year, curPeriod);
  2571. // if(!string.IsNullOrWhiteSpace(gradeInfo.id))
  2572. // {
  2573. // classExtobj.gradeId = gradeInfo.id;
  2574. // if(gradeInfo.name.Equals("graduated")) classExtobj.gradeName = (lang.Equals("zh-cn")) ? "已毕业" : (lang.Equals("zh-tw")) ? "已畢業" : "Graduated";
  2575. // else if(gradeInfo.name.Equals("not-enrollment")) classExtobj.gradeName = (lang.Equals("zh-cn")) ? "未到入学时间" : (lang.Equals("zh-tw")) ? "未到入學時間" : "It is not time for enrollment yet";
  2576. // else classExtobj.gradeName = gradeInfo.name;
  2577. // }
  2578. // }
  2579. // }
  2580. // }
  2581. // }
  2582. // //選課班 (過期者不選)
  2583. // long nowtime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
  2584. // var stuListId = obj.GetProperty("scheduleStulist");
  2585. // if (!stuListId.ValueKind.Equals(JsonValueKind.Null) && !string.IsNullOrWhiteSpace(stuListId.GetString()))
  2586. // {
  2587. // classExtobj.stuListId = Convert.ToString(stuListId);
  2588. // await foreach (var stuitem in client.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryStreamIteratorSql(queryText: $"SELECT c.code, c.id, c.name, c.year, c.tcount, c.scount FROM c WHERE c.id = '{stuListId}' AND ( c.expire = 0 OR IS_DEFINED(c.expire) = false OR c.expire >={nowtime} )", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"GroupList-{school_code}") }))
  2589. // {
  2590. // var jsonstu = await JsonDocument.ParseAsync(stuitem.Content);
  2591. // foreach (var stuobj in jsonstu.RootElement.GetProperty("Documents").EnumerateArray())
  2592. // {
  2593. // groupIds.Add(Convert.ToString(stuobj.GetProperty("id")));
  2594. // classExtobj.id = Convert.ToString(stuobj.GetProperty("id"));
  2595. // classExtobj.code = Convert.ToString(stuobj.GetProperty("code"));
  2596. // classExtobj.name = Convert.ToString(stuobj.GetProperty("name"));
  2597. // classExtobj.gradeId = null;
  2598. // classExtobj.year = (stuobj.TryGetProperty("year", out JsonElement yearJson)) ? yearJson.GetInt32() : 0;
  2599. // classExtobj.teacher = null;
  2600. // classExtobj.stuCnt += stuobj.GetProperty("tcount").GetInt32();
  2601. // classExtobj.stuCnt += stuobj.GetProperty("scount").GetInt32();
  2602. // }
  2603. // }
  2604. // }
  2605. // //課程
  2606. // string courseIdNow = obj.GetProperty("id").ToString();
  2607. // var courseExist = courses.Where((dynamic x) => x.id == courseIdNow).FirstOrDefault();
  2608. // if (courseExist == null)
  2609. // {
  2610. // dynamic courseExtobj = new ExpandoObject();
  2611. // courseExtobj.id = courseIdNow;
  2612. // courseExtobj.name = obj.GetProperty("name").ToString();
  2613. // courseExtobj.scope = obj.GetProperty("scope").ToString();
  2614. // courseExtobj.classes = new List<object>();
  2615. // courseExtobj.subject = obj.GetProperty("subject");
  2616. // //classExtobj.teacher = scheduleTeacherInfo;
  2617. // if (!string.IsNullOrWhiteSpace(classExtobj.id))
  2618. // {
  2619. // courseExtobj.classes.Add(classExtobj);
  2620. // }
  2621. // courses.Add(courseExtobj);
  2622. // }
  2623. // else
  2624. // {
  2625. // //classExtobj.teacher = scheduleTeacherInfo;
  2626. // if (!string.IsNullOrWhiteSpace(classExtobj.id))
  2627. // {
  2628. // courseExist.classes.Add(classExtobj);
  2629. // }
  2630. // }
  2631. // }
  2632. // }
  2633. //}
  2634. #endregion
  2635. //取得校園評測 (HiTeach只取source='1'(課中评量) && progress = 'going'(進行中)) && 排定的學校課程班級
  2636. List<object> exams = new List<object>();
  2637. string classSqlString = "";
  2638. if (classIds.Count > 0 || groupIds.Count > 0)
  2639. {
  2640. if (classIds.Count > 0)
  2641. {
  2642. foreach (string classId in classIds)
  2643. {
  2644. if (!string.IsNullOrWhiteSpace(classSqlString))
  2645. {
  2646. classSqlString += " OR ";
  2647. }
  2648. classSqlString += $"ARRAY_CONTAINS(c.classes, '{classId}')";
  2649. }
  2650. }
  2651. if (groupIds.Count > 0)
  2652. {
  2653. foreach (string groupId in groupIds)
  2654. {
  2655. if (!string.IsNullOrWhiteSpace(classSqlString))
  2656. {
  2657. classSqlString += " OR ";
  2658. }
  2659. classSqlString += $"ARRAY_CONTAINS(c.classes, '{groupId}')";
  2660. }
  2661. }
  2662. }
  2663. else //無分配任何課程教室,校園評測不取
  2664. {
  2665. classSqlString += "c.id = '0'";
  2666. }
  2667. classSqlString = $"AND ({classSqlString})";
  2668. //取得評測ID List
  2669. List<string> examIdList = new List<string>();
  2670. await foreach (var exam in client.GetContainer(Constant.TEAMModelOS, "Common").GetItemQueryStreamIteratorSql(queryText: $"SELECT c.id FROM c WHERE c.source = '1' AND c.progress = 'going' {classSqlString}", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Exam-{school_code}") }))
  2671. {
  2672. using var json = await JsonDocument.ParseAsync(exam.Content);
  2673. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  2674. {
  2675. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  2676. {
  2677. examIdList.Add(obj.GetProperty("id").GetString());
  2678. }
  2679. }
  2680. }
  2681. //取得有作答的評測班級、已完成的 評測ID、班級ID、科目ID 列表製作
  2682. List<ExamFinishClassesSubList> examFinClassSubList = new List<ExamFinishClassesSubList>(); //已完成的 評測ID、班級ID、科目ID 列表
  2683. await foreach (var exam in client.GetContainer(Constant.TEAMModelOS, "Common").GetItemQueryStreamIteratorSql(queryText: $"SELECT c.examId, c.info.id as classId, c.subjectId FROM c WHERE ARRAY_CONTAINS({JsonSerializer.Serialize(examIdList)}, c.examId) AND c.progress=true", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"ExamClassResult-{school_code}") }))
  2684. {
  2685. var jsonecr = await JsonDocument.ParseAsync(exam.Content);
  2686. if (jsonecr.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  2687. {
  2688. foreach (var obj in jsonecr.RootElement.GetProperty("Documents").EnumerateArray())
  2689. {
  2690. string examId = obj.GetProperty("examId").ToString();
  2691. string classId = obj.GetProperty("classId").ToString();
  2692. string subjectId = obj.GetProperty("subjectId").ToString();
  2693. ExamFinishClassesSubList existExamFinishClassesSubRow = examFinClassSubList.Where(e => e.examId == examId && e.classId == classId && e.subjectId == subjectId).FirstOrDefault();
  2694. if (existExamFinishClassesSubRow == null)
  2695. {
  2696. ExamFinishClassesSubList ExamFinishClassesSubRow = new ExamFinishClassesSubList();
  2697. ExamFinishClassesSubRow.examId = examId;
  2698. ExamFinishClassesSubRow.classId = classId;
  2699. ExamFinishClassesSubRow.subjectId = subjectId;
  2700. examFinClassSubList.Add(ExamFinishClassesSubRow);
  2701. }
  2702. }
  2703. }
  2704. }
  2705. //取得評測資料
  2706. await foreach (var exam in client.GetContainer(Constant.TEAMModelOS, "Common").GetItemQueryStreamIteratorSql(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}") }))
  2707. {
  2708. var jsonex = await JsonDocument.ParseAsync(exam.Content);
  2709. if (jsonex.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  2710. {
  2711. foreach (var obj in jsonex.RootElement.GetProperty("Documents").EnumerateArray())
  2712. {
  2713. dynamic examExtobj = new ExpandoObject();
  2714. string examId = obj.GetProperty("id").GetString();
  2715. examExtobj.code = obj.GetProperty("code");
  2716. examExtobj.id = examId;
  2717. examExtobj.name = obj.GetProperty("name");
  2718. examExtobj.createTime = obj.GetProperty("createTime");
  2719. examExtobj.startTime = obj.GetProperty("startTime");
  2720. examExtobj.endTime = obj.GetProperty("endTime");
  2721. examExtobj.year = obj.GetProperty("year");
  2722. examExtobj.source = obj.GetProperty("source");
  2723. examExtobj.type = obj.GetProperty("type");
  2724. examExtobj.progress = obj.GetProperty("progress");
  2725. examExtobj.stuCount = obj.GetProperty("stuCount");
  2726. examExtobj.scope = obj.GetProperty("scope");
  2727. examExtobj.owner = obj.GetProperty("owner");
  2728. examExtobj.period = obj.GetProperty("period");
  2729. examExtobj.grades = obj.GetProperty("grades");
  2730. examExtobj.subjects = (obj.TryGetProperty("subjects", out JsonElement subjectsJobj)) ? subjectsJobj.ToObject<List<GetSchInfoExamSubject>>() : new List<GetSchInfoExamSubject>();
  2731. examExtobj.classes = (obj.TryGetProperty("classes", out JsonElement classes)) ? classes.ToObject<List<string>>() : new List<string>();
  2732. examExtobj.stuLists = (obj.TryGetProperty("stuLists", out JsonElement stuLists)) ? stuLists.ToObject<List<string>>() : new List<string>();
  2733. examExtobj.papers = obj.GetProperty("papers");
  2734. //examExtobj = obj.ToObject<object>();
  2735. //須完成列表、已完成班級列表生成
  2736. examExtobj.finishClasses = new List<string>();
  2737. examExtobj.finishClassesSub = new List<GetSchInfoExamFinishClassesSub>();
  2738. List<GetSchInfoExamFinishClassesSub> examClassSubjectList = new List<GetSchInfoExamFinishClassesSub>();
  2739. foreach (string classIdNow in examExtobj.classes)
  2740. {
  2741. GetSchInfoExamFinishClassesSub examClassSubjectRow = examClassSubjectList.Where(x => x.classId == classIdNow).FirstOrDefault();
  2742. if (examClassSubjectRow == null)
  2743. {
  2744. examClassSubjectRow = new GetSchInfoExamFinishClassesSub();
  2745. examClassSubjectRow.classId = classIdNow;
  2746. examClassSubjectRow.subjectIds = new List<string>();
  2747. examClassSubjectRow.finishSubjectIds = new List<string>();
  2748. }
  2749. foreach (GetSchInfoExamSubject subjectNow in examExtobj.subjects)
  2750. {
  2751. string subjectIdNow = subjectNow.id;
  2752. if (!examClassSubjectRow.subjectIds.Contains(subjectIdNow))
  2753. {
  2754. examClassSubjectRow.subjectIds.Add(subjectIdNow);
  2755. }
  2756. ExamFinishClassesSubList existFinClassesSub = examFinClassSubList.Where(e => e.examId == examId && e.classId == classIdNow && e.subjectId == subjectIdNow).FirstOrDefault();
  2757. if (existFinClassesSub != null)
  2758. {
  2759. examClassSubjectRow.finishSubjectIds.Add(subjectIdNow);
  2760. }
  2761. }
  2762. examExtobj.finishClassesSub.Add(examClassSubjectRow);
  2763. if (examClassSubjectRow.subjectIds.Count.Equals(examClassSubjectRow.finishSubjectIds.Count))
  2764. {
  2765. if (!examExtobj.finishClasses.Contains(classIdNow))
  2766. {
  2767. examExtobj.finishClasses.Add(classIdNow);
  2768. }
  2769. }
  2770. }
  2771. exams.Add(examExtobj);
  2772. }
  2773. }
  2774. }
  2775. //用户在线记录
  2776. //try
  2777. //{
  2778. // _ = _httpTrigger.RequestHttpTrigger(new { school = school_code.GetString(), scope = $"{Constant.ScopeTeacher}", id = $"{id}", ip = $"{ip}", expire = 1 }, _option.Location, "online-record");
  2779. //}
  2780. //catch { }
  2781. //取得School Blob 容器位置及SAS
  2782. string school_code_blob = school_code.GetString().ToLower();
  2783. var (blob_uri, blob_sas) = _azureStorage.GetBlobContainerSAS(school_code_blob, BlobContainerSasPermissions.Read | BlobContainerSasPermissions.List | BlobContainerSasPermissions.Write); //讀列
  2784. var (blob_uri_read, blob_sas_read) = _azureStorage.GetBlobContainerSAS(school_code_blob, BlobContainerSasPermissions.Read); //讀
  2785. var (blob_uri_write, blob_sas_write) = _azureStorage.GetBlobContainerSAS(school_code_blob, BlobContainerSasPermissions.Write); //寫
  2786. await SystemService.RecordAccumulateData(_azureRedis, _dingDing, new SDK.Models.Dtos.Accumulate { client = "hiteach", count = 1, id = school_base.id, key = "teacher_login", name = "醍摩豆账号登录", scope = "school", target = school_base.id });
  2787. return Ok(new { blob_uri, blob_sas, blob_sas_read, blob_sas_write, periods, grades, subjects, courses, examTypes, exams });
  2788. }
  2789. catch (Exception ex)
  2790. {
  2791. await _dingDing.SendBotMsg($"IES5,{_option.Location},hiteach/GetSchoolInfo()\n{ex.Message}\n{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
  2792. return BadRequest();
  2793. }
  2794. }
  2795. //取得試卷
  2796. [Authorize(Roles = "HiTeach")]
  2797. [ProducesDefaultResponseType]
  2798. [HttpPost("get-paper")]
  2799. public async Task<IActionResult> GetPaperList(JsonElement request)
  2800. {
  2801. try
  2802. {
  2803. //Header驗證
  2804. string id_token = HttpContext.GetXAuth("IdToken");
  2805. if (string.IsNullOrEmpty(id_token)) return BadRequest();
  2806. var jwt = new JwtSecurityToken(id_token);
  2807. if (!jwt.Payload.Iss.Equals("account.teammodel", StringComparison.Ordinal)) return BadRequest();
  2808. var id = jwt.Payload.Sub;
  2809. var client = _azureCosmos.GetCosmosClient();
  2810. //參數
  2811. if (!request.TryGetProperty("grant_type", out JsonElement grant_type)) return BadRequest();
  2812. string partitionid = string.Empty;
  2813. string container = string.Empty;
  2814. if (grant_type.ToString().Equals("school"))
  2815. {
  2816. if (!request.TryGetProperty("school_code", out JsonElement school_code_json))
  2817. {
  2818. return BadRequest();
  2819. }
  2820. else
  2821. {
  2822. partitionid = school_code_json.ToString();
  2823. container = "School";
  2824. }
  2825. }
  2826. else
  2827. {
  2828. partitionid = id.ToString();
  2829. container = "Teacher";
  2830. }
  2831. //SQL文
  2832. List<object> papers = new List<object>();
  2833. string queryWhere = $" WHERE (( IS_DEFINED(c.secret) = false OR c.secret=0 ) OR ( c.secret=1 AND c.creatorId='{id.ToString()}') )";
  2834. string queryOption = string.Empty;
  2835. //學段
  2836. if (request.TryGetProperty("periodId", out JsonElement periodId) && container.Equals("School"))
  2837. {
  2838. queryWhere += $" AND c.periodId = '{periodId}'";
  2839. }
  2840. //年級
  2841. if (request.TryGetProperty("gradeId", out JsonElement gradeIds) && gradeIds.GetArrayLength() > 0 && container.Equals("School"))
  2842. {
  2843. string queryOptionForGrade = string.Empty;
  2844. for (int i = 0; i < gradeIds.GetArrayLength(); i++)
  2845. {
  2846. if (!string.IsNullOrWhiteSpace(queryOptionForGrade))
  2847. {
  2848. queryOptionForGrade += " OR ";
  2849. }
  2850. queryOptionForGrade += $" ARRAY_CONTAINS(c.gradeIds, '{gradeIds[i]}', true)";
  2851. }
  2852. queryWhere += $" AND ( {queryOptionForGrade} )";
  2853. }
  2854. //科目ID
  2855. if (request.TryGetProperty("subjectId", out JsonElement subjectId) && container.Equals("School"))
  2856. {
  2857. queryWhere += $" AND c.subjectId = '{subjectId}'";
  2858. }
  2859. //科目名稱
  2860. if (request.TryGetProperty("subjectName", out JsonElement subjectName))
  2861. {
  2862. queryWhere += $" AND c.subjectName = '{subjectName}'";
  2863. }
  2864. //試卷ID
  2865. if (request.TryGetProperty("id", out JsonElement paperId))
  2866. {
  2867. queryWhere += $" AND c.id = '{paperId}'";
  2868. }
  2869. int perpage = 0; //每頁幾條
  2870. if (request.TryGetProperty("perpage", out JsonElement perpage_json)) perpage = perpage_json.GetInt32();
  2871. int page = 0; //目前第幾頁
  2872. if (request.TryGetProperty("page", out JsonElement page_json))
  2873. {
  2874. if (page_json.GetInt32() > 0)
  2875. {
  2876. page = page_json.GetInt32() - 1;
  2877. }
  2878. }
  2879. string order = "createTime"; //排序項目
  2880. if (request.TryGetProperty("order", out JsonElement order_json))
  2881. {
  2882. if (order_json.ToString().Equals("useCount"))
  2883. {
  2884. order = order_json.ToString();
  2885. }
  2886. }
  2887. queryOption += $" Order By c." + order + " DESC ";
  2888. if (perpage > 0)
  2889. {
  2890. queryOption += $" OFFSET {perpage * page} LIMIT {perpage}";
  2891. }
  2892. //資料取得
  2893. await foreach (var item in client.GetContainer(Constant.TEAMModelOS, container).GetItemQueryStreamIteratorSql(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}") }))
  2894. {
  2895. using var json = await JsonDocument.ParseAsync(item.Content);
  2896. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  2897. {
  2898. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  2899. {
  2900. papers.Add(obj.ToObject<object>());
  2901. }
  2902. }
  2903. }
  2904. //總件數
  2905. int totalCount = 0;
  2906. await foreach (var item in client.GetContainer(Constant.TEAMModelOS, container).GetItemQueryStreamIteratorSql(queryText: $"SELECT VALUE COUNT(1) From c {queryWhere}", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Paper-{partitionid}") }))
  2907. {
  2908. using var json = await JsonDocument.ParseAsync(item.Content);
  2909. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  2910. {
  2911. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  2912. {
  2913. totalCount = obj.GetInt32();
  2914. }
  2915. }
  2916. }
  2917. return Ok(new { papers, totalCount });
  2918. }
  2919. catch (Exception ex)
  2920. {
  2921. return BadRequest();
  2922. }
  2923. }
  2924. //取得試題
  2925. [Authorize(Roles = "HiTeach")]
  2926. [ProducesDefaultResponseType]
  2927. [HttpPost("get-item")]
  2928. public async Task<IActionResult> GetItemList(JsonElement request)
  2929. {
  2930. try
  2931. {
  2932. //Header驗證
  2933. string id_token = HttpContext.GetXAuth("IdToken");
  2934. if (string.IsNullOrEmpty(id_token)) return BadRequest();
  2935. var jwt = new JwtSecurityToken(id_token);
  2936. if (!jwt.Payload.Iss.Equals("account.teammodel", StringComparison.Ordinal)) return BadRequest();
  2937. var id = jwt.Payload.Sub;
  2938. var client = _azureCosmos.GetCosmosClient();
  2939. //參數
  2940. if (!request.TryGetProperty("grant_type", out JsonElement grant_type)) return BadRequest();
  2941. string partitionid = string.Empty;
  2942. string container = string.Empty;
  2943. if (grant_type.ToString().Equals("school"))
  2944. {
  2945. if (!request.TryGetProperty("school_code", out JsonElement school_code_json))
  2946. {
  2947. return BadRequest();
  2948. }
  2949. else
  2950. {
  2951. partitionid = school_code_json.ToString();
  2952. container = "School";
  2953. }
  2954. }
  2955. else
  2956. {
  2957. partitionid = id.ToString();
  2958. container = "Teacher";
  2959. }
  2960. //SQL文
  2961. List<object> items = new List<object>();
  2962. string queryWhere = " WHERE 1=1 ";
  2963. string queryOption = string.Empty;
  2964. //學段
  2965. if (request.TryGetProperty("periodId", out JsonElement periodId) && container.Equals("School"))
  2966. {
  2967. queryWhere += $" AND c.periodId = '{periodId}'";
  2968. }
  2969. //年級
  2970. if (request.TryGetProperty("gradeId", out JsonElement gradeIds) && gradeIds.GetArrayLength() > 0 && container.Equals("School"))
  2971. {
  2972. string queryOptionForGrade = string.Empty;
  2973. for (int i = 0; i < gradeIds.GetArrayLength(); i++)
  2974. {
  2975. if (!string.IsNullOrWhiteSpace(queryOptionForGrade))
  2976. {
  2977. queryOptionForGrade += " OR ";
  2978. }
  2979. queryOptionForGrade += $" ARRAY_CONTAINS(c.gradeIds, '{gradeIds[i]}', true)";
  2980. }
  2981. queryWhere += $" AND ( {queryOptionForGrade} )";
  2982. }
  2983. //科目ID
  2984. if (request.TryGetProperty("subjectId", out JsonElement subjectId) && container.Equals("school"))
  2985. {
  2986. queryWhere += $" AND c.subjectId = '{subjectId}'";
  2987. }
  2988. //科目名稱
  2989. if (request.TryGetProperty("subjectName", out JsonElement subjectName))
  2990. {
  2991. queryWhere += $" AND c.subjectName = '{subjectName}'";
  2992. }
  2993. //題型
  2994. int dummy = 0;
  2995. if (request.TryGetProperty("type", out JsonElement type))
  2996. {
  2997. queryWhere += $" AND c.type = '{type}'";
  2998. }
  2999. //難度
  3000. dummy = 0;
  3001. if (request.TryGetProperty("level", out JsonElement level) && int.TryParse(level.ToString(), out dummy))
  3002. {
  3003. queryWhere += $" AND c.level = {level}";
  3004. }
  3005. //層次
  3006. if (request.TryGetProperty("field", out JsonElement field) && int.TryParse(field.ToString(), out dummy))
  3007. {
  3008. queryWhere += $" AND c.field = {field}";
  3009. }
  3010. int perpage = 0; //每頁幾條
  3011. if (request.TryGetProperty("perpage", out JsonElement perpage_json)) perpage = perpage_json.GetInt32();
  3012. int page = 0; //目前第幾頁
  3013. if (request.TryGetProperty("page", out JsonElement page_json))
  3014. {
  3015. if (page_json.GetInt32() > 0)
  3016. {
  3017. page = page_json.GetInt32() - 1;
  3018. }
  3019. }
  3020. string order = "createTime"; //排序項目
  3021. if (request.TryGetProperty("order", out JsonElement order_json))
  3022. {
  3023. if (order_json.ToString().Equals("useCount"))
  3024. {
  3025. order = order_json.ToString();
  3026. }
  3027. }
  3028. queryOption += $" Order By c." + order + " DESC ";
  3029. if (perpage > 0)
  3030. {
  3031. queryOption += $" OFFSET {perpage * page} LIMIT {perpage}";
  3032. }
  3033. //資料取得
  3034. await foreach (var item in client.GetContainer(Constant.TEAMModelOS, container).GetItemQueryStreamIteratorSql(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}") }))
  3035. {
  3036. using var json = await JsonDocument.ParseAsync(item.Content);
  3037. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  3038. {
  3039. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  3040. {
  3041. items.Add(obj.ToObject<object>());
  3042. }
  3043. }
  3044. }
  3045. //總件數
  3046. int totalCount = 0;
  3047. await foreach (var item in client.GetContainer(Constant.TEAMModelOS, container).GetItemQueryStreamIteratorSql(queryText: $"SELECT VALUE COUNT(1) From c {queryWhere}", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Item-{partitionid}") }))
  3048. {
  3049. using var json = await JsonDocument.ParseAsync(item.Content);
  3050. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  3051. {
  3052. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  3053. {
  3054. totalCount = obj.GetInt32();
  3055. }
  3056. }
  3057. }
  3058. return Ok(new { items, totalCount });
  3059. }
  3060. catch (Exception ex)
  3061. {
  3062. return BadRequest();
  3063. }
  3064. }
  3065. //取得知識點
  3066. [Authorize(Roles = "HiTeach")]
  3067. [ProducesDefaultResponseType]
  3068. [HttpPost("get-knowledge")]
  3069. public async Task<IActionResult> GetKnowledgePointList(JsonElement request)
  3070. {
  3071. var client = _azureCosmos.GetCosmosClient();
  3072. if (!request.TryGetProperty("school_code", out JsonElement school_code)) return BadRequest();
  3073. //知識點
  3074. List<object> points = new List<object>();
  3075. await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryStreamIteratorSql(queryText: $"SELECT c.id, c.name, c.subjectId FROM c WHERE c.type = 'point'", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Knowledge-{school_code}") }))
  3076. {
  3077. using var json = await JsonDocument.ParseAsync(item.Content);
  3078. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  3079. {
  3080. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  3081. {
  3082. points.Add(obj.ToObject<object>());
  3083. }
  3084. }
  3085. }
  3086. return Ok(points);
  3087. }
  3088. //取得課綱
  3089. [Authorize(Roles = "HiTeach")]
  3090. [ProducesDefaultResponseType]
  3091. [HttpPost("get-syllabus")]
  3092. public async Task<IActionResult> GetSyllabusList(JsonElement request)
  3093. {
  3094. //Header驗證
  3095. string id_token = HttpContext.GetXAuth("IdToken");
  3096. if (string.IsNullOrEmpty(id_token)) return BadRequest();
  3097. var jwt = new JwtSecurityToken(id_token);
  3098. if (!jwt.Payload.Iss.Equals("account.teammodel", StringComparison.OrdinalIgnoreCase)) return BadRequest();
  3099. var id = jwt.Payload.Sub;
  3100. //參數驗證
  3101. if (!request.TryGetProperty("grant_type", out JsonElement grant_type)) return BadRequest();
  3102. string grantType = (grant_type.GetString().Equals("school")) ? "school" : "private";
  3103. JsonElement school_code = new();
  3104. if (grantType == "school" && !request.TryGetProperty("school_code", out school_code)) return BadRequest();
  3105. string dataId = (grantType.Equals("school")) ? school_code.GetString() : id;
  3106. string container = (grantType.Equals("school")) ? "School" : "Teacher";
  3107. string periodId = (request.TryGetProperty("periodId", out JsonElement periodIdJobj)) ? periodIdJobj.GetString() : String.Empty;
  3108. string subjectId = (request.TryGetProperty("subjectId", out JsonElement subjectIdJobj)) ? subjectIdJobj.GetString() : String.Empty;
  3109. string volumeId = (request.TryGetProperty("volumeId", out JsonElement volumeIdJobj)) ? volumeIdJobj.GetString() : String.Empty;
  3110. string syllabusId = (request.TryGetProperty("syllabusId", out JsonElement syllabusIdJobj)) ? syllabusIdJobj.GetString() : String.Empty;
  3111. int display = (request.TryGetProperty("display", out JsonElement displayJobj)) ? displayJobj.GetInt32() : 0;
  3112. var client = _azureCosmos.GetCosmosClient();
  3113. //取得卷前置作業:有給syllabusId,取得該課綱的卷ID
  3114. string volumeIdFromSyllabusId = String.Empty;
  3115. if (!string.IsNullOrWhiteSpace(syllabusId))
  3116. {
  3117. await foreach (string volid in client.GetContainer(Constant.TEAMModelOS, container).GetItemQueryIteratorSql<string>(queryText: $"SELECT value(c.volumeId) FROM c WHERE c.id = '{syllabusId}'", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Syllabus-{dataId}") }))
  3118. {
  3119. volumeIdFromSyllabusId = volid;
  3120. }
  3121. }
  3122. //取得卷
  3123. List<object> volumes = new();
  3124. List<Volume> volumeList = new();
  3125. List<string> volumeIdList = new();
  3126. string queryTextVol = string.Empty;
  3127. List<string> WhereVol = new();
  3128. if (!string.IsNullOrWhiteSpace(periodId)) WhereVol.Add($" c.periodId = '{periodId}' ");
  3129. if (!string.IsNullOrWhiteSpace(subjectId)) WhereVol.Add($" c.subjectId = '{subjectId}' ");
  3130. if (!string.IsNullOrWhiteSpace(volumeId)) WhereVol.Add($" c.id = '{volumeId}' ");
  3131. if (!string.IsNullOrWhiteSpace(volumeIdFromSyllabusId)) WhereVol.Add($" c.id = '{volumeIdFromSyllabusId}' ");
  3132. foreach (string Where in WhereVol)
  3133. {
  3134. queryTextVol += (String.IsNullOrWhiteSpace(queryTextVol)) ? $" WHERE {Where} " : $" AND {Where} ";
  3135. }
  3136. queryTextVol = "SELECT * FROM c " + queryTextVol;
  3137. await foreach (var itemv in client.GetContainer(Constant.TEAMModelOS, container).GetItemQueryStreamIteratorSql(queryText: queryTextVol, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Volume-{dataId}") }))
  3138. {
  3139. var jsons = await JsonDocument.ParseAsync(itemv.Content);
  3140. if (jsons.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  3141. {
  3142. foreach (var obj in jsons.RootElement.GetProperty("Documents").EnumerateArray())
  3143. {
  3144. Volume volExtobj = obj.ToObject<Volume>();
  3145. volumeList.Add(volExtobj);
  3146. volumeIdList.Add(volExtobj.id);
  3147. }
  3148. }
  3149. }
  3150. //取得課綱 ※display=1時不取任何課綱
  3151. List<SyllabusTreeNode> treeNodes = new();
  3152. if (!display.Equals(1))
  3153. {
  3154. string queryTextSyl = $"SELECT value(c) FROM c WHERE ARRAY_CONTAINS({JsonSerializer.Serialize(volumeIdList)}, c.volumeId, true)";
  3155. if (!string.IsNullOrWhiteSpace(syllabusId)) queryTextSyl += $" AND c.id = '{syllabusId}'";
  3156. await foreach (Syllabus items in client.GetContainer(Constant.TEAMModelOS, container).GetItemQueryIteratorSql<Syllabus>(queryText: queryTextSyl, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Syllabus-{dataId}") }))
  3157. {
  3158. List<SyllabusTree> trees = SyllabusService.ListToTree(items.children);
  3159. SyllabusTreeNode tree = new() { id = items.id, scope = items.scope, trees = trees, volumeId = items.volumeId, auth = items.auth, codeval = $"{id}" };
  3160. treeNodes.Add(tree);
  3161. }
  3162. }
  3163. //輸出結果
  3164. foreach (Volume vr in volumeList)
  3165. {
  3166. //对课纲树形结构排序
  3167. List<SyllabusTreeNode> redt = new List<SyllabusTreeNode>();
  3168. if (vr.syllabusIds.IsNotEmpty())
  3169. {
  3170. vr.syllabusIds.ForEach(x =>
  3171. {
  3172. for (int index = 0; index < treeNodes.Count; index++)
  3173. {
  3174. if (treeNodes[index].id == x && treeNodes[index].volumeId == vr.id)
  3175. {
  3176. redt.Add(treeNodes[index]);
  3177. treeNodes.RemoveAt(index);
  3178. }
  3179. }
  3180. });
  3181. }
  3182. //輸出項
  3183. volumes.Add(new
  3184. {
  3185. vr.periodId,
  3186. vr.subjectId,
  3187. vr.id,
  3188. vr.gradeId,
  3189. vr.semesterId,
  3190. vr.name,
  3191. vr.creatorId,
  3192. vr.creatorName,
  3193. vr.school,
  3194. vr.scope,
  3195. vr.syllabusIds,
  3196. vr.auth,
  3197. vr.order,
  3198. syllabus = redt.Where(t => t.volumeId == vr.id).ToList()
  3199. });
  3200. }
  3201. return Ok(volumes);
  3202. }
  3203. //取得被分享的課綱
  3204. [ProducesDefaultResponseType]
  3205. [Authorize(Roles = "HiTeach")]
  3206. [HttpPost("get-share-syllabus")]
  3207. public async Task<IActionResult> GetShareSyllabusList(JsonElement request)
  3208. {
  3209. //Header驗證
  3210. string id_token = HttpContext.GetXAuth("IdToken");
  3211. if (string.IsNullOrEmpty(id_token)) return BadRequest();
  3212. var jwt = new JwtSecurityToken(id_token);
  3213. if (!jwt.Payload.Iss.Equals("account.teammodel", StringComparison.OrdinalIgnoreCase)) return BadRequest();
  3214. var id = jwt.Payload.Sub;
  3215. //參數驗證
  3216. string volumeId = (request.TryGetProperty("volumeId", out JsonElement volumeIdJobj)) ? volumeIdJobj.GetString() : String.Empty;
  3217. string syllabusId = (request.TryGetProperty("syllabusId", out JsonElement syllabusIdJobj)) ? syllabusIdJobj.GetString() : String.Empty;
  3218. int display = (request.TryGetProperty("display", out JsonElement displayJobj)) ? displayJobj.GetInt32() : 0;
  3219. var client = _azureCosmos.GetCosmosClient();
  3220. //取得分享內容
  3221. Dictionary<string, string> tmidDic = new Dictionary<string, string>();
  3222. List<Volume> volumeList = new List<Volume>();
  3223. StringBuilder queryText = new StringBuilder("SELECT value(c) FROM c WHERE c.type='share' ");
  3224. if (!string.IsNullOrWhiteSpace(volumeId))
  3225. {
  3226. queryText.Append($"AND c.volumeId='{volumeId}'");
  3227. }
  3228. await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "Teacher").GetItemQueryIteratorSql<Share>(queryText: queryText.ToString(),
  3229. requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Share-share-{id}") }))
  3230. {
  3231. if (!string.IsNullOrWhiteSpace(syllabusId))
  3232. {
  3233. if (item.id.Equals(syllabusId))
  3234. {
  3235. Volume volumeExist = volumeList.Where((Volume v) => v.id == item.volumeId).FirstOrDefault();
  3236. if (volumeExist == null)
  3237. {
  3238. Volume volume = new Volume();
  3239. volume.id = item.volumeId;
  3240. volume.name = item.volumeName;
  3241. volume.creatorId = item.issuer;
  3242. volume.creatorName = item.issuerName;
  3243. volume.syllabusIds.Add(item.id);
  3244. volumeList.Add(volume);
  3245. var (blob_uri, blob_sas) = _azureStorage.GetBlobContainerSAS(item.issuer, BlobContainerSasPermissions.Read | BlobContainerSasPermissions.List);
  3246. if (!tmidDic.ContainsKey(item.issuer)) tmidDic.Add(item.issuer, blob_sas);
  3247. }
  3248. else
  3249. {
  3250. volumeExist.syllabusIds.Add(item.id);
  3251. }
  3252. }
  3253. }
  3254. else
  3255. {
  3256. Volume volumeExist = volumeList.Where((Volume v) => v.id == item.volumeId).FirstOrDefault();
  3257. if (volumeExist == null)
  3258. {
  3259. Volume volume = new Volume();
  3260. volume.id = item.volumeId;
  3261. volume.name = item.volumeName;
  3262. volume.creatorId = item.issuer;
  3263. volume.creatorName = item.issuerName;
  3264. volume.syllabusIds.Add(item.id);
  3265. volumeList.Add(volume);
  3266. var (blob_uri, blob_sas) = _azureStorage.GetBlobContainerSAS(item.issuer, BlobContainerSasPermissions.Read | BlobContainerSasPermissions.List);
  3267. if (!tmidDic.ContainsKey(item.issuer)) tmidDic.Add(item.issuer, blob_sas);
  3268. }
  3269. else
  3270. {
  3271. volumeExist.syllabusIds.Add(item.id);
  3272. }
  3273. }
  3274. }
  3275. //取得課綱並輸出回傳值
  3276. List<object> result = new List<object>();
  3277. foreach (Volume volTmp in volumeList)
  3278. {
  3279. List<SyllabusTreeNode> treeNodes = new();
  3280. if (!display.Equals(1))
  3281. {
  3282. string queryTextSyl = $"SELECT value(c) FROM c WHERE ARRAY_CONTAINS({JsonSerializer.Serialize(volTmp.syllabusIds)}, c.id, true)";
  3283. await foreach (Syllabus items in client.GetContainer(Constant.TEAMModelOS, "Teacher").GetItemQueryIteratorSql<Syllabus>(queryText: queryTextSyl, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Syllabus-{volTmp.creatorId}") }))
  3284. {
  3285. List<SyllabusTree> trees = SyllabusService.ListToTree(items.children);
  3286. SyllabusTreeNode tree = new() { id = items.id, scope = items.scope, trees = trees, volumeId = items.volumeId, auth = items.auth, codeval = $"{volTmp.creatorId}" };
  3287. treeNodes.Add(tree);
  3288. }
  3289. }
  3290. result.Add(new
  3291. {
  3292. id = volTmp.id,
  3293. name = volTmp.name,
  3294. creatorId = volTmp.creatorId,
  3295. creatorName = volTmp.creatorName,
  3296. creatorSas = (tmidDic.ContainsKey(volTmp.creatorId)) ? tmidDic[volTmp.creatorId] : null,
  3297. syllabusIds = volTmp.syllabusIds,
  3298. syllabus = treeNodes.ToList(),
  3299. });
  3300. }
  3301. return Ok(result);
  3302. }
  3303. //取得某班級的學生成員
  3304. [Authorize(Roles = "HiTeach")]
  3305. [ProducesDefaultResponseType]
  3306. [HttpPost("get-students-list")]
  3307. public async Task<IActionResult> GetStudentsList(JsonElement request)
  3308. {
  3309. string id_token = HttpContext.GetXAuth("IdToken");
  3310. if (string.IsNullOrWhiteSpace(id_token)) return BadRequest();
  3311. var jwt = new JwtSecurityToken(id_token);
  3312. if (!jwt.Payload.Iss.Equals("account.teammodel", StringComparison.Ordinal)) return BadRequest();
  3313. var id = jwt.Payload.Sub;
  3314. if (!request.TryGetProperty("grant_type", out JsonElement grant_type)) return BadRequest();
  3315. request.TryGetProperty("class_code", out JsonElement class_code);
  3316. request.TryGetProperty("stulist_id", out JsonElement stulist_id);
  3317. string classId = Convert.ToString(class_code);
  3318. string stulist = Convert.ToString(stulist_id);
  3319. if (string.IsNullOrWhiteSpace(classId) && string.IsNullOrWhiteSpace(stulist)) return BadRequest();
  3320. request.TryGetProperty("school_code", out JsonElement school_code);
  3321. if (grant_type.GetString().Equals("school") && string.IsNullOrWhiteSpace(Convert.ToString(school_code))) return BadRequest();
  3322. var client = _azureCosmos.GetCosmosClient();
  3323. Dictionary<string, string> irsDic = new Dictionary<string, string>(); //key:學生ID或TMID value:irs號碼
  3324. string container = (grant_type.GetString().Equals("school")) ? "School" : "Teacher";
  3325. List<string> listids = new List<string>();
  3326. if (!string.IsNullOrWhiteSpace(stulist))
  3327. {
  3328. listids.Add(stulist);
  3329. }
  3330. else if (!string.IsNullOrWhiteSpace(classId))
  3331. {
  3332. listids.Add(classId);
  3333. }
  3334. (List<RMember> students, List<RGroupList> groupList) = await GroupListService.GetMemberByListids(_coreAPIHttpService, client, _dingDing, listids, $"{school_code}");
  3335. return Ok(new { students });
  3336. }
  3337. /// <summary>
  3338. /// 開始課堂(舊版,已被CreateLesson取代)
  3339. /// </summary>
  3340. /// <param name="request"></param>
  3341. /// <returns></returns>
  3342. [Authorize(Roles = "HiTeach")]
  3343. [ProducesDefaultResponseType]
  3344. [HttpPost("start-lesson")]
  3345. public async Task<IActionResult> StartLesson(JsonElement request)
  3346. {
  3347. //醍摩豆ID驗證
  3348. string teacherId = string.Empty;
  3349. string id_token = HttpContext.GetXAuth("IdToken");
  3350. if (!string.IsNullOrWhiteSpace(id_token))
  3351. {
  3352. var jwt = new JwtSecurityToken(id_token);
  3353. if (!jwt.Payload.Iss.Equals("account.teammodel", StringComparison.OrdinalIgnoreCase)) return BadRequest();
  3354. teacherId = jwt.Payload.Sub;
  3355. }
  3356. if (string.IsNullOrWhiteSpace(teacherId)) return BadRequest(); //無醍摩豆ID,BadRequest
  3357. //取得授課ID
  3358. string lesson_code = _snowflakeId.NextId().ToString();
  3359. //bool get_lesson_id = (string.IsNullOrWhiteSpace(Convert.ToString(lesson_id)) || Convert.ToString(lesson_id) != lesson_code) ? false : true;
  3360. //string tableName = "TeacherLesson";
  3361. return Ok(new { lesson_code });
  3362. }
  3363. //上傳評測結果
  3364. //錯誤代碼:error = 1001 message = "Paper blob copy failure."
  3365. // error = 1002 message = "Student answers blob upload failure."
  3366. [Authorize(Roles = "HiTeach")]
  3367. [ProducesDefaultResponseType]
  3368. [HttpPost("upd-exam-result")]
  3369. public async Task<IActionResult> UploadExamResult(JsonElement request)
  3370. {
  3371. try
  3372. {
  3373. string message = "";
  3374. int error = 0;
  3375. string id_token = HttpContext.GetXAuth("IdToken");
  3376. if (string.IsNullOrWhiteSpace(id_token)) return BadRequest();
  3377. var jwt = new JwtSecurityToken(id_token);
  3378. if (!jwt.Payload.Iss.Equals("account.teammodel", StringComparison.Ordinal)) return BadRequest();
  3379. var id = jwt.Payload.Sub;
  3380. if (!request.TryGetProperty("exam", out JsonElement exam)) return BadRequest();
  3381. if (!request.TryGetProperty("examClassResult", out JsonElement examClassResult)) return BadRequest();
  3382. bool blobUploaded = (request.TryGetProperty("blobUploaded", out JsonElement blobUploadedJson)) ? blobUploadedJson.GetBoolean() : false;
  3383. bool recordSwitch = (request.TryGetProperty("recordSwitch", out JsonElement recordSwitchJson)) ? recordSwitchJson.GetBoolean() : false;
  3384. bool cloudas = (request.TryGetProperty("cloudas", out JsonElement cloudasJson)) ? cloudasJson.GetBoolean() : false; //是否啟動cloudas運算
  3385. List<string> blobCopyPath = (request.TryGetProperty("blobCopyPath", out JsonElement blobCopyPathJson)) ? JsonSerializer.Deserialize <List<string>>(blobCopyPathJson.ToJsonString()) : new List<string>();
  3386. //ExamInfo ExamInfoFromReq = exam.ToObject<ExamInfo>();
  3387. string strExam = JsonSerializer.Serialize(exam);
  3388. if (strExam.Contains("\"publish\":\"0\""))
  3389. {
  3390. strExam = strExam.Replace("\"publish\":\"0\"", "\"publish\":0");
  3391. }
  3392. ExamInfo ExamInfoFromReq = JsonSerializer.Deserialize<ExamInfo>(strExam);
  3393. string examId = ExamInfoFromReq.id;
  3394. string excode = ExamInfoFromReq.code;
  3395. string examSubjectId = ExamInfoFromReq.subjects[0].id;
  3396. //ExamInfo dbExamInfo = exam.ToObject<ExamInfo>();
  3397. ExamInfo dbExamInfo = JsonSerializer.Deserialize<ExamInfo>(strExam);
  3398. var queryex = $"SELECT * FROM c WHERE c.id = '{examId}'";
  3399. await foreach (var itemex in _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Common").GetItemQueryStreamIteratorSql(queryText: queryex, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"{excode}") }))
  3400. {
  3401. var jsonex = await JsonDocument.ParseAsync(itemex.Content);
  3402. if (jsonex.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  3403. {
  3404. foreach (var obj in jsonex.RootElement.GetProperty("Documents").EnumerateArray())
  3405. {
  3406. string strExamDb = JsonSerializer.Serialize(obj);
  3407. if (strExamDb.Contains("\"publish\":\"0\""))
  3408. {
  3409. strExamDb = strExamDb.Replace("\"publish\":\"0\"", "\"publish\":0");
  3410. }
  3411. dbExamInfo = JsonSerializer.Deserialize<ExamInfo>(strExamDb);
  3412. //dbExamInfo = obj.ToObject<ExamInfo>();
  3413. }
  3414. }
  3415. }
  3416. if (string.IsNullOrWhiteSpace(dbExamInfo.id))
  3417. {
  3418. dbExamInfo = ExamInfoFromReq;
  3419. }
  3420. if (cloudas) dbExamInfo.cloudas = cloudas;
  3421. //Boolean isTestFlg = (_option.Location.Contains("Test") || _option.Location.Contains("Dep")) ? true : false;
  3422. //ExamInfo內容取得、調整 [2021-7-13 廢除,給予HiTeach學校Blob寫入權限,API不再對Blob做搬運]
  3423. //※規則 owner:"school" => 校園評測 "teacher" => 個人評測
  3424. //※規則 scope:"school" => 校本班級 "private" => 個人班級
  3425. //※規則 BLOB容器: scope:"school" => {學校ID}下 scope:"private" => {個人ID}下
  3426. string blobContainer = (!string.IsNullOrWhiteSpace(dbExamInfo.school) && dbExamInfo.scope.Equals("school")) ? dbExamInfo.school : id; //blob容器
  3427. //dbExamInfo.source = "1"; //評測來源固定為 1:課中評量(不應由API擅自變更)
  3428. //試卷List
  3429. List<Dictionary<string, string>> recordPaperInfo = new List<Dictionary<string, string>>();
  3430. int paperIndex = 0;
  3431. foreach (PaperSimple paperInfo in ExamInfoFromReq.papers)
  3432. {
  3433. string paperBlobPath = (!paperInfo.blob.EndsWith("/")) ? paperInfo.blob + "/" : paperInfo.blob;
  3434. paperBlobPath = (paperInfo.blob.StartsWith("/")) ? paperBlobPath.Remove(0, 1) : paperBlobPath;
  3435. //string subjectId = dbExamInfo.subjects[paperIndex].id;
  3436. string subjectId = examSubjectId;
  3437. recordPaperInfo.Add(new Dictionary<string, string>() { { "id", paperInfo.id }, { "blob", paperBlobPath }, { "subjectId", subjectId }, { "itemcount", paperInfo.point.Count.ToString() } });
  3438. paperIndex++;
  3439. }
  3440. //取得課堂紀錄下的試卷資料(blob)、複製到評測紀錄下
  3441. bool paperDataCopyErrFlg = false; //試卷資料拷貝錯誤Flag true:拷貝錯誤 [2021-7-13 (測試站)不再對Blob做搬運 永為false]
  3442. //Blob搬運 (待HiTeach完善後刪除)
  3443. if (!blobUploaded)
  3444. {
  3445. foreach (Dictionary<string, string> recordPaperInfoDic in recordPaperInfo)
  3446. {
  3447. string targetScope = dbExamInfo.scope; //評測對象 school:校本班級 private:私人課程
  3448. var blobPrivateContainer = _azureStorage.GetBlobContainerClient(id);
  3449. string sourceBlobPath = recordPaperInfoDic["blob"];
  3450. string destBlobPath = $"exam/{dbExamInfo.id}/paper/{recordPaperInfoDic["subjectId"]}/"; //拷貝對象路徑 path:exam/{評測ID}/paper/{subjectID}/ ※2022-1-6 式樣變更[原]/paper/{試卷ID}/ [新]/paper/{subjectID}/
  3451. if (targetScope.Equals("school")) //校本
  3452. {
  3453. string schoolId = dbExamInfo.school;
  3454. var blobSchoolContainer = _azureStorage.GetBlobContainerClient(schoolId);
  3455. var sourceBlobContainer = (recordSwitch) ? blobSchoolContainer : blobPrivateContainer;
  3456. Azure.Pageable<BlobItem> sourceBlobs = sourceBlobContainer.GetBlobs(prefix: sourceBlobPath);
  3457. if (sourceBlobs.Count() > 0)
  3458. {
  3459. foreach (var blob in sourceBlobs)
  3460. {
  3461. var sourceFileBlob = sourceBlobContainer.GetBlobClient(blob.Name);
  3462. if (sourceFileBlob.Exists())
  3463. {
  3464. var sourceFileUri = sourceBlobContainer.GetBlobClient(blob.Name).Uri;
  3465. string fileName = blob.Name.Replace(sourceBlobPath, "");
  3466. string destBlobFilePath = $"{destBlobPath}{fileName}";
  3467. await blobSchoolContainer.GetBlobClient(destBlobFilePath).StartCopyFromUriAsync(sourceFileUri);
  3468. }
  3469. else
  3470. {
  3471. paperDataCopyErrFlg = true;
  3472. }
  3473. }
  3474. }
  3475. }
  3476. else //私人
  3477. {
  3478. var sourceBlobContainer = blobPrivateContainer;
  3479. Azure.Pageable<BlobItem> sourceBlobs = sourceBlobContainer.GetBlobs(prefix: sourceBlobPath);
  3480. if (sourceBlobs.Count() > 0)
  3481. {
  3482. foreach (var blob in sourceBlobs)
  3483. {
  3484. var sourceFileBlob = sourceBlobContainer.GetBlobClient(blob.Name);
  3485. if (sourceFileBlob.Exists())
  3486. {
  3487. var sourceFileUri = sourceBlobContainer.GetBlobClient(blob.Name).Uri;
  3488. string fileName = blob.Name.Replace(sourceBlobPath, "");
  3489. string destBlobFilePath = $"{destBlobPath}{fileName}";
  3490. await blobPrivateContainer.GetBlobClient(destBlobFilePath).StartCopyFromUriAsync(sourceFileUri);
  3491. }
  3492. else
  3493. {
  3494. paperDataCopyErrFlg = true;
  3495. }
  3496. }
  3497. }
  3498. }
  3499. //替換 Exam.papers.blob
  3500. PaperSimple paperInfoNow = dbExamInfo.papers.Where((PaperSimple p) => p.id == recordPaperInfoDic["id"]).FirstOrDefault();
  3501. string destBlobPathForDocument = (destBlobPath.EndsWith("/")) ? destBlobPath.Remove(destBlobPath.Length - 1, 1) : destBlobPath;
  3502. destBlobPathForDocument = (!destBlobPathForDocument.StartsWith("/")) ? "/" + destBlobPathForDocument : destBlobPathForDocument;
  3503. paperInfoNow.blob = destBlobPathForDocument;
  3504. }
  3505. }
  3506. //ExamClassResult內容調整
  3507. bool studentAnswerCopyErrFlg = false; //學生作答資料拷貝錯誤Flag true:拷貝錯誤 [2021-7-13 不再對Blob做搬運 永為false]
  3508. //Blob搬運 (待HiTeach完善後刪除)
  3509. if (!blobUploaded)
  3510. {
  3511. List<ExamClassResultStudentAnswerArrayOld> dbExamClassResultList = examClassResult.ToObject<List<ExamClassResultStudentAnswerArrayOld>>();
  3512. foreach (ExamClassResultStudentAnswerArrayOld examClassResultRow in dbExamClassResultList)
  3513. {
  3514. //以subjectId及classId(info.id)取得DB中的ExamClassResult
  3515. ExamClassResult examClassResultUpd = new ExamClassResult();
  3516. string exclcode = examClassResultRow.code;
  3517. string classId = examClassResultRow.info.id;
  3518. string subjectId = examClassResultRow.subjectId;
  3519. var querycr = $"SELECT * FROM c WHERE c.examId = '{examId}' AND c.info.id = '{classId}' AND c.subjectId = '{subjectId}'";
  3520. await foreach (var itemcr in _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Common").GetItemQueryStreamIteratorSql(queryText: querycr, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"{exclcode}") }))
  3521. {
  3522. var jsontcr = await JsonDocument.ParseAsync(itemcr.Content);
  3523. if (jsontcr.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  3524. {
  3525. foreach (var obj in jsontcr.RootElement.GetProperty("Documents").EnumerateArray())
  3526. {
  3527. examClassResultUpd = obj.ToObject<ExamClassResult>();
  3528. examClassResultUpd.progress = examClassResultRow.progress;
  3529. examClassResultUpd.studentIds = examClassResultRow.studentIds;
  3530. examClassResultUpd.studentAnswers = new List<List<string>>();
  3531. examClassResultUpd.studentScores = examClassResultRow.studentScores;
  3532. examClassResultUpd.status = examClassResultRow.status;
  3533. examClassResultUpd.sum = examClassResultRow.sum;
  3534. }
  3535. }
  3536. }
  3537. //無法取得既有ExamClassResult,新建
  3538. if (string.IsNullOrWhiteSpace(examClassResultUpd.id))
  3539. {
  3540. examClassResultUpd.pk = examClassResultRow.pk;
  3541. examClassResultUpd.code = examClassResultRow.code;
  3542. examClassResultUpd.id = examClassResultRow.id;
  3543. examClassResultUpd.school = examClassResultRow.school;
  3544. examClassResultUpd.examId = examClassResultRow.examId;
  3545. examClassResultUpd.subjectId = examClassResultRow.subjectId;
  3546. examClassResultUpd.gradeId = examClassResultRow.gradeId;
  3547. examClassResultUpd.year = examClassResultRow.year;
  3548. examClassResultUpd.info = examClassResultRow.info;
  3549. examClassResultUpd.progress = examClassResultRow.progress;
  3550. examClassResultUpd.studentIds = examClassResultRow.studentIds;
  3551. examClassResultUpd.studentAnswers = new List<List<string>>(); //學生作答Blob位置
  3552. examClassResultUpd.studentScores = examClassResultRow.studentScores;
  3553. examClassResultUpd.status = examClassResultRow.status;
  3554. examClassResultUpd.scope = examClassResultRow.scope;
  3555. examClassResultUpd.sum = examClassResultRow.sum;
  3556. }
  3557. //examClassResult.studentAnswers (1)將學生答案上傳blob後轉換內容為blob路徑 //[2021-7-13 不再對Blob做搬運] (2)將學生答案放入examClassResult.ans
  3558. if (examClassResultRow.studentIds != null && examClassResultRow.studentIds.Count > 0 && examClassResultRow.studentAnswersArray != null && examClassResultRow.studentAnswersArray.Count > 0)
  3559. {
  3560. for (int i = 0; i < examClassResultRow.studentAnswersArray.Count; i++)
  3561. {
  3562. string studentId = examClassResultRow.studentIds[i];
  3563. string fileName = examId + "/" + subjectId + "/" + studentId;
  3564. string blob = fileName + "/" + "ans.json";
  3565. var uploadFileResult = await _azureStorage.GetBlobContainerClient(blobContainer).UploadFileByContainer(examClassResultRow.studentAnswersArray[i].ToJsonString(), "exam", blob, false);
  3566. if (string.IsNullOrWhiteSpace(uploadFileResult))
  3567. {
  3568. studentAnswerCopyErrFlg = true;
  3569. }
  3570. //studentAnswers
  3571. List<string> studenrAnswerRow = new List<string>();
  3572. studenrAnswerRow.Add(blob);
  3573. examClassResultUpd.studentAnswers.Add(studenrAnswerRow);
  3574. }
  3575. //examClassResult.ans 將學生答案放入
  3576. examClassResultUpd.ans = examClassResultRow.studentAnswersArray;
  3577. }
  3578. //批註欄位處理
  3579. Dictionary<string, string> paperStatusNow = recordPaperInfo.Where((Dictionary<string, string> p) => p.TryGetValue("subjectId", out string paperStatusSubjectId) && paperStatusSubjectId.Equals(subjectId)).FirstOrDefault();
  3580. int itemCount = Convert.ToInt32(paperStatusNow["itemcount"]);
  3581. if (examClassResultUpd.mark.Count != examClassResultUpd.studentIds.Count || (examClassResultUpd.mark.ElementAtOrDefault(0) != null && examClassResultUpd.mark[0].Count != itemCount)) //第一層:學生數不符 OR 第二層:第一位學生批註數!=試卷題數 => 批註格式不符
  3582. {
  3583. examClassResultUpd.mark = this.createEmptyMark(examClassResultUpd.studentIds.Count, itemCount);
  3584. }
  3585. //UPDATE ExamClassResult
  3586. var examClassResultResponse = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Common").UpsertItemAsync(examClassResultUpd, new PartitionKey(examClassResultUpd.code));
  3587. }
  3588. }
  3589. else
  3590. {
  3591. List<ExamClassResultStudentAnswerArray> dbExamClassResultList = examClassResult.ToObject<List<ExamClassResultStudentAnswerArray>>();
  3592. foreach (ExamClassResultStudentAnswerArray examClassResultRow in dbExamClassResultList)
  3593. {
  3594. //以subjectId及classId(info.id)取得DB中的ExamClassResult
  3595. ExamClassResult examClassResultUpd = new ExamClassResult();
  3596. string exclcode = examClassResultRow.code;
  3597. string classId = examClassResultRow.info.id;
  3598. string subjectId = examClassResultRow.subjectId;
  3599. var querycr = $"SELECT * FROM c WHERE c.examId = '{examId}' AND c.info.id = '{classId}' AND c.subjectId = '{subjectId}'";
  3600. await foreach (var itemcr in _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Common").GetItemQueryStreamIteratorSql(queryText: querycr, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"{exclcode}") }))
  3601. {
  3602. var jsontcr = await JsonDocument.ParseAsync(itemcr.Content);
  3603. if (jsontcr.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  3604. {
  3605. foreach (var obj in jsontcr.RootElement.GetProperty("Documents").EnumerateArray())
  3606. {
  3607. examClassResultUpd = obj.ToObject<ExamClassResult>();
  3608. examClassResultUpd.progress = examClassResultRow.progress;
  3609. examClassResultUpd.studentIds = examClassResultRow.studentIds;
  3610. examClassResultUpd.studentAnswers = new List<List<string>>();
  3611. examClassResultUpd.studentScores = examClassResultRow.studentScores;
  3612. examClassResultUpd.status = examClassResultRow.status;
  3613. examClassResultUpd.sum = examClassResultRow.sum;
  3614. }
  3615. }
  3616. }
  3617. //無法取得既有ExamClassResult,新建
  3618. if (string.IsNullOrWhiteSpace(examClassResultUpd.id))
  3619. {
  3620. examClassResultUpd.pk = examClassResultRow.pk;
  3621. examClassResultUpd.code = examClassResultRow.code;
  3622. examClassResultUpd.id = examClassResultRow.id;
  3623. examClassResultUpd.school = examClassResultRow.school;
  3624. examClassResultUpd.examId = examClassResultRow.examId;
  3625. examClassResultUpd.subjectId = examClassResultRow.subjectId;
  3626. examClassResultUpd.gradeId = examClassResultRow.gradeId;
  3627. examClassResultUpd.year = examClassResultRow.year;
  3628. examClassResultUpd.info = examClassResultRow.info;
  3629. examClassResultUpd.progress = examClassResultRow.progress;
  3630. examClassResultUpd.studentIds = examClassResultRow.studentIds;
  3631. examClassResultUpd.studentAnswers = new List<List<string>>();
  3632. examClassResultUpd.studentScores = examClassResultRow.studentScores;
  3633. examClassResultUpd.status = examClassResultRow.status;
  3634. examClassResultUpd.scope = examClassResultRow.scope;
  3635. examClassResultUpd.sum = examClassResultRow.sum;
  3636. }
  3637. //學生作答Blob位置[2021-7-13 不再對Blob做搬運 學生答案直接寫入DB]
  3638. examClassResultUpd.studentAnswers = examClassResultRow.studentAnswersArray;
  3639. //學生作答答案 (從blob取得學生答案、放入ans欄位)
  3640. string targetScope = dbExamInfo.scope; //評測對象 school:校本班級 private:私人課程
  3641. var targetBlobContainer = _azureStorage.GetBlobContainerClient(id);
  3642. string blobCntr = id;
  3643. if (targetScope.Equals("school")) //校本
  3644. {
  3645. string schoolId = dbExamInfo.school;
  3646. blobCntr = schoolId;
  3647. }
  3648. targetBlobContainer = _azureStorage.GetBlobContainerClient(blobCntr);
  3649. if (examClassResultRow.studentAnswersArray.Count > 0)
  3650. {
  3651. foreach (List<string> studentAnswerBlobPath in examClassResultRow.studentAnswersArray)
  3652. {
  3653. string stuAnswerBlobPath = studentAnswerBlobPath.FirstOrDefault();
  3654. if (stuAnswerBlobPath != null)
  3655. {
  3656. StringBuilder builder = new StringBuilder();
  3657. builder.Append("exam").Append("/").Append(stuAnswerBlobPath);
  3658. var Download = await targetBlobContainer.GetBlobClient(builder.ToString()).DownloadAsync();
  3659. var json = await JsonDocument.ParseAsync(Download.Value.Content);
  3660. var Record = json.RootElement.ToObject<List<List<string>>>();
  3661. examClassResultUpd.ans.Add(Record);
  3662. }
  3663. }
  3664. }
  3665. //批註欄位處理
  3666. Dictionary<string, string> paperStatusNow = recordPaperInfo.Where((Dictionary<string, string> p) => p.TryGetValue("subjectId", out string subjectId) && subjectId.Equals(examClassResultUpd.subjectId)).FirstOrDefault();
  3667. int itemCount = Convert.ToInt32(paperStatusNow["itemcount"]);
  3668. if (examClassResultUpd.mark.Count != examClassResultUpd.studentIds.Count || (examClassResultUpd.mark.ElementAtOrDefault(0) != null && examClassResultUpd.mark[0].Count != itemCount)) //第一層:學生數不符 OR 第二層:第一位學生批註數!=試卷題數 => 批註格式不符
  3669. {
  3670. examClassResultUpd.mark = this.createEmptyMark(examClassResultUpd.studentIds.Count, itemCount);
  3671. }
  3672. //UPDATE ExamClassResult
  3673. var examClassResultResponse = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Common").UpsertItemAsync(examClassResultUpd, new PartitionKey(examClassResultUpd.code));
  3674. }
  3675. }
  3676. //搬移Blob (blobCopyPath)
  3677. bool blobDataCopyErrFlg = false;
  3678. if (!blobUploaded && blobCopyPath.Count > 0)
  3679. {
  3680. var sourceBlobContainer = _azureStorage.GetBlobContainerClient(blobContainer);
  3681. var destBlobContainer = (dbExamInfo.scope.Equals("school")) ? _azureStorage.GetBlobContainerClient(dbExamInfo.school) : _azureStorage.GetBlobContainerClient(id);
  3682. foreach (string fromBlobPath in blobCopyPath)
  3683. {
  3684. Azure.Pageable<BlobItem> sourceBlobs = sourceBlobContainer.GetBlobs(prefix: fromBlobPath);
  3685. if (sourceBlobs.Count() > 0)
  3686. {
  3687. foreach (var blob in sourceBlobs)
  3688. {
  3689. var sourceFileBlob = sourceBlobContainer.GetBlobClient(blob.Name);
  3690. if (sourceFileBlob.Exists())
  3691. {
  3692. var sourceFileUri = sourceBlobContainer.GetBlobClient(blob.Name).Uri;
  3693. string fileName = blob.Name.Replace(fromBlobPath, "");
  3694. string destBlobPath = $"exam/{dbExamInfo.id}/{examSubjectId}/"; //拷貝對象路徑 path:exam/{評測ID}/paper/{subjectID}/ ※2022-1-6 式樣變更[原]/paper/{試卷ID}/ [新]/paper/{subjectID}/
  3695. if (fileName.StartsWith("/")) fileName = fileName.Remove(0, 1);
  3696. string destBlobFilePath = $"{destBlobPath}{fileName}";
  3697. await destBlobContainer.GetBlobClient(destBlobFilePath).StartCopyFromUriAsync(sourceFileUri);
  3698. }
  3699. else
  3700. {
  3701. blobDataCopyErrFlg = true;
  3702. }
  3703. }
  3704. }
  3705. }
  3706. }
  3707. //UPDATE Exam
  3708. await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Common").UpsertItemAsync(dbExamInfo, new PartitionKey(dbExamInfo.code));
  3709. //錯誤處理
  3710. if (paperDataCopyErrFlg) //試卷Blob拷貝失敗
  3711. {
  3712. error = 1001;
  3713. message = "Paper blob copy failure.";
  3714. }
  3715. else if (studentAnswerCopyErrFlg) //學生作答資料上傳blob失敗
  3716. {
  3717. error = 1002;
  3718. message = "Student answers blob upload failure.";
  3719. }
  3720. else if (blobDataCopyErrFlg) //blob拷貝失敗
  3721. {
  3722. error = 1003;
  3723. message = "Blob copy failure.";
  3724. }
  3725. return Ok(new { error, message });
  3726. }
  3727. catch (CosmosException cex)
  3728. {
  3729. return BadRequest(cex.Message);
  3730. }
  3731. //catch (StorageException bex)
  3732. //{
  3733. // return BadRequest(bex.Message);
  3734. //}
  3735. catch (Exception ex)
  3736. {
  3737. return BadRequest(ex.Message);
  3738. }
  3739. }
  3740. /**
  3741. * 根据学年获取年级信息
  3742. * @param year 学年
  3743. * @param Period 学段資料
  3744. */
  3745. private ExamSimple getGradeInfoByYear(int year, Period curPeriod)
  3746. {
  3747. ExamSimple result = new ExamSimple();
  3748. if (year > 0)
  3749. {
  3750. DateTime date = DateTime.UtcNow;
  3751. int curYear = date.Year;
  3752. int month = date.Month;
  3753. Semester semesterStart = curPeriod.semesters.Where((Semester x) => x.start.Equals(1)).FirstOrDefault();
  3754. if (semesterStart != null)
  3755. {
  3756. if (month < semesterStart.month)
  3757. {
  3758. curYear--;
  3759. }
  3760. int gradeIndex = curYear - year;
  3761. result.id = gradeIndex.ToString();
  3762. result.name = (gradeIndex >= curPeriod.grades.Count) ? "graduated" : (gradeIndex >= 0) ? curPeriod.grades[gradeIndex] : "not-enrollment";
  3763. }
  3764. }
  3765. return result;
  3766. }
  3767. private List<List<List<Details>>> createEmptyMark(int studentNum, int itemNum)
  3768. {
  3769. List<List<List<Details>>> mark = new List<List<List<Details>>>();
  3770. for (int i = 0; i < studentNum; i++)
  3771. {
  3772. List<List<Details>> markRow = new List<List<Details>>();
  3773. for (int j = 0; j < itemNum; j++)
  3774. {
  3775. markRow.Add(new List<Details>());
  3776. }
  3777. mark.Add(markRow);
  3778. }
  3779. return mark;
  3780. }
  3781. public class ClassStudents
  3782. {
  3783. public string id { get; set; }
  3784. public string name { get; set; }
  3785. public string no { get; set; }
  3786. public string schoolId { get; set; }
  3787. public string groupId { get; set; }
  3788. public string groupName { get; set; }
  3789. public string irs { get; set; }
  3790. public int type { get; set; } //类型 1 tmdid,2 student
  3791. public string nickname { get; set; }
  3792. }
  3793. public class ClassGroups
  3794. {
  3795. public ClassGroups()
  3796. {
  3797. studentIds = new List<string>();
  3798. }
  3799. public string id { get; set; }
  3800. public string name { get; set; }
  3801. public List<string> studentIds { get; set; }
  3802. }
  3803. //ExamClassResult 學生作答紀錄(舊式 正式站暫時用這個)
  3804. public class ExamClassResultStudentAnswerArrayOld : ExamClassResult
  3805. {
  3806. public List<List<List<string>>> studentAnswersArray { get; set; }
  3807. }
  3808. //ExamClassResult 學生作答紀錄
  3809. public class ExamClassResultStudentAnswerArray : ExamClassResult
  3810. {
  3811. public List<List<string>> studentAnswersArray { get; set; }
  3812. }
  3813. //get-teacher-info API輸出 schools
  3814. private class GetTeacherInfoApiSchool
  3815. {
  3816. public string schoolId { get; set; }
  3817. public string name { get; set; }
  3818. public string status { get; set; }
  3819. public string picture { get; set; }
  3820. }
  3821. //get-teacher-info輸出 courses
  3822. private class GetTeacherInfoApiCourse : GetTeacherInfoApiSimlpeBase
  3823. {
  3824. public string scope { get; set; }
  3825. public List<GetTeacherInfoApiCourseClass> classes { get; set; }
  3826. }
  3827. //get-teacher-info輸出 courses.classes
  3828. private class GetTeacherInfoApiCourseClass : GetTeacherInfoApiSimlpeBase
  3829. {
  3830. public string code { get; set; }
  3831. public GetTeacherInfoApiSimlpeBase teacher { get; set; }
  3832. public string scope { get; set; }
  3833. public string stuListId { get; set; }
  3834. public int stuCnt { get; set; }
  3835. public int grpCnt { get; set; }
  3836. public string gradeId { get; set; }
  3837. public string gradeName { get; set; }
  3838. public int year { get; set; }
  3839. }
  3840. //get-teacher-info輸出 基本class(各class繼承用)
  3841. internal class GetTeacherInfoApiSimlpeBase
  3842. {
  3843. public string id { get; set; }
  3844. public string name { get; set; }
  3845. }
  3846. //get-school-info輸出 exams.finishClassesSub(各班級已完成評測的科目列表)
  3847. private class GetSchInfoExamFinishClassesSub
  3848. {
  3849. public string classId { get; set; } //班級ID
  3850. public List<string> subjectIds { get; set; } //需完成科目ID
  3851. public List<string> finishSubjectIds { get; set; } //已完成科目ID
  3852. }
  3853. //get-school-info輸出 courses
  3854. private class GetSchoolInfoApiCourse : GetTeacherInfoApiSimlpeBase
  3855. {
  3856. public string scope { get; set; }
  3857. public GetTeacherInfoApiSimlpeBase subject { get; set; }
  3858. public List<GetTeacherInfoApiCourseClass> classes { get; set; }
  3859. }
  3860. //get-school-info (中間資料)記錄學生已完成的評測、班級、科目
  3861. private class ExamFinishClassesSubList
  3862. {
  3863. public string examId { get; set; } //評測ID
  3864. public string classId { get; set; } //班級ID
  3865. public string subjectId { get; set; } //科目ID
  3866. }
  3867. //get-school-info輸出 exams.subjects
  3868. private class GetSchInfoExamSubject
  3869. {
  3870. public string id { get; set; }
  3871. public string name { get; set; }
  3872. public int classCount { get; set; }
  3873. }
  3874. private class GroupListMemberCnt
  3875. {
  3876. public string id { get; set; }
  3877. public string code { get; set; }
  3878. public string name { get; set; }
  3879. public string scope { get; set; }
  3880. public string type { get; set; } /// class:編制班 teach:選課班
  3881. public int year { get; set; }
  3882. public int memberCount { get; set; }
  3883. }
  3884. #region ===學習記錄用類別===
  3885. #region ===TimeLine.json===
  3886. private class TimeLineEvents
  3887. {
  3888. public TimeLineEvents()
  3889. {
  3890. events = new List<TimeLineEventPg>();
  3891. }
  3892. public List<TimeLineEventPg> events { get; set; }
  3893. }
  3894. private class TimeLineEventPg
  3895. {
  3896. public double Time { get; set; }
  3897. public string Pgid { get; set; }
  3898. public string Event { get; set; }
  3899. }
  3900. #endregion
  3901. #region ===IRS.json===
  3902. /// <summary>
  3903. /// IRS架構第一層
  3904. /// </summary>
  3905. private class IRSItem
  3906. {
  3907. public IRSItem()
  3908. {
  3909. buzzClients = new List<string>();
  3910. }
  3911. public string pageID { get; set; }
  3912. public IRSQuestion question { get; set; }
  3913. public JsonElement clientAnswers { get; set; }
  3914. public List<string> buzzClients { get; set; }
  3915. public bool isBuzz { get; set; }
  3916. }
  3917. /// <summary>
  3918. /// IRS架構第二層
  3919. /// </summary>
  3920. private class IRSQuestion
  3921. {
  3922. public IRSQuestion()
  3923. {
  3924. item = new List<QuestionItem>();
  3925. }
  3926. public QuestionExercise exercise { get; set; }
  3927. public List<QuestionItem> item { get; set; }
  3928. }
  3929. private class QuestionItem
  3930. {
  3931. public QuestionItem()
  3932. {
  3933. option = new List<OptionItem>();
  3934. }
  3935. public string question { get; set; }
  3936. public List<OptionItem> option { get; set; }
  3937. }
  3938. private class OptionItem
  3939. {
  3940. public string code { get; set; }
  3941. public string value { get; set; }
  3942. }
  3943. /// <summary>
  3944. /// IRS架構第三層
  3945. /// </summary>
  3946. private class QuestionExercise
  3947. {
  3948. public string type { get; set; }
  3949. public List<string> knowledges { get; set; }
  3950. public List<string> answer { get; set; }
  3951. }
  3952. #endregion
  3953. #region ===Task.json===
  3954. private class TaskItem
  3955. {
  3956. public List<ClientWork> clientWorks { get; set; }
  3957. public string pageID { get; set; }
  3958. }
  3959. private class ClientWork
  3960. {
  3961. public int seatID { get; set; }
  3962. public string reciveTime { get; set; }
  3963. }
  3964. #endregion
  3965. private class LessonRecordItemPart
  3966. {
  3967. public string id { get; set; }
  3968. public string tmdid { get; set; }
  3969. public string school { get; set; }
  3970. public string scope { get; set; }
  3971. }
  3972. #region ===取學校代碼===
  3973. private class GroupIdsFromLesson
  3974. {
  3975. public List<string> groupIds { get; set; }
  3976. }
  3977. private class SchoolFromgroupIds
  3978. {
  3979. public string school { get; set; }
  3980. }
  3981. #endregion
  3982. #endregion
  3983. #region ===評量 - 學習記錄用類別===
  3984. private class PaperIndex
  3985. {
  3986. public PaperIndex()
  3987. {
  3988. slides = new List<Slide>();
  3989. }
  3990. public List<Slide> slides { get; set; }
  3991. }
  3992. private class Slide
  3993. {
  3994. /// <summary>
  3995. /// blob 路徑
  3996. /// </summary>
  3997. public string url { get; set; }
  3998. /// <summary>
  3999. /// 題型
  4000. /// </summary>
  4001. public string type { get; set; }
  4002. /// <summary>
  4003. /// scoring
  4004. /// </summary>
  4005. public Scoring scoring { get; set; }
  4006. }
  4007. private class Exercise
  4008. {
  4009. /// <summary>
  4010. /// 题目类型
  4011. /// </summary>
  4012. public string type { get; set; }
  4013. /// <summary>
  4014. /// 难度
  4015. /// </summary>
  4016. public int level { get; set; }
  4017. /// <summary>
  4018. /// 知识点
  4019. /// </summary>
  4020. public List<string> knowledges { get; set; }
  4021. }
  4022. private class Scoring
  4023. {
  4024. /// <summary>
  4025. /// knowledge
  4026. /// </summary>
  4027. public List<string> knowledge { get; set; }
  4028. /// <summary>
  4029. /// 答案
  4030. /// </summary>
  4031. public List<string> ans { get; set; }
  4032. }
  4033. private class StudentAnswers
  4034. {
  4035. /// <summary>
  4036. /// 學生作答 blob 路徑
  4037. /// </summary>
  4038. public List<List<string>> studentAnswers { get; set; }
  4039. /// <summary>
  4040. /// 學生ID
  4041. /// </summary>
  4042. public List<string> studentIds { get; set; } = new();
  4043. }
  4044. private class QuestionData
  4045. {
  4046. public QuestionData()
  4047. {
  4048. item = new List<QuestionItem>();
  4049. }
  4050. public QuestionExercise exercise { get; set; }
  4051. public List<QuestionItem> item { get; set; }
  4052. }
  4053. #endregion
  4054. }
  4055. }