TmidController.cs 92 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533
  1. using Microsoft.Azure.Cosmos;
  2. using Microsoft.AspNetCore.Authorization;
  3. using Microsoft.AspNetCore.Mvc;
  4. using Microsoft.Extensions.Configuration;
  5. using Microsoft.Extensions.Options;
  6. using System;
  7. using System.Collections.Generic;
  8. using System.Linq;
  9. using System.Reflection;
  10. using System.Text.Json;
  11. using System.Threading.Tasks;
  12. using TEAMModelOS.Models;
  13. using TEAMModelOS.SDK;
  14. using TEAMModelOS.SDK.DI;
  15. using TEAMModelOS.SDK.Extension;
  16. using TEAMModelOS.SDK.Models;
  17. using TEAMModelOS.SDK.Services;
  18. using static TEAMModelBI.Models.Extension.GeoRegion;
  19. using Microsoft.Azure.Documents.Client;
  20. using Microsoft.Azure.Cosmos.Table;
  21. namespace TEAMModelBI.Controllers.BITmid
  22. {
  23. [Route("tmid")]
  24. [ApiController]
  25. public class TmidController : ControllerBase
  26. {
  27. private readonly AzureCosmosFactory _azureCosmos;
  28. private readonly AzureStorageFactory _azureStorage;
  29. private readonly AzureRedisFactory _azureRedis;
  30. private readonly DingDing _dingDing;
  31. private readonly Option _option;
  32. private readonly IConfiguration _configuration;
  33. private readonly HttpTrigger _httpTrigger;
  34. public TmidController(AzureCosmosFactory azureCosmos, AzureStorageFactory azureStorage, AzureRedisFactory azureRedis, DingDing dingDing, IOptionsSnapshot<Option> option, IConfiguration configuration, HttpTrigger httpTrigger)
  35. {
  36. _azureCosmos = azureCosmos;
  37. _azureStorage = azureStorage;
  38. _azureRedis = azureRedis;
  39. _dingDing = dingDing;
  40. _option = option?.Value;
  41. _configuration = configuration;
  42. _httpTrigger = httpTrigger;
  43. }
  44. /// <summary>
  45. /// 取得TMID綜合資料
  46. /// </summary>
  47. /// <param name="jsonElement"></param>
  48. /// <returns></returns>
  49. [ProducesDefaultResponseType]
  50. [Authorize(Roles = "IES")]
  51. [HttpPost("get-tmidstics")]
  52. public async Task<IActionResult> GetTmidStics(JsonElement jsonElement)
  53. {
  54. try
  55. {
  56. if (!jsonElement.TryGetProperty("tmids", out JsonElement tmidsJobj)) return BadRequest();//TMID(array)
  57. var tmids = tmidsJobj.Deserialize<List<string>>();
  58. if (tmids.Count is 0) return BadRequest();
  59. var datetime = DateTimeOffset.UtcNow.AddDays(-1);
  60. int y = datetime.Year;
  61. int m = datetime.Month;
  62. int d = datetime.Day;
  63. string dateFromStr = (jsonElement.TryGetProperty("dateFrom", out JsonElement dateFromJobj)) ? dateFromJobj.GetString() : string.Empty; //查詢日期:起始(string)[例]2023-03-05
  64. string dateToStr = (jsonElement.TryGetProperty("dateTo", out JsonElement dateToJobj)) ? dateToJobj.GetString() : string.Empty; //查詢日期:結束(string)[例]2023-03-27
  65. string dateUnit = (jsonElement.TryGetProperty("dateUnit", out JsonElement dateUnitJobj)) ? dateUnitJobj.GetString().ToLower() : "month";
  66. string mode = (jsonElement.TryGetProperty("mode", out JsonElement modeJobj)) ? modeJobj.GetString().ToLower() : "default";
  67. //起始終止日期換算
  68. long dateTimeFromSec = 0;
  69. long dateTimeToSec = 0;
  70. if(!string.IsNullOrWhiteSpace(dateFromStr) && !string.IsNullOrWhiteSpace(dateToStr))
  71. {
  72. DateTimeOffset dateTimeFrom;
  73. DateTimeOffset dateTimeTo;
  74. List<string> dateFromList = dateFromStr.Split('-').ToList();
  75. int dateFromYear = Convert.ToInt32(dateFromList[0], 10);
  76. int dateFromMonth = (dateUnit.Equals("day") || dateUnit.Equals("month")) ? Convert.ToInt32(dateFromList[1], 10) : 1;
  77. int dateFromDay = (dateUnit.Equals("day")) ? Convert.ToInt32(dateFromList[2], 10) : 1;
  78. List<string> dateToList = dateToStr.Split('-').ToList();
  79. int dateToYear = Convert.ToInt32(dateToList[0], 10);
  80. int dateToMonth = (dateUnit.Equals("day") || dateUnit.Equals("month")) ? Convert.ToInt32(dateToList[1], 10) : 1;
  81. int dateToDay = (dateUnit.Equals("day")) ? Convert.ToInt32(dateToList[2], 10) : 1;
  82. switch (dateUnit)
  83. {
  84. case "year":
  85. dateTimeFrom = new DateTimeOffset(dateFromYear, 1, 1, 0, 0, 0, TimeSpan.Zero);
  86. dateTimeTo = new DateTimeOffset(dateToYear, 12, 31, 23, 59, 59, TimeSpan.Zero);
  87. break;
  88. case "month":
  89. dateTimeFrom = new DateTimeOffset(dateFromYear, dateFromMonth, 1, 0, 0, 0, TimeSpan.Zero);
  90. dateTimeTo = new DateTimeOffset(dateToYear, dateToMonth, DateTime.DaysInMonth(dateToYear, dateToMonth), 23, 59, 59, TimeSpan.Zero);
  91. break;
  92. case "day":
  93. dateTimeFrom = new DateTimeOffset(dateFromYear, dateFromMonth, dateFromDay, 0, 0, 0, TimeSpan.Zero);
  94. dateTimeTo = new DateTimeOffset(dateToYear, dateToMonth, dateToDay, 23, 59, 59, TimeSpan.Zero);
  95. break;
  96. default:
  97. dateTimeFrom = new DateTimeOffset(y, m, d, 0, 0, 0, TimeSpan.Zero);
  98. dateTimeTo = new DateTimeOffset(y, m, d, 23, 59, 59, TimeSpan.Zero);
  99. break;
  100. }
  101. dateTimeFromSec = dateTimeFrom.ToUnixTimeSeconds();
  102. dateTimeToSec = dateTimeTo.ToUnixTimeSeconds();
  103. y = 0;
  104. m = 0;
  105. d = 0;
  106. }
  107. //取得項目列表
  108. List<string> eventList = new List<string>();
  109. if(mode.Equals("simple"))
  110. {
  111. eventList = new List<string>() { "points" };
  112. } else
  113. {
  114. eventList = new List<string>() { "coupons", "login", "prod", "points", "iot", "ies5", "benefits", "sokrates" };
  115. }
  116. //服務Client端
  117. var cosmosClientIes5 = _azureCosmos.GetCosmosClient(); //CosmosDB IES5
  118. var cosmosClientCsv2 = _azureCosmos.GetCosmosClient(name: "CoreServiceV2"); //CosmosDB CSV2
  119. var storageClientCsv2 = _azureStorage.GetCloudTableClient(name: "CoreServiceV2"); //Storage CSV2
  120. var tableCouponClient = storageClientCsv2.GetTableReference("Coupon");
  121. var tablePointsClient = storageClientCsv2.GetTableReference("Points");
  122. var redisClient = _azureRedis.GetRedisClient(4);
  123. //取得TMID基本資料
  124. Dictionary<string, TmidStics> tmidDic = new();
  125. QueryDefinition query =
  126. new QueryDefinition(@"SELECT c.id, c.name, c.picture, c.mobile, c.mail, c.lang, c.wechat, c.facebook, c.google, c.ding, c.apple, c.educloudtw, c.ts FROM c WHERE (ARRAY_CONTAINS(@key, c.id) OR ARRAY_CONTAINS(@key, c.mobile))")
  127. .WithParameter("@key", tmids);
  128. await foreach (var item in cosmosClientCsv2
  129. .GetContainer("Core", "ID2")
  130. .GetItemQueryStreamIteratorQuery(query, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("base") }))
  131. {
  132. using var json = await JsonDocument.ParseAsync(item.Content);
  133. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  134. {
  135. foreach (var doc in json.RootElement.GetProperty("Documents").EnumerateArray())
  136. {
  137. string id = doc.GetProperty("id").GetString();
  138. if(!tmids.Contains(id)) tmids.Add(id);
  139. //基本資料
  140. TmidStics tmidStics = (tmidDic.ContainsKey(id)) ? tmidDic[id] : new() { id = id };
  141. if (eventList.Contains("ies5")) tmidStics.ies5 = new();
  142. if (eventList.Contains("points")) tmidStics.points = new();
  143. if (eventList.Contains("sokrates")) tmidStics.sokrates = new();
  144. if (eventList.Contains("benefits")) tmidStics.benefits = new();
  145. if (eventList.Contains("iot")) tmidStics.iot = new();
  146. tmidStics.name = doc.GetProperty("name").GetString();
  147. tmidStics.picture = doc.GetProperty("picture").GetString();
  148. tmidStics.mobile = GenDataMask(doc.GetProperty("mobile").GetString(), "mobile");
  149. tmidStics.mail = GenDataMask(doc.GetProperty("mail").GetString(), "mail");
  150. tmidStics.lang = (doc.TryGetProperty("lang", out JsonElement lang)) ? lang.GetString() : string.Empty;
  151. tmidStics.wechat = (doc.TryGetProperty("wechat", out JsonElement wechat) && !string.IsNullOrWhiteSpace(wechat.GetString())) ? true : false;
  152. tmidStics.facebook = (doc.TryGetProperty("facebook", out JsonElement facebook) && !string.IsNullOrWhiteSpace(facebook.GetString())) ? true : false;
  153. tmidStics.google = (doc.TryGetProperty("google", out JsonElement google) && !string.IsNullOrWhiteSpace(google.GetString())) ? true : false;
  154. tmidStics.ding = (doc.TryGetProperty("ding", out JsonElement ding) && !string.IsNullOrWhiteSpace(ding.GetString())) ? true : false;
  155. tmidStics.apple = (doc.TryGetProperty("apple", out JsonElement apple) && !string.IsNullOrWhiteSpace(apple.GetString())) ? true : false;
  156. tmidStics.ts = (doc.TryGetProperty("ts", out JsonElement ts)) ? ts.GetInt64() : 0;
  157. tmidDic.Add(id, tmidStics);
  158. }
  159. }
  160. }
  161. //取得TMID進階資料
  162. regiondata regionData = GetRegionData(); //取得國省市區地理資訊架構
  163. query = new QueryDefinition(@"SELECT c.id, c.name, c.mobile, c.mail, c.country, c.province, c.city, c.schoolCode, c.schoolCodeW, c.unitType, c.unitName, c.jobTitle FROM c WHERE (ARRAY_CONTAINS(@key, c.id) OR ARRAY_CONTAINS(@key, c.mobile))")
  164. .WithParameter("@key", tmids);
  165. await foreach (var item in cosmosClientCsv2
  166. .GetContainer("Core", "ID2")
  167. .GetItemQueryStreamIteratorQuery(query, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("base-ex") }))
  168. {
  169. using var json = await JsonDocument.ParseAsync(item.Content);
  170. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  171. {
  172. foreach (var doc in json.RootElement.GetProperty("Documents").EnumerateArray())
  173. {
  174. string id = doc.GetProperty("id").GetString();
  175. TmidStics tmidStics = (tmidDic.ContainsKey(id)) ? tmidDic[id] : new() { id = id };
  176. if (eventList.Contains("ies5")) tmidStics.ies5 = new();
  177. if (eventList.Contains("points")) tmidStics.points = new();
  178. if (eventList.Contains("sokrates")) tmidStics.sokrates = new();
  179. if (eventList.Contains("benefits")) tmidStics.benefits = new();
  180. if (eventList.Contains("iot")) tmidStics.iot = new();
  181. if (string.IsNullOrWhiteSpace(tmidStics.name)) tmidStics.name = doc.GetProperty("name").GetString();
  182. if (string.IsNullOrWhiteSpace(tmidStics.mobile)) tmidStics.mobile = GenDataMask(doc.GetProperty("mobile").GetString(), "mobile");
  183. if (string.IsNullOrWhiteSpace(tmidStics.mail)) tmidStics.mail = GenDataMask(doc.GetProperty("mail").GetString(), "mail");
  184. string country = doc.GetProperty("country").GetString();
  185. string province = doc.GetProperty("province").GetString();
  186. string city = doc.GetProperty("city").GetString();
  187. string district = string.Empty;
  188. if (!string.IsNullOrWhiteSpace(country) && country.Equals("TW"))
  189. {
  190. district = city;
  191. city = province;
  192. province = string.Empty;
  193. }
  194. tmidStics.country = (!string.IsNullOrWhiteSpace(country) && regionData.country.ContainsKey(country)) ? regionData.country[country].name.Replace("地區", "").Replace("地区", "") : country;
  195. tmidStics.province = (!string.IsNullOrWhiteSpace(country) && regionData.country.ContainsKey(country) && !string.IsNullOrWhiteSpace(province) && regionData.province.ContainsKey(country) && regionData.province[country].ContainsKey(province)) ? regionData.province[country][province].name : province;
  196. if (!string.IsNullOrWhiteSpace(country) && country.Equals("TW"))
  197. tmidStics.city = (regionData.city.ContainsKey(country) && !string.IsNullOrWhiteSpace(city) && regionData.city[country]["tw"].ContainsKey(city)) ? regionData.city[country]["tw"][city].name : city;
  198. else if (!string.IsNullOrWhiteSpace(country) && country.Equals("CN"))
  199. tmidStics.city = (regionData.city.ContainsKey(country) && !string.IsNullOrWhiteSpace(province) && !string.IsNullOrWhiteSpace(city) && regionData.city[country].ContainsKey(province) && regionData.city[country][province].ContainsKey(city)) ? regionData.city[country][province][city].name : city;
  200. tmidStics.dist = (!string.IsNullOrWhiteSpace(district) && country.Equals("TW") && regionData.city[country]["tw"].ContainsKey(city) && regionData.dist[country]["tw"][city].ContainsKey(district)) ? regionData.dist[country]["tw"][city][district].name : district;
  201. tmidStics.schoolCode = (doc.TryGetProperty("schoolCode", out JsonElement schCode)) ? schCode.GetString() : string.Empty;
  202. tmidStics.schoolCodeW = (doc.TryGetProperty("schoolCodeW", out JsonElement schCodeW)) ? schCodeW.GetString() : string.Empty;
  203. tmidStics.unitType = (doc.TryGetProperty("unitType", out JsonElement unitType)) ? unitType.GetString() : string.Empty;
  204. tmidStics.unitName = (doc.TryGetProperty("unitName", out JsonElement unitName)) ? unitName.GetString() : string.Empty;
  205. tmidStics.jobTitle = (doc.TryGetProperty("jobTitle", out JsonElement jobTitle)) ? jobTitle.GetString() : string.Empty;
  206. }
  207. }
  208. }
  209. if(tmidDic.Count.Equals(0)) return Ok(new List<object>());
  210. //取得 票券、登入各服務的時間、取得積分、個人服務授權、IOT
  211. foreach (KeyValuePair<string, TmidStics> dicItem in tmidDic)
  212. {
  213. string id = dicItem.Key;
  214. TmidStics tmidStics = dicItem.Value;
  215. //票券
  216. if (eventList.Contains("coupons"))
  217. {
  218. var usersCoupons = tableCouponClient.Get<DynamicTableEntity>(id);
  219. foreach (var coupon in usersCoupons)
  220. {
  221. tmidStics.coupons.Add(
  222. new TmidCoupon()
  223. {
  224. code = coupon.RowKey,
  225. exchange = coupon.Properties["exchangeTime"].DateTimeOffsetValue.Value.ToUnixTimeSeconds(),
  226. });
  227. }
  228. }
  229. //取得登入各服務的時間
  230. if (eventList.Contains("login"))
  231. {
  232. var loginTime = await redisClient.HashGetAllAsync(id);
  233. foreach (var t in loginTime)
  234. {
  235. if (!t.Name.StartsWith("HiTeach") && !_dicClientIds.ContainsKey(t.Name)) continue;
  236. tmidStics.login.Add(
  237. new TmidLoginTime()
  238. {
  239. product = t.Name.StartsWith("HiTeach") ? t.Name.ToString().Split("-")[0] : _dicClientIds[t.Name],
  240. time = (long)t.Value
  241. });
  242. }
  243. }
  244. //個人服務授權
  245. if (eventList.Contains("prod"))
  246. {
  247. tmidStics.prod = await getTMIDAuthService(cosmosClientCsv2, id, "", true);
  248. }
  249. }
  250. //IOT
  251. if (eventList.Contains("iot"))
  252. {
  253. List<string> ids = tmidDic.Keys.ToList();
  254. List<TmidAnalysisCal> iotResult = new List<TmidAnalysisCal>();
  255. Dictionary<string, List<TmidAnalysisCal>> iotDic = new Dictionary<string, List<TmidAnalysisCal>>();
  256. if (!string.IsNullOrWhiteSpace(dateFromStr) && !string.IsNullOrWhiteSpace(dateToStr) && !string.IsNullOrWhiteSpace(dateUnit))
  257. {
  258. if (dateUnit.ToLower().Equals("year"))
  259. {
  260. iotDic["HiTeach-year"] = await getTMIDIotData(cosmosClientIes5, ids, "HiTeach", "year", y, 0, 0, dateTimeFromSec, dateTimeToSec);
  261. iotDic["HiTeachCC-year"] = await getTMIDIotData(cosmosClientIes5, ids, "HiTeachCC", "year", y, 0, 0, dateTimeFromSec, dateTimeToSec);
  262. }
  263. else if (dateUnit.ToLower().Equals("month"))
  264. {
  265. iotDic["HiTeach-month"] = await getTMIDIotData(cosmosClientIes5, ids, "HiTeach", "month", y, m, 0, dateTimeFromSec, dateTimeToSec);
  266. iotDic["HiTeachCC-month"] = await getTMIDIotData(cosmosClientIes5, ids, "HiTeachCC", "month", y, m, 0, dateTimeFromSec, dateTimeToSec);
  267. }
  268. else if (dateUnit.ToLower().Equals("day"))
  269. {
  270. iotDic["HiTeach-day"] = await getTMIDIotData(cosmosClientIes5, ids, "HiTeach", "day", y, m, d, dateTimeFromSec, dateTimeToSec);
  271. iotDic["HiTeachCC-day"] = await getTMIDIotData(cosmosClientIes5, ids, "HiTeachCC", "day", y, m, d, dateTimeFromSec, dateTimeToSec);
  272. }
  273. }
  274. else
  275. {
  276. iotDic["HiTeach-year"] = await getTMIDIotData(cosmosClientIes5, ids, "HiTeach", "year", y, 0, 0, dateTimeFromSec, dateTimeToSec);
  277. iotDic["HiTeach-month"] = await getTMIDIotData(cosmosClientIes5, ids, "HiTeach", "month", y, m, 0, dateTimeFromSec, dateTimeToSec);
  278. iotDic["HiTeach-day"] = await getTMIDIotData(cosmosClientIes5, ids, "HiTeach", "day", y, m, d, dateTimeFromSec, dateTimeToSec);
  279. iotDic["HiTeachCC-year"] = await getTMIDIotData(cosmosClientIes5, ids, "HiTeachCC", "year", y, 0, 0, dateTimeFromSec, dateTimeToSec);
  280. iotDic["HiTeachCC-month"] = await getTMIDIotData(cosmosClientIes5, ids, "HiTeachCC", "month", y, m, 0, dateTimeFromSec, dateTimeToSec);
  281. iotDic["HiTeachCC-day"] = await getTMIDIotData(cosmosClientIes5, ids, "HiTeachCC", "day", y, m, d, dateTimeFromSec, dateTimeToSec);
  282. }
  283. //資料整理
  284. List<string> iotDicKeyList = new List<string>() { "HiTeach-year", "HiTeachCC-year", "HiTeach-month", "HiTeachCC-month", "HiTeach-day", "HiTeachCC-day" };
  285. foreach(string iotDicKey in iotDicKeyList)
  286. {
  287. if (iotDic.ContainsKey(iotDicKey))
  288. {
  289. foreach(TmidAnalysisCal iotCalData in iotDic[iotDicKey])
  290. {
  291. string id = iotCalData.tmid;
  292. TmidStics tmidStics = tmidDic[id];
  293. switch (iotDicKey)
  294. {
  295. case "HiTeach-year":
  296. tmidStics.iot.hiteach.year = iotCalData;
  297. break;
  298. case "HiTeachCC-year":
  299. tmidStics.iot.hiteachcc.year = iotCalData;
  300. break;
  301. case "HiTeach-month":
  302. tmidStics.iot.hiteach.month = iotCalData;
  303. break;
  304. case "HiTeachCC-month":
  305. tmidStics.iot.hiteachcc.month = iotCalData;
  306. break;
  307. case "HiTeach-day":
  308. tmidStics.iot.hiteach.day = iotCalData;
  309. break;
  310. case "HiTeachCC-day":
  311. tmidStics.iot.hiteachcc.day = iotCalData;
  312. break;
  313. }
  314. }
  315. }
  316. }
  317. }
  318. //積分
  319. if (eventList.Contains("points"))
  320. {
  321. List<string> ids = tmidDic.Keys.ToList();
  322. //防止Query過長,分次取得
  323. Dictionary<int, List<string>> idsDic = new Dictionary<int, List<string>>();
  324. int idCount = 0;
  325. int max = 100; //要分割成多少人一組
  326. foreach (string id in ids)
  327. {
  328. int indx = idCount / max;
  329. if (!idsDic.ContainsKey(indx)) idsDic.Add(indx, new List<string>());
  330. idsDic[indx].Add(id);
  331. idCount++;
  332. }
  333. foreach(var itemd in idsDic)
  334. {
  335. string filter = TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, "Points");
  336. string rkFilterCombine = string.Empty;
  337. foreach (string rowKey in itemd.Value)
  338. {
  339. string rkFilter = TableQuery.GenerateFilterCondition("RowKey", QueryComparisons.Equal, rowKey);
  340. if (string.IsNullOrWhiteSpace(rkFilterCombine))
  341. {
  342. rkFilterCombine = rkFilter;
  343. }
  344. else
  345. {
  346. rkFilterCombine = TableQuery.CombineFilters(rkFilterCombine, TableOperators.Or, rkFilter);
  347. }
  348. }
  349. filter = TableQuery.CombineFilters(filter, TableOperators.And, rkFilterCombine);
  350. TableQuery tableQuery = new TableQuery().Where(filter);
  351. var usersPoints = tablePointsClient.ExecuteQuery(tableQuery);
  352. if (usersPoints.Any())
  353. {
  354. foreach (DynamicTableEntity item in usersPoints)
  355. {
  356. string id = item.RowKey.ToString();
  357. TmidStics tmidStics = tmidDic[id];
  358. tmidStics.points.points = (usersPoints != null) ? item.Properties["Points"].Int32Value.Value : 0;
  359. tmidStics.points.level = (usersPoints != null) ? item.Properties["Level"].Int32Value.Value : 0;
  360. tmidStics.points.balance = (usersPoints != null) ? item.Properties["Balance"].Int32Value.Value : 0;
  361. }
  362. }
  363. }
  364. }
  365. //IES5
  366. if (eventList.Contains("ies5"))
  367. {
  368. query = new QueryDefinition(@"SELECT c.id, c.defaultSchool, c.schools FROM c WHERE ARRAY_CONTAINS(@key, c.id)")
  369. .WithParameter("@key", tmidDic.Keys.ToList());
  370. await foreach (var item in cosmosClientIes5
  371. .GetContainer(Constant.TEAMModelOS, "Teacher")
  372. .GetItemQueryStreamIteratorQuery(query, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("Base") }))
  373. {
  374. using var json = await JsonDocument.ParseAsync(item.Content);
  375. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  376. {
  377. foreach (var doc in json.RootElement.GetProperty("Documents").EnumerateArray())
  378. {
  379. string id = doc.GetProperty("id").GetString();
  380. TmidStics tmidStics = tmidDic[id];
  381. //IES5學校資訊
  382. string defaultschool = (doc.TryGetProperty("defaultSchool", out JsonElement _defaultSchool)) ? _defaultSchool.ToString() : string.Empty; //預設學校
  383. List<string> schoolIds = new List<string>();
  384. if (doc.TryGetProperty("schools", out JsonElement schoolsJobj))
  385. {
  386. foreach (var obj in schoolsJobj.EnumerateArray())
  387. {
  388. string schoolCodeNow = $"{obj.GetProperty("schoolId")}";
  389. string status = $"{obj.GetProperty("status")}";
  390. if (status.Equals("join"))
  391. {
  392. schoolIds.Add(schoolCodeNow); //放入學校ID列,一次取
  393. }
  394. }
  395. }
  396. if (schoolIds.Count > 0)
  397. {
  398. QueryDefinition querysc = new QueryDefinition(@"SELECT c.id, c.name, c.region, c.province, c.city, c.dist, c.size, c.period FROM c WHERE ARRAY_CONTAINS(@key, c.id)")
  399. .WithParameter("@key", schoolIds);
  400. await foreach (var itemsc in cosmosClientIes5
  401. .GetContainer(Constant.TEAMModelOS, "School")
  402. .GetItemQueryStreamIteratorQuery(querysc, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("Base") }))
  403. {
  404. using var jsonsc = await JsonDocument.ParseAsync(itemsc.Content);
  405. if (jsonsc.RootElement.TryGetProperty("_count", out JsonElement countsc) && countsc.GetUInt16() > 0)
  406. {
  407. foreach (var school in jsonsc.RootElement.GetProperty("Documents").EnumerateArray())
  408. {
  409. string schoolCodeNow = school.GetProperty("id").GetString();
  410. //當前學年 ※若有多學段,取第一個學段來取得學年資訊吧
  411. Period period = school.GetProperty("period").Deserialize<List<Period>>().First();
  412. var info = SchoolService.GetSemester(period, 0, DateTime.Now.ToString());
  413. int studyYear = info.studyYear;
  414. //學校Blob使用狀況
  415. (long usedSize, long teach, long total, long surplus, Dictionary<string, double?> catalog) schoolUsedBlob = await BlobService.GetSurplusSpace(schoolCodeNow, "school", _option.Location, _azureCosmos, _azureRedis, _azureStorage, _dingDing, _httpTrigger);
  416. //老師在此學校的課程數 ※只取當前學年
  417. int courseCnt = 0;
  418. var queryCrs = $"SELECT VALUE COUNT(1) FROM ( SELECT DISTINCT VALUE(c.id) FROM c JOIN schedules IN c.schedules WHERE schedules.teacherId = '{id}' AND c.year = {studyYear} )";
  419. await foreach (int itemCrs in cosmosClientIes5.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryIteratorSql<int>(queryText: queryCrs, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"CourseTask-{schoolCodeNow}") }))
  420. {
  421. courseCnt = itemCrs;
  422. }
  423. //IES5學校資料製作
  424. TmidSticsIes5School schoolobj = new TmidSticsIes5School();
  425. long ssize = (school.TryGetProperty("size", out JsonElement ssizeJson)) ? ssizeJson.GetInt32() : 0;
  426. long sused = schoolUsedBlob.usedSize + schoolUsedBlob.teach * 1073741824;
  427. long stotal = ssize * 1073741824;
  428. object schzize = new
  429. {
  430. used = sused,
  431. total = stotal,
  432. avaliable = stotal - sused,
  433. };
  434. schoolobj.schoolId = schoolCodeNow;
  435. schoolobj.name = school.GetProperty("name").GetString();
  436. schoolobj.region = school.GetProperty("region").GetString();
  437. schoolobj.province = school.GetProperty("province").GetString();
  438. schoolobj.city = school.GetProperty("city").GetString();
  439. schoolobj.dist = school.GetProperty("dist").GetString();
  440. schoolobj.size = schzize;
  441. schoolobj.courseCnt = courseCnt;
  442. schoolobj.defaultSchool = (!string.IsNullOrWhiteSpace(defaultschool) && defaultschool.Equals(schoolCodeNow)) ? true : false;
  443. tmidStics.ies5.schools.Add(schoolobj);
  444. }
  445. }
  446. }
  447. }
  448. //IES5個人空間
  449. var (usedSize, teach, total, surplus, catalog) = await BlobService.GetSurplusSpace(id, "private", _option.Location, _azureCosmos, _azureRedis, _azureStorage, _dingDing, _httpTrigger);
  450. tmidStics.ies5.usedSize = usedSize;
  451. tmidStics.ies5.teachSize = teach * 1073741824;
  452. tmidStics.ies5.totalSize = total * 1073741824;
  453. tmidStics.ies5.surplusSize = surplus;
  454. //IES5個人資源數
  455. tmidStics.ies5.resCount = 0; //教材數
  456. tmidStics.ies5.itemCount = 0; //題目數
  457. tmidStics.ies5.paperCount = 0; //試卷數
  458. ///IES5個人題目數
  459. await foreach (int itemC in cosmosClientIes5.GetContainer(Constant.TEAMModelOS, "Teacher").GetItemQueryIteratorSql<int>(queryText: $"SELECT VALUE COUNT(1) FROM c WHERE c.pid = null", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Item-{id}") }))
  460. {
  461. tmidStics.ies5.itemCount = itemC;
  462. }
  463. ///IES5個人試卷數
  464. await foreach (int paperC in cosmosClientIes5.GetContainer(Constant.TEAMModelOS, "Teacher").GetItemQueryIteratorSql<int>(queryText: $"SELECT VALUE COUNT(1) FROM c WHERE c.scope = 'private'", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Paper-{id}") }))
  465. {
  466. tmidStics.ies5.paperCount = paperC;
  467. }
  468. ///IES5個人教材數
  469. List<BlobService.BlobCount> blogCountList = await BlobService.BloblogCount(cosmosClientIes5, "private", id, "", "");
  470. foreach (BlobService.BlobCount blobCountRow in blogCountList)
  471. {
  472. switch (blobCountRow.type)
  473. {
  474. case "doc":
  475. case "image":
  476. case "res":
  477. case "video":
  478. case "audio":
  479. case "other":
  480. tmidStics.ies5.resCount += blobCountRow.count;
  481. break;
  482. }
  483. }
  484. if (!tmidDic.ContainsKey(id)) tmidDic.Add(id, tmidStics);
  485. }
  486. }
  487. }
  488. }
  489. //個人權益
  490. if (eventList.Contains("benefits"))
  491. {
  492. query = new QueryDefinition(@"SELECT * FROM c WHERE (ARRAY_CONTAINS(@key, c.id))")
  493. .WithParameter("@key", tmidDic.Keys.ToList());
  494. await foreach (var item in cosmosClientCsv2
  495. .GetContainer("Core", "ID2")
  496. .GetItemQueryStreamIteratorQuery(query, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("benefits") }))
  497. {
  498. using var json = await JsonDocument.ParseAsync(item.Content);
  499. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  500. {
  501. foreach (var doc in json.RootElement.GetProperty("Documents").EnumerateArray())
  502. {
  503. string id = doc.GetProperty("id").GetString();
  504. if (doc.TryGetProperty("hiteach", out var elementHiteachData))
  505. {
  506. tmidDic[id].benefits.hiteach = elementHiteachData.ToObject<List<object>>();
  507. }
  508. if (doc.TryGetProperty("hiteachcc", out var elementHiteachCCData))
  509. {
  510. tmidDic[id].benefits.hiteachcc = elementHiteachCCData.ToObject<List<object>>();
  511. }
  512. }
  513. }
  514. }
  515. }
  516. //蘇格拉底資料
  517. if (eventList.Contains("sokrates"))
  518. {
  519. query = new QueryDefinition(@"SELECT * FROM c WHERE (ARRAY_CONTAINS(@key, c.id))")
  520. .WithParameter("@key", tmidDic.Keys.ToList());
  521. await foreach (var item in cosmosClientCsv2
  522. .GetContainer("Core", "ID2")
  523. .GetItemQueryStreamIteratorQuery(query, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("sokrates") }))
  524. {
  525. using var json = await JsonDocument.ParseAsync(item.Content);
  526. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  527. {
  528. foreach (var doc in json.RootElement.GetProperty("Documents").EnumerateArray())
  529. {
  530. string id = doc.GetProperty("id").GetString();
  531. if (doc.TryGetProperty("hiteach_data", out var elementHiteachData))
  532. {
  533. tmidDic[id].sokrates.hiteach_data = elementHiteachData.ToObject<TmidSokratesHiteach>();
  534. }
  535. if (doc.TryGetProperty("user_channels", out var elementUserChannels))
  536. {
  537. tmidDic[id].sokrates.user_channels = elementUserChannels.ToObject<object>();
  538. }
  539. }
  540. }
  541. }
  542. }
  543. //輸出
  544. List<object> data = new();
  545. foreach (KeyValuePair<string, TmidStics> dicItem in tmidDic)
  546. {
  547. data.Add(dicItem.Value);
  548. }
  549. return Ok(data);
  550. }
  551. catch (Exception ex)
  552. {
  553. //await _dingDing.SendBotMsg($"BI,{_option.Location} /tmid/get-tmidstics \n {ex.Message}\n{ex.StackTrace}", GroupNames.台北開發測試群組);
  554. return BadRequest();
  555. }
  556. }
  557. /// <summary>
  558. /// 取得TMID IOT資料
  559. /// </summary>
  560. /// <param name="jsonElement"></param>
  561. /// <returns></returns>
  562. [ProducesDefaultResponseType]
  563. [Authorize(Roles = "IES")]
  564. [HttpPost("get-tmid-iot")]
  565. public async Task<IActionResult> GetTmidIot(JsonElement jsonElement)
  566. {
  567. try
  568. {
  569. if (!jsonElement.TryGetProperty("tmids", out JsonElement tmidsJobj)) return BadRequest(); //TMID(array)
  570. var tmids = tmidsJobj.Deserialize<List<string>>();
  571. if (tmids.Count is 0) return BadRequest();
  572. string dateFromStr = (jsonElement.TryGetProperty("dateFrom", out JsonElement dateFromJobj)) ? dateFromJobj.GetString() : string.Empty; //查詢日期:起始(string)[例]2023-03-05
  573. string dateToStr = (jsonElement.TryGetProperty("dateTo", out JsonElement dateToJobj)) ? dateToJobj.GetString() : string.Empty; //查詢日期:結束(string)[例]2023-03-27
  574. string dateUnit = (jsonElement.TryGetProperty("dateUnit", out JsonElement dateUnitJobj)) ? dateUnitJobj.GetString().ToLower() : "month";
  575. if(string.IsNullOrWhiteSpace(dateFromStr) || string.IsNullOrWhiteSpace(dateToStr))
  576. {
  577. return BadRequest();
  578. }
  579. Dictionary<string, TmidStics> tmidDic = await GetTmidIotCore(tmids, dateFromStr, dateToStr, dateUnit);
  580. //輸出
  581. List<object> data = new();
  582. foreach (KeyValuePair<string, TmidStics> dicItem in tmidDic)
  583. {
  584. data.Add(dicItem.Value);
  585. }
  586. return Ok(data);
  587. }
  588. catch (Exception ex)
  589. {
  590. //await _dingDing.SendBotMsg($"BI,{_option.Location} /tmid/get-tmidstics \n {ex.Message}\n{ex.StackTrace}", GroupNames.台北開發測試群組);
  591. return BadRequest();
  592. }
  593. }
  594. private async Task<Dictionary<string, TmidStics>> GetTmidIotCore(List<string> tmids, string dateFromStr, string dateToStr, string dateUnit)
  595. {
  596. //起始終止日期換算
  597. long dateTimeFromSec = 0;
  598. long dateTimeToSec = 0;
  599. DateTimeOffset dateTimeFrom;
  600. DateTimeOffset dateTimeTo;
  601. List<string> dateFromList = dateFromStr.Split('-').ToList();
  602. int dateFromYear = Convert.ToInt32(dateFromList[0], 10);
  603. int dateFromMonth = (dateUnit.Equals("day") || dateUnit.Equals("month")) ? Convert.ToInt32(dateFromList[1], 10) : 1;
  604. int dateFromDay = (dateUnit.Equals("day")) ? Convert.ToInt32(dateFromList[2], 10) : 1;
  605. List<string> dateToList = dateToStr.Split('-').ToList();
  606. int dateToYear = Convert.ToInt32(dateToList[0], 10);
  607. int dateToMonth = (dateUnit.Equals("day") || dateUnit.Equals("month")) ? Convert.ToInt32(dateToList[1], 10) : 1;
  608. int dateToDay = (dateUnit.Equals("day")) ? Convert.ToInt32(dateToList[2], 10) : 1;
  609. switch (dateUnit)
  610. {
  611. case "year":
  612. dateTimeFrom = new DateTimeOffset(dateFromYear, 1, 1, 0, 0, 0, TimeSpan.Zero);
  613. dateTimeTo = new DateTimeOffset(dateToYear, 12, 31, 23, 59, 59, TimeSpan.Zero);
  614. break;
  615. case "month":
  616. dateTimeFrom = new DateTimeOffset(dateFromYear, dateFromMonth, 1, 0, 0, 0, TimeSpan.Zero);
  617. dateTimeTo = new DateTimeOffset(dateToYear, dateToMonth, DateTime.DaysInMonth(dateToYear, dateToMonth), 23, 59, 59, TimeSpan.Zero);
  618. break;
  619. case "day":
  620. dateTimeFrom = new DateTimeOffset(dateFromYear, dateFromMonth, dateFromDay, 0, 0, 0, TimeSpan.Zero);
  621. dateTimeTo = new DateTimeOffset(dateToYear, dateToMonth, dateToDay, 23, 59, 59, TimeSpan.Zero);
  622. break;
  623. default: //同"month"
  624. dateTimeFrom = new DateTimeOffset(dateFromYear, dateFromMonth, 1, 0, 0, 0, TimeSpan.Zero);
  625. dateTimeTo = new DateTimeOffset(dateToYear, dateToMonth, DateTime.DaysInMonth(dateToYear, dateToMonth), 23, 59, 59, TimeSpan.Zero);
  626. break;
  627. }
  628. dateTimeFromSec = dateTimeFrom.ToUnixTimeSeconds();
  629. dateTimeToSec = dateTimeTo.ToUnixTimeSeconds();
  630. //資料取得
  631. Dictionary<string, TmidStics> tmidDic = new();
  632. foreach (string id in tmids)
  633. {
  634. if (!tmidDic.ContainsKey(id))
  635. {
  636. TmidStics tmidStics = new TmidStics() { id = id, iot = new() };
  637. tmidDic.Add(id, tmidStics);
  638. }
  639. }
  640. //IOT資料
  641. var cosmosClientIes5 = _azureCosmos.GetCosmosClient(); //CosmosDB IES5
  642. List<TmidAnalysisCal> iotResult = new List<TmidAnalysisCal>();
  643. Dictionary<string, List<TmidAnalysisCal>> iotDic = new Dictionary<string, List<TmidAnalysisCal>>();
  644. if (dateUnit.ToLower().Equals("year"))
  645. {
  646. iotDic["HiTeach-year"] = await getTMIDIotData(cosmosClientIes5, tmids, "HiTeach", "year", 0, 0, 0, dateTimeFromSec, dateTimeToSec);
  647. iotDic["HiTeachCC-year"] = await getTMIDIotData(cosmosClientIes5, tmids, "HiTeachCC", "year", 0, 0, 0, dateTimeFromSec, dateTimeToSec);
  648. }
  649. else if (dateUnit.ToLower().Equals("month"))
  650. {
  651. iotDic["HiTeach-month"] = await getTMIDIotData(cosmosClientIes5, tmids, "HiTeach", "month", 0, 0, 0, dateTimeFromSec, dateTimeToSec);
  652. iotDic["HiTeachCC-month"] = await getTMIDIotData(cosmosClientIes5, tmids, "HiTeachCC", "month", 0, 0, 0, dateTimeFromSec, dateTimeToSec);
  653. }
  654. else if (dateUnit.ToLower().Equals("day"))
  655. {
  656. iotDic["HiTeach-day"] = await getTMIDIotData(cosmosClientIes5, tmids, "HiTeach", "day", 0, 0, 0, dateTimeFromSec, dateTimeToSec);
  657. iotDic["HiTeachCC-day"] = await getTMIDIotData(cosmosClientIes5, tmids, "HiTeachCC", "day", 0, 0, 0, dateTimeFromSec, dateTimeToSec);
  658. }
  659. //IOT資料整理
  660. List<string> iotDicKeyList = new List<string>() { "HiTeach-year", "HiTeachCC-year", "HiTeach-month", "HiTeachCC-month", "HiTeach-day", "HiTeachCC-day" };
  661. foreach (string iotDicKey in iotDicKeyList)
  662. {
  663. if (iotDic.ContainsKey(iotDicKey))
  664. {
  665. foreach (TmidAnalysisCal iotCalData in iotDic[iotDicKey])
  666. {
  667. string id = iotCalData.tmid;
  668. TmidStics tmidStics = tmidDic[id];
  669. switch (iotDicKey)
  670. {
  671. case "HiTeach-year":
  672. tmidStics.iot.hiteach.year = iotCalData;
  673. break;
  674. case "HiTeachCC-year":
  675. tmidStics.iot.hiteachcc.year = iotCalData;
  676. break;
  677. case "HiTeach-month":
  678. tmidStics.iot.hiteach.month = iotCalData;
  679. break;
  680. case "HiTeachCC-month":
  681. tmidStics.iot.hiteachcc.month = iotCalData;
  682. break;
  683. case "HiTeach-day":
  684. tmidStics.iot.hiteach.day = iotCalData;
  685. break;
  686. case "HiTeachCC-day":
  687. tmidStics.iot.hiteachcc.day = iotCalData;
  688. break;
  689. }
  690. }
  691. }
  692. }
  693. return tmidDic;
  694. }
  695. /// <summary>
  696. /// 取得TMID使用產品資料
  697. /// </summary>
  698. /// <param name="jsonElement"></param>
  699. /// <returns></returns>
  700. [ProducesDefaultResponseType]
  701. [Authorize(Roles = "IES")]
  702. [HttpPost("get-tmid-useprod")]
  703. public async Task<IActionResult> GetTmidUseProd(JsonElement jsonElement)
  704. {
  705. string dateFromStr = (jsonElement.TryGetProperty("dateFrom", out JsonElement dateFromJobj)) ? dateFromJobj.GetString() : string.Empty; //查詢日期:起始(string)[例]2023-03-05
  706. string dateToStr = (jsonElement.TryGetProperty("dateTo", out JsonElement dateToJobj)) ? dateToJobj.GetString() : string.Empty; //查詢日期:結束(string)[例]2023-03-27
  707. List<DateFromTo> dateList = (jsonElement.TryGetProperty("dateList", out JsonElement dateListJobj)) ? dateListJobj.ToObject<List<DateFromTo>>() : new List<DateFromTo>(); //查詢日期起始結束列表
  708. bool dateFromToValid = (!string.IsNullOrWhiteSpace(dateFromStr) && !string.IsNullOrWhiteSpace(dateToStr)) ? true : false;
  709. bool dateListValid = (dateList.Count > 0) ? true : false;
  710. if (dateFromToValid.Equals(false) && dateListValid.Equals(false)) return BadRequest();
  711. List<string> targets = (jsonElement.TryGetProperty("targets", out JsonElement targetsJobj)) ? targetsJobj.ToObject<List<string>>() : new List<string>() {"tmid", "sch", "geo" }; //資料取得 "tmid":必定會取 "sch":學校資訊 "geo":地理資訊
  712. bool getLoginTime = (jsonElement.TryGetProperty("getLoginTime", out JsonElement getLoginTimeJobj)) ? getLoginTimeJobj.GetBoolean() : false; //是否取得各服務Login時間
  713. if (dateFromToValid)
  714. {
  715. object result = await GetTmidUseprodCore(dateFromStr, dateToStr, targets, getLoginTime);
  716. return Ok(result);
  717. }
  718. else if(dateListValid)
  719. {
  720. List<object> result = new List<object>();
  721. foreach (DateFromTo dateFromTo in dateList)
  722. {
  723. string dateFrom = dateFromTo.dateFrom;
  724. string dateTo = dateFromTo.dateTo;
  725. object tmidUseprod = await GetTmidUseprodCore(dateFrom, dateTo, targets, getLoginTime);
  726. result.Add(tmidUseprod);
  727. }
  728. return Ok(result);
  729. }
  730. else
  731. {
  732. return Ok(new { });
  733. }
  734. }
  735. private async Task<object> GetTmidUseprodCore(string dateFromStr, string dateToStr, List<string> targets, bool getLoginTime)
  736. {
  737. Dictionary<string, TmidStics> tmidDic = new();
  738. HashSet<string> schIds = new HashSet<string>(); //生成的TMID所關聯的學校ID列表
  739. DateTime dateFrom = DateTime.ParseExact(dateFromStr, "yyyy-MM-dd", null);
  740. DateTime dateTo = DateTime.ParseExact(dateToStr, "yyyy-MM-dd", null).Add(new TimeSpan(0, 23, 59, 59));
  741. DateTimeOffset dateTimeFrom = new DateTimeOffset(dateFrom, TimeSpan.Zero);
  742. DateTimeOffset dateTimeTo = new DateTimeOffset(dateTo, TimeSpan.Zero);
  743. long unixTimeFrom = dateTimeFrom.ToUnixTimeSeconds();
  744. long unixTimeTo = dateTimeTo.ToUnixTimeSeconds();
  745. //大陸站 ID計算方式特化處理
  746. if (_option.Location.Contains("China"))
  747. {
  748. unixTimeFrom += 5000000000;
  749. unixTimeTo += 5000000000;
  750. }
  751. //服務Client端
  752. var cosmosClientIes5 = _azureCosmos.GetCosmosClient(); //CosmosDB IES5
  753. var cosmosClientCsv2 = _azureCosmos.GetCosmosClient(name: "CoreServiceV2"); //CosmosDB CSV2
  754. var cosmosClientCsv2CnRead = _azureCosmos.GetCosmosClient(name: "CoreServiceV2CnRead"); //CosmosDB CSV2 CN Read
  755. var storageClientCsv2 = _azureStorage.GetCloudTableClient(name: "CoreServiceV2"); //Storage CSV2
  756. var tableCouponClient = storageClientCsv2.GetTableReference("Coupon");
  757. var tablePointsClient = storageClientCsv2.GetTableReference("Points");
  758. var redisClient = _azureRedis.GetRedisClient(4);
  759. //取得TMID資料
  760. HashSet<string> tmids = new HashSet<string>();
  761. int pageSize = (getLoginTime) ? 100 : 5000;
  762. string? continuationToken = null;
  763. string query = $"SELECT * FROM c WHERE {unixTimeFrom} <= StringToNumber(c.id) AND StringToNumber(c.id) <= {unixTimeTo}";
  764. do
  765. {
  766. await foreach (var item in cosmosClientCsv2
  767. .GetContainer("Core", "ID2")
  768. .GetItemQueryStreamIteratorSql(query, continuationToken: continuationToken, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("base"), MaxItemCount = pageSize }))
  769. {
  770. using var json = await JsonDocument.ParseAsync(item.Content);
  771. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  772. {
  773. foreach (var doc in json.RootElement.GetProperty("Documents").EnumerateArray())
  774. {
  775. string id = doc.GetProperty("id").GetString();
  776. if (!tmidDic.ContainsKey(id)) tmidDic.Add(id, new TmidStics());
  777. tmids.Add(id);
  778. TmidStics tmidStics = tmidDic[id];
  779. //基本資料
  780. tmidStics.name = doc.GetProperty("name").GetString();
  781. //取得登入各服務的時間 ※getLoginTime == true 才取
  782. if (getLoginTime)
  783. {
  784. var loginTime = await redisClient.HashGetAllAsync(id);
  785. foreach (var t in loginTime)
  786. {
  787. if (!t.Name.StartsWith("HiTeach") && !_dicClientIds.ContainsKey(t.Name)) continue;
  788. tmidStics.login.Add(
  789. new TmidLoginTime()
  790. {
  791. product = t.Name.StartsWith("HiTeach") ? t.Name.ToString().Split("-")[0] : _dicClientIds[t.Name],
  792. time = (long)t.Value
  793. });
  794. }
  795. }
  796. }
  797. }
  798. continuationToken = item.ContinuationToken;
  799. }
  800. }
  801. while (!string.IsNullOrWhiteSpace(continuationToken));
  802. //取得TMID進階資料
  803. continuationToken = null;
  804. do
  805. {
  806. QueryDefinition querye = new QueryDefinition(@"SELECT c.id, c.name, c.mobile, c.mail, c.country, c.province, c.city, c.schoolCode, c.schoolCodeW, c.unitType, c.unitName, c.jobTitle FROM c WHERE (ARRAY_CONTAINS(@key, c.id) OR ARRAY_CONTAINS(@key, c.mobile))")
  807. .WithParameter("@key", tmids);
  808. await foreach (var item in cosmosClientCsv2
  809. .GetContainer("Core", "ID2")
  810. .GetItemQueryStreamIteratorQuery(querye, continuationToken: continuationToken, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("base-ex"), MaxItemCount = pageSize }))
  811. {
  812. using var json = await JsonDocument.ParseAsync(item.Content);
  813. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  814. {
  815. foreach (var doc in json.RootElement.GetProperty("Documents").EnumerateArray())
  816. {
  817. string id = doc.GetProperty("id").GetString();
  818. TmidStics tmidStics = tmidDic[id];
  819. tmidStics.schoolCode = (doc.TryGetProperty("schoolCode", out JsonElement schCode)) ? schCode.GetString() : string.Empty;
  820. tmidStics.schoolCodeW = (doc.TryGetProperty("schoolCodeW", out JsonElement schCodeW)) ? schCodeW.GetString() : string.Empty;
  821. if (!string.IsNullOrWhiteSpace(tmidStics.schoolCode)) schIds.Add(tmidStics.schoolCode);
  822. if (!string.IsNullOrWhiteSpace(tmidStics.schoolCodeW)) schIds.Add(tmidStics.schoolCodeW);
  823. if (!string.IsNullOrWhiteSpace(tmidStics.schoolCode) && string.IsNullOrWhiteSpace(tmidStics.schoolId))
  824. {
  825. tmidStics.schoolId = tmidStics.schoolCode;
  826. }
  827. else if (!string.IsNullOrWhiteSpace(tmidStics.schoolCodeW) && string.IsNullOrWhiteSpace(tmidStics.schoolId))
  828. {
  829. tmidStics.schoolId = tmidStics.schoolCodeW;
  830. }
  831. }
  832. }
  833. continuationToken = item.ContinuationToken;
  834. }
  835. }
  836. while (!string.IsNullOrWhiteSpace(continuationToken));
  837. //TMID資料整理
  838. foreach (var item in tmidDic)
  839. {
  840. TmidStics tmidStics = item.Value;
  841. if (!string.IsNullOrWhiteSpace(tmidStics.schoolCode) || !string.IsNullOrWhiteSpace(tmidStics.schoolCodeW))
  842. {
  843. if (!string.IsNullOrWhiteSpace(tmidStics.schoolCode))
  844. {
  845. tmidStics.schoolId = tmidStics.schoolCode;
  846. }
  847. else if (!string.IsNullOrWhiteSpace(tmidStics.schoolCodeW))
  848. {
  849. tmidStics.schoolId = tmidStics.schoolCodeW;
  850. }
  851. }
  852. }
  853. //取得學校資料
  854. Dictionary<string, Dictionary<string, string>> school = new Dictionary<string, Dictionary<string, string>>();
  855. if (targets.Contains("sch"))
  856. {
  857. string querys = $"SELECT * FROM c WHERE ARRAY_CONTAINS({JsonSerializer.Serialize(schIds)}, c.shortCode, true)";
  858. await foreach (var item in cosmosClientCsv2CnRead
  859. .GetContainer("Core", "School")
  860. .GetItemQueryStreamIteratorSql(querys, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("base") }))
  861. {
  862. using var json = await JsonDocument.ParseAsync(item.Content);
  863. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  864. {
  865. foreach (var doc in json.RootElement.GetProperty("Documents").EnumerateArray())
  866. {
  867. Dictionary<string, string> schoolRow = new Dictionary<string, string>();
  868. string shortCode = doc.GetProperty("shortCode").GetString();
  869. string name = doc.GetProperty("name").GetString();
  870. string countryId = doc.GetProperty("countryId").GetString();
  871. string countryName = doc.GetProperty("countryName").GetString();
  872. string provinceId = doc.GetProperty("provinceId").GetString();
  873. string provinceName = doc.GetProperty("provinceName").GetString();
  874. string cityId = doc.GetProperty("cityId").GetString();
  875. string cityName = doc.GetProperty("cityName").GetString();
  876. string distId = doc.GetProperty("distId").GetString();
  877. string distName = doc.GetProperty("distName").GetString();
  878. schoolRow.Add("shortCode", shortCode);
  879. schoolRow.Add("name", name);
  880. schoolRow.Add("countryId", countryId);
  881. schoolRow.Add("countryName", countryName);
  882. schoolRow.Add("provinceId", provinceId);
  883. schoolRow.Add("provinceName", provinceName);
  884. schoolRow.Add("cityId", cityId);
  885. schoolRow.Add("cityName", cityName);
  886. schoolRow.Add("distId", distId);
  887. schoolRow.Add("distName", distName);
  888. school.Add(shortCode, schoolRow);
  889. }
  890. }
  891. }
  892. }
  893. //返回值整理
  894. List<TmidIotProdCnt> tmid = new List<TmidIotProdCnt>();
  895. List<TmidIotProdCntGeo> sch = new List<TmidIotProdCntGeo>(); //取得的IOT學校統計資訊
  896. foreach (var item in tmidDic)
  897. {
  898. //TMID
  899. string id = item.Key;
  900. TmidIotProdCnt tmidRow = new TmidIotProdCnt();
  901. tmidRow.id = id;
  902. tmidRow.name = item.Value.name;
  903. tmidRow.schid = (!string.IsNullOrWhiteSpace(item.Value.schoolId)) ? item.Value.schoolId : null;
  904. foreach (TmidLoginTime login in item.Value.login)
  905. {
  906. switch (login.product)
  907. {
  908. case "HiTeach":
  909. tmidRow.hiteach++;
  910. break;
  911. case "HiTeachCC":
  912. tmidRow.hiteachcc++;
  913. break;
  914. case "HiTA":
  915. tmidRow.hita++;
  916. break;
  917. case "IES5":
  918. tmidRow.ies5++;
  919. break;
  920. case "Account":
  921. tmidRow.account++;
  922. break;
  923. case "Sokrates":
  924. tmidRow.sokrates++;
  925. break;
  926. case "SokAPP":
  927. tmidRow.sokapp++;
  928. break;
  929. case "IRS":
  930. tmidRow.irs++;
  931. break;
  932. }
  933. }
  934. tmid.Add(tmidRow);
  935. //school
  936. if (targets.Contains("sch"))
  937. {
  938. if (!string.IsNullOrWhiteSpace(tmidRow.schid))
  939. {
  940. var schRow = sch.Where(s => s.id.Equals(tmidRow.schid)).FirstOrDefault();
  941. if (schRow == null)
  942. {
  943. TmidIotProdCntGeo tmpData = new TmidIotProdCntGeo();
  944. tmpData.id = tmidRow.schid;
  945. tmpData.schid = tmidRow.schid;
  946. if (school.ContainsKey(tmidRow.schid))
  947. {
  948. tmpData.name = school[tmidRow.schid]["name"];
  949. tmpData.countryId = school[tmidRow.schid]["countryId"];
  950. tmpData.countryName = school[tmidRow.schid]["countryName"];
  951. tmpData.provinceId = school[tmidRow.schid]["provinceId"];
  952. tmpData.provinceName = school[tmidRow.schid]["provinceName"];
  953. tmpData.cityId = school[tmidRow.schid]["cityId"];
  954. tmpData.cityName = school[tmidRow.schid]["cityName"];
  955. tmpData.distId = school[tmidRow.schid]["distId"];
  956. tmpData.distName = school[tmidRow.schid]["distName"];
  957. }
  958. sch.Add(tmpData);
  959. schRow = sch.Where(s => s.id.Equals(tmidRow.schid)).FirstOrDefault();
  960. }
  961. if (tmidRow.hiteach > 0) schRow.hiteach++;
  962. if (tmidRow.hiteachcc > 0) schRow.hiteachcc++;
  963. if (tmidRow.hita > 0) schRow.hita++;
  964. if (tmidRow.ies5 > 0) schRow.ies5++;
  965. if (tmidRow.account > 0) schRow.account++;
  966. if (tmidRow.sokrates > 0) schRow.sokrates++;
  967. if (tmidRow.sokapp > 0) schRow.sokapp++;
  968. if (tmidRow.irs > 0) schRow.irs++;
  969. if (!schRow.tmids.Contains(id)) schRow.tmids.Add(id);
  970. }
  971. }
  972. }
  973. //geo
  974. List<TmidIotProdCntGeo> geo = new List<TmidIotProdCntGeo>();
  975. if (targets.Contains("geo"))
  976. {
  977. foreach (TmidIotProdCntGeo schRow in sch)
  978. {
  979. string countryId = (!string.IsNullOrWhiteSpace(schRow.countryId)) ? schRow.countryId : string.Empty;
  980. string provinceId = (!string.IsNullOrWhiteSpace(schRow.provinceId)) ? schRow.provinceId : string.Empty;
  981. string cityId = (!string.IsNullOrWhiteSpace(schRow.cityId)) ? schRow.cityId : string.Empty;
  982. string distId = (!string.IsNullOrWhiteSpace(schRow.distId)) ? schRow.distId : string.Empty;
  983. string geoid = $"{countryId}-{provinceId}-{cityId}-{distId}";
  984. var geoRow = geo.Where(s => s.id.Equals(geoid)).FirstOrDefault();
  985. if (geoRow == null)
  986. {
  987. geo.Add(new TmidIotProdCntGeo()
  988. {
  989. id = geoid,
  990. name = string.Empty,
  991. countryId = schRow.countryId,
  992. countryName = schRow.countryName,
  993. provinceId = schRow.provinceId,
  994. provinceName = schRow.provinceName,
  995. cityId = schRow.cityId,
  996. cityName = schRow.cityName,
  997. distId = schRow.distId,
  998. distName = schRow.distName,
  999. });
  1000. geoRow = geo.Where(s => s.id.Equals(geoid)).FirstOrDefault();
  1001. }
  1002. geoRow.hiteach += schRow.hiteach;
  1003. geoRow.hiteachcc += schRow.hiteachcc;
  1004. geoRow.hita += schRow.hita;
  1005. geoRow.ies5 += schRow.ies5;
  1006. geoRow.account += schRow.account;
  1007. geoRow.sokrates += schRow.sokrates;
  1008. geoRow.sokapp += schRow.sokapp;
  1009. geoRow.irs += schRow.irs;
  1010. geoRow.tmids = geoRow.tmids.Union(schRow.tmids).ToList();
  1011. }
  1012. }
  1013. //IOT(學校)
  1014. string queryiot = $"SELECT * FROM c WHERE ARRAY_CONTAINS({JsonSerializer.Serialize(schIds)}, c.schoolId, true) AND c.toolType = 'HiTeach' AND c.dateUnit = 'month' AND c.dateTime >= {dateTimeFrom.ToUnixTimeSeconds()} AND c.dateTime <= {dateTimeTo.ToUnixTimeSeconds()}";
  1015. await foreach (ProdAnalysisCosmos item in cosmosClientIes5.GetContainer("TEAMModelOS", "School").GetItemQueryIteratorSql<ProdAnalysisCosmos>(queryText: queryiot, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"ProdAnalysis") }))
  1016. {
  1017. //各校IOT累加
  1018. var schRow = sch.Where(s => s.id.Equals(item.schoolId)).FirstOrDefault();
  1019. if (schRow != null)
  1020. {
  1021. schRow.tLesson += item.tLesson;
  1022. schRow.tGreen += item.tGreen;
  1023. //地理位置IOT累加
  1024. string countryId = (!string.IsNullOrWhiteSpace(schRow.countryId)) ? schRow.countryId : string.Empty;
  1025. string provinceId = (!string.IsNullOrWhiteSpace(schRow.provinceId)) ? schRow.provinceId : string.Empty;
  1026. string cityId = (!string.IsNullOrWhiteSpace(schRow.cityId)) ? schRow.cityId : string.Empty;
  1027. string distId = (!string.IsNullOrWhiteSpace(schRow.distId)) ? schRow.distId : string.Empty;
  1028. string geoid = $"{countryId}-{provinceId}-{cityId}-{distId}";
  1029. var geoRow = geo.Where(s => s.id.Equals(geoid)).FirstOrDefault();
  1030. if (geoRow != null)
  1031. {
  1032. geoRow.tLesson += item.tLesson;
  1033. geoRow.tGreen += item.tGreen;
  1034. }
  1035. }
  1036. }
  1037. //總IOT累加
  1038. Dictionary<string, int> iotResult = new Dictionary<string, int>();
  1039. iotResult.Add("tLesson", 0);
  1040. iotResult.Add("tGreen", 0);
  1041. Dictionary<string, TmidStics> iotDic = await GetTmidIotCore(tmids.ToList(), dateFromStr, dateToStr, "month");
  1042. foreach (KeyValuePair<string, TmidStics> iot in iotDic)
  1043. {
  1044. string tid = iot.Key;
  1045. TmidStics iotCalData = iot.Value;
  1046. if (iotCalData.iot.hiteach.month != null)
  1047. {
  1048. iotResult["tLesson"] += iotCalData.iot.hiteach.month.tLesson;
  1049. iotResult["tGreen"] += iotCalData.iot.hiteach.month.tGreen;
  1050. }
  1051. }
  1052. return new { dateFrom = dateFromStr, dateTo = dateToStr, tmids, tmid, sch, geo, iot = iotResult };
  1053. }
  1054. //取得TMID服務授權週期
  1055. public async Task<List<object>> getTMIDAuthService(CosmosClient cosmosClientCsv2, string tmid, string prodCode, bool validFlg)
  1056. {
  1057. long now = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
  1058. var qryOption = new QueryRequestOptions() { PartitionKey = new PartitionKey("servicePeriod") };
  1059. string whereSql = $"c.saleClient.tmid = '{tmid}'";
  1060. if (!string.IsNullOrWhiteSpace(prodCode)) whereSql += $" AND c.prodCode = '{prodCode}'";
  1061. if (validFlg) whereSql += $" AND c.startDate <= {now} AND {now} <= c.endDate";
  1062. string sql = $"SELECT c.id, c.prodCode, c.type, c.startDate, c.endDate, c.number, c.unit, c.aprule FROM c WHERE {whereSql}";
  1063. var client = cosmosClientCsv2.GetContainer("Habb", "Auth");
  1064. var result = new List<object>();
  1065. await foreach (var item in client.GetItemQueryStreamIteratorSql(queryText: sql, requestOptions: qryOption))
  1066. {
  1067. var json = await JsonDocument.ParseAsync(item.Content);
  1068. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  1069. {
  1070. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  1071. {
  1072. result.Add(
  1073. new
  1074. {
  1075. id = obj.GetProperty("id").GetString(),
  1076. prodCode = obj.GetProperty("prodCode").GetString(),
  1077. type = obj.GetProperty("type").GetInt32(),
  1078. startDate = obj.GetProperty("startDate").GetInt64(),
  1079. endDate = obj.GetProperty("endDate").GetInt64(),
  1080. number = obj.GetProperty("number").GetInt32(),
  1081. aprule = obj.GetProperty("aprule")
  1082. });
  1083. }
  1084. }
  1085. }
  1086. return result;
  1087. }
  1088. //取得TMID購買紀錄
  1089. public async Task<List<Ies5OrderHis>> getTMIDAuthOrder(CosmosClient cosmosClientCsv2, string tmid, string prodCode)
  1090. {
  1091. SortedDictionary<string, Ies5OrderHis> OrderDic = new SortedDictionary<string, Ies5OrderHis>();
  1092. var qryOption = new QueryRequestOptions() { PartitionKey = new PartitionKey("order") };
  1093. //服務 ※序號、硬體 不列入購買紀錄
  1094. string strQueryV = $"SELECT * FROM c WHERE IS_DEFINED(c.saleClient.tmid) AND c.saleClient.tmid = '{tmid}' AND c.prodType = 'service'";
  1095. if (!string.IsNullOrWhiteSpace(prodCode))
  1096. {
  1097. strQueryV += $" AND c.prodCode = '{prodCode}'";
  1098. }
  1099. await foreach (OrderHisService orderService in cosmosClientCsv2.GetContainer("Habb", "Auth").GetItemQueryIteratorSql<OrderHisService>(queryText:strQueryV, continuationToken: null, requestOptions: qryOption))
  1100. {
  1101. Ies5OrderHisService ies5OrderVRow = new Ies5OrderHisService();
  1102. ies5OrderVRow.prodCode = orderService.prodCode;
  1103. ies5OrderVRow.type = (orderService.contract.Equals(1)) ? "C" : (orderService.contract.Equals(2)) ? "G" : "N"; //授權方式 [FROM]contract契約型態 0:新約 1:續約 2:變更 [TO]type N:新約 C:續約 G:變更
  1104. ies5OrderVRow.sdate = orderService.startDate;
  1105. ies5OrderVRow.edate = orderService.endDate;
  1106. ies5OrderVRow.number = orderService.number;
  1107. ies5OrderVRow.unit = orderService.unit;
  1108. //加入Order字典
  1109. string OrderID = orderService.orderinfo.orderid.ToString();
  1110. long OrderDate = orderService.orderinfo.createDate;
  1111. if (OrderDic.ContainsKey(OrderID))
  1112. {
  1113. OrderDic[OrderID].service.Add(ies5OrderVRow);
  1114. }
  1115. else
  1116. {
  1117. Ies5OrderHis ies5OrderHisNew = new Ies5OrderHis();
  1118. ies5OrderHisNew.id = OrderID;
  1119. ies5OrderHisNew.date = OrderDate;
  1120. ies5OrderHisNew.service.Add(ies5OrderVRow);
  1121. OrderDic.Add(OrderID, ies5OrderHisNew);
  1122. }
  1123. }
  1124. //輸出項
  1125. List<Ies5OrderHis> Result = new List<Ies5OrderHis>();
  1126. foreach (KeyValuePair<string, Ies5OrderHis> item in OrderDic)
  1127. {
  1128. Result.Add(item.Value);
  1129. }
  1130. return Result;
  1131. }
  1132. //Tool
  1133. //資料遮罩
  1134. ///規則:
  1135. ///1.手機、電話:隱藏後4碼
  1136. ///2.Mail:隱藏@前資料
  1137. ///3.姓名:只顯示第一個字元
  1138. ///4.身分證、護照:隱藏後4碼
  1139. public string GenDataMask(string data, string type)
  1140. {
  1141. int length = 0;
  1142. string subString = string.Empty;
  1143. if (!string.IsNullOrWhiteSpace(data))
  1144. {
  1145. switch (type)
  1146. {
  1147. case "mobile":
  1148. case "id":
  1149. length = (data.Length > 4) ? 4 : data.Length;
  1150. subString = data.Substring(data.Length - length, length);
  1151. data = data.Replace(subString, "****");
  1152. break;
  1153. case "mail":
  1154. string[] dataList = data.Split("@");
  1155. subString = dataList.First();
  1156. data = data.Replace(subString, "****");
  1157. break;
  1158. case "name":
  1159. length = 2;
  1160. subString = data.Substring(data.Length - length, length);
  1161. data = data.Replace(subString, "〇〇");
  1162. break;
  1163. }
  1164. }
  1165. return data;
  1166. }
  1167. private async Task<List<TmidAnalysisCal>> getTMIDIotData(CosmosClient cosmosClientIes5, List<string> tmids, string toolType, string dateUnit, int year, int month, int day, long from, long to)
  1168. {
  1169. List<TmidAnalysisCal> result = new List<TmidAnalysisCal>();
  1170. List<string> calPropList = new List<string>() { "lessonRecord", "useIES", "useIES5Resource", "useWebIrs", "useDeviceIrs", "useHaboard", "useHita", "lessonLengMin", "lessonLeng0", "stuShow", "stuLessonLengMin", "tGreen", "tLesson", "lTypeCoop", "lTypeIact", "lTypeMis", "lTypeTst", "lTypeDif", "lTypeNone", "lessonCnt928", "lessonCntId", "lessonCntDevice", "lessonCntIdDevice", "mission", "missionFin", "item", "interact", "sendSok", "learnPeer", "learnCoop", "useWordCloud", "useClouDAS", "useGPT", "useIes5Test", "usePaperTest", "useExcelTest", "useScoreBoard", "learnParticipationCnt", "learnParticipationT", "coopMission", "coopWork", "coopContributionT", "peerAct", "peerStuParticipationT" }; //要計算的ProdAnalysis欄位列表
  1171. string tmidsListStr = JsonSerializer.Serialize(tmids);
  1172. string strQuery = $"SELECT * FROM c WHERE ARRAY_CONTAINS({tmidsListStr}, c.tmid, true) AND c.toolType = '{toolType}'";
  1173. var qryOption = new QueryRequestOptions() { PartitionKey = new PartitionKey("TmidAnalysis") };
  1174. if (!string.IsNullOrWhiteSpace(dateUnit)) strQuery += $" AND c.dateUnit = '{dateUnit}'";
  1175. if (year > 0) strQuery += $" AND c.year = {year}";
  1176. if (month > 0) strQuery += $" AND c.month = {month}";
  1177. if (day > 0) strQuery += $" AND c.day = {day}";
  1178. if (from > 0) strQuery += $" AND c.dateTime >= {from}";
  1179. if (to > 0) strQuery += $" AND c.dateTime <= {to}";
  1180. await foreach (TmidAnalysisCosmos tmidAnalysis in cosmosClientIes5.GetContainer(Constant.TEAMModelOS, "Teacher").GetItemQueryIteratorSql<TmidAnalysisCosmos>(queryText:strQuery, continuationToken:null, requestOptions:qryOption))
  1181. {
  1182. //特殊修正
  1183. tmidAnalysis.lTypeNone = ((tmidAnalysis.lTypeNone - tmidAnalysis.lessonLeng0) < 0) ? 0 : tmidAnalysis.lTypeNone - tmidAnalysis.lessonLeng0; //無學習型態數值須扣除未上課
  1184. //一般項
  1185. TmidAnalysisCal tmidAnalysisCalNow = result.Where(t => t.tmid.Equals(tmidAnalysis.tmid)).FirstOrDefault();
  1186. if(tmidAnalysisCalNow == null)
  1187. {
  1188. TmidAnalysisCal tmpTmidAnalysisCal = new TmidAnalysisCal();
  1189. tmpTmidAnalysisCal.tmid = tmidAnalysis.tmid;
  1190. result.Add(tmpTmidAnalysisCal);
  1191. tmidAnalysisCalNow = result.Where(t => t.tmid.Equals(tmidAnalysis.tmid)).FirstOrDefault();
  1192. }
  1193. tmidAnalysisCalNow.dateUnit = tmidAnalysis.dateUnit;
  1194. tmidAnalysisCalNow.toolType = tmidAnalysis.toolType;
  1195. tmidAnalysisCalNow.date = tmidAnalysis.date;
  1196. if (tmidAnalysis.year > 0) tmidAnalysisCalNow.year = tmidAnalysis.year;
  1197. if (tmidAnalysis.month > 0) tmidAnalysisCalNow.month = tmidAnalysis.month;
  1198. if (tmidAnalysis.day > 0) tmidAnalysisCalNow.day = tmidAnalysis.day;
  1199. if (tmidAnalysisCalNow.from.Equals(0) || tmidAnalysis.dateTime <= tmidAnalysisCalNow.from) tmidAnalysisCalNow.from = tmidAnalysis.dateTime;
  1200. if (tmidAnalysisCalNow.to.Equals(0) || tmidAnalysis.dateTime >= tmidAnalysisCalNow.to) tmidAnalysisCalNow.to = tmidAnalysis.dateTime;
  1201. tmidAnalysisCalNow.dateList.Add(tmidAnalysis.date);
  1202. //統計項
  1203. foreach (PropertyInfo propertyInfo in tmidAnalysis.GetType().GetProperties()) //累加項目
  1204. {
  1205. if (calPropList.Contains(propertyInfo.Name))
  1206. {
  1207. var propType = propertyInfo.PropertyType;
  1208. var valNow = propertyInfo.GetValue(tmidAnalysisCalNow);
  1209. var valTodo = tmidAnalysis.GetType().GetProperty(propertyInfo.Name).GetValue(tmidAnalysis);
  1210. if (propType.Equals(typeof(long))) propertyInfo.SetValue(tmidAnalysisCalNow, Convert.ToInt64(valNow.ToString(), 10) + Convert.ToInt64(valTodo.ToString(), 10));
  1211. else propertyInfo.SetValue(tmidAnalysisCalNow, Convert.ToInt32(valNow.ToString(), 10) + Convert.ToInt32(valTodo.ToString(), 10));
  1212. }
  1213. }
  1214. tmidAnalysisCalNow.deviceList = tmidAnalysisCalNow.deviceList.Union(tmidAnalysis.deviceList).ToList();
  1215. tmidAnalysisCalNow.deviceCnt = tmidAnalysisCalNow.deviceList.Count;
  1216. tmidAnalysisCalNow.deviceNoAuthList = tmidAnalysisCalNow.deviceNoAuthList.Union(tmidAnalysis.deviceNoAuthList).ToList();
  1217. tmidAnalysisCalNow.deviceNoAuth = tmidAnalysisCalNow.deviceNoAuthList.Count;
  1218. tmidAnalysisCalNow.deviceAuthList = tmidAnalysisCalNow.deviceAuthList.Union(tmidAnalysis.deviceAuthList).ToList();
  1219. tmidAnalysisCalNow.deviceAuth = tmidAnalysisCalNow.deviceAuthList.Count;
  1220. tmidAnalysisCalNow.tmidList = tmidAnalysisCalNow.tmidList.Union(tmidAnalysis.tmidList).ToList();
  1221. tmidAnalysisCalNow.tmidCnt = tmidAnalysisCalNow.tmidList.Count;
  1222. tmidAnalysisCalNow.verList = tmidAnalysisCalNow.verList.Union(tmidAnalysis.verList).ToList();
  1223. decimal learnParticipationTmp = (tmidAnalysisCalNow.learnParticipationCnt > 0) ? (decimal)tmidAnalysisCalNow.learnParticipationT / (decimal)tmidAnalysisCalNow.learnParticipationCnt : 0;
  1224. tmidAnalysisCalNow.learnParticipation = Math.Round(learnParticipationTmp, 2); //學習參與度指數(平均)
  1225. }
  1226. return result;
  1227. }
  1228. //Model
  1229. //TMID統計 基本資訊
  1230. private class TmidStics
  1231. {
  1232. public string id { get; set; }
  1233. public string name { get; set; }
  1234. public string picture { get; set; }
  1235. public string mobile { get; set; }
  1236. public string mail { get; set; }
  1237. public string country { get; set; }
  1238. public string province { get; set; }
  1239. public string city { get; set; }
  1240. public string dist { get; set; }
  1241. public string schoolCode { get; set; }
  1242. public string schoolCodeW { get; set; }
  1243. public string lang { get; set; }
  1244. public string unitType { get; set; } //1:基礎教育機構(K-小學) 2:中等教育機構(國中、高中/職) 3:高等教育機構 4:政府單位機構 5:NGO機構 6:企業機構 7:其他 8:學前教育 9:特殊教育
  1245. public string unitName { get; set; }
  1246. public string jobTitle { get; set; }
  1247. public TmidSticsIes5 ies5 { get; set; } //IES統計資料
  1248. public TmidPoints points { get; set; }
  1249. public TmidSokrates sokrates { get; set; }
  1250. public TmidBenefits benefits { get; set; }
  1251. public List<TmidCoupon> coupons { get; set; } = new();
  1252. public List<TmidLoginTime> login { get; set; } = new();
  1253. public List<object> prod { get; set; } = new();
  1254. public TmidIot iot { get; set; }
  1255. public bool wechat { get; set; }
  1256. public bool facebook { get; set; }
  1257. public bool google { get; set; }
  1258. public bool ding { get; set; }
  1259. public bool apple { get; set; }
  1260. public bool educloudtw { get; set; }
  1261. public long ts { get; set; } //資料最終變更時間
  1262. public string schoolId { get; set; } //歸屬學校資訊
  1263. }
  1264. //TMID統計 IES5資訊
  1265. private class TmidSticsIes5
  1266. {
  1267. public long usedSize { get; set; } //已使用的存儲空間(Byte)
  1268. public long teachSize { get; set; } //分配給教師的空間(Byte)
  1269. public long totalSize { get; set; } //總空間(Byte)
  1270. public long surplusSize { get; set; } //剩余的空間(Byte)
  1271. public List<TmidSticsIes5School> schools { get; set; } = new(); //各學校資料
  1272. public int resCount { get; set; } //教材數
  1273. public int itemCount { get; set; } //題目數
  1274. public int paperCount { get; set; } //試卷數
  1275. }
  1276. private class TmidSticsIes5School
  1277. {
  1278. public string schoolId { get; set; }
  1279. public string name { get; set; }
  1280. public string region { get; set; }
  1281. public string province { get; set; }
  1282. public string city { get; set; }
  1283. public string dist { get; set; }
  1284. public object size { get; set; } = new();
  1285. public int courseCnt { get; set; }
  1286. public bool defaultSchool { get; set; }
  1287. }
  1288. private class TmidPoints
  1289. {
  1290. public int points { get; set; } = 0; //累積的點數(只加不減)
  1291. public int balance { get; set; } = 0; //可用的點數
  1292. public int level { get; set; } = 0; //等級
  1293. }
  1294. private class TmidCoupon
  1295. {
  1296. public string code { get; set; }
  1297. public long exchange { get; set; }
  1298. }
  1299. private class TmidLoginTime
  1300. {
  1301. public string product { get; set; }
  1302. public long time { get; set; }
  1303. }
  1304. private class TmidSokrates
  1305. {
  1306. public object hiteach_data { get; set; }
  1307. public object user_channels { get; set; }
  1308. }
  1309. public class TmidSokratesHiteach
  1310. {
  1311. public TmidSokratesHiteachItem total { get; set; }
  1312. public TmidSokratesHiteachItem this_year { get; set; }
  1313. public TmidSokratesHiteachItem this_month { get; set; }
  1314. public TmidSokratesHiteachItem this_week { get; set; }
  1315. }
  1316. public class TmidSokratesHiteachItem
  1317. {
  1318. public string start_date { get; set; } = "";
  1319. public string end_date { get; set; } = "";
  1320. public string t_data { get; set; } = "0";
  1321. public string duration { get; set; } = "0";
  1322. public string attendance { get; set; } = "0";
  1323. public string interaction { get; set; } = "0";
  1324. public string t_green { get; set; } = "0";
  1325. public string learning_duration { get; set; } = "0";
  1326. public string all_total { get; set; } = "0";
  1327. public string double_green_light { get; set; } = "0";
  1328. public string public_count { get; set; } = "0";
  1329. public string material_count { get; set; } = "0";
  1330. public string personal_comment { get; set; } = "0";
  1331. public string comment_count { get; set; } = "0";
  1332. public string watchHistory { get; set; } = "0";
  1333. public string sokrates_summary { get; set; } = "0";
  1334. }
  1335. private class TmidBenefits
  1336. {
  1337. public List<object> hiteach { get; set; }
  1338. public List<object> hiteachcc { get; set; }
  1339. }
  1340. private class TmidIot
  1341. {
  1342. public TmidIotYMD hiteach { get; set; } = new();
  1343. public TmidIotYMD hiteachcc { get; set; } = new();
  1344. }
  1345. private class TmidIotYMD
  1346. {
  1347. public TmidAnalysisCal year { get; set; }
  1348. public TmidAnalysisCal month { get; set; }
  1349. public TmidAnalysisCal day { get; set; }
  1350. }
  1351. private class TmidIotSchool
  1352. {
  1353. public string id { get; set; }
  1354. public string name { get; set; }
  1355. public string country { get; set; }
  1356. public string province { get; set; }
  1357. public string city { get; set; }
  1358. public string dist { get; set; }
  1359. }
  1360. private class TmidIotProdCnt
  1361. {
  1362. public string id { get; set; }
  1363. public string name { get; set; }
  1364. public string schid { get; set; }
  1365. public int hiteach { get; set; }
  1366. public int hiteachcc { get; set; }
  1367. public int hita { get; set; }
  1368. public int ies5 { get; set; }
  1369. public int account { get; set; }
  1370. public int sokrates { get; set; }
  1371. public int sokapp { get; set; }
  1372. public int irs { get; set; }
  1373. public int tLesson { get; set; }
  1374. public int tGreen { get; set; }
  1375. }
  1376. private class TmidIotProdCntGeo : TmidIotProdCnt
  1377. {
  1378. public string countryId { get; set; }
  1379. public string countryName { get; set; }
  1380. public string provinceId { get; set; }
  1381. public string provinceName { get; set; }
  1382. public string cityName { get; set; }
  1383. public string cityId { get; set; }
  1384. public string distId { get; set; }
  1385. public string distName { get; set; }
  1386. public List<string> tmids { get; set; } = new();
  1387. }
  1388. //[API輸出] 訂單履歷
  1389. public class Ies5OrderHis
  1390. {
  1391. public Ies5OrderHis()
  1392. {
  1393. service = new List<Ies5OrderHisService>();
  1394. }
  1395. public string id { get; set; }
  1396. public long date { get; set; }
  1397. public List<Ies5OrderHisService> service { get; set; } = new ();
  1398. }
  1399. //[API輸出] 訂單履歷.服務型產品
  1400. public class Ies5OrderHisService
  1401. {
  1402. public string prodCode { get; set; } //產品代碼
  1403. public string type { get; set; } //授權方式 N:新約 C:續約
  1404. public long sdate { get; set; } //授權起始時間
  1405. public long edate { get; set; } //授權終止時間
  1406. public int number { get; set; } //購買數量
  1407. public string unit { get; set; } //購買單位 無單位者為null
  1408. }
  1409. //[承接DB] DB: Habb.Auth
  1410. //[承接DB] 訂單履歷基本Class
  1411. public class OrderHisBase
  1412. {
  1413. public string id { get; set; } //OPID
  1414. public OrderHisOrderInfo orderinfo { get; set; } //訂單資訊
  1415. public string prodCode { get; set; } //產品代碼
  1416. public SerialSaleClient saleClient { get; set; } = null; //銷售終端
  1417. public string prodType { get; set; } //產品類型 serial:序號 service:服務 hard:硬體
  1418. public string dataType { get; set; } //資料類型
  1419. public long operationTime { get; set; } //最新資料變更時間戳記
  1420. public int ttl { get; set; } = -1; //過期刪除秒數
  1421. }
  1422. //[承接DB] 訂單履歷基本Class.銷售終端
  1423. public class SerialSaleClient
  1424. {
  1425. public string name { get; set; } //[String]銷售終端姓名
  1426. public string schoolCode { get; set; } = null; //[String]銷售終端學校代碼
  1427. public string clientId { get; set; } = null; //[String]銷售終端客戶ID
  1428. public string tmid { get; set; } = null; //[String]TMID
  1429. public string type { get; set; } //[String]銷售終端資料類型 school:學校 client:經銷商客戶
  1430. public string countryId { get; set; } //[String]國家代碼
  1431. public string provinceId { get; set; } //[String]省代碼
  1432. public string cityId { get; set; } //[String]市代碼
  1433. public string schoolShortCode { get; set; } //[String]學校簡碼
  1434. public string districtId { get; set; } //[String]區代碼
  1435. public string dataCenter { get; set; } //[String]數據中心
  1436. }
  1437. //[承接DB] 訂單履歷基本Class.訂單資訊
  1438. public class OrderHisOrderInfo
  1439. {
  1440. public string orderid { get; set; } //[String]訂單編號
  1441. public int orderAudit { get; set; } //[Int]訂單審核狀態 0:待審, 1:通過, 2:否決, 3:問題
  1442. public int orderProperty { get; set; } //[Int]訂單類型 0:銷售,1:展示申請 2:內部申請
  1443. public long createDate { get; set; } //訂單創建時間
  1444. }
  1445. //[承接DB] 訂單履歷 服務類
  1446. public class OrderHisService : OrderHisBase
  1447. {
  1448. public int type { get; set; } //[Int]授權類型 0:銷售 1:試用
  1449. public int contract { get; set; } //[Int]契約型態 0:新約 1:續約
  1450. public long startDate { get; set; } //[long]授權起始日期 Timestamp(UTC)
  1451. public long endDate { get; set; } //[long]授權終止日期 Timestamp(UTC)
  1452. public int number { get; set; } //[Int]數量
  1453. public string unit { get; set; } //[String]單位 (無者為空)
  1454. }
  1455. //雲端服務ClientId列表
  1456. private readonly Dictionary<string, string> _dicClientIds = new()
  1457. {
  1458. { "917d02f2-5b91-404e-a71d-7bdd926ddd81", "HiTeach" },
  1459. { "c5328340-b8eb-4489-8650-8c8862d7acfe", "HiTeach" },
  1460. { "118d2d4d-32a3-44c0-808a-792ca73d3706", "HiTeachCC" },
  1461. { "227082f4-8db0-4517-b281-93dba9428bc7", "HiTeachCC" },
  1462. { "371a99aa-7efd-4c3a-90a8-95b7db4f42c0", "HiTA" },
  1463. { "0ed38cde-e7fe-4fa6-9eaa-33ad404eb532", "HiTA" },
  1464. { "531fecd1-b1a5-469a-93ca-7984e1d392f2", "IES5" },
  1465. { "c7317f88-7cea-4e48-ac57-a16071f7b884", "IES5" },
  1466. { "c8de822b-3fcf-4831-adba-cdd14653bf7f", "Account" },
  1467. { "516148eb-6a38-4657-ba98-a3699061937f", "Account" },
  1468. { "d7193896-9b12-4046-ad44-c991dd48cc39", "Sokrates" },
  1469. { "59009e38-6e30-4116-814b-7605939edc47", "Sokrates" },
  1470. { "626c7285-15aa-4abe-8c0d-2c5ff1381538", "SokAPP" },
  1471. { "5bcc16a5-7d20-4cc4-b5c2-8ed0416f32b6", "SokAPP" },
  1472. { "044482b5-d024-4f23-9b55-906884243405", "IRS" },
  1473. { "5e27b7e3-b36c-4ce9-b838-e94fd0cea080", "IRS" }
  1474. };
  1475. //TMID IOT date
  1476. public class tmidIotDate
  1477. {
  1478. public string dateUnit { get; set; } //[string]日期單位:年月日 year, month, day
  1479. public int year { get; set; } //[Int]年
  1480. public int month { get; set; } //[Int]月
  1481. public int day { get; set; } //[Int]日
  1482. public long from { get; set; } //[long]UTC timestamp 起始日
  1483. public long to { get; set; } //[long]UTC timestamp 終止日
  1484. }
  1485. public class TmidAnalysisCal : TmidAnalysisCosmos
  1486. {
  1487. public long from { get; set; } //[long]UTC timestamp 起始日
  1488. public long to { get; set; } //[long]UTC timestamp 終止日
  1489. public List<string> dateList { get; set; } = new();
  1490. }
  1491. private class DateFromTo
  1492. {
  1493. public string dateFrom { get; set; }
  1494. public string dateTo { get; set; }
  1495. }
  1496. }
  1497. }