BIProdAnalysis.cs 48 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667
  1. using Azure.Cosmos;
  2. using DocumentFormat.OpenXml.Office2010.Excel;
  3. using MathNet.Numerics;
  4. using Microsoft.OData.Edm;
  5. using Newtonsoft.Json;
  6. using OpenXmlPowerTools;
  7. using StackExchange.Redis;
  8. using System;
  9. using System.Collections.Generic;
  10. using System.Linq;
  11. using System.Reflection;
  12. using System.Text;
  13. using System.Text.Json;
  14. using System.Text.RegularExpressions;
  15. using System.Threading.Tasks;
  16. using TEAMModelOS.SDK.DI;
  17. using TEAMModelOS.SDK.Extension;
  18. using TEAMModelOS.SDK.Models.Cosmos;
  19. using TEAMModelOS.SDK.Models.Cosmos.BI.BISchool;
  20. namespace TEAMModelOS.SDK.Models.Service.BI
  21. {
  22. public static class BIProdAnalysis
  23. {
  24. /// <summary>
  25. /// 取得某日CS IOT 資料並生成IES5各校產品分析統計資料
  26. /// </summary>
  27. /// <param name="_azureRedis"></param>
  28. /// <param name="_dingDing"></param>
  29. /// <param name="y">年</param>
  30. /// <param name="m">月(2位數)</param>
  31. /// <param name="d">日(2位數)</param>
  32. /// <returns></returns>
  33. public static async Task BICreatDailyAnalData(AzureRedisFactory _azureRedis, CosmosClient _azureCosmosClient, CosmosClient _azureCosmosClientCsv1, DingDing _dingDing, string y, string m, string d)
  34. {
  35. try
  36. {
  37. var redisClinet2 = _azureRedis.GetRedisClient(2);
  38. var redisClinet8 = _azureRedis.GetRedisClient(8);
  39. var datetime = DateTimeOffset.UtcNow;
  40. var ynow = datetime.Year;
  41. List<string> calPropList = new List<string>() { "lessonRecord", "useIES", "useIES5Resource", "useWebIrs", "useDeviceIrs", "useHaboard", "useHita", "lessonLengMin", "stuShow", "stuLessonLengMin", "tGreen", "lTypeCoop", "lTypeIact", "lTypeMis", "lTypeTst", "lTypeDif", "lTypeNone", "lessonCnt928", "lessonCntId", "lessonCntDevice", "lessonCntIdDevice", "mission", "missionFin", "item", "interact", "sendSok" }; //要計算的ProdAnalysis欄位列表
  42. //取得CS Redis TeachingData (IOT紀錄只有三個月分)
  43. List<IotTeachingData> IotTeachingDataList = new List<IotTeachingData>();
  44. if (y.Equals(ynow.ToString()))
  45. {
  46. bool TeachingDataExist = await redisClinet2.KeyExistsAsync($"TeachingData:{m}{d}");
  47. if (TeachingDataExist)
  48. {
  49. RedisValue[] TeachingData = redisClinet2.ListRange($"TeachingData:{m}{d}");
  50. foreach (RedisValue tdataRow in TeachingData)
  51. {
  52. string[] tdata = tdataRow.ToString().Split(',');
  53. IotTeachingData IotTeachingData = new IotTeachingData();
  54. IotTeachingData.timestamp = Convert.ToInt64(tdata[0]);
  55. IotTeachingData.deviceId = tdata[1];
  56. IotTeachingData.channel = tdata[2];
  57. IotTeachingData.tmid = tdata[3];
  58. IotTeachingData.schoolId = tdata[4];
  59. IotTeachingData.useIES = tdata[5];
  60. IotTeachingData.useIES5Resource = (!string.IsNullOrWhiteSpace(tdata[6])) ? Convert.ToInt32(tdata[6]) : 0;
  61. IotTeachingData.useWebIrs = tdata[7];
  62. IotTeachingData.useDeviceIrs = tdata[8];
  63. IotTeachingData.useHaboard = tdata[9];
  64. IotTeachingData.useHita = tdata[10];
  65. IotTeachingData.lessonLengMin = (!string.IsNullOrWhiteSpace(tdata[11])) ? Convert.ToInt32(tdata[11]) : 0;
  66. IotTeachingData.stuShow = (!string.IsNullOrWhiteSpace(tdata[12])) ? Convert.ToInt32(tdata[12]) : 0;
  67. IotTeachingData.tPoint = (!string.IsNullOrWhiteSpace(tdata[13])) ? Convert.ToInt32(tdata[13]) : 0;
  68. IotTeachingData.lTypeCoop = tdata[14];
  69. IotTeachingData.lTypeIact = tdata[15];
  70. IotTeachingData.lTypeMis = tdata[16];
  71. IotTeachingData.lTypeTst = tdata[17];
  72. IotTeachingData.lTypeDif = tdata[18];
  73. IotTeachingData.authType = tdata[19];
  74. IotTeachingData.mission = (!string.IsNullOrWhiteSpace(tdata[20])) ? Convert.ToInt32(tdata[20]) : 0;
  75. IotTeachingData.missionFin = (!string.IsNullOrWhiteSpace(tdata[21])) ? Convert.ToInt32(tdata[21]) : 0;
  76. IotTeachingData.item = (!string.IsNullOrWhiteSpace(tdata[22])) ? Convert.ToInt32(tdata[22]) : 0;
  77. IotTeachingData.interact = (!string.IsNullOrWhiteSpace(tdata[23])) ? Convert.ToInt32(tdata[23]) : 0;
  78. IotTeachingData.ip = (tdata.Length > 24) ? tdata[24] : "";
  79. IotTeachingData.version = (tdata.Length > 25) ? tdata[25] : "";
  80. IotTeachingData.sendSok = (!string.IsNullOrWhiteSpace(tdata[26])) ? tdata[26] : "";
  81. IotTeachingDataList.Add(IotTeachingData);
  82. }
  83. }
  84. }
  85. //生成各校每日產品分析數據
  86. //1.生成 Redis ProdAnalysis:Month
  87. //2.生成 CosmosDB Month
  88. //3.生成 CosmosDB 系統所有學校數值加總
  89. List<string> crtVirtualSchoolId = new List<string>(); //虛擬學校創建ID列表
  90. List<ProdAnalysis> ProdAnalysisList = new List<ProdAnalysis>();
  91. if (IotTeachingDataList.Count > 0)
  92. {
  93. foreach (IotTeachingData IotTeachingDatRow in IotTeachingDataList)
  94. {
  95. string schoolId = (!string.IsNullOrWhiteSpace(IotTeachingDatRow.schoolId)) ? IotTeachingDatRow.schoolId.ToLower() : "noschoolid"; //noschoolid:無任何學校ID
  96. if(!schoolId.Equals("noschoolid") && !schoolId.Equals("allschool") && !crtVirtualSchoolId.Contains(schoolId))
  97. {
  98. crtVirtualSchoolId.Add(schoolId);
  99. }
  100. string toolType = string.Empty;
  101. if (!string.IsNullOrWhiteSpace(IotTeachingDatRow.deviceId))
  102. {
  103. if (IotTeachingDatRow.deviceId.Contains("HiTeachCC-")) toolType = "HiTeachCC";
  104. else if (IotTeachingDatRow.deviceId.Contains("HiTeach-")) toolType = "HiTeach";
  105. else if (IotTeachingDatRow.deviceId.Contains("HiTA-")) toolType = "HiTA";
  106. }
  107. //學校數據生成
  108. if (!string.IsNullOrWhiteSpace(schoolId) && !string.IsNullOrWhiteSpace(toolType))
  109. {
  110. bool addFlg = false;
  111. ProdAnalysis prodAnalysisRow = ProdAnalysisList.Where(s => s.schoolId.Equals(schoolId) && s.toolType.Equals(toolType)).FirstOrDefault();
  112. //無此校數據=>創建
  113. if (prodAnalysisRow == null)
  114. {
  115. prodAnalysisRow = new ProdAnalysis();
  116. prodAnalysisRow.schoolId = schoolId;
  117. prodAnalysisRow.toolType = toolType;
  118. addFlg = true;
  119. }
  120. //欄位加總
  121. if (!string.IsNullOrWhiteSpace(IotTeachingDatRow.deviceId) && !prodAnalysisRow.deviceList.Contains(IotTeachingDatRow.deviceId))
  122. {
  123. prodAnalysisRow.deviceList.Add(IotTeachingDatRow.deviceId);
  124. }
  125. prodAnalysisRow.deviceCnt = prodAnalysisRow.deviceList.Count;
  126. switch (IotTeachingDatRow.authType)
  127. {
  128. case "0": //928授權
  129. if (!prodAnalysisRow.deviceNoAuthList.Contains(IotTeachingDatRow.deviceId)) prodAnalysisRow.deviceNoAuthList.Add(IotTeachingDatRow.deviceId);
  130. prodAnalysisRow.lessonCnt928++;
  131. break;
  132. case "1": //ID授權
  133. if (!prodAnalysisRow.deviceNoAuthList.Contains(IotTeachingDatRow.deviceId)) prodAnalysisRow.deviceNoAuthList.Add(IotTeachingDatRow.deviceId);
  134. prodAnalysisRow.lessonCntId++;
  135. break;
  136. case "2": //機器授權
  137. if (!prodAnalysisRow.deviceAuthList.Contains(IotTeachingDatRow.deviceId)) prodAnalysisRow.deviceAuthList.Add(IotTeachingDatRow.deviceId);
  138. prodAnalysisRow.lessonCntDevice++;
  139. break;
  140. case "3": //ID+機器授權
  141. if (!prodAnalysisRow.deviceAuthList.Contains(IotTeachingDatRow.deviceId)) prodAnalysisRow.deviceAuthList.Add(IotTeachingDatRow.deviceId);
  142. prodAnalysisRow.lessonCntIdDevice++;
  143. break;
  144. }
  145. prodAnalysisRow.deviceNoAuth = prodAnalysisRow.deviceNoAuthList.Count;
  146. prodAnalysisRow.deviceAuth = prodAnalysisRow.deviceAuthList.Count;
  147. if (!string.IsNullOrWhiteSpace(IotTeachingDatRow.tmid) && !prodAnalysisRow.tmidList.Contains(IotTeachingDatRow.tmid))
  148. {
  149. prodAnalysisRow.tmidList.Add(IotTeachingDatRow.tmid);
  150. }
  151. prodAnalysisRow.tmidCnt = prodAnalysisRow.tmidList.Count;
  152. prodAnalysisRow.lessonRecord++;
  153. if (IotTeachingDatRow.useIES.Equals("1")) prodAnalysisRow.useIES++;
  154. prodAnalysisRow.useIES5Resource += IotTeachingDatRow.useIES5Resource;
  155. if (IotTeachingDatRow.useWebIrs.Equals("1")) prodAnalysisRow.useWebIrs++;
  156. if (IotTeachingDatRow.useDeviceIrs.Equals("1")) prodAnalysisRow.useDeviceIrs++;
  157. if (IotTeachingDatRow.useHaboard.Equals("1")) prodAnalysisRow.useHaboard++;
  158. if (IotTeachingDatRow.useHita.Equals("1")) prodAnalysisRow.useHita++;
  159. prodAnalysisRow.lessonLengMin += IotTeachingDatRow.lessonLengMin;
  160. prodAnalysisRow.stuShow += IotTeachingDatRow.stuShow;
  161. prodAnalysisRow.stuLessonLengMin += IotTeachingDatRow.lessonLengMin * IotTeachingDatRow.stuShow;
  162. if (IotTeachingDatRow.tPoint >= 70) prodAnalysisRow.tGreen++;
  163. if (IotTeachingDatRow.lTypeCoop.Equals("1")) prodAnalysisRow.lTypeCoop++;
  164. if (IotTeachingDatRow.lTypeIact.Equals("1")) prodAnalysisRow.lTypeIact++;
  165. if (IotTeachingDatRow.lTypeMis.Equals("1")) prodAnalysisRow.lTypeMis++;
  166. if (IotTeachingDatRow.lTypeTst.Equals("1")) prodAnalysisRow.lTypeTst++;
  167. if (IotTeachingDatRow.lTypeDif.Equals("1")) prodAnalysisRow.lTypeDif++;
  168. if (IotTeachingDatRow.lTypeCoop.Equals("0") && IotTeachingDatRow.lTypeIact.Equals("0") && IotTeachingDatRow.lTypeMis.Equals("0") && IotTeachingDatRow.lTypeTst.Equals("0") && IotTeachingDatRow.lTypeDif.Equals("0")) prodAnalysisRow.lTypeNone++;
  169. prodAnalysisRow.mission += IotTeachingDatRow.mission;
  170. prodAnalysisRow.missionFin += IotTeachingDatRow.missionFin;
  171. prodAnalysisRow.item += IotTeachingDatRow.item;
  172. prodAnalysisRow.interact += IotTeachingDatRow.interact;
  173. if (IotTeachingDatRow.sendSok.Equals("1")) prodAnalysisRow.sendSok++;
  174. if (addFlg)
  175. {
  176. ProdAnalysisList.Add(prodAnalysisRow);
  177. }
  178. }
  179. }
  180. }
  181. //1.記入IES5 Redis ProdAnalysis:Day
  182. //2.記入IES5 CosmosDB Day
  183. if (ProdAnalysisList.Count > 0)
  184. {
  185. //資料整形
  186. Dictionary<string, Dictionary<string, string>> redisSchFieldDic = new Dictionary<string, Dictionary<string, string>>(); //架構: key => schId => schJsonContent
  187. List<ProdAnalysisCosmos> cosmosSchList = new List<ProdAnalysisCosmos>();
  188. Dictionary <string,ProdAnalysisCosmos> cosmosAllSchSumDayDic = new Dictionary<string, ProdAnalysisCosmos>();
  189. foreach (ProdAnalysis prodAnalysisRow in ProdAnalysisList)
  190. {
  191. //Redis整形
  192. string toolType = prodAnalysisRow.toolType;
  193. string schoolId = prodAnalysisRow.schoolId;
  194. string hkey = $"ProdAnalysis:Day:{toolType}:{y}{m}{d}";
  195. string fieldContent = prodAnalysisRow.ToJsonString();
  196. if (redisSchFieldDic.ContainsKey(hkey)) redisSchFieldDic[hkey].Add(schoolId, fieldContent);
  197. else redisSchFieldDic.Add(hkey, new Dictionary<string, string>() { { schoolId, fieldContent } });
  198. //CosmosDB整形
  199. ProdAnalysisCosmos ProdAnalysisCosmosRow = prodAnalysisRow.ToJsonString().ToObject<ProdAnalysisCosmos>();
  200. ProdAnalysisCosmosRow.date = $"{y}{m}{d}";
  201. ProdAnalysisCosmosRow.year = Convert.ToInt32(y, 10);
  202. ProdAnalysisCosmosRow.month = Convert.ToInt32(m, 10);
  203. ProdAnalysisCosmosRow.day = Convert.ToInt32(d, 10);
  204. DateTimeOffset dateTime = new DateTimeOffset(ProdAnalysisCosmosRow.year, ProdAnalysisCosmosRow.month, ProdAnalysisCosmosRow.day, 0, 0, 0, TimeSpan.Zero);
  205. ProdAnalysisCosmosRow.dateTime = dateTime.ToUnixTimeSeconds();
  206. ProdAnalysisCosmosRow.id = $"{ProdAnalysisCosmosRow.toolType}-{ProdAnalysisCosmosRow.date}-{schoolId}";
  207. ProdAnalysisCosmosRow.dateUnit = "day";
  208. ProdAnalysisCosmosRow.createDate = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
  209. cosmosSchList.Add(ProdAnalysisCosmosRow);
  210. }
  211. //記入Redis
  212. if (redisSchFieldDic.Count > 0)
  213. {
  214. foreach (KeyValuePair<string, Dictionary<string, string>> hkeyItem in redisSchFieldDic)
  215. {
  216. //記入Redis
  217. string hkey = hkeyItem.Key;
  218. List<HashEntry> hvalList = new List<HashEntry>();
  219. Dictionary<string, string> fieldDic = hkeyItem.Value;
  220. foreach (KeyValuePair<string, string> schItem in fieldDic)
  221. {
  222. string schoolId = schItem.Key;
  223. string fieldContent = schItem.Value;
  224. hvalList.Add(new HashEntry($"{schoolId}", fieldContent));
  225. }
  226. await redisClinet8.HashSetAsync(hkey, hvalList.ToArray());
  227. }
  228. }
  229. //記入CosmosDB
  230. if (cosmosSchList.Count > 0)
  231. {
  232. foreach (ProdAnalysisCosmos cosmosSchRow in cosmosSchList)
  233. {
  234. //各校每日CosmosDB記入
  235. await _azureCosmosClient.GetContainer(Constant.TEAMModelOS, "School").UpsertItemAsync<ProdAnalysisCosmos>(cosmosSchRow);
  236. //所有學校數值加總 數值生成 cosmosAllSchSumDayDic -> key:toolType val:cosmosAllSchSumDay
  237. string toolType = cosmosSchRow.toolType;
  238. string schoolId = "allschool";
  239. ProdAnalysisCosmos cosmosAllSchSumDay = new ProdAnalysisCosmos();
  240. if (cosmosAllSchSumDayDic.ContainsKey(toolType)) cosmosAllSchSumDay = cosmosAllSchSumDayDic[toolType];
  241. foreach (PropertyInfo propertyInfo in cosmosAllSchSumDay.GetType().GetProperties()) //累加項目
  242. {
  243. if (calPropList.Contains(propertyInfo.Name))
  244. {
  245. var propType = propertyInfo.PropertyType;
  246. var valNow = propertyInfo.GetValue(cosmosAllSchSumDay);
  247. var valTodo = cosmosSchRow.GetType().GetProperty(propertyInfo.Name).GetValue(cosmosSchRow);
  248. if (propType.Equals(typeof(long))) propertyInfo.SetValue(cosmosAllSchSumDay, Convert.ToInt64(valNow.ToString(), 10) + Convert.ToInt64(valTodo.ToString(), 10));
  249. else propertyInfo.SetValue(cosmosAllSchSumDay, Convert.ToInt32(valNow.ToString(), 10) + Convert.ToInt32(valTodo.ToString(), 10));
  250. }
  251. }
  252. cosmosAllSchSumDay.schoolId = schoolId;
  253. cosmosAllSchSumDay.toolType = cosmosSchRow.toolType;
  254. cosmosAllSchSumDay.date = cosmosSchRow.date;
  255. cosmosAllSchSumDay.id = $"{cosmosAllSchSumDay.toolType}-{cosmosAllSchSumDay.date}-{schoolId}";
  256. cosmosAllSchSumDay.dateUnit = "day";
  257. cosmosAllSchSumDay.year = cosmosSchRow.year;
  258. cosmosAllSchSumDay.month = cosmosSchRow.month;
  259. cosmosAllSchSumDay.day = cosmosSchRow.day;
  260. DateTimeOffset dateTime = new DateTimeOffset(cosmosAllSchSumDay.year, cosmosAllSchSumDay.month, cosmosAllSchSumDay.day, 0, 0, 0, TimeSpan.Zero);
  261. cosmosAllSchSumDay.dateTime = dateTime.ToUnixTimeSeconds();
  262. cosmosAllSchSumDay.deviceList = cosmosAllSchSumDay.deviceList.Union(cosmosSchRow.deviceList).ToList();
  263. cosmosAllSchSumDay.deviceCnt = cosmosAllSchSumDay.deviceList.Count;
  264. cosmosAllSchSumDay.deviceAuthList = cosmosAllSchSumDay.deviceAuthList.Union(cosmosSchRow.deviceAuthList).ToList();
  265. cosmosAllSchSumDay.deviceAuth = cosmosAllSchSumDay.deviceAuthList.Count;
  266. cosmosAllSchSumDay.deviceNoAuthList = cosmosAllSchSumDay.deviceNoAuthList.Union(cosmosSchRow.deviceNoAuthList).ToList();
  267. cosmosAllSchSumDay.deviceNoAuth = cosmosAllSchSumDay.deviceNoAuthList.Count;
  268. cosmosAllSchSumDay.tmidList = cosmosAllSchSumDay.tmidList.Union(cosmosSchRow.tmidList).ToList();
  269. cosmosAllSchSumDay.tmidCnt = cosmosAllSchSumDay.tmidList.Count;
  270. if (cosmosAllSchSumDayDic.ContainsKey(toolType)) cosmosAllSchSumDayDic[toolType] = cosmosAllSchSumDay;
  271. else cosmosAllSchSumDayDic.Add(toolType, cosmosAllSchSumDay);
  272. }
  273. //每日所有學校數據總計CosmosDB記入
  274. foreach (KeyValuePair<string, ProdAnalysisCosmos> schItem in cosmosAllSchSumDayDic)
  275. {
  276. string toolType = schItem.Key;
  277. ProdAnalysisCosmos cosmosAllSchSumDay = schItem.Value;
  278. cosmosAllSchSumDay.createDate = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
  279. await _azureCosmosClient.GetContainer(Constant.TEAMModelOS, "School").UpsertItemAsync<ProdAnalysisCosmos>(cosmosAllSchSumDay);
  280. }
  281. }
  282. }
  283. //取 Redis 該月所有 ProdAnalysis:Day
  284. //1.生成 Redis ProdAnalysis:Month
  285. //2.生成 CosmosDB Month
  286. Dictionary<string, Dictionary<string, ProdAnalysis>> ProdAnalysisListMonth = new Dictionary<string, Dictionary<string, ProdAnalysis>>();
  287. Dictionary<string, ProdAnalysisCosmos> cosmosAllSchSumMonthDic = new Dictionary<string, ProdAnalysisCosmos>();
  288. List<ProdAnalysisCosmos> cosmosSchListMonth = new List<ProdAnalysisCosmos>();
  289. string patternD = $"ProdAnalysis:Day:HiT*:{y}{m}*";
  290. List<string> keysDayList = ScanRedisKeysByPattern(_azureRedis, patternD);
  291. if (keysDayList.Count > 0)
  292. {
  293. foreach (string keyDay in keysDayList)
  294. {
  295. string[] keyItemList = keyDay.Split(':'); //ProdAnalysis:Day:HiTeach:20230326
  296. string toolType = keyItemList[2];
  297. string dateStrD = keyItemList[3];
  298. string dateStrD_y = string.Empty;
  299. string dateStrD_m = string.Empty;
  300. string dateStrD_d = string.Empty;
  301. Regex rgx = new Regex(@"[0-9]{8}");
  302. if (rgx.IsMatch(dateStrD))
  303. {
  304. dateStrD_y = dateStrD.Substring(0, 4);
  305. dateStrD_m = dateStrD.Substring(4, 2);
  306. dateStrD_d = dateStrD.Substring(6, 2);
  307. }
  308. string prodAnalMonthKey = $"ProdAnalysis:Month:{toolType}:{dateStrD_y}{dateStrD_m}";
  309. bool ProdAnalysisDayExist = await redisClinet8.KeyExistsAsync(keyDay);
  310. if (ProdAnalysisDayExist && !string.IsNullOrWhiteSpace(dateStrD_y) && !string.IsNullOrWhiteSpace(dateStrD_m) && !string.IsNullOrWhiteSpace(dateStrD_d))
  311. {
  312. HashEntry[] hsetDay = redisClinet8.HashGetAll(keyDay); //某日 ProdAnalysis:Day所有學校的統計項目
  313. foreach (HashEntry hset in hsetDay) {
  314. string keySchId = hset.Name;
  315. string valSchDataJson = hset.Value;
  316. ProdAnalysis SchDataTodo = valSchDataJson.ToObject<ProdAnalysis>();
  317. //Redis Month 資料生成
  318. if (ProdAnalysisListMonth.ContainsKey(prodAnalMonthKey)) //月Dic已有此key => 分校累加
  319. {
  320. if (ProdAnalysisListMonth[$"{prodAnalMonthKey}"].ContainsKey($"{keySchId}"))
  321. {
  322. ProdAnalysis SchDataNow = ProdAnalysisListMonth[$"{prodAnalMonthKey}"][$"{keySchId}"];
  323. foreach (PropertyInfo propertyInfo in SchDataNow.GetType().GetProperties())
  324. {
  325. if (calPropList.Contains(propertyInfo.Name))
  326. {
  327. var propType = propertyInfo.PropertyType;
  328. var valNow = propertyInfo.GetValue(SchDataNow);
  329. var valTodo = SchDataTodo.GetType().GetProperty(propertyInfo.Name).GetValue(SchDataTodo);
  330. if (propType.Equals(typeof(long))) propertyInfo.SetValue(SchDataNow, Convert.ToInt64(valNow.ToString(), 10) + Convert.ToInt64(valTodo.ToString(), 10));
  331. else propertyInfo.SetValue(SchDataNow, Convert.ToInt32(valNow.ToString(), 10) + Convert.ToInt32(valTodo.ToString(), 10));
  332. }
  333. }
  334. SchDataNow.deviceList = SchDataNow.deviceList.Union(SchDataTodo.deviceList).ToList();
  335. SchDataNow.deviceCnt = SchDataNow.deviceList.Count;
  336. SchDataNow.deviceAuthList = SchDataNow.deviceAuthList.Union(SchDataTodo.deviceAuthList).ToList();
  337. SchDataNow.deviceAuth = SchDataNow.deviceAuthList.Count;
  338. SchDataNow.deviceNoAuthList = SchDataNow.deviceNoAuthList.Union(SchDataTodo.deviceNoAuthList).ToList();
  339. SchDataNow.deviceNoAuth = SchDataNow.deviceNoAuthList.Count;
  340. SchDataNow.tmidList = SchDataNow.tmidList.Union(SchDataTodo.tmidList).ToList();
  341. SchDataNow.tmidCnt = SchDataNow.tmidList.Count;
  342. }
  343. //無此校資料 => 該校資料放入
  344. else
  345. {
  346. ProdAnalysisListMonth[$"{prodAnalMonthKey}"][$"{keySchId}"] = SchDataTodo;
  347. }
  348. }
  349. else //無此月資料 => 所有學校資料放入
  350. {
  351. ProdAnalysisListMonth.Add(prodAnalMonthKey, new Dictionary<string, ProdAnalysis>() { { keySchId, SchDataTodo } });
  352. }
  353. }
  354. }
  355. }
  356. }
  357. if (ProdAnalysisListMonth.Count > 0)
  358. {
  359. foreach (KeyValuePair<string, Dictionary<string, ProdAnalysis>> item in ProdAnalysisListMonth)
  360. {
  361. string monthRedisKey = item.Key;
  362. List<HashEntry> hvalList = new List<HashEntry>();
  363. Dictionary<string, ProdAnalysis> monthRedisSchDIc = item.Value;
  364. foreach (KeyValuePair<string, ProdAnalysis> monthRedisSchItem in monthRedisSchDIc)
  365. {
  366. string monthRedisSchId = monthRedisSchItem.Key;
  367. ProdAnalysis monthRedisSchData = monthRedisSchItem.Value;
  368. //Redis資料製作
  369. hvalList.Add(new HashEntry(monthRedisSchId, monthRedisSchData.ToJsonString()));
  370. //CosmosDB資料製作
  371. ProdAnalysisCosmos cosmosSchRow = monthRedisSchData.ToJsonString().ToObject<ProdAnalysisCosmos>();
  372. cosmosSchRow.date = $"{y}{m}";
  373. cosmosSchRow.year = Convert.ToInt32(y, 10);
  374. cosmosSchRow.month = Convert.ToInt32(m, 10);
  375. DateTimeOffset dateTime = new DateTimeOffset(cosmosSchRow.year, cosmosSchRow.month, 1, 0, 0, 0, TimeSpan.Zero);
  376. cosmosSchRow.dateTime = dateTime.ToUnixTimeSeconds();
  377. cosmosSchRow.id = $"{monthRedisSchData.toolType}-{cosmosSchRow.date}-{monthRedisSchId}";
  378. cosmosSchRow.dateUnit = "month";
  379. cosmosSchRow.createDate = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
  380. cosmosSchListMonth.Add(cosmosSchRow);
  381. }
  382. //記入Redis
  383. await redisClinet8.HashSetAsync($"{monthRedisKey}", hvalList.ToArray());
  384. }
  385. //記入CosmosDB
  386. if (cosmosSchListMonth.Count > 0)
  387. {
  388. foreach (ProdAnalysisCosmos cosmosSchRow in cosmosSchListMonth)
  389. {
  390. await _azureCosmosClient.GetContainer(Constant.TEAMModelOS, "School").UpsertItemAsync<ProdAnalysisCosmos>(cosmosSchRow);
  391. //所有學校數值加總 數值生成 cosmosAllSchSumDayDic -> key:toolType val:cosmosAllSchSumDay
  392. string toolType = cosmosSchRow.toolType;
  393. string schoolId = "allschool";
  394. ProdAnalysisCosmos cosmosAllSchSumMonth = new ProdAnalysisCosmos();
  395. if (cosmosAllSchSumMonthDic.ContainsKey(toolType)) cosmosAllSchSumMonth = cosmosAllSchSumMonthDic[toolType];
  396. foreach (PropertyInfo propertyInfo in cosmosAllSchSumMonth.GetType().GetProperties()) //累加項目
  397. {
  398. if (calPropList.Contains(propertyInfo.Name))
  399. {
  400. var propType = propertyInfo.PropertyType;
  401. var valNow = propertyInfo.GetValue(cosmosAllSchSumMonth);
  402. var valTodo = cosmosSchRow.GetType().GetProperty(propertyInfo.Name).GetValue(cosmosSchRow);
  403. if (propType.Equals(typeof(long))) propertyInfo.SetValue(cosmosAllSchSumMonth, Convert.ToInt64(valNow.ToString(), 10) + Convert.ToInt64(valTodo.ToString(), 10));
  404. else propertyInfo.SetValue(cosmosAllSchSumMonth, Convert.ToInt32(valNow.ToString(), 10) + Convert.ToInt32(valTodo.ToString(), 10));
  405. }
  406. }
  407. cosmosAllSchSumMonth.schoolId = schoolId;
  408. cosmosAllSchSumMonth.toolType = cosmosSchRow.toolType;
  409. cosmosAllSchSumMonth.date = cosmosSchRow.date;
  410. cosmosAllSchSumMonth.id = $"{cosmosAllSchSumMonth.toolType}-{cosmosAllSchSumMonth.date}-{schoolId}";
  411. cosmosAllSchSumMonth.dateUnit = "month";
  412. cosmosAllSchSumMonth.year = cosmosSchRow.year;
  413. cosmosAllSchSumMonth.month = cosmosSchRow.month;
  414. DateTimeOffset dateTime = new DateTimeOffset(cosmosAllSchSumMonth.year, cosmosAllSchSumMonth.month, 1, 0, 0, 0, TimeSpan.Zero);
  415. cosmosAllSchSumMonth.dateTime = dateTime.ToUnixTimeSeconds();
  416. cosmosAllSchSumMonth.deviceList = cosmosAllSchSumMonth.deviceList.Union(cosmosSchRow.deviceList).ToList();
  417. cosmosAllSchSumMonth.deviceCnt = cosmosAllSchSumMonth.deviceList.Count;
  418. cosmosAllSchSumMonth.deviceAuthList = cosmosAllSchSumMonth.deviceAuthList.Union(cosmosSchRow.deviceAuthList).ToList();
  419. cosmosAllSchSumMonth.deviceAuth = cosmosAllSchSumMonth.deviceAuthList.Count;
  420. cosmosAllSchSumMonth.deviceNoAuthList = cosmosAllSchSumMonth.deviceNoAuthList.Union(cosmosSchRow.deviceNoAuthList).ToList();
  421. cosmosAllSchSumMonth.deviceNoAuth = cosmosAllSchSumMonth.deviceNoAuthList.Count;
  422. cosmosAllSchSumMonth.tmidList = cosmosAllSchSumMonth.tmidList.Union(cosmosSchRow.tmidList).ToList();
  423. cosmosAllSchSumMonth.tmidCnt = cosmosAllSchSumMonth.tmidList.Count;
  424. if (cosmosAllSchSumMonthDic.ContainsKey(toolType)) cosmosAllSchSumMonthDic[toolType] = cosmosAllSchSumMonth;
  425. else cosmosAllSchSumMonthDic.Add(toolType, cosmosAllSchSumMonth);
  426. }
  427. //每月所有學校數據總計CosmosDB記入
  428. foreach (KeyValuePair<string, ProdAnalysisCosmos> schItem in cosmosAllSchSumMonthDic)
  429. {
  430. string toolType = schItem.Key;
  431. ProdAnalysisCosmos cosmosAllSchSumMonth = schItem.Value;
  432. cosmosAllSchSumMonth.createDate = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
  433. await _azureCosmosClient.GetContainer(Constant.TEAMModelOS, "School").UpsertItemAsync<ProdAnalysisCosmos>(cosmosAllSchSumMonth);
  434. }
  435. }
  436. }
  437. //取該年所有 CosmosDB 該年所有月份 生成CosmosDB年資料
  438. Dictionary<string, Dictionary<string, ProdAnalysisCosmos>> ProdAnalysisListYear = new Dictionary<string, Dictionary<string, ProdAnalysisCosmos>>();
  439. var query = $"SELECT * FROM c WHERE c.year = {y} AND c.dateUnit = 'month' AND c.schoolId != 'allschool'";
  440. await foreach (var itemcr in _azureCosmosClient.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryStreamIterator(queryText: query, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("ProdAnalysis") }))
  441. {
  442. var json = await JsonDocument.ParseAsync(itemcr.ContentStream);
  443. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  444. {
  445. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  446. {
  447. ProdAnalysisCosmos SchDataTodo = obj.ToObject<ProdAnalysisCosmos>();
  448. string toolType = SchDataTodo.toolType;
  449. string schId = SchDataTodo.schoolId;
  450. //年Dic已有此key => 分校累加
  451. if (ProdAnalysisListYear.ContainsKey(toolType))
  452. {
  453. if (ProdAnalysisListYear[$"{toolType}"].ContainsKey($"{schId}"))
  454. {
  455. ProdAnalysisCosmos SchDataNow = ProdAnalysisListYear[$"{toolType}"][$"{schId}"];
  456. foreach (PropertyInfo propertyInfo in SchDataNow.GetType().GetProperties())
  457. {
  458. if (calPropList.Contains(propertyInfo.Name))
  459. {
  460. var propType = propertyInfo.PropertyType;
  461. var valNow = propertyInfo.GetValue(SchDataNow);
  462. var valTodo = SchDataTodo.GetType().GetProperty(propertyInfo.Name).GetValue(SchDataTodo);
  463. if (propType.Equals(typeof(long))) propertyInfo.SetValue(SchDataNow, Convert.ToInt64(valNow.ToString(), 10) + Convert.ToInt64(valTodo.ToString(), 10));
  464. else propertyInfo.SetValue(SchDataNow, Convert.ToInt32(valNow.ToString(), 10) + Convert.ToInt32(valTodo.ToString(), 10));
  465. }
  466. }
  467. SchDataNow.deviceList = SchDataNow.deviceList.Union(SchDataTodo.deviceList).ToList();
  468. SchDataNow.deviceCnt = SchDataNow.deviceList.Count;
  469. SchDataNow.deviceAuthList = SchDataNow.deviceAuthList.Union(SchDataTodo.deviceAuthList).ToList();
  470. SchDataNow.deviceAuth = SchDataNow.deviceAuthList.Count;
  471. SchDataNow.deviceNoAuthList = SchDataNow.deviceNoAuthList.Union(SchDataTodo.deviceNoAuthList).ToList();
  472. SchDataNow.deviceNoAuth = SchDataNow.deviceNoAuthList.Count;
  473. SchDataNow.tmidList = SchDataNow.tmidList.Union(SchDataTodo.tmidList).ToList();
  474. SchDataNow.tmidCnt = SchDataNow.tmidList.Count;
  475. }
  476. //無此校資料 => 該校資料放入
  477. else
  478. {
  479. ProdAnalysisCosmos SchDataNow = SchDataTodo;
  480. SchDataNow.date = $"{y}";
  481. SchDataNow.year = Convert.ToInt32(y, 10);
  482. SchDataNow.month = 0;
  483. DateTimeOffset dateTime = new DateTimeOffset(SchDataNow.year, 1, 1, 0, 0, 0, TimeSpan.Zero);
  484. SchDataNow.dateTime = dateTime.ToUnixTimeSeconds();
  485. SchDataNow.id = $"{toolType}-{y}-{schId}";
  486. SchDataNow.dateUnit = "year";
  487. ProdAnalysisListYear[$"{toolType}"][$"{schId}"] = SchDataNow;
  488. }
  489. }
  490. //無此年資料 => 所有學校資料放入
  491. else
  492. {
  493. ProdAnalysisCosmos SchDataNow = SchDataTodo;
  494. SchDataNow.date = $"{y}";
  495. SchDataNow.year = Convert.ToInt32(y, 10);
  496. SchDataNow.month = 0;
  497. DateTimeOffset dateTime = new DateTimeOffset(SchDataNow.year, 1, 1, 0, 0, 0, TimeSpan.Zero);
  498. SchDataNow.dateTime = dateTime.ToUnixTimeSeconds();
  499. SchDataNow.id = $"{toolType}-{y}-{schId}";
  500. SchDataNow.dateUnit = "year";
  501. ProdAnalysisListYear.Add(toolType, new Dictionary<string, ProdAnalysisCosmos>() { { schId, SchDataNow } });
  502. }
  503. }
  504. }
  505. }
  506. if (ProdAnalysisListYear.Count > 0)
  507. {
  508. foreach (KeyValuePair<string, Dictionary<string, ProdAnalysisCosmos>> item in ProdAnalysisListYear)
  509. {
  510. string toolType = item.Key;
  511. ProdAnalysisCosmos SchDataNow = new ProdAnalysisCosmos(); //當年某產品所有學校總和
  512. Dictionary<string, ProdAnalysisCosmos> yearCosmosSchDIc = item.Value;
  513. foreach (KeyValuePair<string, ProdAnalysisCosmos> yearCosmosSchItem in yearCosmosSchDIc)
  514. {
  515. string schId = yearCosmosSchItem.Key;
  516. ProdAnalysisCosmos yearCosmosSchData = yearCosmosSchItem.Value;
  517. await _azureCosmosClient.GetContainer(Constant.TEAMModelOS, "School").UpsertItemAsync<ProdAnalysisCosmos>(yearCosmosSchData);
  518. //每年所有學校數據總計CosmosDB記入
  519. foreach (PropertyInfo propertyInfo in SchDataNow.GetType().GetProperties())
  520. {
  521. if (calPropList.Contains(propertyInfo.Name))
  522. {
  523. var propType = propertyInfo.PropertyType;
  524. var valNow = propertyInfo.GetValue(SchDataNow);
  525. var valTodo = yearCosmosSchData.GetType().GetProperty(propertyInfo.Name).GetValue(yearCosmosSchData);
  526. if (propType.Equals(typeof(long))) propertyInfo.SetValue(SchDataNow, Convert.ToInt64(valNow.ToString(), 10) + Convert.ToInt64(valTodo.ToString(), 10));
  527. else propertyInfo.SetValue(SchDataNow, Convert.ToInt32(valNow.ToString(), 10) + Convert.ToInt32(valTodo.ToString(), 10));
  528. }
  529. }
  530. SchDataNow.schoolId = "allschool";
  531. SchDataNow.toolType = yearCosmosSchData.toolType;
  532. SchDataNow.date = yearCosmosSchData.date;
  533. SchDataNow.id = $"{yearCosmosSchData.toolType}-{yearCosmosSchData.date}-allschool";
  534. SchDataNow.dateUnit = "year";
  535. SchDataNow.year = yearCosmosSchData.year;
  536. SchDataNow.month = 0;
  537. DateTimeOffset dateTime = new DateTimeOffset(SchDataNow.year, 1, 1, 0, 0, 0, TimeSpan.Zero);
  538. SchDataNow.dateTime = dateTime.ToUnixTimeSeconds();
  539. SchDataNow.deviceList = SchDataNow.deviceList.Union(yearCosmosSchData.deviceList).ToList();
  540. SchDataNow.deviceCnt = SchDataNow.deviceList.Count;
  541. SchDataNow.deviceAuthList = SchDataNow.deviceAuthList.Union(yearCosmosSchData.deviceAuthList).ToList();
  542. SchDataNow.deviceAuth = SchDataNow.deviceAuthList.Count;
  543. SchDataNow.deviceNoAuthList = SchDataNow.deviceNoAuthList.Union(yearCosmosSchData.deviceNoAuthList).ToList();
  544. SchDataNow.deviceNoAuth = SchDataNow.deviceNoAuthList.Count;
  545. SchDataNow.tmidList = SchDataNow.tmidList.Union(yearCosmosSchData.tmidList).ToList();
  546. SchDataNow.tmidCnt = SchDataNow.tmidList.Count;
  547. }
  548. await _azureCosmosClient.GetContainer(Constant.TEAMModelOS, "School").UpsertItemAsync<ProdAnalysisCosmos>(SchDataNow);
  549. }
  550. }
  551. //虛擬學校創建
  552. if (crtVirtualSchoolId.Count > 0)
  553. {
  554. //取得IES5 school Base
  555. string schIdListStr = JsonConvert.SerializeObject(crtVirtualSchoolId);
  556. string schBaseQueryText = $"SELECT c.id FROM c where ARRAY_CONTAINS({schIdListStr}, c.id, true)";
  557. await foreach (var itemsr in _azureCosmosClient.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryStreamIterator(queryText: schBaseQueryText, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Base") }))
  558. {
  559. using var json = await JsonDocument.ParseAsync(itemsr.ContentStream);
  560. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  561. {
  562. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  563. {
  564. string existSchid = obj.GetProperty("id").GetString();
  565. if (crtVirtualSchoolId.Contains(existSchid))
  566. {
  567. crtVirtualSchoolId.Remove(existSchid);
  568. }
  569. }
  570. }
  571. }
  572. //取得IES5 school VirtualBase
  573. schIdListStr = JsonConvert.SerializeObject(crtVirtualSchoolId);
  574. schBaseQueryText = $"SELECT c.id FROM c where ARRAY_CONTAINS({schIdListStr}, c.id, true)";
  575. await foreach (var itemsr in _azureCosmosClient.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryStreamIterator(queryText: schBaseQueryText, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"VirtualBase") }))
  576. {
  577. using var json = await JsonDocument.ParseAsync(itemsr.ContentStream);
  578. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  579. {
  580. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  581. {
  582. string existSchid = obj.GetProperty("id").GetString();
  583. if (crtVirtualSchoolId.Contains(existSchid))
  584. {
  585. crtVirtualSchoolId.Remove(existSchid);
  586. }
  587. }
  588. }
  589. }
  590. //VirtualBase school 建立
  591. if(crtVirtualSchoolId.Count > 0)
  592. {
  593. //取得CSV1 schoolcode,存在者建立IES5 VirtualBase school
  594. List<School> crtVSchoolList = new List<School>();
  595. string csv1SchIdListStr = JsonConvert.SerializeObject(crtVirtualSchoolId);
  596. string schCsv1QueryText = $"SELECT c.shortCode, c.name, c.countryName, c.provinceName, c.cityName, c.distName, c.address FROM c where ARRAY_CONTAINS({csv1SchIdListStr}, c.shortCode, true)";
  597. await foreach (var item in _azureCosmosClientCsv1.GetContainer("Core", "SchoolCode").GetItemQueryStreamIterator(queryText: schCsv1QueryText, requestOptions: null ))
  598. {
  599. using var json = await JsonDocument.ParseAsync(item.ContentStream);
  600. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  601. {
  602. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  603. {
  604. VirtualBase crtVSchool = new VirtualBase();
  605. crtVSchool.code = "VirtualBase";
  606. crtVSchool.id = obj.GetProperty("shortCode").GetString();
  607. crtVSchool.pk = "School";
  608. crtVSchool.schoolCode = obj.GetProperty("shortCode").GetString();
  609. crtVSchool.name = obj.GetProperty("name").GetString();
  610. crtVSchool.region = (obj.TryGetProperty("countryName", out JsonElement countryNameJ)) ? Convert.ToString(countryNameJ) : null;
  611. crtVSchool.province = (obj.TryGetProperty("provinceName", out JsonElement provinceNameJ)) ? Convert.ToString(provinceNameJ) : null;
  612. crtVSchool.city = (obj.TryGetProperty("cityName", out JsonElement cityNameJ)) ? Convert.ToString(cityNameJ) : null;
  613. crtVSchool.dist = (obj.TryGetProperty("distName", out JsonElement distNameJ)) ? Convert.ToString(distNameJ) : null;
  614. crtVSchool.address = (obj.TryGetProperty("address", out JsonElement addressJ)) ? Convert.ToString(addressJ) : null;
  615. crtVSchool.createTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
  616. await _azureCosmosClient.GetContainer(Constant.TEAMModelOS, "School").CreateItemAsync<VirtualBase>(crtVSchool);
  617. }
  618. }
  619. }
  620. }
  621. }
  622. }
  623. catch (Exception ex)
  624. {
  625. _ = _dingDing.SendBotMsg($"BI,{Environment.GetEnvironmentVariable("Option:Location")},BICreatDailyAnalData() IOT產品分析資料生成錯誤\n{ex.Message}\n{ex.StackTrace}\n", GroupNames.成都开发測試群組);
  626. }
  627. }
  628. /// <summary>
  629. /// 取得Redis(8)符合搜尋模式的key
  630. /// </summary>
  631. /// <param name="pattern"></param>
  632. /// <returns></returns>
  633. public static List<string> ScanRedisKeysByPattern(AzureRedisFactory _azureRedis, string pattern)
  634. {
  635. var redisClinet8 = _azureRedis.GetRedisClient(8);
  636. var keys = new HashSet<RedisKey>();
  637. int nextCursor = 0;
  638. do
  639. {
  640. RedisResult redisResult = redisClinet8.Execute("SCAN", nextCursor.ToString(), "MATCH", pattern, "COUNT", "1000");
  641. var innerResult = (RedisResult[])redisResult;
  642. nextCursor = int.Parse((string)innerResult[0]);
  643. List<RedisKey> resultLines = ((RedisKey[])innerResult[1]).ToList();
  644. keys.UnionWith(resultLines);
  645. }
  646. while (nextCursor != 0);
  647. List<string> result = new List<string>();
  648. if (keys.Count > 0)
  649. {
  650. foreach (RedisKey key in keys)
  651. {
  652. result.Add(key.ToString());
  653. }
  654. }
  655. return result;
  656. }
  657. }
  658. }