Browse Source

產品分析 IOT統計API:資料取得源改為CosmosDB

jeff 2 years ago
parent
commit
b95e6c2559

+ 29 - 179
TEAMModelBI/Controllers/BIProductAnalysis/ProductAnalysisController.cs

@@ -21,6 +21,8 @@ using TEAMModelOS.SDK.DI;
 using TEAMModelOS.SDK.Extension;
 using TEAMModelOS.SDK.Models;
 using TEAMModelOS.SDK.Models.Service.BI;
+using PartitionKey = Azure.Cosmos.PartitionKey;
+using QueryRequestOptions = Azure.Cosmos.QueryRequestOptions;
 
 namespace TEAMModelBI.Controllers.ProductAnalysis
 {
@@ -59,6 +61,7 @@ namespace TEAMModelBI.Controllers.ProductAnalysis
         {
             try
             {
+                var cosmosClient = _azureCosmos.GetCosmosClient();
                 var redisClinet8 = _azureRedis.GetRedisClient(8);
                 if (!jsonElement.TryGetProperty("dateFrom", out JsonElement dateFromJobj)) return BadRequest();//查詢日期:起始(string)[例]2023-03-05
                 if(!jsonElement.TryGetProperty("dateTo", out JsonElement dateToJobj)) return BadRequest();//查詢日期:結束(string)[例]2023-03-27
@@ -67,95 +70,34 @@ namespace TEAMModelBI.Controllers.ProductAnalysis
                 if(string.IsNullOrWhiteSpace(prod)) return BadRequest();
                 if (!jsonElement.TryGetProperty("schoolIds", out JsonElement schoolIdsJobj)) return BadRequest();//學校ID(array)
                 List<string> schoolIds = schoolIdsJobj.ToObject<List<string>>();
-                string dateUnit = (jsonElement.TryGetProperty("dateUnit", out JsonElement dateUnitJobj)) ? (!string.IsNullOrWhiteSpace(Convert.ToString(dateUnitJobj))) ? Convert.ToString(dateUnitJobj) : "Day" : "Day";//時間統計單位 ※以每年(Year)、每月(Month)、每日(Day) 為統計單位 預設值:每日
-                var dateDicDay = CalYmdObjFromToDateDay(dateFromJobj.ToString(), dateToJobj.ToString());
-                var dateDicMonth = CalYmdObjFromToDateMonth(dateFromJobj.ToString(), dateToJobj.ToString());
-                var dateDicYear = CalYmdObjFromToDateYear(dateFromJobj.ToString(), dateToJobj.ToString());
-                //Redis用 學校ID List
-                List<string> schFieldList = new List<string>();
-                foreach(string schId in schoolIds)
-                {
-                    schFieldList.Add(schId);
-                }
-                RedisValue[] schidFieldRedisList = schFieldList.Select(x => (RedisValue)x).ToArray();
-                
-                string key = string.Empty;
-                var result = new List<dynamic>();
-                if (dateUnit.Equals("Day"))
-                {
-                    foreach (KeyValuePair<string, Dictionary<string, List<string>>> yItem in dateDicDay)
-                    {
-                        string y = yItem.Key;
-                        foreach (KeyValuePair<string, List<string>> mItem in yItem.Value)
-                        {
-                            string m = mItem.Key;
-                            foreach (string d in mItem.Value)
-                            {
-                                key = $"ProdAnalysis:{dateUnit}:{prod}:{y}{m}{d}";
-                                if (await redisClinet8.KeyExistsAsync(key))
-                                {
-                                    var hdata = await redisClinet8.HashGetAsync(key, schidFieldRedisList);
-                                    foreach (RedisValue hset in hdata)
-                                    {
-                                        string schDataJson = hset.ToString();
-                                        if (!string.IsNullOrWhiteSpace(schDataJson))
-                                        {
-                                            ProdAnalysisApiResult schData = schDataJson.ToObject<ProdAnalysisApiResult>();
-                                            schData.date = $"{y}{m}{d}";
-                                            result.Add(schData);
-                                        }
-                                    }
-                                }
-                            }
-                        }
-                    }
-                }
-                else if (dateUnit.Equals("Month"))
-                {
-                    foreach (KeyValuePair<string, List<string>> yItem in dateDicMonth)
-                    {
-                        string y = yItem.Key;
-                        foreach (string mItem in yItem.Value)
-                        {
-                            string m = mItem;
-                            key = $"ProdAnalysis:{dateUnit}:{prod}:{y}{m}";
-                            if (await redisClinet8.KeyExistsAsync(key))
-                            {
-                                var hdata = await redisClinet8.HashGetAsync(key, schidFieldRedisList);
-                                foreach (RedisValue hset in hdata)
-                                {
-                                    string schDataJson = hset.ToString();
-                                    if (!string.IsNullOrWhiteSpace(schDataJson))
-                                    {
-                                        ProdAnalysisApiResult schData = schDataJson.ToObject<ProdAnalysisApiResult>();
-                                        schData.date = $"{y}{m}";
-                                        result.Add(schData);
-                                    }
-                                }
-                            }
-                        }
-                    }
-                }
-                else if (dateUnit.Equals("Year"))
+                schoolIds.Add("noschoolid");
+                schoolIds.Add("allschool");
+                string dateUnit = (jsonElement.TryGetProperty("dateUnit", out JsonElement dateUnitJobj)) ? (!string.IsNullOrWhiteSpace(Convert.ToString(dateUnitJobj))) ? Convert.ToString(dateUnitJobj).ToLower() : "day" : "day";//時間統計單位 ※以每年(Year)、每月(Month)、每日(Day) 為統計單位 預設值:每日
+                //起始終止日期換算
+                List<string> dateFromList = dateFromJobj.ToString().Split('-').ToList();
+                int dateFromYear = Convert.ToInt32(dateFromList[0], 10);
+                int dateFromMonth = (dateUnit.Equals("day") || dateUnit.Equals("month")) ? Convert.ToInt32(dateFromList[1], 10) : 1;
+                int dateFromDay = (dateUnit.Equals("day")) ? Convert.ToInt32(dateFromList[2], 10) : 1;
+                List<string> dateToList = dateToJobj.ToString().Split('-').ToList();
+                int dateToYear = Convert.ToInt32(dateToList[0], 10);
+                int dateToMonth = (dateUnit.Equals("day") || dateUnit.Equals("month")) ? Convert.ToInt32(dateToList[1], 10) : 1;
+                int dateToDay = (dateUnit.Equals("day")) ? Convert.ToInt32(dateToList[2], 10) : 1;
+                DateTimeOffset dateTimeFrom = new DateTimeOffset(dateFromYear, dateFromMonth, dateFromDay, 0, 0, 0, TimeSpan.Zero);
+                DateTimeOffset dateTimeTo = new DateTimeOffset(dateToYear, dateToMonth, dateToDay, 23, 59, 59, TimeSpan.Zero);
+                long dateTimeFromSec = dateTimeFrom.ToUnixTimeSeconds();
+                long dateTimeToSec = dateTimeTo.ToUnixTimeSeconds();
+
+                //CosmosDB資料取得
+                List<ProdAnalysisApiResult> result = new List<ProdAnalysisApiResult>();
+                string schIdListStr = JsonSerializer.Serialize(schoolIds);
+                string Sql = $"SELECT * FROM c WHERE c.toolType = '{prod}' AND c.dateUnit = '{dateUnit}' AND ARRAY_CONTAINS({schIdListStr}, c.schoolId, true) AND c.dateTime >= {dateTimeFromSec} AND c.dateTime <= {dateTimeToSec}";
+                await foreach (var item in cosmosClient.GetContainer("TEAMModelOS", "School").GetItemQueryStreamIterator(queryText: Sql, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"ProdAnalysis") }))
                 {
-                    foreach (string yItem in dateDicYear)
+                    var json = await JsonDocument.ParseAsync(item.ContentStream);
+                    foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
                     {
-                        string y = yItem;
-                        key = $"ProdAnalysis:{dateUnit}:{prod}:{y}";
-                        if (await redisClinet8.KeyExistsAsync(key))
-                        {
-                            var hdata = await redisClinet8.HashGetAsync(key, schidFieldRedisList);
-                            foreach (RedisValue hset in hdata)
-                            {
-                                string schDataJson = hset.ToString();
-                                if (!string.IsNullOrWhiteSpace(schDataJson))
-                                {
-                                    ProdAnalysisApiResult schData = schDataJson.ToObject<ProdAnalysisApiResult>();
-                                    schData.date = $"{y}";
-                                    result.Add(schData);
-                                }
-                            }
-                        }
+                        ProdAnalysisApiResult resultRow = obj.ToObject<ProdAnalysisApiResult>();
+                        result.Add(resultRow);
                     }
                 }
 
@@ -167,97 +109,5 @@ namespace TEAMModelBI.Controllers.ProductAnalysis
                 return BadRequest();
             }
         }
