HiTeachController.cs 249 KB

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