JointEventController.cs 75 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298
  1. using Microsoft.AspNetCore.Hosting;
  2. using Microsoft.AspNetCore.Http;
  3. using Microsoft.AspNetCore.Mvc;
  4. using Microsoft.Extensions.Configuration;
  5. using TEAMModelOS.SDK.DI;
  6. using TEAMModelOS.SDK;
  7. using Microsoft.Extensions.Options;
  8. using TEAMModelOS.Models;
  9. using System.Net.Http;
  10. using Microsoft.AspNetCore.Authorization;
  11. using TEAMModelOS.Filter;
  12. using System.Text.Json;
  13. using System.Threading.Tasks;
  14. using TEAMModelOS.SDK.Extension;
  15. using System.Text;
  16. using System.Collections.Generic;
  17. using TEAMModelOS.SDK.Models;
  18. using Microsoft.Azure.Cosmos;
  19. using System;
  20. using static TEAMModelOS.SDK.Models.JointEvent;
  21. using System.Linq;
  22. using TEAMModelOS.SDK.Models.Service;
  23. using Azure.Messaging.ServiceBus;
  24. using Azure.Storage.Sas;
  25. namespace TEAMModelOS.Controllers.Common
  26. {
  27. [ProducesResponseType(StatusCodes.Status200OK)]
  28. [ProducesResponseType(StatusCodes.Status400BadRequest)]
  29. [Route("joint")]
  30. [ApiController]
  31. public class JointEventController : ControllerBase
  32. {
  33. private readonly AzureCosmosFactory _azureCosmos;
  34. private readonly SnowflakeId _snowflakeId;
  35. private readonly AzureServiceBusFactory _serviceBus;
  36. private readonly DingDing _dingDing;
  37. private readonly Option _option;
  38. private readonly AzureStorageFactory _azureStorage;
  39. private readonly AzureRedisFactory _azureRedis;
  40. private readonly IHttpClientFactory _httpClient;
  41. public IConfiguration _configuration { get; set; }
  42. private readonly CoreAPIHttpService _coreAPIHttpService;
  43. private readonly IWebHostEnvironment _environment;
  44. public JointEventController(IWebHostEnvironment environment, CoreAPIHttpService coreAPIHttpService, AzureCosmosFactory azureCosmos, AzureServiceBusFactory serviceBus, SnowflakeId snowflakeId, DingDing dingDing,
  45. IOptionsSnapshot<Option> option, AzureStorageFactory azureStorage, AzureRedisFactory azureRedis, IConfiguration configuration, IHttpClientFactory httpClient)
  46. {
  47. _azureCosmos = azureCosmos;
  48. _serviceBus = serviceBus;
  49. _snowflakeId = snowflakeId;
  50. _dingDing = dingDing;
  51. _option = option?.Value;
  52. _azureStorage = azureStorage;
  53. _azureRedis = azureRedis;
  54. _configuration = configuration;
  55. _coreAPIHttpService = coreAPIHttpService;
  56. _environment = environment;
  57. _httpClient = httpClient;
  58. }
  59. /// <summary>
  60. /// 取得統測活動
  61. /// </summary>
  62. [ProducesDefaultResponseType]
  63. [Authorize(Roles = "IES,HTCommunity")]
  64. [HttpPost("event/find")]
  65. public async Task<IActionResult> EventFind(JsonElement request)
  66. {
  67. try
  68. {
  69. var client = _azureCosmos.GetCosmosClient();
  70. StringBuilder stringBuilder = new($"SELECT * FROM c WHERE 1=1 ");
  71. string jointEventId = (request.TryGetProperty("jointEventId", out JsonElement _jointEventId)) ? _jointEventId.ToString() : string.Empty;
  72. if (!string.IsNullOrWhiteSpace(jointEventId))
  73. {
  74. stringBuilder.Append($" AND c.id = '{jointEventId}' ");
  75. }
  76. string creatorId = (request.TryGetProperty("creatorId", out JsonElement _creatorId)) ? _creatorId.ToString() : string.Empty;
  77. if (!string.IsNullOrWhiteSpace(creatorId))
  78. {
  79. stringBuilder.Append($" AND c.creatorId = '{creatorId}' ");
  80. }
  81. if (request.TryGetProperty("name", out JsonElement name) && !string.IsNullOrWhiteSpace($"{name}"))
  82. {
  83. stringBuilder.Append($" AND Contains( c.name , '{name}') = true ");
  84. }
  85. if (request.TryGetProperty("progress", out JsonElement progress) && !string.IsNullOrWhiteSpace($"{progress}"))
  86. {
  87. stringBuilder.Append($" AND c.progress = '{progress}' ");
  88. }
  89. if (request.TryGetProperty("geo", out JsonElement _geo))
  90. {
  91. JointEventGeo geo = _geo.ToObject<JointEventGeo>();
  92. //國級活動
  93. string whereCountry = string.Empty;
  94. if (!string.IsNullOrWhiteSpace(geo.countryId)) {
  95. whereCountry = $" (c.geo.countryId = '{geo.countryId}' AND ( IS_NULL(c.geo.provinceId) OR c.geo.provinceId = '' ) AND ( IS_NULL(c.geo.cityId) OR c.geo.cityId = '' ) AND ( IS_NULL(c.geo.distId) OR c.geo.distId = '' ) )";
  96. }
  97. //省級活動
  98. string whereProvince = string.Empty;
  99. if (!string.IsNullOrWhiteSpace(geo.provinceId))
  100. {
  101. whereProvince = $" (c.geo.countryId = '{geo.countryId}' AND c.geo.provinceId = '{geo.provinceId}' AND ( IS_NULL(c.geo.cityId) OR c.geo.cityId = '' ) AND ( IS_NULL(c.geo.distId) OR c.geo.distId = '' ) ) ";
  102. }
  103. //市級活動
  104. string whereCity = string.Empty;
  105. if (!string.IsNullOrWhiteSpace(geo.cityId))
  106. {
  107. if(!string.IsNullOrWhiteSpace(geo.provinceId))
  108. {
  109. whereCity = $"( c.geo.countryId = '{geo.countryId}' AND c.geo.provinceId = '{geo.provinceId}' AND c.geo.cityId = '{geo.cityId}' AND ( IS_NULL(c.geo.distId) OR c.geo.distId = '' ) )";
  110. }
  111. else
  112. {
  113. whereCity = $"( c.geo.countryId = '{geo.countryId}' AND ( IS_NULL(c.geo.provinceId) OR c.geo.provinceId = '' ) AND c.geo.cityId = '{geo.cityId}' AND ( IS_NULL(c.geo.distId) OR c.geo.distId = '' ) )";
  114. }
  115. }
  116. //區級活動
  117. string whereDist = string.Empty;
  118. if (!string.IsNullOrWhiteSpace(geo.distId))
  119. {
  120. whereDist = $"( c.geo.countryId = '{geo.countryId}' AND c.geo.provinceId = '{geo.provinceId}' AND c.geo.cityId = '{geo.cityId}' AND c.geo.distId = '{geo.distId}' )";
  121. }
  122. //SQL文整理
  123. StringBuilder whereGeo = new();
  124. if(!string.IsNullOrWhiteSpace(whereCountry))
  125. {
  126. if(whereGeo.Length > 0) whereGeo.Append(" OR ");
  127. whereGeo.Append(whereCountry);
  128. }
  129. if (!string.IsNullOrWhiteSpace(whereProvince))
  130. {
  131. if (whereGeo.Length > 0) whereGeo.Append(" OR ");
  132. whereGeo.Append(whereProvince);
  133. }
  134. if (!string.IsNullOrWhiteSpace(whereCity))
  135. {
  136. if (whereGeo.Length > 0) whereGeo.Append(" OR ");
  137. whereGeo.Append(whereCity);
  138. }
  139. if (!string.IsNullOrWhiteSpace(whereDist))
  140. {
  141. if (whereGeo.Length > 0) whereGeo.Append(" OR ");
  142. whereGeo.Append(whereDist);
  143. }
  144. if (whereGeo.Length > 0)
  145. {
  146. stringBuilder.Append(" AND " + whereGeo);
  147. }
  148. }
  149. List<JointEventDto> eventInfo = new List<JointEventDto>();
  150. await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "Teacher").GetItemQueryStreamIteratorSql(queryText: stringBuilder.ToString(), requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("JointEvent") }))
  151. {
  152. using var json = await JsonDocument.ParseAsync(item.Content);
  153. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  154. {
  155. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  156. {
  157. JointEventDto jointEventDto = obj.ToObject<JointEventDto>();// blobsas
  158. var (blob_uri, blob_sas) = _azureStorage.GetBlobContainerSAS(jointEventDto.creatorId, BlobContainerSasPermissions.Read | BlobContainerSasPermissions.List );
  159. jointEventDto.blobsas = blob_sas;
  160. eventInfo.Add(jointEventDto);
  161. }
  162. }
  163. }
  164. return Ok(new { data = eventInfo });
  165. }
  166. catch (Exception e)
  167. {
  168. //await _dingDing.SendBotMsg($"OS,{_option.Location},jointevent/find()\n{e.Message}\n{e.StackTrace}", GroupNames.醍摩豆服務運維群組);
  169. return BadRequest();
  170. }
  171. }
  172. /// <summary>
  173. /// 新增修改統測活動
  174. /// </summary>
  175. [ProducesDefaultResponseType]
  176. [Authorize(Roles = "IES")]
  177. #if !DEBUG
  178. [AuthToken(Roles = "teacher")]
  179. #endif
  180. [HttpPost("event/upsert")]
  181. public async Task<IActionResult> EventUpsert(JsonElement request)
  182. {
  183. try
  184. {
  185. var client = _azureCosmos.GetCosmosClient();
  186. long now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
  187. string creatorId = (request.TryGetProperty("creatorId", out JsonElement _creatorId)) ? _creatorId.ToString() : string.Empty;
  188. string name = (request.TryGetProperty("name", out JsonElement _name)) ? _name.ToString() : string.Empty;
  189. string jointEventId = (request.TryGetProperty("jointEventId", out JsonElement _jointEventId)) ? _jointEventId.ToString() : string.Empty;
  190. JointEventGeo geo = (request.TryGetProperty("geo", out JsonElement _geo)) ? _geo.ToObject<JointEventGeo>() : null;
  191. List<string> admin = (request.TryGetProperty("admin", out JsonElement _admin)) ? _admin.ToObject<List<string>>() : new List<string>();
  192. long startTime = (request.TryGetProperty("startTime", out JsonElement _startTime)) ? _startTime.GetInt64() : 0;
  193. long endTime = (request.TryGetProperty("endTime", out JsonElement _endTime)) ? _endTime.GetInt64() : 0;
  194. //新建統測活動 輸入項檢測
  195. if (string.IsNullOrWhiteSpace(creatorId) || string.IsNullOrWhiteSpace(name) || startTime.Equals(0) || endTime.Equals(0))
  196. {
  197. return Ok(new { errCode = "1", err = "Invalid param." });
  198. }
  199. if (startTime > endTime)
  200. {
  201. return Ok(new { errCode = "2", err = "Invalid startTime or endTime." });
  202. }
  203. JointEvent jointEvent = new JointEvent();
  204. if (string.IsNullOrWhiteSpace(jointEventId))
  205. {
  206. jointEvent.id = Guid.NewGuid().ToString();
  207. jointEvent.creatorId = creatorId;
  208. jointEvent.code = "JointEvent";
  209. jointEvent.createTime = now;
  210. }
  211. else
  212. {
  213. jointEvent = await client.GetContainer(Constant.TEAMModelOS, "Teacher").ReadItemAsync<JointEvent>(jointEventId, new PartitionKey("JointEvent"));
  214. if (jointEvent == null)
  215. {
  216. return Ok(new { errCode = "3", err = "Invalid jointEventId." });
  217. }
  218. }
  219. jointEvent.name = name;
  220. jointEvent.startTime = startTime;
  221. jointEvent.endTime = endTime;
  222. jointEvent.progress = (startTime <= now && now <= endTime) ? "going" : (now < startTime) ? "pending" : "finish";
  223. if (geo != null) jointEvent.geo = geo;
  224. if (admin.Count > 0) jointEvent.admin = admin;
  225. await client.GetContainer(Constant.TEAMModelOS, "Teacher").UpsertItemAsync<JointEvent>(jointEvent, new PartitionKey("JointEvent"));
  226. return Ok(new { errCode = "", err = "", jointEvent });
  227. }
  228. catch (Exception e)
  229. {
  230. //await _dingDing.SendBotMsg($"OS,{_option.Location},jointevent/find()\n{e.Message}\n{e.StackTrace}", GroupNames.醍摩豆服務運維群組);
  231. return BadRequest();
  232. }
  233. }
  234. /// <summary>
  235. /// 新增修改統測活動分組
  236. /// </summary>
  237. [ProducesDefaultResponseType]
  238. [Authorize(Roles = "IES")]
  239. #if !DEBUG
  240. [AuthToken(Roles = "teacher")]
  241. #endif
  242. [HttpPost("event-group/upsert")]
  243. public async Task<IActionResult> EventGroupUpsert(JsonElement request)
  244. {
  245. try
  246. {
  247. string errCode = string.Empty;
  248. string err = string.Empty;
  249. var client = _azureCosmos.GetCosmosClient();
  250. string jointEventId = (request.TryGetProperty("jointEventId", out JsonElement _jointEventId)) ? _jointEventId.ToString() : string.Empty;
  251. if (string.IsNullOrWhiteSpace(jointEventId)) return BadRequest();
  252. List<JointEventGroup> groups = (request.TryGetProperty("groups", out JsonElement _groups)) ? _groups.ToObject<List<JointEventGroup>>() : new List<JointEventGroup>();
  253. if (groups.Count.Equals(0)) return BadRequest();
  254. JointEvent jointEvent = await client.GetContainer(Constant.TEAMModelOS, "Teacher").ReadItemAsync<JointEvent>(jointEventId, new PartitionKey("JointEvent"));
  255. if (jointEvent == null)
  256. {
  257. return Ok(new { errCode = "3", err = "Invalid jointEventId." });
  258. }
  259. foreach (JointEventGroup groupRow in groups)
  260. {
  261. if (!string.IsNullOrWhiteSpace(groupRow.id))
  262. {
  263. JointEventGroup groupNow = jointEvent.groups.Where(g => g.id.Equals(groupRow.id)).FirstOrDefault();
  264. if (groupNow == null)
  265. {
  266. errCode = "4";
  267. err += $"Invalid groupId:{groupRow.id}. \r\n";
  268. continue;
  269. }
  270. groupNow.name = groupRow.name;
  271. groupNow.assistants = groupRow.assistants;
  272. }
  273. else
  274. {
  275. JointEventGroup groupNow = new JointEventGroup();
  276. groupNow.id = Guid.NewGuid().ToString();
  277. groupNow.name = groupRow.name;
  278. groupNow.assistants = groupRow.assistants;
  279. jointEvent.groups.Add(groupNow);
  280. }
  281. }
  282. await client.GetContainer(Constant.TEAMModelOS, "Teacher").ReplaceItemAsync<JointEvent>(jointEvent, jointEvent.id, new PartitionKey("JointEvent"));
  283. return Ok(new { errCode, err, jointEvent });
  284. }
  285. catch (Exception e)
  286. {
  287. //await _dingDing.SendBotMsg($"OS,{_option.Location},jointevent/find()\n{e.Message}\n{e.StackTrace}", GroupNames.醍摩豆服務運維群組);
  288. return BadRequest();
  289. }
  290. }
  291. /// <summary>
  292. /// 刪除統測活動分組
  293. /// </summary>
  294. [ProducesDefaultResponseType]
  295. [Authorize(Roles = "IES")]
  296. #if !DEBUG
  297. [AuthToken(Roles = "teacher")]
  298. #endif
  299. [HttpPost("event-group/delete")]
  300. public async Task<IActionResult> EventGroupDelete(JsonElement request)
  301. {
  302. var client = _azureCosmos.GetCosmosClient();
  303. string jointEventId = (request.TryGetProperty("jointEventId", out JsonElement _jointEventId)) ? _jointEventId.ToString() : string.Empty;
  304. if (string.IsNullOrWhiteSpace(jointEventId)) return BadRequest();
  305. string groupId = (request.TryGetProperty("groupId", out JsonElement _groupId)) ? _groupId.GetString() : string.Empty;
  306. if (string.IsNullOrWhiteSpace(groupId)) return BadRequest();
  307. JointEvent jointEvent = await client.GetContainer(Constant.TEAMModelOS, "Teacher").ReadItemAsync<JointEvent>(jointEventId, new PartitionKey("JointEvent"));
  308. if (jointEvent == null)
  309. {
  310. return Ok(new { errCode = "3", err = "Invalid jointEventId." });
  311. }
  312. JointEventGroup group = jointEvent.groups.Where(g => g.id.Equals(groupId)).FirstOrDefault();
  313. if (group == null)
  314. {
  315. return Ok(new { errCode = "4", err = "Invalid groupId." });
  316. }
  317. jointEvent.groups.Remove(group);
  318. await client.GetContainer(Constant.TEAMModelOS, "Teacher").ReplaceItemAsync<JointEvent>(jointEvent, jointEvent.id, new PartitionKey("JointEvent"));
  319. return Ok(new { errCode = "", err = "", jointEvent });
  320. }
  321. /// <summary>
  322. /// 新增修改統測行程
  323. /// </summary>
  324. [ProducesDefaultResponseType]
  325. [Authorize(Roles = "IES")]
  326. #if !DEBUG
  327. [AuthToken(Roles = "teacher")]
  328. #endif
  329. [HttpPost("schedule/upsert")]
  330. public async Task<IActionResult> EventScheduleUpsert(JsonElement request)
  331. {
  332. try
  333. {
  334. string errCode = string.Empty;
  335. string err = string.Empty;
  336. var client = _azureCosmos.GetCosmosClient();
  337. long now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
  338. string jointEventId = (request.TryGetProperty("jointEventId", out JsonElement _jointEventId)) ? _jointEventId.ToString() : string.Empty;
  339. if (string.IsNullOrWhiteSpace(jointEventId)) return BadRequest();
  340. List<JointEventSchedule> schedules = (request.TryGetProperty("schedules", out JsonElement _schedules)) ? _schedules.ToObject<List<JointEventSchedule>>() : new List<JointEventSchedule>();
  341. if (schedules.Count.Equals(0)) return BadRequest();
  342. var sbm = new List<ServiceBusMessage>(); //ServiceBus訊息列表
  343. //取得活動本體
  344. JointEvent jointEvent = await client.GetContainer(Constant.TEAMModelOS, "Teacher").ReadItemAsync<JointEvent>(jointEventId, new PartitionKey("JointEvent"));
  345. if (jointEvent == null)
  346. {
  347. return Ok(new { errCode = "3", err = "Invalid jointEventId." });
  348. }
  349. //取得活動Schedule
  350. foreach (JointEventSchedule schedule in schedules)
  351. {
  352. string mode = "add";
  353. JointEventSchedule jointEventSchedule = new JointEventSchedule();
  354. if (!string.IsNullOrWhiteSpace(schedule.id))
  355. {
  356. jointEventSchedule = jointEvent.schedule.Where(s => s.id.Equals(schedule.id)).FirstOrDefault();
  357. if (jointEventSchedule == null)
  358. {
  359. errCode = "5";
  360. err += $"Invalid scheduleId:{schedule.id}. \r\n";
  361. continue;
  362. }
  363. if (jointEventSchedule.progress.Equals("going"))
  364. {
  365. errCode = "10";
  366. err += $"JointSchedule (id:{schedule.id}) is going:, can't be changed. \r\n";
  367. continue;
  368. }
  369. mode = "upd";
  370. }
  371. else
  372. {
  373. jointEventSchedule.id = Guid.NewGuid().ToString();
  374. }
  375. jointEventSchedule.name = schedule.name;
  376. jointEventSchedule.type = schedule.type;
  377. jointEventSchedule.examType = (schedule.type.Equals("join")) ? null : schedule.examType;
  378. jointEventSchedule.examOverwrite = schedule.examOverwrite;
  379. long startTimeOld = jointEventSchedule.startTime;
  380. long endTimeOld = jointEventSchedule.endTime;
  381. jointEventSchedule.startTime = schedule.startTime;
  382. jointEventSchedule.endTime = schedule.endTime;
  383. jointEventSchedule.progress = (schedule.startTime <= now && now <= schedule.endTime) ? "going" : (now < schedule.startTime) ? "pending" : "finish";
  384. jointEventSchedule.orderby = schedule.orderby;
  385. if (schedule.location != null) jointEventSchedule.location = schedule.location;
  386. if (schedule.description != null) jointEventSchedule.description = schedule.description;
  387. if (schedule.blobs != null) jointEventSchedule.blobs = schedule.blobs;
  388. if (mode.Equals("add"))
  389. {
  390. jointEvent.schedule.Add(jointEventSchedule);
  391. }
  392. //製作Schedule progress更新訊息(active-task)
  393. bool sendScheduleMsg = (!startTimeOld.Equals(jointEventSchedule.startTime) || !endTimeOld.Equals(jointEventSchedule.endTime) ) ? true : false; //是否發送變更Schedule.progress訊息
  394. if(sendScheduleMsg)
  395. {
  396. var msg = new ServiceBusMessage(new { jointEventId = $"{jointEvent.id}", jointScheduleId = $"{jointEventSchedule.id}", progress = jointEventSchedule.progress }.ToJsonString());
  397. msg.ApplicationProperties.Add("name", "JointEventSchedule");
  398. sbm.Add(msg);
  399. }
  400. }
  401. //排序
  402. jointEvent.schedule = jointEvent.schedule.OrderBy(s => s.orderby).ToList();
  403. int index = 0;
  404. foreach (JointEventSchedule schedule in jointEvent.schedule)
  405. {
  406. schedule.orderby = index;
  407. index++;
  408. }
  409. //DB更新:JointEvent
  410. await client.GetContainer(Constant.TEAMModelOS, "Teacher").ReplaceItemAsync<JointEvent>(jointEvent, jointEvent.id, new PartitionKey("JointEvent"));
  411. //批量發送Schedule progress消息
  412. if(sbm.Count > 0)
  413. {
  414. var s = await _serviceBus.GetServiceBusClient().SendBatchMessageAsync(_configuration.GetValue<string>("Azure:ServiceBus:ActiveTask"), sbm);
  415. }
  416. return Ok(new { errCode, err, jointEvent });
  417. }
  418. catch (Exception e)
  419. {
  420. //await _dingDing.SendBotMsg($"OS,{_option.Location},jointevent/find()\n{e.Message}\n{e.StackTrace}", GroupNames.醍摩豆服務運維群組);
  421. return BadRequest();
  422. }
  423. }
  424. /// <summary>
  425. /// 刪除統測統測行程
  426. /// </summary>
  427. [ProducesDefaultResponseType]
  428. [Authorize(Roles = "IES")]
  429. #if !DEBUG
  430. [AuthToken(Roles = "teacher")]
  431. #endif
  432. [HttpPost("schedule/delete")]
  433. public async Task<IActionResult> EventScheduleDelete(JsonElement request)
  434. {
  435. try
  436. {
  437. var client = _azureCosmos.GetCosmosClient();
  438. string jointEventId = (request.TryGetProperty("jointEventId", out JsonElement _jointEventId)) ? _jointEventId.ToString() : string.Empty;
  439. if (string.IsNullOrWhiteSpace(jointEventId)) return BadRequest();
  440. string scheduleId = (request.TryGetProperty("scheduleId", out JsonElement _scheduleId)) ? _scheduleId.ToString() : string.Empty;
  441. if (string.IsNullOrWhiteSpace(scheduleId)) return BadRequest();
  442. //取得活動本體
  443. JointEvent jointEvent = await client.GetContainer(Constant.TEAMModelOS, "Teacher").ReadItemAsync<JointEvent>(jointEventId, new PartitionKey("JointEvent"));
  444. if (jointEvent == null)
  445. {
  446. return Ok(new { errCode = "3", err = "Invalid jointEventId." });
  447. }
  448. //取得活動Schedule
  449. JointEventSchedule jointEventSchedule = jointEvent.schedule.Where(s => s.id.Equals(scheduleId)).FirstOrDefault();
  450. if (jointEventSchedule == null)
  451. {
  452. return Ok(new { errCode = "5", err = "Invalid scheduleId." });
  453. }
  454. else
  455. {
  456. jointEvent.schedule.Remove(jointEventSchedule);
  457. //排序
  458. jointEvent.schedule = jointEvent.schedule.OrderBy(s => s.orderby).ToList();
  459. int index = 0;
  460. foreach (JointEventSchedule schedule in jointEvent.schedule)
  461. {
  462. schedule.orderby = index;
  463. index++;
  464. }
  465. //DB更新:JointEvent
  466. await client.GetContainer(Constant.TEAMModelOS, "Teacher").ReplaceItemAsync<JointEvent>(jointEvent, jointEvent.id, new PartitionKey("JointEvent"));
  467. return Ok(new { errCode = "", err = "", jointEvent });
  468. }
  469. }
  470. catch (Exception e)
  471. {
  472. //await _dingDing.SendBotMsg($"OS,{_option.Location},jointevent/find()\n{e.Message}\n{e.StackTrace}", GroupNames.醍摩豆服務運維群組);
  473. return BadRequest();
  474. }
  475. }
  476. /// <summary>
  477. /// 取得統測教師課程及組別
  478. /// </summary>
  479. [ProducesDefaultResponseType]
  480. [Authorize(Roles = "IES,HTCommunity")]
  481. [HttpPost("course/find")]
  482. public async Task<IActionResult> JointCourseFind(JsonElement request)
  483. {
  484. try {
  485. var client = _azureCosmos.GetCosmosClient();
  486. List<JointEventGroupDto> JointCourseDto = new List<JointEventGroupDto>();
  487. StringBuilder stringBuilder = new($"SELECT * FROM c WHERE 1=1 ");
  488. string jointEventId = (request.TryGetProperty("jointEventId", out JsonElement _jointEventId)) ? _jointEventId.ToString() : string.Empty;
  489. string jointGroupId = (request.TryGetProperty("jointGroupId", out JsonElement _jointGroupId)) ? _jointGroupId.ToString() : string.Empty;
  490. string type = (request.TryGetProperty("type", out JsonElement _type)) ? _type.ToString() : "regular"; //預設取得報名名單
  491. if (string.IsNullOrWhiteSpace(jointEventId)) return BadRequest();
  492. //取得活動
  493. JointEvent jointEvent = await client.GetContainer(Constant.TEAMModelOS, "Teacher").ReadItemAsync<JointEvent>(jointEventId, new PartitionKey("JointEvent"));
  494. if (jointEvent == null)
  495. {
  496. return Ok(new { errCode = "3", err = "Invalid jointEventId.", data = JointCourseDto });
  497. }
  498. //取得報名課程
  499. stringBuilder.Append($" AND c.jointEventId = '{jointEventId}' ");
  500. if (!string.IsNullOrWhiteSpace(jointGroupId))
  501. {
  502. stringBuilder.Append($" AND c.jointGroupId = '{jointGroupId}' ");
  503. }
  504. string creatorId = (request.TryGetProperty("creatorId", out JsonElement _creatorId)) ? _creatorId.ToString() : string.Empty;
  505. if (!string.IsNullOrWhiteSpace(creatorId))
  506. {
  507. stringBuilder.Append($" AND c.creatorId = '{creatorId}' ");
  508. }
  509. if(type.Equals("regular"))
  510. {
  511. stringBuilder.Append($" AND (c.type = 'regular' OR NOT IS_DEFINED(c.type) OR IS_NULL(c.type)) ");
  512. }
  513. else
  514. {
  515. stringBuilder.Append($" AND c.type = '{type}' ");
  516. }
  517. await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "Teacher").GetItemQueryStreamIteratorSql(queryText: stringBuilder.ToString(), requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("JointCourse") }))
  518. {
  519. using var json = await JsonDocument.ParseAsync(item.Content);
  520. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  521. {
  522. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  523. {
  524. JointEventGroupDto jointEventGroupDto = obj.ToObject<JointEventGroupDto>();
  525. foreach(JointEventGroupDto.JointEventGroupCourseDto course in jointEventGroupDto.courseLists)
  526. {
  527. foreach(JointEventGroupDto.JointEventGroupCourseGroupDto group in course.groupLists)
  528. {
  529. foreach(JointEventSchedule schedule in jointEvent.schedule)
  530. {
  531. string scheduleStatus = await GetGroupFinishJointSchedule(jointEventGroupDto.jointEventId, jointEventGroupDto.jointGroupId, group.id, schedule);
  532. group.schedule.Add(new {id = schedule.id, name = schedule.name, status = scheduleStatus });
  533. }
  534. }
  535. }
  536. JointCourseDto.Add(jointEventGroupDto);
  537. }
  538. }
  539. }
  540. return Ok(new { data = JointCourseDto });
  541. }
  542. catch (Exception e)
  543. {
  544. //await _dingDing.SendBotMsg($"OS,{_option.Location},jointevent/jointcourse/find()\n{e.Message}\n{e.StackTrace}", GroupNames.醍摩豆服務運維群組);
  545. return BadRequest();
  546. }
  547. }
  548. /// <summary>
  549. /// 新建修改教師課程及組別
  550. /// </summary>
  551. [ProducesDefaultResponseType]
  552. [Authorize(Roles = "IES,HTCommunity")]
  553. [HttpPost("course/upsert")]
  554. public async Task<IActionResult> JointCourseUpsert(JsonElement request)
  555. {
  556. try
  557. {
  558. var client = _azureCosmos.GetCosmosClient();
  559. string jointEventId = (request.TryGetProperty("jointEventId", out JsonElement _jointEventId)) ? _jointEventId.ToString() : string.Empty;
  560. string jointGroupId = (request.TryGetProperty("jointGroupId", out JsonElement _jointGroupId)) ? _jointGroupId.ToString() : string.Empty;
  561. string creatorId = (request.TryGetProperty("creatorId", out JsonElement _creatorId)) ? _creatorId.ToString() : string.Empty;
  562. string creatorName = (request.TryGetProperty("creatorName", out JsonElement _creatorName)) ? _creatorName.ToString() : string.Empty;
  563. string creatorEmail = (request.TryGetProperty("creatorEmail", out JsonElement _creatorEmail)) ? _creatorEmail.ToString() : string.Empty;
  564. string schoolId = (request.TryGetProperty("schoolId", out JsonElement _schoolId)) ? _schoolId.ToString() : string.Empty;
  565. string schoolName = (request.TryGetProperty("schoolName", out JsonElement _schoolName)) ? _schoolName.ToString() : string.Empty;
  566. string scope = (request.TryGetProperty("scope", out JsonElement _scope)) ? _scope.ToString() : string.Empty;
  567. List<JointEventGroupBase.JointEventGroupCourse> courseLists = (request.TryGetProperty("courseLists", out JsonElement _courseLists)) ? _courseLists.ToObject<List<JointEventGroupBase.JointEventGroupCourse>>() : null;
  568. if (string.IsNullOrWhiteSpace(jointEventId) || string.IsNullOrWhiteSpace(jointGroupId) || string.IsNullOrWhiteSpace(creatorId) || courseLists == null || string.IsNullOrWhiteSpace(scope))
  569. {
  570. return Ok(new { errCode = "1", err = "Invalid param." });
  571. }
  572. JointEventGroupDb jointCourse = new JointEventGroupDb();
  573. StringBuilder stringBuilder = new($"SELECT * FROM c WHERE c.jointEventId = '{jointEventId}' AND c.jointGroupId = '{jointGroupId}' AND c.creatorId = '{creatorId}' ");
  574. await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "Teacher").GetItemQueryStreamIteratorSql(queryText: stringBuilder.ToString(), requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("JointCourse") }))
  575. {
  576. using var json = await JsonDocument.ParseAsync(item.Content);
  577. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  578. {
  579. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  580. {
  581. jointCourse = obj.ToObject<JointEventGroupDb>();
  582. }
  583. }
  584. }
  585. //刪除
  586. if (!string.IsNullOrWhiteSpace(jointCourse.id) && courseLists.Count.Equals(0))
  587. {
  588. await client.GetContainer(Constant.TEAMModelOS, "Teacher").DeleteItemAsync<JointEventGroupDb>(jointCourse.id, new PartitionKey("JointCourse"));
  589. return Ok(new { errCode = "", err = "", jointCourse = new { } });
  590. }
  591. //新建修改
  592. else
  593. {
  594. //新建
  595. if (string.IsNullOrWhiteSpace(jointCourse.id))
  596. {
  597. jointCourse.scope = scope;
  598. jointCourse.id = Guid.NewGuid().ToString();
  599. jointCourse.code = "JointCourse";
  600. jointCourse.pk = "JointCourse";
  601. jointCourse.jointEventId = jointEventId;
  602. jointCourse.jointGroupId = jointGroupId;
  603. jointCourse.creatorId = creatorId;
  604. jointCourse.creatorName = creatorName;
  605. jointCourse.creatorEmail = creatorEmail;
  606. jointCourse.schoolId = schoolId;
  607. jointCourse.schoolName = schoolName;
  608. }
  609. //修改
  610. jointCourse.courseLists = courseLists;
  611. jointCourse.type = "regular"; //教師報名名單
  612. await client.GetContainer(Constant.TEAMModelOS, "Teacher").UpsertItemAsync<JointEventGroupDb>(jointCourse, new PartitionKey("JointCourse"));
  613. }
  614. //取得該老師報名該活動該組別且progress="going"的JointExam並生成Exam
  615. List<JointExam> jointExams = new List<JointExam>();
  616. ///個人
  617. if (jointCourse.scope.Equals("private"))
  618. {
  619. StringBuilder sqlJointExam = new($"SELECT * FROM c WHERE c.jointEventId = '{jointCourse.jointEventId}' AND c.jointGroupId = '{jointCourse.jointGroupId}' AND c.progress = 'going' AND c.examType = 'regular' ");
  620. await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "Common").GetItemQueryStreamIteratorSql(queryText: sqlJointExam.ToString(), requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("JointExam") }))
  621. {
  622. using var json = await JsonDocument.ParseAsync(item.Content);
  623. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  624. {
  625. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  626. {
  627. jointExams.Add(obj.ToObject<JointExam>());
  628. }
  629. }
  630. }
  631. }
  632. ///學校[待做]
  633. else if (scope.Equals("school"))
  634. {
  635. }
  636. //Exam生成邏輯
  637. if (jointExams.Count > 0)
  638. {
  639. foreach (var info in jointExams)
  640. {
  641. //生成該報名教師的統測活動個人評量
  642. await JointService.GenerateExamFromJointExamAsync(client, _azureStorage, _serviceBus, _coreAPIHttpService, _azureRedis, _configuration, _dingDing, info, creatorId);
  643. }
  644. }
  645. return Ok(new { errCode = "", err = "", jointCourse });
  646. }
  647. catch (Exception e)
  648. {
  649. //await _dingDing.SendBotMsg($"OS,{_option.Location},jointevent/jointcourse/find()\n{e.Message}\n{e.StackTrace}", GroupNames.醍摩豆服務運維群組);
  650. return BadRequest();
  651. }
  652. }
  653. /// <summary>
  654. /// 刪除教師課程及組別
  655. /// </summary>
  656. [ProducesDefaultResponseType]
  657. [Authorize(Roles = "IES")]
  658. #if !DEBUG
  659. [AuthToken(Roles = "teacher")]
  660. #endif
  661. [HttpPost("course/delete")]
  662. public async Task<IActionResult> JointCourseDelete(JsonElement request)
  663. {
  664. var client = _azureCosmos.GetCosmosClient();
  665. string jointCourseId = (request.TryGetProperty("jointCourseId", out JsonElement _jointCourseId)) ? _jointCourseId.ToString() : string.Empty;
  666. if (string.IsNullOrWhiteSpace(jointCourseId))
  667. {
  668. return BadRequest();
  669. }
  670. await client.GetContainer(Constant.TEAMModelOS, "Teacher").DeleteItemAsync<JointEventGroupDb>(jointCourseId, new PartitionKey("JointCourse"));
  671. return Ok(new { errCode = "", err = "" });
  672. }
  673. /// <summary>
  674. /// 取得統測評量
  675. /// </summary>
  676. /// <param name="request"></param>
  677. /// <returns></returns>
  678. [ProducesDefaultResponseType]
  679. [Authorize(Roles = "IES,HTCommunity")]
  680. [HttpPost("exam/find")]
  681. public async Task<IActionResult> ExamFind(JsonElement request)
  682. {
  683. try
  684. {
  685. var client = _azureCosmos.GetCosmosClient();
  686. StringBuilder stringBuilder = new($"SELECT * FROM c WHERE 1=1 ");
  687. string jointEventId = (request.TryGetProperty("jointEventId", out JsonElement _jointEventId)) ? _jointEventId.ToString() : string.Empty;
  688. if (string.IsNullOrWhiteSpace(jointEventId)) return BadRequest();
  689. stringBuilder.Append($" AND c.jointEventId = '{jointEventId}' ");
  690. string creatorId = (request.TryGetProperty("creatorId", out JsonElement _creatorId)) ? _creatorId.ToString() : string.Empty;
  691. if (!string.IsNullOrWhiteSpace(creatorId))
  692. {
  693. stringBuilder.Append($" AND c.creatorId = '{creatorId}' ");
  694. }
  695. string jointScheduleId = (request.TryGetProperty("jointScheduleId", out JsonElement _jointScheduleId)) ? _jointScheduleId.ToString() : string.Empty;
  696. if (!string.IsNullOrWhiteSpace(jointScheduleId))
  697. {
  698. stringBuilder.Append($" AND c.jointScheduleId = '{jointScheduleId}' ");
  699. }
  700. string jointGroupId = (request.TryGetProperty("jointGroupId", out JsonElement _jointGroupId)) ? _jointGroupId.ToString() : string.Empty;
  701. if (!string.IsNullOrWhiteSpace(jointGroupId))
  702. {
  703. stringBuilder.Append($" AND c.jointGroupId = '{jointGroupId}' ");
  704. }
  705. if (request.TryGetProperty("stime", out JsonElement _stime))
  706. {
  707. if (long.TryParse($"{_stime}", out long stime))
  708. {
  709. stringBuilder.Append($" AND c.createTime >= {stime} ");
  710. };
  711. };
  712. if (request.TryGetProperty("etime", out JsonElement _etime))
  713. {
  714. if (long.TryParse($"{_etime}", out long etime))
  715. {
  716. stringBuilder.Append($" AND c.createTime <= {etime} ");
  717. };
  718. };
  719. if (request.TryGetProperty("name", out JsonElement name) && !string.IsNullOrWhiteSpace($"{name}"))
  720. {
  721. stringBuilder.Append($" AND Contains( c.name , '{name}') = true ");
  722. }
  723. if (request.TryGetProperty("progress", out JsonElement progress) && !string.IsNullOrWhiteSpace($"{progress}"))
  724. {
  725. stringBuilder.Append($" AND c.progress = '{progress}' ");
  726. }
  727. if (request.TryGetProperty("source", out JsonElement source) && !string.IsNullOrWhiteSpace($"{source}"))
  728. {
  729. stringBuilder.Append($" AND c.source = '{source}' ");
  730. }
  731. stringBuilder.Append("order by c.createTime desc");
  732. //string token = null;
  733. string token = default;
  734. //默认不指定返回大小
  735. int? topcout = null;
  736. if (request.TryGetProperty("count", out JsonElement jcount))
  737. {
  738. if (!jcount.ValueKind.Equals(JsonValueKind.Undefined) && !jcount.ValueKind.Equals(JsonValueKind.Null) && jcount.TryGetInt32(out int data))
  739. {
  740. topcout = data;
  741. }
  742. }
  743. //是否需要进行分页查询,默认不分页
  744. bool iscontinuation = false;
  745. if (topcout != null && topcout.Value > 0)
  746. {
  747. iscontinuation = true;
  748. }
  749. //如果指定了返回大小
  750. if (request.TryGetProperty("token", out JsonElement token_1))
  751. {
  752. if (!token_1.ValueKind.Equals(JsonValueKind.Null) && token_1.ValueKind.Equals(JsonValueKind.String))
  753. {
  754. token = token_1.GetString();
  755. }
  756. }
  757. List<JointExamIdCollector> jointExamIdCollectorsPrivate = new List<JointExamIdCollector>();
  758. List<JointExamIdCollector> jointExamIdCollectorsSchool = new List<JointExamIdCollector>();
  759. List<JointExam> jointExamInfo = new List<JointExam>();
  760. await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "Common").GetItemQueryStreamIteratorSql(queryText: stringBuilder.ToString(), continuationToken: token, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("JointExam"), MaxItemCount = topcout }))
  761. {
  762. using var json = await JsonDocument.ParseAsync(item.Content);
  763. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  764. {
  765. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  766. {
  767. JointExam jointExamRow = obj.ToObject<JointExam>();
  768. //取得JointCourse用ID束
  769. if(jointExamRow.scope.Equals("private"))
  770. {
  771. JointExamIdCollector jointExamIdCollectorRowNow = jointExamIdCollectorsPrivate.Where(c => c.jointEventId.Equals(jointExamRow.jointEventId) && c.jointGroupId.Equals(jointExamRow.jointGroupId)).FirstOrDefault();
  772. if (jointExamIdCollectorRowNow == null)
  773. {
  774. JointExamIdCollector jointExamIdCollectorRow = new JointExamIdCollector();
  775. jointExamIdCollectorRow.jointEventId = jointExamRow.jointEventId;
  776. jointExamIdCollectorRow.jointGroupId = jointExamRow.jointGroupId;
  777. jointExamIdCollectorsPrivate.Add(jointExamIdCollectorRow);
  778. }
  779. }
  780. else
  781. {
  782. JointExamIdCollector jointExamIdCollectorRowNow = jointExamIdCollectorsSchool.Where(c => c.jointEventId.Equals(jointExamRow.jointEventId) && c.jointGroupId.Equals(jointExamRow.jointGroupId)).FirstOrDefault();
  783. if (jointExamIdCollectorRowNow == null)
  784. {
  785. JointExamIdCollector jointExamIdCollectorRow = new JointExamIdCollector();
  786. jointExamIdCollectorRow.jointEventId = jointExamRow.jointEventId;
  787. jointExamIdCollectorRow.jointGroupId = jointExamRow.jointGroupId;
  788. jointExamIdCollectorsSchool.Add(jointExamIdCollectorRow);
  789. }
  790. }
  791. //jointExam列表
  792. jointExamInfo.Add(jointExamRow);
  793. }
  794. }
  795. if (iscontinuation)
  796. {
  797. token = item.ContinuationToken;
  798. break;
  799. }
  800. }
  801. //取得Exam
  802. List<ExamInfo> exams = new List<ExamInfo>();
  803. List<string> jointExamIds = jointExamInfo.Select(x => x.id).ToList();
  804. if (jointExamIds.Count > 0)
  805. {
  806. StringBuilder stringBuilderExam = new($"SELECT * FROM c WHERE CONTAINS(c.code, 'Exam-', true) AND ARRAY_CONTAINS({JsonSerializer.Serialize(jointExamIds)}, c.jointExamId)");
  807. await foreach (var item in client.GetContainer(Constant.TEAMModelOS, Constant.Common).GetItemQueryStreamIteratorSql(queryText: stringBuilderExam.ToString(), requestOptions: null))
  808. {
  809. using var json = await JsonDocument.ParseAsync(item.Content);
  810. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  811. {
  812. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  813. {
  814. exams.Add(obj.ToObject<ExamInfo>());
  815. }
  816. }
  817. }
  818. }
  819. //取得老師報名的JointCourse
  820. List<JointEventGroupDb> jointEventGroup = new List<JointEventGroupDb>(); //個人課程 ※報名、決賽 一起取,最後整理時再分
  821. Dictionary<string, Dictionary<string,string>> courseToExamIdRegularDic = new Dictionary<string, Dictionary<string, string>>(); //活動評量ID => 課程ID、個人評量ID對應表 (報名)
  822. Dictionary<string, Dictionary<string, string>> courseToExamIdCustomDic = new Dictionary<string, Dictionary<string, string>>(); //活動評量ID => 課程ID、個人評量ID對應表 (決賽)
  823. if (jointExamIdCollectorsPrivate.Count > 0)
  824. {
  825. StringBuilder stringBuilderEventGroup = new($"SELECT * FROM c ");
  826. StringBuilder Where = new();
  827. foreach (JointExamIdCollector jointExamIdCollector in jointExamIdCollectorsPrivate)
  828. {
  829. if (Where.Length > 0)
  830. {
  831. Where.Append(" OR ");
  832. }
  833. Where.Append($" ( c.jointEventId = '{jointExamIdCollector.jointEventId}' AND c.jointGroupId = '{jointExamIdCollector.jointGroupId}' ) ");
  834. }
  835. stringBuilderEventGroup.Append(" WHERE ").Append(Where);
  836. await foreach (var item in client.GetContainer(Constant.TEAMModelOS, Constant.Teacher).GetItemQueryStreamIteratorSql(queryText: stringBuilderEventGroup.ToString(), requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("JointCourse") }))
  837. {
  838. using var json = await JsonDocument.ParseAsync(item.Content);
  839. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  840. {
  841. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  842. {
  843. JointEventGroupDb jointEventGroupRow = obj.ToObject<JointEventGroupDb>();
  844. foreach(var course in jointEventGroupRow.courseLists)
  845. {
  846. if(jointEventGroupRow.scope.Equals("private") && string.IsNullOrWhiteSpace(course.subjectId) && !string.IsNullOrWhiteSpace(course.courseId)) course.subjectId = course.courseId;
  847. //課程ID => 活動ID、個人評量ID對應表製作
  848. List<ExamInfo> examByCreator = exams.Where(e => e.subjects.Select(s => s.id).ToList().Contains(course.courseId) && e.creatorId.Equals(jointEventGroupRow.creatorId)).ToList();
  849. if(!string.IsNullOrWhiteSpace(jointEventGroupRow.type) && jointEventGroupRow.type.Equals("custom")) //決賽
  850. {
  851. foreach (ExamInfo exam in examByCreator)
  852. {
  853. if(!courseToExamIdCustomDic.ContainsKey(exam.jointExamId))
  854. {
  855. courseToExamIdCustomDic.Add(exam.jointExamId, new Dictionary<string, string>());
  856. }
  857. if (!courseToExamIdCustomDic[exam.jointExamId].ContainsKey(course.courseId))
  858. {
  859. courseToExamIdCustomDic[exam.jointExamId].Add(course.courseId, exam.id);
  860. }
  861. }
  862. }
  863. else //報名
  864. {
  865. foreach (ExamInfo exam in examByCreator)
  866. {
  867. if (!courseToExamIdRegularDic.ContainsKey(exam.jointExamId))
  868. {
  869. courseToExamIdRegularDic.Add(exam.jointExamId, new Dictionary<string, string>());
  870. }
  871. if (!courseToExamIdRegularDic[exam.jointExamId].ContainsKey(course.courseId))
  872. {
  873. courseToExamIdRegularDic[exam.jointExamId].Add(course.courseId, exam.id);
  874. }
  875. }
  876. }
  877. }
  878. jointEventGroup.Add(jointEventGroupRow);
  879. }
  880. }
  881. }
  882. }
  883. List<JointEventClassDb> jointEventClass = new List<JointEventClassDb>(); //學校班級 [待學校班級架構定義完成後再做]
  884. if (jointExamIdCollectorsSchool.Count > 0)
  885. {
  886. }
  887. //整理 jointExamInfo
  888. if (jointExamInfo.Count > 0)
  889. {
  890. foreach(JointExam jointExam in jointExamInfo)
  891. {
  892. List<JointEventGroupDb> stuLists = new List<JointEventGroupDb>();
  893. List<JointEventClassDb> classes = new List<JointEventClassDb>();
  894. if (jointExam.examType.Equals("regular")) //一般競賽
  895. {
  896. stuLists = jointEventGroup.Where(g => g.jointEventId.Equals(jointExam.jointEventId) && g.jointGroupId.Equals(jointExam.jointGroupId) && (g.type == null || g.type.Equals("regular"))).ToList();
  897. classes = jointEventClass.Where(g => g.jointEventId.Equals(jointExam.jointEventId) && g.jointGroupId.Equals(jointExam.jointGroupId) && (g.type == null || g.type.Equals("regular"))).ToList();
  898. }
  899. else if(jointExam.examType.Equals("custom")) //決賽
  900. {
  901. stuLists = jointEventGroup.Where(g => g.jointEventId.Equals(jointExam.jointEventId) && g.jointGroupId.Equals(jointExam.jointGroupId) && (g.type.Equals("custom"))).ToList();
  902. classes = jointEventClass.Where(g => g.jointEventId.Equals(jointExam.jointEventId) && g.jointGroupId.Equals(jointExam.jointGroupId) && (g.type.Equals("custom"))).ToList();
  903. }
  904. //private (stuLists)
  905. if (stuLists.Count > 0)
  906. {
  907. foreach (JointEventGroupDb stu in stuLists)
  908. {
  909. JointEventGroupBase stuListRow = new JointEventGroupBase();
  910. stuListRow.scope = "private";
  911. stuListRow.type = stu.type;
  912. stuListRow.creatorId = stu.creatorId;
  913. stuListRow.creatorName = stu.creatorName;
  914. stuListRow.creatorEmail = stu.creatorEmail;
  915. stuListRow.schoolId = stu.schoolId;
  916. stuListRow.schoolName = stu.schoolName;
  917. stuListRow.courseLists = Newtonsoft.Json.JsonConvert.DeserializeObject<List<JointEventGroupBase.JointEventGroupCourse>>(Newtonsoft.Json.JsonConvert.SerializeObject(stu.courseLists));
  918. Dictionary<string, Dictionary<string, string>> courseToExamIdDic = (!string.IsNullOrWhiteSpace(stu.type) && stu.type.Equals("custom")) ? courseToExamIdCustomDic : courseToExamIdRegularDic;
  919. foreach (JointEventGroupBase.JointEventGroupCourse course in stuListRow.courseLists)
  920. {
  921. course.examId = (courseToExamIdDic.ContainsKey(jointExam.id) && courseToExamIdDic[jointExam.id].ContainsKey(course.courseId)) ? courseToExamIdDic[jointExam.id][course.courseId] : null;
  922. }
  923. jointExam.stuLists.Add(stuListRow);
  924. }
  925. }
  926. //school (classes)
  927. if (classes.Count > 0)
  928. {
  929. foreach (JointEventClassDb clas in classes)
  930. {
  931. JointEventClassBase classRow = new JointEventClassBase();
  932. //樣式未定
  933. jointExam.classes.Add(classRow);
  934. }
  935. }
  936. }
  937. }
  938. return Ok(new { data = jointExamInfo, token = token });
  939. }
  940. catch (Exception e)
  941. {
  942. //await _dingDing.SendBotMsg($"OS,{_option.Location},jointevent/jointexam/find()\n{e.Message}\n{e.StackTrace}", GroupNames.醍摩豆服務運維群組);
  943. return BadRequest();
  944. }
  945. }
  946. /// <summary>
  947. /// 新建變更統測評量
  948. /// </summary>
  949. /// <param name="request"></param>
  950. /// <returns></returns>
  951. [ProducesDefaultResponseType]
  952. [Authorize(Roles = "IES")]
  953. #if !DEBUG
  954. [AuthToken(Roles = "teacher")]
  955. #endif
  956. [HttpPost("exam/upsert")]
  957. public async Task<IActionResult> upsertJointExam(JsonElement request)
  958. {
  959. try
  960. {
  961. var client = _azureCosmos.GetCosmosClient();
  962. long now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
  963. string jointEventId = (request.TryGetProperty("jointEventId", out JsonElement _jointEventId)) ? _jointEventId.ToString() : string.Empty;
  964. string jointGroupId = (request.TryGetProperty("jointGroupId", out JsonElement _jointGroupId)) ? _jointGroupId.ToString() : string.Empty;
  965. string jointScheduleId = (request.TryGetProperty("jointScheduleId", out JsonElement _jointScheduleId)) ? _jointScheduleId.ToString() : string.Empty;
  966. string jointExamId = (request.TryGetProperty("jointExamId", out JsonElement _jointExamId)) ? _jointExamId.ToString() : string.Empty;
  967. string creatorId = (request.TryGetProperty("creatorId", out JsonElement _creatorId)) ? _creatorId.ToString() : string.Empty;
  968. string name = (request.TryGetProperty("name", out JsonElement _name)) ? _name.ToString() : string.Empty;
  969. string source = (request.TryGetProperty("source", out JsonElement _source)) ? _source.ToString() : string.Empty;
  970. string scope = (request.TryGetProperty("scope", out JsonElement _scope)) ? _scope.ToString() : "private";
  971. List<PaperSimple> papers = (request.TryGetProperty("papers", out JsonElement _papers)) ? _papers.ToObject<List<PaperSimple>>() : null;
  972. List<JointEventGroupBase> stuLists = (request.TryGetProperty("stuLists", out JsonElement _stuLists)) ? _stuLists.ToObject<List<JointEventGroupBase>>() : null;
  973. List<JointEventClassBase> classes = (request.TryGetProperty("classes", out JsonElement _classes)) ? _classes.ToObject<List<JointEventClassBase>>() : null;
  974. if (string.IsNullOrWhiteSpace(jointEventId) || string.IsNullOrWhiteSpace(jointGroupId) || string.IsNullOrWhiteSpace(jointScheduleId) || string.IsNullOrWhiteSpace(jointExamId))
  975. {
  976. return BadRequest();
  977. }
  978. //取得schedule
  979. JointEventGroup jointEventGroup = new JointEventGroup();
  980. JointEventSchedule jointEventSchedule = new JointEventSchedule();
  981. StringBuilder stringBuilderSchedule = new($"SELECT cg.id as groupId, cg.name as groupName, cg.assistants, cs.id as scheduleId, cs.type as scheduleType, cs.examType, cs.examOverwrite, cs.name as scheduleName, cs.startTime as scheduleStartTime, cs.endTime as scheduleEndTime, cs.progress as scheduleProgress FROM c JOIN cs IN c.schedule JOIN cg IN c.groups WHERE c.id = '{jointEventId}' AND cs.id = '{jointScheduleId}' AND cg.id = '{jointGroupId}' ");
  982. await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "Teacher").GetItemQueryStreamIteratorSql(queryText: stringBuilderSchedule.ToString(), requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("JointEvent") }))
  983. {
  984. using var json = await JsonDocument.ParseAsync(item.Content);
  985. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  986. {
  987. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  988. {
  989. jointEventGroup.id = obj.GetProperty("groupId").GetString();
  990. jointEventGroup.name = obj.GetProperty("groupName").GetString();
  991. jointEventGroup.assistants = obj.GetProperty("assistants").ToObject<HashSet<string>>();
  992. jointEventSchedule.id = obj.GetProperty("scheduleId").GetString();
  993. jointEventSchedule.examType = obj.GetProperty("examType").GetString();
  994. jointEventSchedule.examOverwrite = obj.GetProperty("examOverwrite").GetBoolean();
  995. jointEventSchedule.type = obj.GetProperty("scheduleType").GetString();
  996. jointEventSchedule.name = obj.GetProperty("scheduleName").GetString();
  997. jointEventSchedule.startTime = obj.GetProperty("scheduleStartTime").GetInt64();
  998. jointEventSchedule.endTime = obj.GetProperty("scheduleEndTime").GetInt64();
  999. jointEventSchedule.progress = obj.GetProperty("scheduleProgress").GetString();
  1000. }
  1001. }
  1002. }
  1003. if (string.IsNullOrWhiteSpace(jointEventGroup.id) || string.IsNullOrWhiteSpace(jointEventSchedule.id))
  1004. {
  1005. return Ok(new { errCode = "3", err = "Invalid jointEventId." });
  1006. }
  1007. JointExam jointExam = new JointExam();
  1008. string mode = "upd";
  1009. try
  1010. {
  1011. jointExam = await client.GetContainer(Constant.TEAMModelOS, "Common").ReadItemAsync<JointExam>(jointExamId, new PartitionKey("JointExam"));
  1012. }
  1013. catch (Exception e)
  1014. {
  1015. mode = "add";
  1016. }
  1017. if (mode.Equals("upd")) //更新
  1018. {
  1019. if (jointExam.progress.Equals("going"))
  1020. {
  1021. return Ok(new { errCode = "9", err = "JointExam is going, can't be changed." });
  1022. }
  1023. if (!string.IsNullOrWhiteSpace(name)) jointExam.name = name;
  1024. if (!string.IsNullOrWhiteSpace(source)) jointExam.source = source;
  1025. //if (jointEventSchedule.examType.Equals("custom")) //決賽,要填classes或stuLists擇一必須
  1026. //{
  1027. // if (classes == null || stuLists == null)
  1028. // {
  1029. // return Ok(new { errCode = "8", err = "Invalid classes or stuLists." });
  1030. // }
  1031. // if (classes != null) jointExam.classes = classes;
  1032. // if (stuLists != null) jointExam.stuLists = stuLists;
  1033. //}
  1034. if (papers != null) jointExam.papers = papers;
  1035. }
  1036. else //新建
  1037. {
  1038. if (papers == null) return Ok(new { errCode = "7", err = "Invalid paper." });
  1039. if (string.IsNullOrWhiteSpace(creatorId)) return Ok(new { errCode = "8", err = "Invalid creatorId." });
  1040. if (string.IsNullOrWhiteSpace(name)) return Ok(new { errCode = "9", err = "Invalid name." });
  1041. if (string.IsNullOrWhiteSpace(source)) return Ok(new { errCode = "10", err = "Invalid source." });
  1042. //if (jointEventSchedule.examType.Equals("custom")) //決賽,要填classes或stuLists擇一必須
  1043. //{
  1044. // if (classes == null || stuLists == null)
  1045. // {
  1046. // return Ok(new { errCode = "8", err = "Invalid classes or stuLists." });
  1047. // }
  1048. // if (classes != null) jointExam.classes = classes;
  1049. // if (stuLists != null) jointExam.stuLists = stuLists;
  1050. //}
  1051. jointExam.pk = "JointExam";
  1052. jointExam.code = "JointExam";
  1053. jointExam.id = jointExamId;
  1054. jointExam.jointEventId = jointEventId;
  1055. jointExam.jointScheduleId = jointScheduleId;
  1056. jointExam.jointGroupId = jointGroupId;
  1057. jointExam.creatorId = creatorId;
  1058. jointExam.name = name;
  1059. jointExam.source = source;
  1060. jointExam.papers = papers;
  1061. jointExam.createTime = now;
  1062. jointExam.scope = scope;
  1063. }
  1064. //共通
  1065. jointExam.examType = jointEventSchedule.examType;
  1066. jointExam.examOverwrite = jointEventSchedule.examOverwrite;
  1067. jointExam.startTime = jointEventSchedule.startTime;
  1068. jointExam.endTime = jointEventSchedule.endTime;
  1069. jointExam.progress = (jointExam.startTime > now) ? "pending" : (now > jointExam.endTime) ? "finish" : "going";
  1070. jointExam.updateTime = now;
  1071. await client.GetContainer(Constant.TEAMModelOS, "Common").UpsertItemAsync<JointExam>(jointExam, new PartitionKey("JointExam"));
  1072. return Ok(new { errCode = "", err = "", jointExam });
  1073. }
  1074. catch (Exception e)
  1075. {
  1076. //await _dingDing.SendBotMsg($"OS,{_option.Location},jointevent/jointexam/find()\n{e.Message}\n{e.StackTrace}", GroupNames.醍摩豆服務運維群組);
  1077. return BadRequest();
  1078. }
  1079. }
  1080. //判斷某groupId在某JointSchedule是否完成
  1081. private async Task<string> GetGroupFinishJointSchedule(string jointEventId, string jointGroupId, string groupId, JointEventSchedule schedule)
  1082. {
  1083. string result = "undo";
  1084. var client = _azureCosmos.GetCosmosClient();
  1085. string scope = "private"; //先只指定個人
  1086. string container = (scope.Equals("school")) ? Constant.School : Constant.Teacher;
  1087. switch (schedule.type)
  1088. {
  1089. //報名
  1090. case "join":
  1091. StringBuilder stringBuilder = new($"SELECT DISTINCT ccg.id FROM c JOIN cc IN c.courseLists JOIN ccg IN cc.groupLists WHERE c.jointEventId = '{jointEventId}' AND c.jointGroupId = '{jointGroupId}' AND (c.type = 'regular' OR NOT IS_DEFINED(c.type) OR IS_NULL(c.type)) AND ccg.id = '{groupId}' ");
  1092. await foreach (var item in client.GetContainer(Constant.TEAMModelOS, container).GetItemQueryStreamIteratorSql(queryText: stringBuilder.ToString(), requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("JointCourse") }))
  1093. {
  1094. using var json = await JsonDocument.ParseAsync(item.Content);
  1095. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  1096. {
  1097. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  1098. {
  1099. if (obj.GetProperty("id").GetString().Equals(groupId))
  1100. {
  1101. result = "complete";
  1102. }
  1103. }
  1104. }
  1105. }
  1106. break;
  1107. //競賽
  1108. case "exam":
  1109. //取得老師報名課程或決賽老師課程
  1110. List<JointEventGroupDb> jointEventGroup = new List<JointEventGroupDb>(); //個人課程
  1111. StringBuilder stringBuilderEventGroup = new($"SELECT * FROM c WHERE c.jointEventId = '{jointEventId}' AND c.jointGroupId = '{jointGroupId}' ");
  1112. if (schedule.examType.Equals("regular")) //熱身賽
  1113. {
  1114. stringBuilderEventGroup.Append($" AND (c.type = 'regular' OR NOT IS_DEFINED(c.type) OR IS_NULL(c.type)) ");
  1115. }
  1116. else if(schedule.examType.Equals("custom")) //決賽
  1117. {
  1118. stringBuilderEventGroup.Append($" AND c.type = 'custom' AND c.jointScheduleId = '{schedule.id}' ");
  1119. }
  1120. await foreach (var item in client.GetContainer(Constant.TEAMModelOS, Constant.Teacher).GetItemQueryStreamIteratorSql(queryText: stringBuilderEventGroup.ToString(), requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("JointCourse") }))
  1121. {
  1122. using var json = await JsonDocument.ParseAsync(item.Content);
  1123. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  1124. {
  1125. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  1126. {
  1127. jointEventGroup.Add(obj.ToObject<JointEventGroupDb>());
  1128. }
  1129. }
  1130. }
  1131. //取得本Schedule的所有JointExam
  1132. List<string> jointExamIdList = new List<string>();
  1133. StringBuilder stringBuilderJointExam = new($"SELECT DISTINCT VALUE c.id FROM c WHERE c.jointEventId = '{jointEventId}' AND c.jointGroupId = '{jointGroupId}' AND c.jointScheduleId = '{schedule.id}' ");
  1134. var resultJointExam = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Common).GetList<string>(stringBuilderJointExam.ToString(), $"JointExam");
  1135. if (resultJointExam.list.IsNotEmpty())
  1136. {
  1137. jointExamIdList = new List<string>(resultJointExam.list);
  1138. }
  1139. //取得所有個人評量
  1140. List<string> examIdList = new List<string>();
  1141. string sqlExam = $"SELECT DISTINCT VALUE c.id FROM c WHERE ARRAY_CONTAINS({JsonSerializer.Serialize(jointExamIdList)}, c.jointExamId) AND ARRAY_CONTAINS(c.stuLists, '{groupId}') AND CONTAINS(c.code, 'Exam-')";
  1142. await foreach (var item in client.GetContainer(Constant.TEAMModelOS, Constant.Common).GetItemQueryIteratorSql<string>(queryText: sqlExam, requestOptions: new QueryRequestOptions { }))
  1143. {
  1144. examIdList.Add(item);
  1145. }
  1146. //取得所有考試的作答結果
  1147. List<string> finishExamIdList = new List<string>();
  1148. string sqlExamClassResult = $"SELECT c.examId, c.info.id as classId, c.studentAnswers FROM c WHERE ARRAY_CONTAINS({JsonSerializer.Serialize(examIdList)}, c.examId) AND c.info.id = '{groupId}' AND c.progress=true AND CONTAINS(c.code, 'ExamClassResult')";
  1149. await foreach (var item in client.GetContainer(Constant.TEAMModelOS, Constant.Common).GetItemQueryStreamIteratorSql(queryText: sqlExamClassResult, requestOptions: new QueryRequestOptions() { }))
  1150. {
  1151. using var json = await JsonDocument.ParseAsync(item.Content);
  1152. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  1153. {
  1154. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  1155. {
  1156. string examId = obj.GetProperty("examId").ToString();
  1157. string classId = obj.GetProperty("classId").ToString();
  1158. List<List<string>> studentAnswers = obj.GetProperty("studentAnswers").ToObject<List<List<string>>>();
  1159. bool isFinish = false; //評量是否已完成 ※有任一學生有作答則視為已完成
  1160. foreach (List<string> studentAnswer in studentAnswers)
  1161. {
  1162. if (studentAnswer.Count > 0) { isFinish = true; break; }
  1163. }
  1164. if (isFinish)
  1165. {
  1166. finishExamIdList.Add(examId);
  1167. }
  1168. }
  1169. }
  1170. }
  1171. //結果判斷
  1172. if(jointEventGroup.Count.Equals(0)) //資格不符
  1173. {
  1174. result = "disqualify";
  1175. }
  1176. else if (jointExamIdList.Count > 0 && jointExamIdList.Count.Equals(finishExamIdList.Count))
  1177. {
  1178. result = "complete";
  1179. }
  1180. else if (jointExamIdList.Count > 0 && finishExamIdList.Count > 0 && jointExamIdList.Count > finishExamIdList.Count)
  1181. {
  1182. result = "doing";
  1183. }
  1184. break;
  1185. }
  1186. return result;
  1187. }
  1188. [ProducesDefaultResponseType]
  1189. #if !DEBUG
  1190. [Authorize(Roles = "IES")]
  1191. [AuthToken(Roles = "teacher")]
  1192. #endif
  1193. [HttpPost("exam/create-final-teachercourse")]
  1194. public async Task<IActionResult> createFinalJointTeacherCourse(JsonElement request)
  1195. {
  1196. List<JointEventGroupDb> result = new List<JointEventGroupDb>();
  1197. var client = _azureCosmos.GetCosmosClient();
  1198. string jointEventId = (request.TryGetProperty("jointEventId", out JsonElement _jointEventId)) ? _jointEventId.ToString() : string.Empty;
  1199. string jointScheduleId = (request.TryGetProperty("jointScheduleId", out JsonElement _jointScheduleId)) ? _jointScheduleId.ToString() : string.Empty; //要計算的行程
  1200. string scope = "private";
  1201. JointEvent jointEvent = await client.GetContainer(Constant.TEAMModelOS, "Teacher").ReadItemAsync<JointEvent>(jointEventId, new PartitionKey("JointEvent"));
  1202. if (jointEvent == null)
  1203. {
  1204. return BadRequest();
  1205. }
  1206. List<string> jointGroupIds = jointEvent.groups.Select(g => g.id).ToList();
  1207. if(jointGroupIds.Count > 0)
  1208. {
  1209. foreach(string jointGroupId in jointGroupIds)
  1210. {
  1211. List<JointEventGroupDb> addResult = await JointService.CreatePassJointCourseBySchedule(client, jointEventId, jointGroupId, jointScheduleId, scope);
  1212. result = result.Union(addResult).ToList();
  1213. }
  1214. }
  1215. return Ok(result);
  1216. }
  1217. public class JointExamIdCollector
  1218. {
  1219. public string jointEventId { get; set; }
  1220. public string jointGroupId { get; set; }
  1221. }
  1222. /// <summary>
  1223. /// 統測活動 API輸出用
  1224. /// </summary>
  1225. public class JointEventDto : JointEvent
  1226. {
  1227. public string blobsas { get; set; }
  1228. }
  1229. /// <summary>
  1230. /// 統測活動老師報名課程 API輸出用
  1231. /// </summary>
  1232. public class JointEventGroupDto
  1233. {
  1234. public string id { get; set; }
  1235. public string code { get; set; }
  1236. public string pk { get; set; }
  1237. public string jointEventId { get; set; }
  1238. public string jointGroupId { get; set; }
  1239. public string scope { get; set; } //school:學校(選課班) private:個人
  1240. public string creatorId { get; set; }
  1241. public string creatorName { get; set; }
  1242. public string schoolId { get; set; } //老師教育雲綁定的學校ID
  1243. public string schoolName { get; set; } //老師教育雲綁定的學校名稱
  1244. public string periodId { get; set; } //老師個人課程時為null
  1245. public List<JointEventGroupCourseDto> courseLists { get; set; } = new();
  1246. public class JointEventGroupCourseDto
  1247. {
  1248. public string subjectId { get; set; } //老師個人課程時為null
  1249. public string courseId { get; set; }
  1250. public string courseName { get; set; }
  1251. public List<JointEventGroupCourseGroupDto> groupLists { get; set; } = new();
  1252. }
  1253. public class JointEventGroupCourseGroupDto
  1254. {
  1255. public string id { get; set; }
  1256. public string name { get; set; }
  1257. public List<object> schedule { get; set; } = new List<object>(); //已完成的行程
  1258. }
  1259. }
  1260. }
  1261. }