-
-        //計算產品分析用Redis的年月日Key
-        private Dictionary<string, Dictionary<string, List<string>>> CalYmdObjFromToDateDay(string dateFrom, string dateTo)
-        {
-            DateTime datetimeFrom = DateTime.Parse(dateFrom);
-            DateTime datetimeTo = DateTime.Parse(dateTo);
-            Dictionary<string, Dictionary<string, List<string>>> dateDic = new Dictionary<string, Dictionary<string, List<string>>>();
-            int res = DateTime.Compare(datetimeFrom, datetimeTo);
-            DateTime datetimeFromPlus = datetimeFrom;
-            do
-            {
-                string y = $"{datetimeFromPlus.Year}";
-                string m = (datetimeFromPlus.Month < 10) ? $"0{datetimeFromPlus.Month}" : $"{datetimeFromPlus.Month}";
-                string d = (datetimeFromPlus.Day < 10) ? $"0{datetimeFromPlus.Day}" : $"{datetimeFromPlus.Day}";
-                if(dateDic.ContainsKey(y))
-                {
-                    if (dateDic[y].ContainsKey(m))
-                    {
-                        if (!dateDic[y][m].Contains(d))
-                        {
-                            dateDic[y][m].Add(d);
-                        }
-                    }
-                    else
-                    {
-                        dateDic[y].Add(m, new List<string>(){ d });
-                    }
-                }
-                else
-                {
-                    var monthDic = new Dictionary<string, List<string>>();
-                    monthDic.Add(m, new List<string>(){d});
-                    dateDic.Add(y, monthDic);
-                }
-                datetimeFromPlus = datetimeFromPlus.AddDays(1);
-                res = DateTime.Compare(datetimeFromPlus, datetimeTo);
-            } while (res <= 0);
-
-            return dateDic;
-
-        }
-        private Dictionary<string, List<string>> CalYmdObjFromToDateMonth(string dateFrom, string dateTo)
-        {
-
-            DateTime datetimeFrom = DateTime.Parse(dateFrom);
-            DateTime datetimeTo = DateTime.Parse(dateTo);
-            Dictionary<string, List<string>> dateDic = new Dictionary<string, List<string>>();
-            int res = DateTime.Compare(datetimeFrom, datetimeTo);
-            DateTime datetimeFromPlus = datetimeFrom;
-            do
-            {
-                string y = $"{datetimeFromPlus.Year}";
-                string m = (datetimeFromPlus.Month < 10) ? $"0{datetimeFromPlus.Month}" : $"{datetimeFromPlus.Month}";
-                if (dateDic.ContainsKey(y))
-                {
-                    if (!dateDic[y].Contains(m))
-                    {
-                        dateDic[y].Add(m);
-                    }
-                }
-                else
-                {
-                    var monthDic = new List<string>() { m };
-                    dateDic.Add(y, monthDic);
-                }
-                datetimeFromPlus = datetimeFromPlus.AddMonths(1);
-                res = DateTime.Compare(datetimeFromPlus, datetimeTo);
-            } while (res <= 0);
-
-            return dateDic;
-        }
-        private List<string> CalYmdObjFromToDateYear(string dateFrom, string dateTo)
-        {
-            DateTime datetimeFrom = DateTime.Parse(dateFrom);
-            DateTime datetimeTo = DateTime.Parse(dateTo);
-            List<string> dateDic = new List<string>();
-            int res = DateTime.Compare(datetimeFrom, datetimeTo);
-            DateTime datetimeFromPlus = datetimeFrom;
-            do
-            {
-                string y = $"{datetimeFromPlus.Year}";
-                if (!dateDic.Contains(y))
-                {
-                    dateDic.Add(y);
-                }
-                datetimeFromPlus = datetimeFromPlus.AddYears(1);
-                res = DateTime.Compare(datetimeFromPlus, datetimeTo);
-            } while (res <= 0);
-
-            return dateDic;
-        }
-
     }
 }

+ 81 - 55
TEAMModelOS.SDK/Models/Cosmos/School/ProductAnalysis.cs

@@ -1,4 +1,4 @@
-using System;
+嚜簑sing System;
 using System.Collections.Generic;
 using System.Text;
 using TEAMModelOS.SDK.Models.Dtos;
@@ -16,79 +16,79 @@ namespace TEAMModelOS.SDK.Models
         public string tmid { get; set; }
         public string schoolId { get; set; }
         /// <summary>
-        /// 開課使用IES課程 0:false 1:true
+        /// 開課使用IES課程 0:false 1:true
         /// </summary>
         public string useIES { get; set; }
         /// <summary>
-        /// 課堂中取用IES5資源數
+        /// 課堂中取用IES5資源數
         /// </summary>
         public int useIES5Resource { get; set; }
         /// <summary>
-        /// 課堂中有使用webirs 0:false 1:true
+        /// 課堂中有使用webirs 0:false 1:true
         /// </summary>
         public string useWebIrs { get; set; }
         /// <summary>
-        /// 課堂中有使用硬體IRS 0:false 1:true
+        /// 課堂中有使用硬體IRS 0:false 1:true
         /// </summary>
         public string useDeviceIrs { get; set; }
         /// <summary>
-        /// 課堂中有使用Haboard 0:false 1:true
+        /// 課堂中有使用Haboard 0:false 1:true
         /// </summary>
         public string useHaboard { get; set; }
         /// <summary>
-        /// 課堂中有使用HiTA 0:false 1:true
+        /// 課堂中有使用HiTA 0:false 1:true
         /// </summary>
         public string useHita { get; set; }
         /// <summary>
-        /// 課堂時間(分鐘整數)
+        /// 課堂時間(分鐘整數)
         /// </summary>
         public int lessonLengMin { get; set; }
         /// <summary>
-        /// 學生出席數(整數)
+        /// 學生出席數(整數)
         /// </summary>
         public int stuShow { get; set; }
         /// <summary>
-        /// T指數(整數)
+        /// T指數(整數)
         /// </summary>
         public int tPoint { get; set; }
         /// <summary>
