SchoolController.cs 68 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205
  1. using Microsoft.AspNetCore.Mvc;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Text;
  5. using System.Threading.Tasks;
  6. using TEAMModelOS.Models;
  7. using TEAMModelOS.SDK;
  8. using TEAMModelOS.SDK.DI;
  9. using System.Text.Json;
  10. using TEAMModelOS.SDK.Models;
  11. using TEAMModelOS.SDK.Extension;
  12. using Azure.Cosmos;
  13. using Microsoft.AspNetCore.Http;
  14. using Microsoft.Extensions.Options;
  15. using System.IO;
  16. using System.Dynamic;
  17. using TEAMModelOS.SDK.Context.Configuration;
  18. using System.Net.Http;
  19. using System.Net;
  20. using Newtonsoft.Json;
  21. using System.Linq;
  22. using StackExchange.Redis;
  23. namespace TEAMModelOS.Controllers
  24. {
  25. [ProducesResponseType(StatusCodes.Status200OK)]
  26. [ProducesResponseType(StatusCodes.Status400BadRequest)]
  27. //[Authorize(Roles = "IES5")]
  28. [Route("school/init")]
  29. [ApiController]
  30. public class SchoolController : ControllerBase
  31. {
  32. public AzureCosmosFactory _azureCosmos;
  33. private readonly AzureStorageFactory _azureStorage;
  34. private readonly AzureRedisFactory _azureRedis;
  35. private readonly DingDing _dingDing;
  36. private readonly Option _option;
  37. int baseSpaceSize = 1; //學校保底空間大小(1G)
  38. private readonly double bytes = 1073741824;
  39. private readonly int redisAclassoneDbNum = 8; //AclassOne Redis DB號
  40. public SchoolController(AzureCosmosFactory azureCosmos, AzureStorageFactory azureStorage, AzureRedisFactory azureRedis, DingDing dingDing, IOptionsSnapshot<Option> option)
  41. {
  42. _azureCosmos = azureCosmos;
  43. _azureStorage = azureStorage;
  44. _azureRedis = azureRedis;
  45. _dingDing = dingDing;
  46. _option = option?.Value;
  47. }
  48. /// <summary>
  49. /// 保存或更新学校
  50. /// </summary>
  51. /// <param name="request"></param>
  52. /// <returns></returns>
  53. [ProducesDefaultResponseType]
  54. //[AuthToken(Roles = "admin")]
  55. [HttpPost("upsert")]
  56. public async Task<IActionResult> Upsert(School requert)
  57. {
  58. var (_, _, _, school) = HttpContext.GetAuthTokenInfo();
  59. try
  60. {
  61. School schoolInfo = new School();
  62. var client = _azureCosmos.GetCosmosClient();
  63. var schoolContainer = client.GetContainer("TEAMModelOS", "School");
  64. var response = await schoolContainer.ReadItemStreamAsync(requert.id, new PartitionKey($"Base"));
  65. if (response.Status == 200)
  66. {
  67. schoolInfo = await schoolContainer.UpsertItemAsync(requert, new PartitionKey($"Base"));
  68. }
  69. else
  70. {
  71. requert.code = "Base";
  72. schoolInfo = await schoolContainer.CreateItemAsync(requert, new PartitionKey($"Base"));
  73. }
  74. return Ok(new { schoolInfo });
  75. }
  76. catch (Exception ex)
  77. {
  78. await _dingDing.SendBotMsg($"CoreAPI2,{_option.Location},school/upsert()\n{ex.Message}", GroupNames.醍摩豆服務運維群組);
  79. return BadRequest();
  80. }
  81. /*ResponseBuilder builder = ResponseBuilder.custom();
  82. if (string.IsNullOrEmpty(request.id))
  83. {
  84. List<School> schools = await _azureCosmos.FindByDict<School>(new Dictionary<string, object> { { "id", request.schoolCode } });
  85. if (schools.IsNotEmpty())
  86. {
  87. //return builder.Error(ResponseCode.DATA_EXIST, "学校已存在!").build();
  88. return Ok(new { V = "学校已经存在!" });
  89. }
  90. request.code = "Base";
  91. //request.id = request.schoolCode;
  92. }
  93. else {
  94. *//*if (request.id.Equals(request.schoolCode))
  95. {*//*
  96. School datas = await _azureCosmos.SaveOrUpdate<School>(request);
  97. return Ok(new{ datas});
  98. *//*}
  99. else
  100. {
  101. //return builder.Error(ResponseCode.PARAMS_ERROR, "id,schoolCode必须相同!").build();
  102. return Ok(new { V = "id,schoolCode必须相同!" });
  103. }*/
  104. /*List<School> schools = await _azureCosmos.FindByDict<School>(new Dictionary<string, object> { { "id", request.id } });
  105. if (schools.IsEmpty())
  106. {
  107. return builder.Error(ResponseCode.PARAMS_ERROR, "id不存在,不能更新").build();
  108. }*//*
  109. }
  110. return Ok();*/
  111. }
  112. /// <summary>
  113. /// 查找学校
  114. /// </summary>
  115. /// <param name="request"></param>
  116. /// <returns></returns>
  117. [ProducesDefaultResponseType]
  118. //[AuthToken(Roles = "teacher")]
  119. [HttpPost("find")]
  120. public async Task<IActionResult> Find(JsonElement requert)
  121. {
  122. if (!requert.TryGetProperty("school_code", out JsonElement school_code)) return BadRequest();
  123. var client = _azureCosmos.GetCosmosClient();
  124. List<School> schools = new List<School>();
  125. var query = $"select c.id,c.pk,c.code, c.name,c.region,c.province,c.city,c.timeZone,c.picture,c.size,c.period,c.campuses from c where c.id ='{school_code}'";
  126. await foreach (var item in client.GetContainer("TEAMModelOS", "School").GetItemQueryStreamIterator(queryText: query, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("Base") }))
  127. {
  128. using var json = await JsonDocument.ParseAsync(item.ContentStream);
  129. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  130. {
  131. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  132. {
  133. schools.Add(obj.ToObject<School>());
  134. }
  135. }
  136. }
  137. return Ok(new { schools });
  138. /* ResponseBuilder builder = ResponseBuilder.custom();
  139. if (request.TryGetProperty("code", out JsonElement code) &&! string.IsNullOrEmpty(code.ToString()))
  140. {
  141. List<School> sc = await _azureCosmos.FindByDict<School>(request);
  142. return builder.Data(sc).build();
  143. }
  144. else {
  145. return builder.Data(null).build();
  146. }*/
  147. }
  148. /// <summary>
  149. /// 取得所有學校(只取基本資料)
  150. /// </summary>
  151. /// <param name="request"></param>
  152. /// <returns></returns>
  153. [ProducesDefaultResponseType]
  154. [HttpPost("get-all-school-base")]
  155. public async Task<IActionResult> GetAllSchoolBaesInfo(JsonElement request)
  156. {
  157. var client = _azureCosmos.GetCosmosClient();
  158. List<object> schools = new List<object>();
  159. await foreach (var item in client.GetContainer("TEAMModelOS", "School").GetItemQueryStreamIterator(queryText: $"SELECT c.id, c.code, c.name, c.region, c.province, c.city, c.address, c.picture FROM c ", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Base") }))
  160. {
  161. using var json = await JsonDocument.ParseAsync(item.ContentStream);
  162. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  163. {
  164. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  165. {
  166. schools.Add(obj.ToObject<object>());
  167. }
  168. }
  169. }
  170. return Ok(new { schools });
  171. }
  172. /// <summary>
  173. /// 取得某學校基本資料
  174. /// </summary>
  175. /// <param name="request"></param>
  176. /// <returns></returns>
  177. [ProducesDefaultResponseType]
  178. [HttpPost("get-school-base")]
  179. public async Task<IActionResult> GetSchoolBaesInfo(JsonElement request)
  180. {
  181. if (!request.TryGetProperty("school_code", out JsonElement school_code)) return BadRequest();
  182. var client = _azureCosmos.GetCosmosClient();
  183. var schoolContainer = client.GetContainer("TEAMModelOS", "School");
  184. //取得學校學制、年級、教室
  185. List<object> classes = new List<object>();
  186. await foreach (var item in schoolContainer.GetItemQueryStreamIterator(queryText: $"SELECT c.id, c.name, c.gradeId FROM c", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Class-{school_code}") }))
  187. {
  188. var jsonc = await JsonDocument.ParseAsync(item.ContentStream);
  189. foreach (var classeinfo in jsonc.RootElement.GetProperty("Documents").EnumerateArray())
  190. {
  191. dynamic classExtobj = new ExpandoObject();
  192. classExtobj.id = classeinfo.GetProperty("id");
  193. classExtobj.name = classeinfo.GetProperty("name");
  194. classExtobj.gradeId = classeinfo.GetProperty("gradeId");
  195. classes.Add(classExtobj);
  196. }
  197. }
  198. List<object> periods = new List<object>();
  199. List<object> grades = new List<object>();
  200. var responsesch = await schoolContainer.ReadItemStreamAsync(school_code.ToString(), new PartitionKey($"Base"));
  201. if (responsesch.Status == 200)
  202. {
  203. var jsons = await JsonDocument.ParseAsync(responsesch.ContentStream);
  204. if (jsons.RootElement.TryGetProperty("period", out JsonElement periodJobj))
  205. {
  206. foreach (var periodinfo in periodJobj.EnumerateArray())
  207. {
  208. dynamic periodExtobj = new ExpandoObject();
  209. periodExtobj.id = periodinfo.GetProperty("id");
  210. periodExtobj.name = periodinfo.GetProperty("name");
  211. periods.Add(periodExtobj);
  212. if (periodinfo.TryGetProperty("grades", out JsonElement gradesJobj))
  213. {
  214. foreach (var gradeinfo in gradesJobj.EnumerateArray())
  215. {
  216. dynamic gradeExtobj = new ExpandoObject();
  217. gradeExtobj.id = gradeinfo.GetProperty("id");
  218. gradeExtobj.name = gradeinfo.GetProperty("name");
  219. gradeExtobj.periodId = periodinfo.GetProperty("id");
  220. grades.Add(gradeExtobj);
  221. }
  222. }
  223. }
  224. }
  225. }
  226. return Ok(new { periods, grades, classes });
  227. }
  228. /// <summary>
  229. /// 取得某學校產品資料
  230. /// </summary>
  231. /// <param name="request"></param>
  232. /// <returns></returns>
  233. [ProducesDefaultResponseType]
  234. [HttpPost("get-school-product")]
  235. public async Task<IActionResult> GetSchoolProductInfo(JsonElement request)
  236. {
  237. if (!request.TryGetProperty("school_code", out JsonElement school_code)) return BadRequest();
  238. var clientContainer = _azureCosmos.GetCosmosClient().GetContainer("TEAMModelOS", "School");
  239. List<deviceBoundRich> serialDeviceUpdList = new List<deviceBoundRich>(); //要更新DB的序號內容
  240. List<SerialInfoBaseWithdeviceBoundExt> serial = new List<SerialInfoBaseWithdeviceBoundExt>(); //最後要輸出的序號結果
  241. List<object> service = new List<object>();
  242. List<object> hard = new List<object>();
  243. List<deviceForCoreService> uuidList = new List<deviceForCoreService>(); //要向CoreService詢問deviceID及硬體資訊的UUID列表
  244. var response = await clientContainer.ReadItemStreamAsync(school_code.ToString(), new PartitionKey("Product"));
  245. if (response.Status == 200)
  246. {
  247. var json = await JsonDocument.ParseAsync(response.ContentStream);
  248. //軟體
  249. if (json.RootElement.TryGetProperty("serial", out JsonElement serialJobj))
  250. {
  251. foreach (var serialInfo in serialJobj.EnumerateArray())
  252. {
  253. serial.Add(serialInfo.ToObject<SerialInfoBaseWithdeviceBoundExt>());
  254. if (serialInfo.TryGetProperty("deviceBound", out JsonElement deviceBoundJobj) && !string.IsNullOrWhiteSpace(deviceBoundJobj.ToString()))
  255. {
  256. foreach (var deviceBoundRow in deviceBoundJobj.EnumerateArray())
  257. {
  258. deviceForCoreService uuidForCore = new deviceForCoreService();
  259. uuidForCore.sn = (!string.IsNullOrWhiteSpace(Convert.ToString(serialInfo.GetProperty("serial")))) ? Convert.ToString(serialInfo.GetProperty("serial")) : null;
  260. uuidForCore.uuid1 = (!string.IsNullOrWhiteSpace(Convert.ToString(deviceBoundRow.GetProperty("uuid")))) ? Convert.ToString(deviceBoundRow.GetProperty("uuid")) : null;
  261. uuidForCore.uuid2 = (!string.IsNullOrWhiteSpace(Convert.ToString(deviceBoundRow.GetProperty("uuid2")))) ? Convert.ToString(deviceBoundRow.GetProperty("uuid2")) : null;
  262. uuidForCore.device_id = (!string.IsNullOrWhiteSpace(Convert.ToString(deviceBoundRow.GetProperty("deviceId")))) ? Convert.ToString(deviceBoundRow.GetProperty("deviceId")) : null;
  263. uuidForCore.class_id = (!string.IsNullOrWhiteSpace(Convert.ToString(deviceBoundRow.GetProperty("classId")))) ? Convert.ToString(deviceBoundRow.GetProperty("classId")) : null;
  264. uuidList.Add(uuidForCore);
  265. }
  266. }
  267. }
  268. }
  269. //取得DeviceInfo From Core
  270. List<deviceFromCoreService> coreUuidList = (List<deviceFromCoreService>)GetDeviceFromCoreAsync(uuidList).GetAwaiter().GetResult();
  271. foreach (SerialInfoBaseWithdeviceBoundExt serialRow in serial)
  272. {
  273. var deviceBoundArray = new List<deviceBoundExt>();
  274. List<deviceFromCoreService> coreUuid = coreUuidList
  275. .Where((deviceFromCoreService x) => x.sn == serialRow.serial)
  276. .ToList();
  277. foreach (deviceFromCoreService deviceRow in coreUuid)
  278. {
  279. //前端顯示用
  280. deviceForCoreService deviceBoundRow = uuidList.Where(u => u.sn == deviceRow.sn && u.uuid1 == deviceRow.uuid1 && u.uuid2 == deviceRow.uuid2).FirstOrDefault();
  281. deviceBoundExt deviceBoundExt = new deviceBoundExt();
  282. deviceBoundExt.uuid = deviceBoundRow.uuid1;
  283. deviceBoundExt.uuid2 = deviceBoundRow.uuid2;
  284. deviceBoundExt.classId = deviceBoundRow.class_id;
  285. deviceBoundExt.deviceId = deviceRow.device_id;
  286. deviceBoundExt.os = deviceRow.os;
  287. deviceBoundExt.ip = deviceRow.local_ip;
  288. deviceBoundExt.cpu = deviceRow.cpu;
  289. deviceBoundExt.pcname = deviceRow.pc_name;
  290. deviceBoundExt.osver = deviceRow.os_ver;
  291. deviceBoundArray.Add(deviceBoundExt);
  292. //DB更新用
  293. deviceBound serialDeviceBoundRow = serialRow.deviceBound.Where(d => d.uuid == deviceBoundRow.uuid1 && d.uuid2 == deviceBoundRow.uuid2).FirstOrDefault();
  294. if (serialDeviceBoundRow.deviceId != deviceRow.device_id)
  295. {
  296. deviceBoundRich deviceBoundUpdRow = new deviceBoundRich();
  297. deviceBoundUpdRow.uuid = deviceBoundRow.uuid1;
  298. deviceBoundUpdRow.uuid2 = deviceBoundRow.uuid2;
  299. deviceBoundUpdRow.classId = deviceBoundRow.class_id;
  300. deviceBoundUpdRow.deviceId = deviceRow.device_id;
  301. serialDeviceUpdList.Add(deviceBoundUpdRow);
  302. }
  303. }
  304. serialRow.deviceBound = deviceBoundArray;
  305. }
  306. //服務
  307. if (json.RootElement.TryGetProperty("service", out JsonElement serviceJobj))
  308. {
  309. //取得active的主週期、所有主週期歷史
  310. List<ServiceMainPeriod> activeMainPeriod = new List<ServiceMainPeriod>();
  311. List<ServiceMainPeriod> historyMainPeriod = new List<ServiceMainPeriod>();
  312. //ServiceMainPeriod activeMainPeriod = new ServiceMainPeriod();
  313. if (serviceJobj.TryGetProperty("mainperiod", out JsonElement mainperiodJobj))
  314. {
  315. foreach (var mainperiodRow in mainperiodJobj.EnumerateArray())
  316. {
  317. historyMainPeriod.Add(mainperiodRow.ToObject<ServiceMainPeriod>());
  318. if (mainperiodRow.GetProperty("active").GetBoolean())
  319. {
  320. activeMainPeriod.Add(mainperiodRow.ToObject<ServiceMainPeriod>());
  321. }
  322. }
  323. }
  324. //取得active主週期的副週期
  325. List<ServicePeriod> activePeriodOfMain = new List<ServicePeriod>();
  326. List<ServicePeriod> historyPeriodOfMain = new List<ServicePeriod>();
  327. if (serviceJobj.TryGetProperty("period", out JsonElement periodJobj))
  328. {
  329. foreach (var periodRow in periodJobj.EnumerateArray())
  330. {
  331. historyPeriodOfMain.Add(periodRow.ToObject<ServicePeriod>());
  332. foreach (ServiceMainPeriod mainPeriodRow in activeMainPeriod)
  333. {
  334. if (periodRow.GetProperty("mainPeriodId").ToString() == mainPeriodRow.mainPeriodId)
  335. {
  336. activePeriodOfMain.Add(periodRow.ToObject<ServicePeriod>());
  337. }
  338. }
  339. }
  340. }
  341. //計算各產品時間區域
  342. bool buySpaceFlg = false;
  343. ////取得學校空間使用狀況
  344. var blobClient = _azureStorage.GetBlobContainerClient(school_code.ToString());
  345. long? docSize = await blobClient.GetBlobsSize("doc");
  346. long? videoSize = await blobClient.GetBlobsSize("video");
  347. long? imageSize = await blobClient.GetBlobsSize("image");
  348. long? paperSize = await blobClient.GetBlobsSize("paper");
  349. long? itemSize = await blobClient.GetBlobsSize("item");
  350. long? otherSize = await blobClient.GetBlobsSize("other");
  351. long? studentSize = await blobClient.GetBlobsSize("student");
  352. long teacherSize = 0;
  353. if (serviceJobj.TryGetProperty("product", out JsonElement serviceProductJobj))
  354. {
  355. foreach (var serviceProductRow in serviceProductJobj.EnumerateArray())
  356. {
  357. ServiceProductResult serviceProductResultRow = CalServiceProductAuth(activeMainPeriod, activePeriodOfMain, serviceProductRow.ToObject<ServiceProduct>());
  358. if (!string.IsNullOrWhiteSpace(serviceProductResultRow.prodCode))
  359. {
  360. switch (serviceProductResultRow.prodCode)
  361. {
  362. case "RYGVCPLY": //AClassOne買斷、週期
  363. case "AEGMCPLY":
  364. ServiceProductAclassoneResult serviceProductAclassoneResult = new ServiceProductAclassoneResult();
  365. serviceProductAclassoneResult.prodCode = serviceProductResultRow.prodCode;
  366. serviceProductAclassoneResult.noperiod = serviceProductResultRow.noperiod;
  367. serviceProductAclassoneResult.serviceType = serviceProductResultRow.serviceType;
  368. serviceProductAclassoneResult.startDate = serviceProductResultRow.startDate;
  369. serviceProductAclassoneResult.endDate = serviceProductResultRow.endDate;
  370. serviceProductAclassoneResult.avaliable = serviceProductResultRow.avaliable;
  371. serviceProductAclassoneResult.staUsed = 0;
  372. serviceProductAclassoneResult.dynUsed = 0;
  373. if (json.RootElement.TryGetProperty("aclassone", out JsonElement aclassoneJobj) && aclassoneJobj.ValueKind != JsonValueKind.Null)
  374. {
  375. int total = (aclassoneJobj.TryGetProperty("total", out JsonElement totalJobj)) ? totalJobj.GetInt32() : 0;
  376. serviceProductAclassoneResult.staUsed = (aclassoneJobj.TryGetProperty("used", out JsonElement usedJobj)) ? usedJobj.GetInt32() : 0; //固定分配數
  377. List<string> dyncIdList = GetSchoolDynamicAclassOneIDList(school_code.GetString()); //動態使用ID
  378. serviceProductAclassoneResult.dynUsed = dyncIdList.Count;
  379. }
  380. service.Add(serviceProductAclassoneResult);
  381. break;
  382. case "IPALYEIY": //智慧教學服務空間
  383. ////取得目前使用狀況
  384. //取得教師分配空間
  385. var query = $"SELECT SUM(c.size) as size FROM c";
  386. await foreach (var item in clientContainer.GetItemQueryStreamIterator(queryText: query, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Teacher-{school_code}") }))
  387. {
  388. var jsonts = await JsonDocument.ParseAsync(item.ContentStream);
  389. foreach (var obj in jsonts.RootElement.GetProperty("Documents").EnumerateArray())
  390. {
  391. teacherSize = obj.GetProperty("size").GetInt64() * 1073741824;//G換算成bytes
  392. }
  393. }
  394. //欄位取得
  395. dynamic serviceSpaceProductInfo = new ExpandoObject();
  396. serviceSpaceProductInfo.prodCode = serviceProductResultRow.prodCode;
  397. serviceSpaceProductInfo.noperiod = serviceProductResultRow.noperiod;
  398. serviceSpaceProductInfo.serviceType = serviceProductResultRow.serviceType;
  399. serviceSpaceProductInfo.startDate = serviceProductResultRow.startDate;
  400. serviceSpaceProductInfo.endDate = serviceProductResultRow.endDate;
  401. serviceSpaceProductInfo.avaliable = (long)(serviceProductResultRow.avaliable + this.baseSpaceSize) * 1073741824; //G換算成bytes
  402. serviceSpaceProductInfo.doc = docSize;
  403. serviceSpaceProductInfo.video = videoSize;
  404. serviceSpaceProductInfo.image = imageSize;
  405. serviceSpaceProductInfo.paper = paperSize;
  406. serviceSpaceProductInfo.item = itemSize;
  407. serviceSpaceProductInfo.other = otherSize;
  408. serviceSpaceProductInfo.student = studentSize;
  409. serviceSpaceProductInfo.teacher = teacherSize;
  410. serviceSpaceProductInfo.history = CalServiceProductOrderHistory(historyMainPeriod, historyPeriodOfMain, serviceProductRow.ToObject<ServiceProduct>()); ////購買紀錄
  411. service.Add(serviceSpaceProductInfo);
  412. buySpaceFlg = true;
  413. break;
  414. default:
  415. service.Add(serviceProductResultRow);
  416. break;
  417. }
  418. }
  419. }
  420. }
  421. //學校保底空間追加
  422. if (!buySpaceFlg) //未購買任何空間
  423. {
  424. dynamic serviceSpaceProductInfo = new ExpandoObject();
  425. serviceSpaceProductInfo.prodCode = "IPALYEIY";
  426. serviceSpaceProductInfo.noperiod = false;
  427. serviceSpaceProductInfo.serviceType = "space";
  428. serviceSpaceProductInfo.startDate = 0;
  429. serviceSpaceProductInfo.endDate = 0;
  430. serviceSpaceProductInfo.avaliable = this.baseSpaceSize * 1073741824; //1G換算成bytes
  431. serviceSpaceProductInfo.doc = docSize;
  432. serviceSpaceProductInfo.video = videoSize;
  433. serviceSpaceProductInfo.image = imageSize;
  434. serviceSpaceProductInfo.paper = paperSize;
  435. serviceSpaceProductInfo.item = itemSize;
  436. serviceSpaceProductInfo.other = otherSize;
  437. serviceSpaceProductInfo.student = studentSize;
  438. serviceSpaceProductInfo.teacher = teacherSize;
  439. serviceSpaceProductInfo.history = null;
  440. service.Add(serviceSpaceProductInfo);
  441. }
  442. }
  443. //硬體
  444. if (json.RootElement.TryGetProperty("hard", out JsonElement hardJobj) && hardJobj.ValueKind != JsonValueKind.Null)
  445. {
  446. hard.Add(hardJobj.ToObject<object>());
  447. }
  448. //更新DB
  449. if (serialDeviceUpdList.Count > 0)
  450. {
  451. SchoolProduct schoolProductItem = json.ToObject<SchoolProduct>();
  452. foreach (deviceBoundRich serialDeviceUpdRow in serialDeviceUpdList)
  453. {
  454. SerialInfoBaseWithdeviceBound updSerialInfo = schoolProductItem.serial.Where(s => s.serial == serialDeviceUpdRow.serial).FirstOrDefault();
  455. deviceBound updDeviceBound = updSerialInfo.deviceBound.Where(d => d.uuid == serialDeviceUpdRow.uuid && d.uuid2 == serialDeviceUpdRow.uuid2).FirstOrDefault();
  456. updDeviceBound.deviceId = serialDeviceUpdRow.deviceId;
  457. }
  458. await clientContainer.ReplaceItemAsync<SchoolProduct>(schoolProductItem, schoolProductItem.id, new PartitionKey("Product"));
  459. }
  460. }
  461. return Ok(new { serial, service, hard });
  462. }
  463. /// <summary>
  464. /// 取得CoreDevice資訊
  465. /// </summary>
  466. /// <param name="request"></param>
  467. /// <returns></returns>
  468. private async Task<List<deviceFromCoreService>> GetDeviceFromCoreAsync(List<deviceForCoreService> uuidList)
  469. {
  470. List<deviceFromCoreService> result = new List<deviceFromCoreService>();
  471. try
  472. {
  473. string url = BaseConfigModel.Configuration["HaBookAuth:CoreService:deviceinfo"];
  474. HttpClient client = new HttpClient();
  475. var content = new StringContent(JsonConvert.SerializeObject(uuidList), Encoding.UTF8, "application/json");
  476. HttpResponseMessage responseMessage = await client.PostAsync(url, content);
  477. if (responseMessage.StatusCode == HttpStatusCode.OK)
  478. {
  479. string responseBody = responseMessage.Content.ReadAsStringAsync().Result;
  480. result = System.Text.Json.JsonSerializer.Deserialize<List<deviceFromCoreService>>(responseBody.ToString());
  481. }
  482. return result;
  483. }
  484. catch (Exception ex)
  485. {
  486. return result;
  487. }
  488. }
  489. /// <summary>
  490. /// 計算各產品的初始結束時間及可用數
  491. /// </summary>
  492. /// <param name="mainPeriodList">active的主周期</param>
  493. /// <param name="periodList">附屬周期</param>
  494. /// <param name="serviceProduct">產品資訊</param>
  495. /// <returns></returns>
  496. private ServiceProductResult CalServiceProductAuth(List<ServiceMainPeriod> mainPeriodList, List<ServicePeriod> periodList, ServiceProduct serviceProduct)
  497. {
  498. periodZone periodZoneResult = new periodZone();
  499. List<periodZone> periodZone = new List<periodZone>();
  500. //篩選出產品有的副週期
  501. List<string> existPeriodIdList = new List<string>();
  502. if (serviceProduct.auth.Count > 0)
  503. {
  504. foreach (ServiceProductAuth serviceProductAuthRow in serviceProduct.auth)
  505. {
  506. if (!string.IsNullOrWhiteSpace(serviceProductAuthRow.periodId) && !existPeriodIdList.Contains(serviceProductAuthRow.periodId))
  507. {
  508. existPeriodIdList.Add(serviceProductAuthRow.periodId);
  509. }
  510. }
  511. }
  512. List<ServicePeriod> periodOrder = periodList.Where(p => existPeriodIdList.Contains(p.periodId)).OrderBy(p => Int32.Parse(p.periodId)).ToList();
  513. //combine period
  514. if (periodOrder.Count() > 0)
  515. {
  516. //篩選出主週期(複數active主週期對策) ※最終篩出唯一一組 篩選原則:(1)由此產品有的副週期ID篩出 (2)若還有複數筆,則期間長者勝出
  517. List<string> existMainPeriodIdList = new List<string>();
  518. foreach (ServicePeriod periodRow in periodOrder)
  519. {
  520. if (!existMainPeriodIdList.Contains(periodRow.mainPeriodId))
  521. {
  522. existMainPeriodIdList.Add(periodRow.mainPeriodId);
  523. }
  524. }
  525. ServiceMainPeriod mainPeriod = mainPeriodList.Where(w => existMainPeriodIdList.Contains(w.mainPeriodId)).OrderByDescending(w => w.endDate - w.startDate).First();
  526. List<ServicePeriod> periods = periodOrder.Where(p => p.mainPeriodId == mainPeriod.mainPeriodId).ToList();
  527. //combine period,compare to mainPeriod start, end
  528. periodZone = combinePeriodZone(periods);
  529. periodZoneResult.startDate = (periodZone[0].startDate > mainPeriod.startDate) ? periodZone[0].startDate : mainPeriod.startDate;
  530. periodZoneResult.endDate = (periodZone[0].endDate < mainPeriod.endDate) ? periodZone[0].endDate : mainPeriod.endDate;
  531. }
  532. //return
  533. ServiceProductResult serviceProductResult = new ServiceProductResult();
  534. if ((serviceProduct.noperiod && periodZoneResult.startDate == 0 && periodZoneResult.endDate == 0) || (!serviceProduct.noperiod && periodZoneResult.startDate > 0 && periodZoneResult.endDate > 0))
  535. {
  536. serviceProductResult.prodCode = serviceProduct.prodCode;
  537. serviceProductResult.noperiod = serviceProduct.noperiod;
  538. serviceProductResult.serviceType = serviceProduct.serviceType;
  539. serviceProductResult.startDate = periodZoneResult.startDate;
  540. serviceProductResult.endDate = periodZoneResult.endDate;
  541. serviceProductResult.avaliable = serviceProduct.avaliable;
  542. }
  543. return serviceProductResult;
  544. }
  545. /// <summary>
  546. /// 計算各產品的購買歷史紀錄
  547. /// </summary>
  548. /// <param name="mainPeriodList">所有的主周期</param>
  549. /// <param name="periodList">所有附屬周期</param>
  550. /// <param name="serviceProduct">產品資訊</param>
  551. /// <returns></returns>
  552. private List<ServiceProductAuthHistoryStartEnd> CalServiceProductOrderHistory(List<ServiceMainPeriod> mainPeriodList, List<ServicePeriod> periodList, ServiceProduct serviceProduct)
  553. {
  554. List<ServiceProductAuthHistoryStartEnd> serviceProductOrderStartEndList = new List<ServiceProductAuthHistoryStartEnd>();
  555. //GROUP BY orderId
  556. if (serviceProduct.auth.Count > 0)
  557. {
  558. List<ServiceProductAuthHistory> serviceProductOrderList = new List<ServiceProductAuthHistory>();
  559. foreach (ServiceProductAuth serviceProductAuthRow in serviceProduct.auth)
  560. {
  561. ServiceProductAuthHistory existServiceProductAuthHistory = serviceProductOrderList.Where(a => a.orderId == serviceProductAuthRow.orderId).FirstOrDefault();
  562. if (existServiceProductAuthHistory != null)
  563. {
  564. if (!existServiceProductAuthHistory.periodIdList.Contains(serviceProductAuthRow.periodId))
  565. {
  566. existServiceProductAuthHistory.periodIdList.Add(serviceProductAuthRow.periodId);
  567. }
  568. }
  569. else
  570. {
  571. ServiceProductAuthHistory serviceProductAuthHistoryRow = new ServiceProductAuthHistory();
  572. serviceProductAuthHistoryRow.orderId = serviceProductAuthRow.orderId;
  573. int y = Int32.Parse(serviceProductAuthRow.orderId.Substring(0, 4));
  574. int m = Int32.Parse(serviceProductAuthRow.orderId.Substring(4, 2));
  575. int d = Int32.Parse(serviceProductAuthRow.orderId.Substring(6, 2));
  576. serviceProductAuthHistoryRow.orderDate = new DateTimeOffset(y, m, d, 0, 0, 0, TimeSpan.Zero).ToUnixTimeSeconds();
  577. serviceProductAuthHistoryRow.number = serviceProductAuthRow.number;
  578. serviceProductAuthHistoryRow.unit = serviceProductAuthRow.unit;
  579. serviceProductAuthHistoryRow.periodIdList = new List<string>();
  580. serviceProductAuthHistoryRow.periodIdList.Add(serviceProductAuthRow.periodId);
  581. serviceProductOrderList.Add(serviceProductAuthHistoryRow);
  582. }
  583. }
  584. foreach (ServiceProductAuthHistory serviceProductOrderRow in serviceProductOrderList)
  585. {
  586. ServiceProductAuthHistoryStartEnd ServiceProductAuthHistoryStartEndRow = new ServiceProductAuthHistoryStartEnd();
  587. //combine period
  588. List<ServicePeriod> periodOrder = periodList.Where((ServicePeriod x) => serviceProductOrderRow.periodIdList.Contains(x.periodId)).OrderBy(p => Int32.Parse(p.periodId)).ToList();
  589. List<periodZone> periodZone = combinePeriodZone(periodOrder);
  590. ServiceProductAuthHistoryStartEndRow.startDate = periodZone[0].startDate;
  591. ServiceProductAuthHistoryStartEndRow.endDate = periodZone[0].endDate;
  592. ServiceProductAuthHistoryStartEndRow.orderId = serviceProductOrderRow.orderId;
  593. ServiceProductAuthHistoryStartEndRow.orderDate = serviceProductOrderRow.orderDate;
  594. ServiceProductAuthHistoryStartEndRow.number = serviceProductOrderRow.number;
  595. ServiceProductAuthHistoryStartEndRow.unit = serviceProductOrderRow.unit;
  596. serviceProductOrderStartEndList.Add(ServiceProductAuthHistoryStartEndRow);
  597. }
  598. }
  599. //return
  600. return serviceProductOrderStartEndList;
  601. }
  602. //子週期合併開始結束時間
  603. private List<periodZone> combinePeriodZone(List<ServicePeriod> periods)
  604. {
  605. List<periodZone> periodZone = new List<periodZone>();
  606. foreach (ServicePeriod periodRow in periods)
  607. {
  608. if (periodZone.Count == 0)
  609. {
  610. periodZone periodZoneNew = new periodZone();
  611. periodZoneNew.startDate = periodRow.startDate;
  612. periodZoneNew.endDate = periodRow.endDate;
  613. periodZone.Add(periodZoneNew);
  614. }
  615. else
  616. {
  617. bool zoneExistFlg = false;
  618. for (int i = 0; i < periodZone.Count; i++) //考慮到副週期可能斷成數個block,整合後取第一個
  619. {
  620. if (periodRow.startDate <= periodZone[i].endDate + 10 && periodZone[i].endDate + 10 < periodRow.endDate)
  621. {
  622. periodZone[i].endDate = periodRow.endDate;
  623. zoneExistFlg = true;
  624. }
  625. else if (periodZone[i].startDate <= periodRow.endDate + 10 && periodRow.endDate + 10 < periodZone[i].endDate)
  626. {
  627. periodZone[i].startDate = periodRow.startDate;
  628. zoneExistFlg = true;
  629. }
  630. }
  631. if (!zoneExistFlg)
  632. {
  633. periodZone periodZoneNew = new periodZone();
  634. periodZoneNew.startDate = periodRow.startDate;
  635. periodZoneNew.endDate = periodRow.endDate;
  636. periodZone.Add(periodZoneNew);
  637. }
  638. }
  639. }
  640. return periodZone;
  641. }
  642. /// <summary>
  643. /// 取得某學校AClassOne授權及使用資料
  644. /// </summary>
  645. /// <param name="request"></param>
  646. /// <returns></returns>
  647. [ProducesDefaultResponseType]
  648. [HttpPost("get-school-aclassone")]
  649. public async Task<IActionResult> GetSchoolAclassoneInfo(JsonElement request)
  650. {
  651. if (!request.TryGetProperty("school_code", out JsonElement school_code)) return BadRequest();
  652. var clientContainer = _azureCosmos.GetCosmosClient().GetContainer("TEAMModelOS", "School");
  653. var response = await clientContainer.ReadItemStreamAsync(school_code.ToString(), new PartitionKey("Product"));
  654. int total = 0; //可分配總數
  655. int staNum = 0; //固定分配數
  656. int dyncNum = 0; //動態分配數
  657. List<string> staIds = new List<string>(); //固定分配學生ID列
  658. List<string> dyncIds = new List<string>(); //動態分配學生ID列
  659. if (response.Status == 200)
  660. {
  661. var json = await JsonDocument.ParseAsync(response.ContentStream);
  662. if (json.RootElement.TryGetProperty("service", out JsonElement serviceJobj))
  663. {
  664. //取得active主週期
  665. List<ServiceMainPeriod> activeMainPeriod = new List<ServiceMainPeriod>();
  666. if (serviceJobj.TryGetProperty("mainperiod", out JsonElement mainperiodJobj))
  667. {
  668. foreach (var mainperiodRow in mainperiodJobj.EnumerateArray())
  669. {
  670. if (mainperiodRow.GetProperty("active").GetBoolean())
  671. {
  672. activeMainPeriod.Add(mainperiodRow.ToObject<ServiceMainPeriod>());
  673. }
  674. }
  675. }
  676. //取得active主週期的副週期
  677. List<ServicePeriod> activePeriodOfMain = new List<ServicePeriod>();
  678. if (serviceJobj.TryGetProperty("period", out JsonElement periodJobj))
  679. {
  680. foreach (var periodRow in periodJobj.EnumerateArray())
  681. {
  682. foreach (ServiceMainPeriod mainPeriodRow in activeMainPeriod)
  683. {
  684. if (periodRow.GetProperty("mainPeriodId").ToString() == mainPeriodRow.mainPeriodId)
  685. {
  686. activePeriodOfMain.Add(periodRow.ToObject<ServicePeriod>());
  687. }
  688. }
  689. }
  690. }
  691. //取得AClassOne可用數量
  692. if (serviceJobj.TryGetProperty("product", out JsonElement serviceProductJobj))
  693. {
  694. foreach (var serviceProductRow in serviceProductJobj.EnumerateArray())
  695. {
  696. //AClassOne買斷、週期
  697. string prodCode = serviceProductRow.GetProperty("prodCode").GetString();
  698. if (prodCode == "RYGVCPLY" || prodCode == "AEGMCPLY")
  699. {
  700. ServiceProductResult serviceProductResultRow = CalServiceProductAuth(activeMainPeriod, activePeriodOfMain, serviceProductRow.ToObject<ServiceProduct>());
  701. total += serviceProductResultRow.avaliable;
  702. }
  703. }
  704. }
  705. //取得AClassOne固定分配數、動態分配數、固定分配學生ID
  706. if (json.RootElement.TryGetProperty("aclassone", out JsonElement aclassoneJobj))
  707. {
  708. if (aclassoneJobj.ValueKind != JsonValueKind.Null)
  709. {
  710. if (aclassoneJobj.TryGetProperty("ids", out JsonElement ids) && ids.ValueKind != JsonValueKind.Null)
  711. {
  712. staIds = System.Text.Json.JsonSerializer.Deserialize<List<string>>(ids.ToJsonString());
  713. staNum = staIds.Count;
  714. }
  715. }
  716. dyncIds = GetSchoolDynamicAclassOneIDList(school_code.GetString());
  717. dyncNum = dyncIds.Count;
  718. }
  719. }
  720. }
  721. return Ok(new { total, staNum, dyncNum, staIds, dyncIds });
  722. }
  723. /// <summary>
  724. /// 取得某學校AClassOne動態可使用數
  725. /// </summary>
  726. public int GetSchoolDynamicAclassOne(string schoolCode)
  727. {
  728. try
  729. {
  730. int result = 0;
  731. string key = schoolCode + ":" + "AclassOne" + ":" + "dynamic";
  732. var redisClient = _azureRedis.GetRedisClient(redisAclassoneDbNum);
  733. var dyncount = redisClient.StringGet(key);
  734. if (redisClient.KeyExists(key))
  735. {
  736. result = Int32.Parse(redisClient.StringGet(key));
  737. }
  738. return result;
  739. }
  740. catch (Exception ex)
  741. {
  742. return 0;
  743. }
  744. }
  745. /// <summary>
  746. /// 取得某學校AClassOne動態已取用的ID List
  747. /// </summary>
  748. public List<string> GetSchoolDynamicAclassOneIDList(string schoolCode)
  749. {
  750. string key = schoolCode + ":" + "AclassOne" + ":" + "dynamicIds";
  751. var redisClient = _azureRedis.GetRedisClient(redisAclassoneDbNum);
  752. RedisValue[] redisResult = redisClient.SetMembers(key);
  753. List<string> result = Array.ConvertAll(redisResult, x => (string)x).ToList();
  754. return result;
  755. }
  756. public long GetSchoolDynamicAclassOneIDCount(string schoolCode)
  757. {
  758. string key = schoolCode + ":" + "AclassOne" + ":" + "dynamicIds";
  759. var redisClient = _azureRedis.GetRedisClient(redisAclassoneDbNum);
  760. long result = redisClient.SetLength(key);
  761. return result;
  762. }
  763. /// <summary>
  764. /// [測試用]放入某學校的AClassOne動態學生ID
  765. /// </summary>
  766. [ProducesDefaultResponseType]
  767. [HttpPost("set-school-aclassone-dync")]
  768. public IActionResult SetSchoolAclassoneDync(JsonElement request)
  769. {
  770. if (!request.TryGetProperty("school_code", out JsonElement school_code)) return BadRequest();
  771. request.TryGetProperty("student_id", out JsonElement student_id);
  772. var redisClient = _azureRedis.GetRedisClient(redisAclassoneDbNum);
  773. string key1 = school_code + ":" + "AclassOne" + ":" + "dynamicIds";
  774. RedisValue[] redisIdsOriginal = redisClient.SetMembers(key1);
  775. var resultIdsOriginal = Array.ConvertAll(redisIdsOriginal, x => (string)x).ToList();
  776. redisClient.SetAdd(key1, student_id.GetString());
  777. RedisValue[] redisIds = redisClient.SetMembers(key1);
  778. var resultIds = Array.ConvertAll(redisIds, x => (string)x).ToList();
  779. string key2 = school_code + ":" + "AclassOne" + ":" + "dynamic";
  780. if (resultIds.Count > resultIdsOriginal.Count)
  781. {
  782. redisClient.StringDecrement(key2, 1);
  783. }
  784. string resultCount = redisClient.StringGet(key2);
  785. return Ok(new { resultIds, resultCount });
  786. }
  787. /// <summary>
  788. /// 設定某學校AClassOne固定分配授權學生
  789. /// </summary>
  790. /// <param name="request"></param>
  791. /// <returns></returns>
  792. [ProducesDefaultResponseType]
  793. [HttpPost("set-school-aclasson-sta")]
  794. public async Task<IActionResult> SetSchoolAclassoneStatic(JsonElement request)
  795. {
  796. if (!request.TryGetProperty("school_code", out JsonElement school_code)) return BadRequest();
  797. if (!request.TryGetProperty("student_ids", out JsonElement student_ids)) return BadRequest();
  798. try
  799. {
  800. List<string> studentIds = System.Text.Json.JsonSerializer.Deserialize<List<string>>(student_ids.ToJsonString()); //新的學生ID列
  801. /* ERROR CODE定義:
  802. * 0: 無錯誤
  803. * 1: AClassOne授權數不足或無AClassOne授權
  804. */
  805. int status = 0;
  806. string err = "";
  807. var clientContainer = _azureCosmos.GetCosmosClient().GetContainer("TEAMModelOS", "School");
  808. var response = await clientContainer.ReadItemStreamAsync(school_code.ToString(), new PartitionKey("Product"));
  809. if (response.Status == 200)
  810. {
  811. int total = 0; //(回傳值)可分配總數
  812. int staNum = 0; //(回傳值)固定分配數
  813. int dyncNum = 0; //(回傳值)動態分配數
  814. List<string> staIds = new List<string>(); //(回傳值)固定學生ID列
  815. List<string> dyncIds = new List<string>(); //(回傳值)動態學生ID列
  816. using (Stream stream = response.ContentStream)
  817. {
  818. using (StreamReader streamReader = new StreamReader(stream))
  819. {
  820. string content = streamReader.ReadToEnd();
  821. SchoolProduct schoolProduct = System.Text.Json.JsonSerializer.Deserialize<SchoolProduct>(content);
  822. if (schoolProduct.aclassone != null) //有AClassOne產品授權,schoolProduct.aclassone不會為null
  823. {
  824. total = schoolProduct.aclassone.total;
  825. List<string> studentIdsInDB = schoolProduct.aclassone.ids; //舊的學生ID列
  826. int totalInDB = schoolProduct.aclassone.total;
  827. //STEP1 驗證所給的ID數是否超過AClassOne總數
  828. if (studentIds.Count > totalInDB)
  829. {
  830. status = 1;
  831. err = "AclassOne numbers is not enouth.";
  832. return Ok(new { status, err });
  833. }
  834. //STEP2 將學生ID放入 School.Product.aclassone.ids,更新aclassone.used
  835. staIds = schoolProduct.aclassone.ids = studentIds;
  836. staNum = schoolProduct.aclassone.used = studentIds.Count;
  837. ////Update
  838. await clientContainer.ReplaceItemAsync<SchoolProduct>(schoolProduct, schoolProduct.id, new PartitionKey("Product"));
  839. //STEP3更新Redis.hbcn:AclassOne:dynamic ※註:只更新動態ID可用數,不更動動態ID列表,所以若可用數為0,當天被動態分配到的學生仍可使用至今日0時才會被清掉
  840. int dynamicAclassCount = totalInDB - studentIds.Count; //動態可使用數
  841. dyncNum = (int)GetSchoolDynamicAclassOneIDCount(school_code.GetString()); //動態ID已使用數
  842. var redisClient = _azureRedis.GetRedisClient(redisAclassoneDbNum);
  843. string keys = school_code.GetString() + ":" + "AclassOne" + ":" + "dynamic";
  844. int updDynAclassCount = (dynamicAclassCount - dyncNum > 0) ? dynamicAclassCount - dyncNum : 0;
  845. redisClient.StringSet(keys, updDynAclassCount);
  846. string keyd = school_code.GetString() + ":" + "AclassOne" + ":" + "dynamicIds";
  847. RedisValue[] redisIds = redisClient.SetMembers(keyd);
  848. dyncIds = Array.ConvertAll(redisIds, x => (string)x).ToList();
  849. return Ok(new { status, err, total, staNum, dyncNum, staIds, dyncIds });
  850. }
  851. else
  852. {
  853. status = 1;
  854. err = "AClassOne has no authorization.";
  855. return Ok(new { status, err });
  856. }
  857. }
  858. }
  859. }
  860. else
  861. {
  862. return BadRequest();
  863. }
  864. }
  865. catch (Exception ex)
  866. {
  867. return BadRequest();
  868. }
  869. }
  870. /// <summary>
  871. /// 回收某學校AClassOne固定分配、動態分配授權學生
  872. /// </summary>
  873. /// <param name="request"></param>
  874. /// <returns></returns>
  875. [ProducesDefaultResponseType]
  876. [HttpPost("recall-school-aclasson")]
  877. public async Task<IActionResult> RecallSchoolAclassone(JsonElement request)
  878. {
  879. if (!request.TryGetProperty("school_code", out JsonElement school_code)) return BadRequest();
  880. string action = "all"; //預設回收:固定及動態均回收 sta:回收固定 dync:回收動態
  881. if (request.TryGetProperty("mode", out JsonElement mode))
  882. {
  883. if(mode.GetString() == "sta" || mode.GetString() == "dync")
  884. {
  885. action = mode.GetString();
  886. }
  887. }
  888. int status = 0;
  889. string err = "";
  890. var redisClient = _azureRedis.GetRedisClient(redisAclassoneDbNum);
  891. string keys = school_code.GetString() + ":" + "AclassOne" + ":" + "dynamic";
  892. string keyd = school_code.GetString() + ":" + "AclassOne" + ":" + "dynamicIds";
  893. var clientContainer = _azureCosmos.GetCosmosClient().GetContainer("TEAMModelOS", "School");
  894. var response = await clientContainer.ReadItemStreamAsync(school_code.ToString(), new PartitionKey("Product"));
  895. if (response.Status == 200)
  896. {
  897. int total = 0; //(回傳值)可分配總數
  898. int staNum = 0; //(回傳值)固定分配數
  899. int dyncNum = 0; //(回傳值)動態分配數
  900. List<string> staIds = new List<string>(); //(回傳值)固定學生ID列
  901. List<string> dyncIds = new List<string>(); //(回傳值)動態學生ID列
  902. using (Stream stream = response.ContentStream)
  903. {
  904. using (StreamReader streamReader = new StreamReader(stream))
  905. {
  906. string content = streamReader.ReadToEnd();
  907. SchoolProduct schoolProduct = System.Text.Json.JsonSerializer.Deserialize<SchoolProduct>(content);
  908. if (schoolProduct.aclassone != null) //有AClassOne產品授權,schoolProduct.aclassone不會為null
  909. {
  910. total = schoolProduct.aclassone.total;
  911. staNum = schoolProduct.aclassone.used;
  912. staIds = schoolProduct.aclassone.ids;
  913. dyncNum = (int)GetSchoolDynamicAclassOneIDCount(school_code.GetString());
  914. dyncIds = GetSchoolDynamicAclassOneIDList(school_code.GetString());
  915. switch (action)
  916. {
  917. case "sta":
  918. staIds = schoolProduct.aclassone.ids = new List<string>();
  919. staNum = schoolProduct.aclassone.used = 0;
  920. await clientContainer.ReplaceItemAsync<SchoolProduct>(schoolProduct, schoolProduct.id, new PartitionKey("Product"));
  921. int updDynAclassCount = (total - dyncNum > 0) ? total - dyncNum : 0;
  922. redisClient.StringSet(keys, updDynAclassCount);
  923. break;
  924. case "dync": //動態回收
  925. dyncNum = 0;
  926. dyncIds = new List<string>();
  927. redisClient.StringSet(keys, total - staNum);
  928. redisClient.KeyDelete(keyd);
  929. break;
  930. case "all": //全回收
  931. staIds = schoolProduct.aclassone.ids = new List<string>();
  932. staNum = schoolProduct.aclassone.used = 0;
  933. await clientContainer.ReplaceItemAsync<SchoolProduct>(schoolProduct, schoolProduct.id, new PartitionKey("Product"));
  934. dyncNum = 0;
  935. dyncIds = new List<string>();
  936. redisClient.StringSet(keys, total);
  937. redisClient.KeyDelete(keyd);
  938. break;
  939. }
  940. return Ok(new { status, err, total, staNum, dyncNum, staIds, dyncIds });
  941. }
  942. else
  943. {
  944. status = 1;
  945. err = "AClassOne has no authorization.";
  946. return Ok(new { status, err });
  947. }
  948. }
  949. }
  950. }
  951. else
  952. {
  953. return BadRequest();
  954. }
  955. }
  956. /// <summary>
  957. /// 教師空間
  958. /// </summary>
  959. [ProducesDefaultResponseType]
  960. [HttpPost("teacher-space")]
  961. public async Task<IActionResult> TeacherSpace(JsonElement request)
  962. {
  963. // 必要檢查
  964. if (!request.TryGetProperty("school_code", out JsonElement code)) return BadRequest();
  965. if (!request.TryGetProperty("action", out JsonElement type)) return BadRequest();
  966. try
  967. {
  968. // [變數宣告]
  969. string school_code = code.ToString(); // 學校簡碼
  970. string action = type.ToString(); // 功能分類
  971. string queryText = ""; // 統一sql文記憶體
  972. // response
  973. string status = "0";
  974. string err = "";
  975. List<string> errTeachers = new List<string>();
  976. //[學校空間]
  977. var blobClient = _azureStorage.GetBlobContainerClient(school_code.ToString());
  978. long? docSize = await blobClient.GetBlobsSize("doc");
  979. long? videoSize = await blobClient.GetBlobsSize("video");
  980. long? imageSize = await blobClient.GetBlobsSize("image");
  981. long? paperSize = await blobClient.GetBlobsSize("paper");
  982. long? itemSize = await blobClient.GetBlobsSize("item");
  983. long? otherSize = await blobClient.GetBlobsSize("other");
  984. long? studentSize = await blobClient.GetBlobsSize("student");
  985. int avaliable = this.baseSpaceSize; // (G) 目前學校申請空間總數
  986. int teacherSpace = 0; // 教師可設定的空間量(G)
  987. int usedSpace = 0;//已被使用的空間量(G)
  988. // 取得學校目前的總空間數(G)
  989. queryText = $"SELECT p.prodCode, p.avaliable FROM c JOIN p IN c.service.product WHERE c.id = '{school_code}' AND p.prodCode = 'IPALYEIY'";
  990. await foreach (var item in _azureCosmos.GetCosmosClient().GetContainer("TEAMModelOS", "School").GetItemQueryStreamIterator(queryText: queryText, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("Product") }))
  991. {
  992. using var jsoncm = await JsonDocument.ParseAsync(item.ContentStream);
  993. if (jsoncm.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  994. {
  995. foreach (var obj in jsoncm.RootElement.GetProperty("Documents").EnumerateArray())
  996. {
  997. avaliable += obj.GetProperty("avaliable").GetInt32();
  998. }
  999. docSize = docSize != null ? docSize : 0;
  1000. videoSize = videoSize != null ? videoSize : 0;
  1001. imageSize = imageSize != null ? imageSize : 0;
  1002. paperSize = paperSize != null ? paperSize : 0;
  1003. itemSize = itemSize != null ? itemSize : 0;
  1004. otherSize = otherSize != null ? otherSize : 0;
  1005. studentSize = studentSize != null ? studentSize : 0;
  1006. usedSpace = Convert.ToInt32(Math.Ceiling((double)((docSize + videoSize + imageSize + paperSize + itemSize + otherSize + studentSize) / bytes)));
  1007. teacherSpace = avaliable - usedSpace;
  1008. }
  1009. }
  1010. switch (action)
  1011. {
  1012. case "baseSpace": // 現在的教師空間
  1013. return Ok(new {
  1014. status,
  1015. avaliable = avaliable.ToString(),
  1016. usedSpace = usedSpace.ToString()
  1017. });
  1018. case "upd": // 修改教師空間
  1019. // [二次檢核]
  1020. if (!request.TryGetProperty("teachers", out JsonElement list)) return BadRequest();
  1021. if (list.GetArrayLength() == 0) return BadRequest();
  1022. // 教師設定總數
  1023. int setSpaceTotal = 0;
  1024. // 教師List
  1025. JsonElement.ArrayEnumerator teachers = request.GetProperty("teachers").EnumerateArray();
  1026. List<string> ids = new List<string>();
  1027. foreach (var obj in teachers) {
  1028. ids.Add(obj.GetProperty("id").ToString());
  1029. setSpaceTotal += String.IsNullOrEmpty(obj.GetProperty("size").ToString()) ? 0 : Convert.ToInt32(obj.GetProperty("size").ToString());
  1030. }
  1031. // 檢核總數是否超過
  1032. if (setSpaceTotal > teacherSpace)
  1033. {
  1034. status = "1"; // 要設定的空間總數大於目前學校有的
  1035. err = $"設定總量({setSpaceTotal}G)超過目前空間({teacherSpace}G)";
  1036. return Ok(new { status, err });
  1037. }
  1038. queryText = $"select * from c where c.id in ({string.Join(",", ids.Select(o => $"'{o}'"))})";
  1039. await foreach (var item in _azureCosmos.GetCosmosClient().GetContainer("TEAMModelOS", "School").GetItemQueryStreamIterator(queryText: queryText, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Teacher-{school_code}") }))
  1040. {
  1041. using var jsoncm = await JsonDocument.ParseAsync(item.ContentStream);
  1042. if (jsoncm.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0 && count.GetInt16() == list.GetArrayLength())
  1043. {
  1044. List<SchoolTeacher> teachersInschool = new List<SchoolTeacher>();
  1045. foreach (var obj in jsoncm.RootElement.GetProperty("Documents").EnumerateArray())
  1046. {
  1047. teachersInschool.Add(obj.ToObject<SchoolTeacher>());
  1048. }
  1049. foreach (var obj in teachers)
  1050. {
  1051. SchoolTeacher teacher = teachersInschool.Where(t => t.id == obj.GetProperty("id").ToString()).FirstOrDefault<SchoolTeacher>();
  1052. int orgTeacherSize = teacher.size;
  1053. teacher.size = Convert.ToInt32(obj.GetProperty("size").ToString());
  1054. // 修改DB裡Teacher的size
  1055. var response = await _azureCosmos.GetCosmosClient().GetContainer("TEAMModelOS", "Teacher").ReadItemStreamAsync(teacher.id, new PartitionKey("Base"));
  1056. if (response.Status == 200)
  1057. {
  1058. var json = await JsonDocument.ParseAsync(response.ContentStream);
  1059. //軟體
  1060. Teacher teacherHimself = json.ToObject<Teacher>();
  1061. teacherHimself.size = teacherHimself.size - orgTeacherSize + teacher.size;
  1062. //最後一起修改
  1063. await _azureCosmos.GetCosmosClient().GetContainer("TEAMModelOS", "Teacher").ReplaceItemAsync<Teacher>(teacherHimself, teacherHimself.id, new PartitionKey("Base"));
  1064. await _azureCosmos.GetCosmosClient().GetContainer("TEAMModelOS", "School").ReplaceItemAsync<SchoolTeacher>(teacher, obj.GetProperty("id").ToString(), new PartitionKey($"Teacher-{school_code}"));
  1065. }
  1066. else
  1067. {
  1068. status = "3";
  1069. err = "有老師不存在於 Teacher 的DB";
  1070. }
  1071. }
  1072. }
  1073. else
  1074. {
  1075. // 有不存在的的老師ID
  1076. status = "2";
  1077. err = "有不存在的的老師ID";
  1078. List<string> teachersInschool = new List<string>();
  1079. foreach (var obj in jsoncm.RootElement.GetProperty("Documents").EnumerateArray())
  1080. {
  1081. teachersInschool.Add(obj.GetProperty("id").ToString());
  1082. }
  1083. // 差集處理
  1084. errTeachers = ids.Except(teachersInschool).ToList();
  1085. }
  1086. }
  1087. return Ok(new { status, err, errTeachers });
  1088. case "retract": // 收回教師空間
  1089. // [取得DB資料]
  1090. queryText = "select * from c ";
  1091. string record = "0";
  1092. await foreach (var item in _azureCosmos.GetCosmosClient().GetContainer("TEAMModelOS", "School").GetItemQueryStreamIterator(queryText: queryText, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Teacher-{school_code}") }))
  1093. {
  1094. using var jsoncm = await JsonDocument.ParseAsync(item.ContentStream);
  1095. if (jsoncm.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  1096. {
  1097. // 修改的筆數
  1098. record = count.ToString();
  1099. List<SchoolTeacher> teachersInschool = new List<SchoolTeacher>();
  1100. foreach (var obj in jsoncm.RootElement.GetProperty("Documents").EnumerateArray())
  1101. {
  1102. teachersInschool.Add(obj.ToObject<SchoolTeacher>());
  1103. }
  1104. foreach (SchoolTeacher teacher in teachersInschool)
  1105. {
  1106. // 修改DB裡School的老師size
  1107. int teacherSizeInSchool = teacher.size;
  1108. teacher.size = 0;
  1109. // 修改DB裡Teacher的size
  1110. var response = await _azureCosmos.GetCosmosClient().GetContainer("TEAMModelOS", "Teacher").ReadItemStreamAsync(teacher.id, new PartitionKey("Base"));
  1111. if (response.Status == 200)
  1112. {
  1113. var json = await JsonDocument.ParseAsync(response.ContentStream);
  1114. //軟體
  1115. Teacher teacherHimself = json.ToObject<Teacher>();
  1116. teacherHimself.size -= teacherSizeInSchool;
  1117. // 最後一起修改
  1118. await _azureCosmos.GetCosmosClient().GetContainer("TEAMModelOS", "School").ReplaceItemAsync<SchoolTeacher>(teacher, teacher.id, new PartitionKey($"Teacher-{school_code}"));
  1119. await _azureCosmos.GetCosmosClient().GetContainer("TEAMModelOS", "Teacher").ReplaceItemAsync<Teacher>(teacherHimself, teacherHimself.id, new PartitionKey("Base"));
  1120. }
  1121. else
  1122. {
  1123. status = "3";
  1124. err = "有老師不存在於 Teacher 的DB";
  1125. }
  1126. }
  1127. }
  1128. }
  1129. return Ok(new { status, record });
  1130. default:
  1131. return BadRequest(); // 不存在的功能BadRequest
  1132. }
  1133. }
  1134. catch (Exception ex)
  1135. {
  1136. return BadRequest();
  1137. }
  1138. }
  1139. }
  1140. }