-        /// 學習型態: 合作 0:false 1:true
+        /// 學習型態: 合作 0:false 1:true
         /// </summary>
         public string lTypeCoop { get; set; }
         /// <summary>
-        /// 學習型態: 互動 0:false 1:true
+        /// 學習型態: 互動 0:false 1:true
         /// </summary>
         public string lTypeIact { get; set; }
         /// <summary>
-        /// 學習型態: 任務 0:false 1:true
+        /// 學習型態: 任務 0:false 1:true
         /// </summary>
         public string lTypeMis { get; set; }
         /// <summary>
-        /// 學習型態: 測驗 0:false 1:true
+        /// 學習型態: 測驗 0:false 1:true
         /// </summary>
         public string lTypeTst { get; set; }
         /// <summary>
-        /// 學習型態: 差異化 0:false 1:true
+        /// 學習型態: 差異化 0:false 1:true
         /// </summary>
         public string lTypeDif { get; set; }
         /// <summary>
-        /// 授權方式 0:無授權   1:id授權  2:機器授權  3:ID和機器授權
+        /// 授權方式 0:無授權   1:id授權  2:機器授權  3:ID和機器授權
         /// </summary>
         public string authType { get; set; }
         /// <summary>
-        /// 任務數
+        /// 任務數
         /// </summary>
         public int mission { get; set; }
         /// <summary>
-        /// 作品任務完成總數
+        /// 作品任務完成總數
         /// </summary>
         public int missionFin { get; set; }
         /// <summary>
-        /// 題數
+        /// 憿峕彍
         /// </summary>
         public int item { get; set; }
         /// <summary>
-        /// 互動總次數
+        /// 互動總次數
         /// </summary>
         public int interact { get; set; }
         /// <summary>
@@ -96,45 +96,44 @@ namespace TEAMModelOS.SDK.Models
         /// </summary>
         public string ip { get; set; }
         /// <summary>
-        /// 版本
+        /// 版本
         /// </summary>
-        /// [例]ex. 5.0.21.0000 -> 500210000
+        /// [靘尜ex. 5.0.21.0000 -> 500210000
         public string version { get; set; }
     }
 
 
     public class ProdAnalysisBase
     {
-        public string schoolId { get; set; } //學校ID
-        public string toolType { get; set; } //HiTeach、HiTeachCC、HiTA
-        public int deviceCnt { get; set; } //機器數(不重複)
-        public int deviceNoAuth { get; set; } //無機器授權數
-        public int deviceAuth { get; set; } //有安裝機器授權數
-        public int lessonRecord { get; set; } //課堂記錄數
-        public int useIES { get; set; } //使用IES課程
-        public int useIES5Resource { get; set; } //課堂中取用IES5資源數
-        public int useWebIrs { get; set; } //課堂中有使用webirs
-        public int useDeviceIrs { get; set; } //課堂中有使用硬體IRS
-        public int useHaboard { get; set; } //課堂中有使用Haboard
-        public int useHita { get; set; } //課堂中有使用HiTA
-        public int lessonLengMin { get; set; } //課堂時間(分鐘整數)
-        public int stuShow { get; set; } //學生出席人次
-        public long stuLessonLengMin { get; set; } //學生參與總時數 ※課堂時間x學生出席人次
-        public int tGreen { get; set; } //T綠燈數(T>=70)
-        public int lTypeCoop { get; set; } //學習型態: 合作
-        public int lTypeIact { get; set; } //學習型態: 互動
-        public int lTypeMis { get; set; } //學習型態: 任務
-        public int lTypeTst { get; set; } //學習型態: 測驗
-        public int lTypeDif { get; set; } //學習型態: 差異化
-        public int lessonCnt928 { get; set; } //使用928授權課堂數
-        public int lessonCntId { get; set; } //僅使用ID授權課堂數
-        public int lessonCntDevice { get; set; } //僅使用機器授權課堂數
-        public int lessonCntIdDevice { get; set; } //使用ID+機器授權課堂數
-        public int mission { get; set; } //任務數
-        public int missionFin { get; set; } //作品任務完成總數
-        public int item { get; set; } //題數
-        public int interact { get; set; } //互動總次數
-        public int time { get; set; } //統計時間
+        public string schoolId { get; set; } //摮豢嵗ID
+        public string toolType { get; set; } //HiTeach、HiTeachCC、HiTA
+        public int deviceCnt { get; set; } //機器數(不重複)
+        public int deviceNoAuth { get; set; } //無機器授權數
+        public int deviceAuth { get; set; } //有安裝機器授權數
+        public int lessonRecord { get; set; } //課堂記錄數
+        public int useIES { get; set; } //使用IES課程
+        public int useIES5Resource { get; set; } //課堂中取用IES5資源數
+        public int useWebIrs { get; set; } //課堂中有使用webirs
+        public int useDeviceIrs { get; set; } //課堂中有使用硬體IRS
+        public int useHaboard { get; set; } //課堂中有使用Haboard
+        public int useHita { get; set; } //課堂中有使用HiTA
+        public int lessonLengMin { get; set; } //課堂時間(分鐘整數)
+        public int stuShow { get; set; } //學生出席人次
+        public long stuLessonLengMin { get; set; } //學生參與總時數 ※課堂時間x學生出席人次
+        public int tGreen { get; set; } //T綠燈數(T>=70)
+        public int lTypeCoop { get; set; } //學習型態: 合作
+        public int lTypeIact { get; set; } //學習型態: 互動
+        public int lTypeMis { get; set; } //學習型態: 任務
+        public int lTypeTst { get; set; } //學習型態: 測驗
+        public int lTypeDif { get; set; } //學習型態: 差異化
+        public int lessonCnt928 { get; set; } //使用928授權課堂數
+        public int lessonCntId { get; set; } //僅使用ID授權課堂數
+        public int lessonCntDevice { get; set; } //僅使用機器授權課堂數
+        public int lessonCntIdDevice { get; set; } //使用ID+機器授權課堂數
+        public int mission { get; set; } //任務數
+        public int missionFin { get; set; } //作品任務完成總數
+        public int item { get; set; } //憿峕彍
+        public int interact { get; set; } //互動總次數
     }
     /// <summary>
     /// IES5 ProdAnalysis (Redis)
@@ -144,12 +143,39 @@ namespace TEAMModelOS.SDK.Models
         public ProdAnalysis()
         {
             deviceList = new List<string>();
+            tmidList = new List<string>();
         }
-        public List<string> deviceList { get; set; } //機器ID列表
+        public List<string> deviceList { get; set; } //機器ID列表
+        public List<string> tmidList { get; set; } //TMID列表
     }
 
-    public class ProdAnalysisApiResult : ProdAnalysisBase
+    public class ProdAnalysisApiResult : ProdAnalysis
     {
-        public string date { get; set; } //日期 [例] 2022、202203、20220314
+        public string dateUnit { get; set; }
+        public string date { get; set; } //日期 [例] 2022、202203、20220314
+        public int year { get; set; } //统计日期:年
+        public int month { get; set; } //统计日期:月
+        public int day { get; set; } //统计日期:日
+    }
+
+    public class ProdAnalysisCosmos : ProdAnalysis
+    {
+        public ProdAnalysisCosmos()
+        {
+            pk = "ProdAnalysis";
+            code = "ProdAnalysis";
+            deviceList = new List<string>();
+        }
+        public string pk { get; set; }
+        public string code { get; set; }
+        public string id { get; set; }
+        public string dateUnit { get; set; } //統計日期單位 "year":年統計 "month":月統計 "day":日統計
+        public string date { get; set; } //统计日期 ※依据dateUnit变化 [例]"year":2023 "month":202302 "day":20230210
+        public long dateTime { get; set; } //timestamp UTC milisecond 比較時間用
+        public int year { get; set; } //统计日期:年
+        public int month { get; set; } //统计日期:月
+        public int day { get; set; } //统计日期:日
+        public long createDate { get; set; } //統計時間
+        public int? ttl { get; set; } = -1;
     }
 }

+ 248 - 113
TEAMModelOS.SDK/Models/Service/BI/BIProdAnalysis.cs

@@ -8,6 +8,7 @@ using StackExchange.Redis;
 using System;
 using System.Collections.Generic;
 using System.Linq;
+using System.Reflection;
 using System.Text;
 using System.Text.Json;
 using System.Text.RegularExpressions;
@@ -38,7 +39,8 @@ namespace TEAMModelOS.SDK.Models.Service.BI
                 var redisClinet8 = _azureRedis.GetRedisClient(8);
                 var datetime = DateTimeOffset.UtcNow;
                 var ynow = datetime.Year;
-                //取得CS TeachingData (IOT紀錄只有一年分)
+                List<string> calPropList = new List<string>() { "deviceNoAuth", "deviceAuth", "lessonRecord", "useIES", "useIES5Resource", "useWebIrs", "useDeviceIrs", "useHaboard", "useHita", "lessonLengMin", "stuShow", "stuLessonLengMin", "tGreen", "lTypeCoop", "lTypeIact", "lTypeMis", "lTypeTst", "lTypeDif", "lessonCnt928", "lessonCntId", "lessonCntDevice", "lessonCntIdDevice", "mission", "missionFin", "item", "interact" }; //要計算的ProdAnalysis欄位列表
+                //取得CS Redis TeachingData (IOT紀錄只有三個月分)
                 List<IotTeachingData> IotTeachingDataList = new List<IotTeachingData>();
                 if (y.Equals(ynow.ToString()))
                 {
@@ -80,7 +82,10 @@ namespace TEAMModelOS.SDK.Models.Service.BI
                         }
                     }
                 }
-                //統計各校產品分析數據
+                //生成各校每日產品分析數據
+                //1.生成 Redis ProdAnalysis:Month
+                //2.生成 CosmosDB Month
+                //3.生成 CosmosDB 系統所有學校數值加總
                 List<string> crtVirtualSchoolId = new List<string>(); //虛擬學校創建ID列表
                 List<ProdAnalysis> ProdAnalysisList = new List<ProdAnalysis>();
                 if (IotTeachingDataList.Count > 0)
@@ -88,7 +93,7 @@ namespace TEAMModelOS.SDK.Models.Service.BI
                     foreach (IotTeachingData IotTeachingDatRow in IotTeachingDataList)
                     {
                         string schoolId = (!string.IsNullOrWhiteSpace(IotTeachingDatRow.schoolId)) ? IotTeachingDatRow.schoolId.ToLower() : "noschoolid"; //noschoolid:無任何學校ID
-                        if(!schoolId.Equals("noschoolid") && !crtVirtualSchoolId.Contains(schoolId))
+                        if(!schoolId.Equals("noschoolid") && !schoolId.Equals("allschool") && !crtVirtualSchoolId.Contains(schoolId))
                         {
                             crtVirtualSchoolId.Add(schoolId);
                         }
@@ -113,11 +118,11 @@ namespace TEAMModelOS.SDK.Models.Service.BI
                                 addFlg = true;
                             }
                             //欄位加總
-                            if (!prodAnalysisRow.deviceList.Contains(IotTeachingDatRow.deviceId))
+                            if (!string.IsNullOrWhiteSpace(IotTeachingDatRow.deviceId) && !prodAnalysisRow.deviceList.Contains(IotTeachingDatRow.deviceId))
                             {
                                 prodAnalysisRow.deviceList.Add(IotTeachingDatRow.deviceId);
-                                prodAnalysisRow.deviceCnt++;
                             }
+                            prodAnalysisRow.deviceCnt = prodAnalysisRow.deviceList.Count;
                             switch (IotTeachingDatRow.authType)
                             {
                                 case "0": //928授權
@@ -140,6 +145,10 @@ namespace TEAMModelOS.SDK.Models.Service.BI
                                     prodAnalysisRow.lessonCntIdDevice++;
                                     break;
                             }
+                            if (!string.IsNullOrWhiteSpace(IotTeachingDatRow.tmid) && !prodAnalysisRow.tmidList.Contains(IotTeachingDatRow.tmid))
+                            {
+                                prodAnalysisRow.tmidList.Add(IotTeachingDatRow.tmid);
+                            }
                             prodAnalysisRow.lessonRecord++;
                             if (IotTeachingDatRow.useIES.Equals("1")) prodAnalysisRow.useIES++;
                             prodAnalysisRow.useIES5Resource += IotTeachingDatRow.useIES5Resource;
@@ -167,28 +176,46 @@ namespace TEAMModelOS.SDK.Models.Service.BI
                         }
                     }
                 }
-                //記入IES5 ProdAnalysis:Day
+                //1.記入IES5 Redis ProdAnalysis:Day
+                //2.記入IES5 CosmosDB Day
                 if (ProdAnalysisList.Count > 0)
                 {
                     //資料整形
                     Dictionary<string, Dictionary<string, string>> redisSchFieldDic = new Dictionary<string, Dictionary<string, string>>(); //架構: key => schId => schJsonContent
+                    List<ProdAnalysisCosmos> cosmosSchList = new List<ProdAnalysisCosmos>();
+                    Dictionary <string,ProdAnalysisCosmos> cosmosAllSchSumDayDic = new Dictionary<string, ProdAnalysisCosmos>();
                     foreach (ProdAnalysis prodAnalysisRow in ProdAnalysisList)
                     {
+                        //Redis整形
                         string toolType = prodAnalysisRow.toolType;
                         string schoolId = prodAnalysisRow.schoolId;
                         string hkey = $"ProdAnalysis:Day:{toolType}:{y}{m}{d}";
                         string fieldContent = prodAnalysisRow.ToJsonString();
                         if (redisSchFieldDic.ContainsKey(hkey)) redisSchFieldDic[hkey].Add(schoolId, fieldContent);
                         else redisSchFieldDic.Add(hkey, new Dictionary<string, string>() { { schoolId, fieldContent } });
+                        //CosmosDB整形
+                        ProdAnalysisCosmos ProdAnalysisCosmosRow = prodAnalysisRow.ToJsonString().ToObject<ProdAnalysisCosmos>();
+                        ProdAnalysisCosmosRow.date = $"{y}{m}{d}";
+                        ProdAnalysisCosmosRow.year = Convert.ToInt32(y, 10);
+                        ProdAnalysisCosmosRow.month = Convert.ToInt32(m, 10);
+                        ProdAnalysisCosmosRow.day = Convert.ToInt32(d, 10);
+                        DateTimeOffset dateTime = new DateTimeOffset(ProdAnalysisCosmosRow.year, ProdAnalysisCosmosRow.month, ProdAnalysisCosmosRow.day, 0, 0, 0, TimeSpan.Zero);
+                        ProdAnalysisCosmosRow.dateTime = dateTime.ToUnixTimeSeconds();
+                        ProdAnalysisCosmosRow.id = $"{ProdAnalysisCosmosRow.toolType}-{ProdAnalysisCosmosRow.date}-{schoolId}";
+                        ProdAnalysisCosmosRow.dateUnit = "day";
+                        ProdAnalysisCosmosRow.createDate = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
+                        cosmosSchList.Add(ProdAnalysisCosmosRow);
                     }
                     //記入Redis
-                    if(redisSchFieldDic.Count > 0) {
+                    if (redisSchFieldDic.Count > 0)
+                    {
                         foreach (KeyValuePair<string, Dictionary<string, string>> hkeyItem in redisSchFieldDic)
                         {
+                            //記入Redis
                             string hkey = hkeyItem.Key;
                             List<HashEntry> hvalList = new List<HashEntry>();
                             Dictionary<string, string> fieldDic = hkeyItem.Value;
-                            foreach(KeyValuePair<string, string> schItem in fieldDic)
+                            foreach (KeyValuePair<string, string> schItem in fieldDic)
                             {
                                 string schoolId = schItem.Key;
                                 string fieldContent = schItem.Value;
@@ -197,9 +224,62 @@ namespace TEAMModelOS.SDK.Models.Service.BI
                             await redisClinet8.HashSetAsync(hkey, hvalList.ToArray());
                         }
                     }
+                    //記入CosmosDB
+                    if (cosmosSchList.Count > 0)
+                    {
+                        foreach (ProdAnalysisCosmos cosmosSchRow in cosmosSchList)
+                        {
+                            //各校每日CosmosDB記入
+                            await _azureCosmosClient.GetContainer(Constant.TEAMModelOS, "School").UpsertItemAsync<ProdAnalysisCosmos>(cosmosSchRow);
+                            //所有學校數值加總 數值生成 cosmosAllSchSumDayDic -> key:toolType val:cosmosAllSchSumDay
+                            string toolType = cosmosSchRow.toolType;
+                            string schoolId = "allschool";
+                            ProdAnalysisCosmos cosmosAllSchSumDay = new ProdAnalysisCosmos();
+                            if (cosmosAllSchSumDayDic.ContainsKey(toolType)) cosmosAllSchSumDay = cosmosAllSchSumDayDic[toolType];
+                            foreach (PropertyInfo propertyInfo in cosmosAllSchSumDay.GetType().GetProperties()) //累加項目
+                            {
+                                if (calPropList.Contains(propertyInfo.Name))
+                                {
+                                    var propType = propertyInfo.PropertyType;
+                                    var valNow = propertyInfo.GetValue(cosmosAllSchSumDay);
+                                    var valTodo = cosmosSchRow.GetType().GetProperty(propertyInfo.Name).GetValue(cosmosSchRow);
+                                    if (propType.Equals(typeof(long))) propertyInfo.SetValue(cosmosAllSchSumDay, Convert.ToInt64(valNow.ToString(), 10) + Convert.ToInt64(valTodo.ToString(), 10));
+                                    else propertyInfo.SetValue(cosmosAllSchSumDay, Convert.ToInt32(valNow.ToString(), 10) + Convert.ToInt32(valTodo.ToString(), 10));
+                                }
+                            }
+                            cosmosAllSchSumDay.schoolId = schoolId;
+                            cosmosAllSchSumDay.toolType = cosmosSchRow.toolType;
+                            cosmosAllSchSumDay.date = cosmosSchRow.date;
+                            cosmosAllSchSumDay.id = $"{cosmosAllSchSumDay.toolType}-{cosmosAllSchSumDay.date}-{schoolId}";
+                            cosmosAllSchSumDay.dateUnit = "day";
+                            cosmosAllSchSumDay.year = cosmosSchRow.year;
+                            cosmosAllSchSumDay.month = cosmosSchRow.month;
+                            cosmosAllSchSumDay.day = cosmosSchRow.day;
+                            DateTimeOffset dateTime = new DateTimeOffset(cosmosAllSchSumDay.year, cosmosAllSchSumDay.month, cosmosAllSchSumDay.day, 0, 0, 0, TimeSpan.Zero);
+                            cosmosAllSchSumDay.dateTime = dateTime.ToUnixTimeSeconds();
+                            cosmosAllSchSumDay.deviceList = cosmosAllSchSumDay.deviceList.Union(cosmosSchRow.deviceList).ToList();
+                            cosmosAllSchSumDay.deviceCnt = cosmosAllSchSumDay.deviceList.Count;
+                            cosmosAllSchSumDay.tmidList = cosmosAllSchSumDay.tmidList.Union(cosmosSchRow.tmidList).ToList();
+                            if (cosmosAllSchSumDayDic.ContainsKey(toolType)) cosmosAllSchSumDayDic[toolType] = cosmosAllSchSumDay;
+                            else cosmosAllSchSumDayDic.Add(toolType, cosmosAllSchSumDay);
+                        }
+                        //每日所有學校數據總計CosmosDB記入
+                        foreach (KeyValuePair<string, ProdAnalysisCosmos> schItem in cosmosAllSchSumDayDic)
+                        {
+                            string toolType = schItem.Key;
+                            ProdAnalysisCosmos cosmosAllSchSumDay = schItem.Value;
+                            cosmosAllSchSumDay.createDate = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
+                            await _azureCosmosClient.GetContainer(Constant.TEAMModelOS, "School").UpsertItemAsync<ProdAnalysisCosmos>(cosmosAllSchSumDay);
+                        }
+                    }
                 }
-                //取該月所有 ProdAnalysis:Day 生成 ProdAnalysis:Month
+
+                //取 Redis 該月所有 ProdAnalysis:Day
+                //1.生成 Redis ProdAnalysis:Month
+                //2.生成 CosmosDB Month
                 Dictionary<string, Dictionary<string, ProdAnalysis>> ProdAnalysisListMonth = new Dictionary<string, Dictionary<string, ProdAnalysis>>();
+                Dictionary<string, ProdAnalysisCosmos> cosmosAllSchSumMonthDic = new Dictionary<string, ProdAnalysisCosmos>();
+                List<ProdAnalysisCosmos> cosmosSchListMonth = new List<ProdAnalysisCosmos>();
                 string patternD = $"ProdAnalysis:Day:HiT*:{y}{m}*";
                 List<string> keysDayList = ScanRedisKeysByPattern(_azureRedis, patternD);
                 if (keysDayList.Count > 0)
@@ -228,40 +308,26 @@ namespace TEAMModelOS.SDK.Models.Service.BI
                                 string keySchId = hset.Name;
                                 string valSchDataJson = hset.Value;
                                 ProdAnalysis SchDataTodo = valSchDataJson.ToObject<ProdAnalysis>();
-                                //月Dic已有此key => 分校累加
-                                if (ProdAnalysisListMonth.ContainsKey(prodAnalMonthKey))
+                                //Redis Month 資料生成
+                                if (ProdAnalysisListMonth.ContainsKey(prodAnalMonthKey)) //月Dic已有此key => 分校累加
                                 {
                                     if (ProdAnalysisListMonth[$"{prodAnalMonthKey}"].ContainsKey($"{keySchId}"))
                                     {
                                         ProdAnalysis SchDataNow = ProdAnalysisListMonth[$"{prodAnalMonthKey}"][$"{keySchId}"];
-                                        SchDataNow.deviceCnt += SchDataTodo.deviceCnt;
-                                        SchDataNow.deviceNoAuth += SchDataTodo.deviceNoAuth;
-                                        SchDataNow.deviceAuth += SchDataTodo.deviceAuth;
-                                        SchDataNow.lessonRecord += SchDataTodo.lessonRecord;
-                                        SchDataNow.useIES += SchDataTodo.useIES;
-                                        SchDataNow.useIES5Resource += SchDataTodo.useIES5Resource;
-                                        SchDataNow.useWebIrs += SchDataTodo.useWebIrs;
-                                        SchDataNow.useDeviceIrs += SchDataTodo.useDeviceIrs;
-                                        SchDataNow.useHaboard += SchDataTodo.useHaboard;
-                                        SchDataNow.useHita += SchDataTodo.useHita;
-                                        SchDataNow.lessonLengMin += SchDataTodo.lessonLengMin;
-                                        SchDataNow.stuShow += SchDataTodo.stuShow;
-                                        SchDataNow.stuLessonLengMin += SchDataTodo.stuLessonLengMin;
-                                        SchDataNow.tGreen += SchDataTodo.tGreen;
-                                        SchDataNow.lTypeCoop += SchDataTodo.lTypeCoop;
-                                        SchDataNow.lTypeIact += SchDataTodo.lTypeIact;
-                                        SchDataNow.lTypeMis += SchDataTodo.lTypeMis;
-                                        SchDataNow.lTypeTst += SchDataTodo.lTypeTst;
-                                        SchDataNow.lTypeDif += SchDataTodo.lTypeDif;
-                                        SchDataNow.lessonCnt928 += SchDataTodo.lessonCnt928;
-                                        SchDataNow.lessonCntId += SchDataTodo.lessonCntId;
-                                        SchDataNow.lessonCntDevice += SchDataTodo.lessonCntDevice;
-                                        SchDataNow.lessonCntIdDevice += SchDataTodo.lessonCntIdDevice;
-                                        SchDataNow.mission += SchDataTodo.mission;
-                                        SchDataNow.missionFin += SchDataTodo.missionFin;
-                                        SchDataNow.item += SchDataTodo.item;
-                                        SchDataNow.interact += SchDataTodo.interact;
+                                        foreach (PropertyInfo propertyInfo in SchDataNow.GetType().GetProperties())
+                                        {
+                                            if (calPropList.Contains(propertyInfo.Name))
+                                            {
+                                                var propType = propertyInfo.PropertyType;
+                                                var valNow = propertyInfo.GetValue(SchDataNow);
+                                                var valTodo = SchDataTodo.GetType().GetProperty(propertyInfo.Name).GetValue(SchDataTodo);
+                                                if (propType.Equals(typeof(long))) propertyInfo.SetValue(SchDataNow, Convert.ToInt64(valNow.ToString(), 10) + Convert.ToInt64(valTodo.ToString(), 10));
+                                                else propertyInfo.SetValue(SchDataNow, Convert.ToInt32(valNow.ToString(), 10) + Convert.ToInt32(valTodo.ToString(), 10));
+                                            }
+                                        }
                                         SchDataNow.deviceList = SchDataNow.deviceList.Union(SchDataTodo.deviceList).ToList();
+                                        SchDataNow.deviceCnt = SchDataNow.deviceList.Count;
+                                        SchDataNow.tmidList = SchDataNow.tmidList.Union(SchDataTodo.tmidList).ToList();
                                     }
                                     //無此校資料 => 該校資料放入
                                     else
@@ -269,13 +335,13 @@ namespace TEAMModelOS.SDK.Models.Service.BI
                                         ProdAnalysisListMonth[$"{prodAnalMonthKey}"][$"{keySchId}"] = SchDataTodo;
                                     }
                                 }
-                                //無此月資料 => 所有學校資料放入
-                                else
+                                else //無此月資料 => 所有學校資料放入
                                 {
                                     ProdAnalysisListMonth.Add(prodAnalMonthKey, new Dictionary<string, ProdAnalysis>() { { keySchId, SchDataTodo } });
                                 }
                             }
                         }
+                        
                     }
                 }
                 if (ProdAnalysisListMonth.Count > 0)
@@ -289,104 +355,173 @@ namespace TEAMModelOS.SDK.Models.Service.BI
                         {
                             string monthRedisSchId = monthRedisSchItem.Key;
                             ProdAnalysis monthRedisSchData = monthRedisSchItem.Value;
+                            //Redis資料製作
                             hvalList.Add(new HashEntry(monthRedisSchId, monthRedisSchData.ToJsonString()));
+                            //CosmosDB資料製作
+                            ProdAnalysisCosmos cosmosSchRow = monthRedisSchData.ToJsonString().ToObject<ProdAnalysisCosmos>();
+                            cosmosSchRow.date = $"{y}{m}";
+                            cosmosSchRow.year = Convert.ToInt32(y, 10);
+                            cosmosSchRow.month = Convert.ToInt32(m, 10);
+                            DateTimeOffset dateTime = new DateTimeOffset(cosmosSchRow.year, cosmosSchRow.month, 1, 0, 0, 0, TimeSpan.Zero);
+                            cosmosSchRow.dateTime = dateTime.ToUnixTimeSeconds();
+                            cosmosSchRow.id = $"{monthRedisSchData.toolType}-{cosmosSchRow.date}-{monthRedisSchId}";
+                            cosmosSchRow.dateUnit = "month";
+                            cosmosSchRow.createDate = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
+                            cosmosSchListMonth.Add(cosmosSchRow);
                         }
+                        //記入Redis
                         await redisClinet8.HashSetAsync($"{monthRedisKey}", hvalList.ToArray());
                     }
-                }
-                //取該年所有 ProdAnalysis:Month 生成 ProdAnalysis:Year
-                Dictionary<string, Dictionary<string, ProdAnalysis>> ProdAnalysisListYear = new Dictionary<string, Dictionary<string, ProdAnalysis>>();
-                string patternM = $"ProdAnalysis:Month:HiT*:{y}*";
-                List<string> keysMonthList = ScanRedisKeysByPattern(_azureRedis, patternM);
-                if (keysMonthList.Count > 0)
-                {
-                    foreach (string keyMonth in keysMonthList)
+                    //記入CosmosDB
+                    if (cosmosSchListMonth.Count > 0)
                     {
-                        string[] keyItemList = keyMonth.Split(':'); //ProdAnalysis:Month:HiTeach:202303
-                        string toolType = keyItemList[2];
-                        string dateStrD = keyItemList[3];
-                        string dateStrD_y = string.Empty;
-                        string dateStrD_m = string.Empty;
-                        Regex rgx = new Regex(@"[0-9]{6}");
-                        if (rgx.IsMatch(dateStrD))
+                        foreach (ProdAnalysisCosmos cosmosSchRow in cosmosSchListMonth)
                         {
-                            dateStrD_y = dateStrD.Substring(0, 4);
-                            dateStrD_m = dateStrD.Substring(4, 2);
+                            await _azureCosmosClient.GetContainer(Constant.TEAMModelOS, "School").UpsertItemAsync<ProdAnalysisCosmos>(cosmosSchRow);
+                            //所有學校數值加總 數值生成 cosmosAllSchSumDayDic -> key:toolType val:cosmosAllSchSumDay
+                            string toolType = cosmosSchRow.toolType;
+                            string schoolId = "allschool";
+                            ProdAnalysisCosmos cosmosAllSchSumMonth = new ProdAnalysisCosmos();
+                            if (cosmosAllSchSumMonthDic.ContainsKey(toolType)) cosmosAllSchSumMonth = cosmosAllSchSumMonthDic[toolType];
+                            foreach (PropertyInfo propertyInfo in cosmosAllSchSumMonth.GetType().GetProperties()) //累加項目
+                            {
+                                if (calPropList.Contains(propertyInfo.Name))
+                                {
+                                    var propType = propertyInfo.PropertyType;
+                                    var valNow = propertyInfo.GetValue(cosmosAllSchSumMonth);
+                                    var valTodo = cosmosSchRow.GetType().GetProperty(propertyInfo.Name).GetValue(cosmosSchRow);
+                                    if (propType.Equals(typeof(long))) propertyInfo.SetValue(cosmosAllSchSumMonth, Convert.ToInt64(valNow.ToString(), 10) + Convert.ToInt64(valTodo.ToString(), 10));
+                                    else propertyInfo.SetValue(cosmosAllSchSumMonth, Convert.ToInt32(valNow.ToString(), 10) + Convert.ToInt32(valTodo.ToString(), 10));
+                                }
+                            }
+                            cosmosAllSchSumMonth.schoolId = schoolId;
+                            cosmosAllSchSumMonth.toolType = cosmosSchRow.toolType;
+                            cosmosAllSchSumMonth.date = cosmosSchRow.date;
+                            cosmosAllSchSumMonth.id = $"{cosmosAllSchSumMonth.toolType}-{cosmosAllSchSumMonth.date}-{schoolId}";
+                            cosmosAllSchSumMonth.dateUnit = "month";
+                            cosmosAllSchSumMonth.year = cosmosSchRow.year;
+                            cosmosAllSchSumMonth.month = cosmosSchRow.month;
+                            DateTimeOffset dateTime = new DateTimeOffset(cosmosAllSchSumMonth.year, cosmosAllSchSumMonth.month, 1, 0, 0, 0, TimeSpan.Zero);
+                            cosmosAllSchSumMonth.dateTime = dateTime.ToUnixTimeSeconds();
+                            cosmosAllSchSumMonth.deviceList = cosmosAllSchSumMonth.deviceList.Union(cosmosSchRow.deviceList).ToList();
+                            cosmosAllSchSumMonth.deviceCnt = cosmosAllSchSumMonth.deviceList.Count;
+                            cosmosAllSchSumMonth.tmidList = cosmosAllSchSumMonth.tmidList.Union(cosmosSchRow.tmidList).ToList();
+                            if (cosmosAllSchSumMonthDic.ContainsKey(toolType)) cosmosAllSchSumMonthDic[toolType] = cosmosAllSchSumMonth;
+                            else cosmosAllSchSumMonthDic.Add(toolType, cosmosAllSchSumMonth);
                         }
-                        string prodAnalYearKey = $"ProdAnalysis:Year:{toolType}:{dateStrD_y}";
-                        bool ProdAnalysisMonthExist = await redisClinet8.KeyExistsAsync(keyMonth);
-                        if (ProdAnalysisMonthExist && !string.IsNullOrWhiteSpace(dateStrD_y) && !string.IsNullOrWhiteSpace(dateStrD_m))
+                        //每月所有學校數據總計CosmosDB記入
+                        foreach (KeyValuePair<string, ProdAnalysisCosmos> schItem in cosmosAllSchSumMonthDic)
                         {
-                            HashEntry[] hsetMonth = redisClinet8.HashGetAll(keyMonth); //某月 ProdAnalysis:Month所有學校資料
-                            foreach (HashEntry hset in hsetMonth)
+                            string toolType = schItem.Key;
+                            ProdAnalysisCosmos cosmosAllSchSumMonth = schItem.Value;
+                            cosmosAllSchSumMonth.createDate = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
+                            await _azureCosmosClient.GetContainer(Constant.TEAMModelOS, "School").UpsertItemAsync<ProdAnalysisCosmos>(cosmosAllSchSumMonth);
+                        }
+                    }
+                }
+                //取該年所有 CosmosDB 該年所有月份 生成CosmosDB年資料
+                Dictionary<string, Dictionary<string, ProdAnalysisCosmos>> ProdAnalysisListYear = new Dictionary<string, Dictionary<string, ProdAnalysisCosmos>>();
+                var query = $"SELECT * FROM c WHERE c.year = {y} AND c.dateUnit = 'month' AND c.schoolId != 'allschool'";
+                await foreach (var itemcr in _azureCosmosClient.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryStreamIterator(queryText: query, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("ProdAnalysis") }))
+                {
+                    var json = await JsonDocument.ParseAsync(itemcr.ContentStream);
+                    if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
+                    {
+                        foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
+                        {
+                            ProdAnalysisCosmos SchDataTodo = obj.ToObject<ProdAnalysisCosmos>();
+                            string toolType = SchDataTodo.toolType;
+                            string schId = SchDataTodo.schoolId;
+                            //年Dic已有此key => 分校累加
+                            if (ProdAnalysisListYear.ContainsKey(toolType))
                             {
-                                string keySchId = hset.Name;
-                                string valSchDataJson = hset.Value;
-                                ProdAnalysis SchDataTodo = valSchDataJson.ToObject<ProdAnalysis>();
-                                //年Dic已有此key => 分校累加
-                                if (ProdAnalysisListYear.ContainsKey(prodAnalYearKey))
+                                if (ProdAnalysisListYear[$"{toolType}"].ContainsKey($"{schId}"))
                                 {
-                                    if (ProdAnalysisListYear[$"{prodAnalYearKey}"].ContainsKey($"{keySchId}"))
+                                    ProdAnalysisCosmos SchDataNow = ProdAnalysisListYear[$"{toolType}"][$"{schId}"];
+                                    foreach (PropertyInfo propertyInfo in SchDataNow.GetType().GetProperties())
                                     {
-                                        ProdAnalysis SchDataNow = ProdAnalysisListYear[$"{prodAnalYearKey}"][$"{keySchId}"];
-                                        SchDataNow.deviceCnt += SchDataTodo.deviceCnt;
-                                        SchDataNow.deviceNoAuth += SchDataTodo.deviceNoAuth;
-                                        SchDataNow.deviceAuth += SchDataTodo.deviceAuth;
-                                        SchDataNow.lessonRecord += SchDataTodo.lessonRecord;
-                                        SchDataNow.useIES += SchDataTodo.useIES;
-                                        SchDataNow.useIES5Resource += SchDataTodo.useIES5Resource;
-                                        SchDataNow.useWebIrs += SchDataTodo.useWebIrs;
-                                        SchDataNow.useDeviceIrs += SchDataTodo.useDeviceIrs;
-                                        SchDataNow.useHaboard += SchDataTodo.useHaboard;
-                                        SchDataNow.useHita += SchDataTodo.useHita;
-                                        SchDataNow.lessonLengMin += SchDataTodo.lessonLengMin;
-                                        SchDataNow.stuShow += SchDataTodo.stuShow;
-                                        SchDataNow.stuLessonLengMin += SchDataTodo.stuLessonLengMin;
-                                        SchDataNow.tGreen += SchDataTodo.tGreen;
-                                        SchDataNow.lTypeCoop += SchDataTodo.lTypeCoop;
-                                        SchDataNow.lTypeIact += SchDataTodo.lTypeIact;
-                                        SchDataNow.lTypeMis += SchDataTodo.lTypeMis;
-                                        SchDataNow.lTypeTst += SchDataTodo.lTypeTst;
-                                        SchDataNow.lTypeDif += SchDataTodo.lTypeDif;
-                                        SchDataNow.lessonCnt928 += SchDataTodo.lessonCnt928;
-                                        SchDataNow.lessonCntId += SchDataTodo.lessonCntId;
-                                        SchDataNow.lessonCntDevice += SchDataTodo.lessonCntDevice;
-                                        SchDataNow.lessonCntIdDevice += SchDataTodo.lessonCntIdDevice;
-                                        SchDataNow.mission += SchDataTodo.mission;
-                                        SchDataNow.missionFin += SchDataTodo.missionFin;
-                                        SchDataNow.item += SchDataTodo.item;
-                                        SchDataNow.interact += SchDataTodo.interact;
-                                        SchDataNow.deviceList = SchDataNow.deviceList.Union(SchDataTodo.deviceList).ToList();
-                                    }
-                                    //無此校資料 => 該校資料放入
-                                    else
-                                    {
-                                        ProdAnalysisListYear[$"{prodAnalYearKey}"][$"{keySchId}"] = SchDataTodo;
+                                        if (calPropList.Contains(propertyInfo.Name))
+                                        {
+                                            var propType = propertyInfo.PropertyType;
+                                            var valNow = propertyInfo.GetValue(SchDataNow);
+                                            var valTodo = SchDataTodo.GetType().GetProperty(propertyInfo.Name).GetValue(SchDataTodo);
+                                            if (propType.Equals(typeof(long))) propertyInfo.SetValue(SchDataNow, Convert.ToInt64(valNow.ToString(), 10) + Convert.ToInt64(valTodo.ToString(), 10));
+                                            else propertyInfo.SetValue(SchDataNow, Convert.ToInt32(valNow.ToString(), 10) + Convert.ToInt32(valTodo.ToString(), 10));
+                                        }
                                     }
+                                    SchDataNow.deviceList = SchDataNow.deviceList.Union(SchDataTodo.deviceList).ToList();
+                                    SchDataNow.deviceCnt = SchDataNow.deviceList.Count;
+                                    SchDataNow.tmidList = SchDataNow.tmidList.Union(SchDataTodo.tmidList).ToList();
                                 }
-                                //無此年資料 => 所有學校資料放入
+                                //無此校資料 => 該校資料放入
                                 else
                                 {
-                                    ProdAnalysisListYear.Add(prodAnalYearKey, new Dictionary<string, ProdAnalysis>() { { keySchId, SchDataTodo } });
+                                    ProdAnalysisCosmos SchDataNow = SchDataTodo;
+                                    SchDataNow.date = $"{y}";
+                                    SchDataNow.year = Convert.ToInt32(y, 10);
+                                    SchDataNow.month = 0;
+                                    DateTimeOffset dateTime = new DateTimeOffset(SchDataNow.year, 1, 1, 0, 0, 0, TimeSpan.Zero);
+                                    SchDataNow.dateTime = dateTime.ToUnixTimeSeconds();
+                                    SchDataNow.id = $"{toolType}-{y}-{schId}";
+                                    SchDataNow.dateUnit = "year";
+                                    ProdAnalysisListYear[$"{toolType}"][$"{schId}"] = SchDataNow;
                                 }
                             }
+                            //無此年資料 => 所有學校資料放入
+                            else
+                            {
+                                ProdAnalysisCosmos SchDataNow = SchDataTodo;
+                                SchDataNow.date = $"{y}";
+                                SchDataNow.year = Convert.ToInt32(y, 10);
+                                SchDataNow.month = 0;
+                                DateTimeOffset dateTime = new DateTimeOffset(SchDataNow.year, 1, 1, 0, 0, 0, TimeSpan.Zero);
+                                SchDataNow.dateTime = dateTime.ToUnixTimeSeconds();
+                                SchDataNow.id = $"{toolType}-{y}-{schId}";
+                                SchDataNow.dateUnit = "year";
+                                ProdAnalysisListYear.Add(toolType, new Dictionary<string, ProdAnalysisCosmos>() { { schId, SchDataNow } });
+                            }
                         }
                     }
                 }
                 if (ProdAnalysisListYear.Count > 0)
                 {
-                    foreach (KeyValuePair<string, Dictionary<string, ProdAnalysis>> item in ProdAnalysisListYear)
+                    foreach (KeyValuePair<string, Dictionary<string, ProdAnalysisCosmos>> item in ProdAnalysisListYear)
                     {
-                        string yearRedisKey = item.Key;
-                        List<HashEntry> hvalList = new List<HashEntry>();
-                        Dictionary<string, ProdAnalysis> yearRedisSchDIc = item.Value;
-                        foreach (KeyValuePair<string, ProdAnalysis> yearRedisSchItem in yearRedisSchDIc)
+                        string toolType = item.Key;
+                        ProdAnalysisCosmos SchDataNow = new ProdAnalysisCosmos(); //當年某產品所有學校總和
+                        Dictionary<string, ProdAnalysisCosmos> yearCosmosSchDIc = item.Value;
+                        foreach (KeyValuePair<string, ProdAnalysisCosmos> yearCosmosSchItem in yearCosmosSchDIc)
                         {
-                            string yearRedisSchId = yearRedisSchItem.Key;
-                            ProdAnalysis yearRedisSchData = yearRedisSchItem.Value;
-                            hvalList.Add(new HashEntry(yearRedisSchId, yearRedisSchData.ToJsonString()));
+                            string schId = yearCosmosSchItem.Key;
+                            ProdAnalysisCosmos yearCosmosSchData = yearCosmosSchItem.Value;
+                            await _azureCosmosClient.GetContainer(Constant.TEAMModelOS, "School").UpsertItemAsync<ProdAnalysisCosmos>(yearCosmosSchData);
+                            //每年所有學校數據總計CosmosDB記入
+                            foreach (PropertyInfo propertyInfo in SchDataNow.GetType().GetProperties())
+                            {
+                                if (calPropList.Contains(propertyInfo.Name))
+                                {
+                                    var propType = propertyInfo.PropertyType;
+                                    var valNow = propertyInfo.GetValue(SchDataNow);
+                                    var valTodo = yearCosmosSchData.GetType().GetProperty(propertyInfo.Name).GetValue(yearCosmosSchData);
+                                    if (propType.Equals(typeof(long))) propertyInfo.SetValue(SchDataNow, Convert.ToInt64(valNow.ToString(), 10) + Convert.ToInt64(valTodo.ToString(), 10));
+                                    else propertyInfo.SetValue(SchDataNow, Convert.ToInt32(valNow.ToString(), 10) + Convert.ToInt32(valTodo.ToString(), 10));
+                                }
+
+                            }
+                            SchDataNow.schoolId = "allschool";
+                            SchDataNow.toolType = yearCosmosSchData.toolType;
+                            SchDataNow.date = yearCosmosSchData.date;
+                            SchDataNow.id = $"{yearCosmosSchData.toolType}-{yearCosmosSchData.date}-allschool";
+                            SchDataNow.dateUnit = "year";
+                            SchDataNow.year = yearCosmosSchData.year;
+                            SchDataNow.month = 0;
+                            DateTimeOffset dateTime = new DateTimeOffset(SchDataNow.year, 1, 1, 0, 0, 0, TimeSpan.Zero);
+                            SchDataNow.dateTime = dateTime.ToUnixTimeSeconds();
+                            SchDataNow.deviceList = SchDataNow.deviceList.Union(yearCosmosSchData.deviceList).ToList();
+                            SchDataNow.deviceCnt = SchDataNow.deviceList.Count;
+                            SchDataNow.tmidList = SchDataNow.tmidList.Union(yearCosmosSchData.tmidList).ToList();
                         }
-                        await redisClinet8.HashSetAsync($"{yearRedisKey}", hvalList.ToArray());
+                        await _azureCosmosClient.GetContainer(Constant.TEAMModelOS, "School").UpsertItemAsync<ProdAnalysisCosmos>(SchDataNow);
                     }
                 }