using Microsoft.Azure.Cosmos; using StackExchange.Redis; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Text.Json; using System.Threading; using System.Threading.Tasks; using TEAMModelOS.SDK.DI; using TEAMModelOS.SDK.Extension; using TEAMModelOS.SDK.Models.Cosmos.BI.BISchool; using TEAMModelOS.SDK.Models.Cosmos.BI.BITable; namespace TEAMModelOS.SDK.Models.Service.BI { public class BILeesonService { /// /// 通过时间戳保存开课记录和课例记录统计 /// 统计数据存储在CosmosDB表中 /// /// cosmosDB连接 /// redis /// 错误消息发送 /// 13位时间戳 /// 数量 /// 类型:0 开课记录 1 课例记录 /// 学校编码 /// public static async Task SetCosmosDBStats(CosmosClient cosmosClient, AzureRedisFactory _azureRedis, DingDing _dingDing, long unix, int num = 1, int type = 0, string schoolId = null) { try { DateTimeOffset dateTime = DateTimeOffset.FromUnixTimeMilliseconds(unix); int year, month, day, hour, days; year = dateTime.Year; month = dateTime.Month; day = dateTime.Day; hour = dateTime.Hour; days = dateTime.DayOfYear; var dateDay = dateTime.ToString("yyyyMMdd"); //获取当天的日期 var yearDays = (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) ? 366 : 365; DateTime expireDay = DateTime.UtcNow.AddDays(8); //8天后到期 DateTime expireYear = DateTime.UtcNow.AddDays(396); //一年后到期 var redisClient = _azureRedis.GetRedisClient(8); string LessType = "Open"; if (type == 1) LessType = "Lesson"; await redisClient.SortedSetIncrementAsync($"BIStats:Less:All:{LessType}:{dateDay}", $"{hour}", num);//一天24小时课例数 有上传 base.josn 小时为单位 await redisClient.SortedSetIncrementAsync($"BIStats:Less:All:{LessType}:{year}", $"{days}", num);//一年的课例数量 有上传 base.josn 小时为单位 var expDay = await _azureRedis.GetRedisClient(8).KeyTimeToLiveAsync($"BIStats:Less:All:{LessType}:{dateDay}"); if (expDay == null) await _azureRedis.GetRedisClient(8).KeyExpireAsync($"BIStats:Less:All:{LessType}:{dateDay}", expireDay); //设置八天后到期 var expYear = await _azureRedis.GetRedisClient(8).KeyTimeToLiveAsync($"BIStats:Less:All:{LessType}:{year}"); if (expYear == null) await _azureRedis.GetRedisClient(8).KeyExpireAsync($"BIStats:Less:All:{LessType}:{year}", expireYear); //设置一年后到期 //保存当天的统计 小时 var dayCnt = redisClient.SortedSetRangeByScoreWithScores($"BIStats:Less:All:{LessType}:{dateDay}"); if (dayCnt != null && dayCnt.Length > 0) { double[] daHour = new double[23]; foreach (var item in dayCnt) { double val = ((double)item.Score); int key = ((int)item.Element); daHour[key] = val; } List lessHours = new(daHour); var lessRes = await cosmosClient.GetContainer("TEAMModelOS", "School").ReadItemStreamAsync($"{dateDay}", new PartitionKey("LessonHour")); if (lessRes.StatusCode == System.Net.HttpStatusCode.OK) { using var json = await JsonDocument.ParseAsync(lessRes.Content); LessonStats lessonStats = json.ToObject(); lessonStats.code = "LessonHour"; lessonStats.pk = "LessonHour"; if (type == 1) lessonStats.lesson = lessHours; else lessonStats.open = lessHours; await cosmosClient.GetContainer("TEAMModelOS", "School").ReplaceItemAsync(lessonStats, lessonStats.id, new PartitionKey("LessonHour")); } else { LessonStats lessonStats = new(); lessonStats.id = $"{dateDay}"; lessonStats.code = "LessonHour"; lessonStats.pk = "LessonHour"; if (type == 1) lessonStats.lesson = lessHours; else lessonStats.open = lessHours; await cosmosClient.GetContainer("TEAMModelOS", "School").CreateItemAsync(lessonStats, new PartitionKey("LessonHour")); } } //保一年的统计 天 var yearCnt = redisClient.SortedSetRangeByScoreWithScores($"BIStats:Less:All:{LessType}:{year}"); if (yearCnt != null && yearCnt.Length > 0) { double[] daYear = new double[yearDays]; foreach (var item in yearCnt) { double val = ((double)item.Score); int key = ((int)item.Element); daYear[key] = val; } List lessYear = new(daYear); var lessRes = await cosmosClient.GetContainer("TEAMModelOS", "School").ReadItemStreamAsync($"{year}", new PartitionKey("LessonYear")); if (lessRes.StatusCode == System.Net.HttpStatusCode.OK) { using var json = await JsonDocument.ParseAsync(lessRes.Content); LessonStats lessonYear = json.ToObject(); lessonYear.code = "LessonYear"; lessonYear.pk = "LessonYear"; if (type == 1) lessonYear.lesson = lessYear; else lessonYear.open = lessYear; await cosmosClient.GetContainer("TEAMModelOS", "School").ReplaceItemAsync(lessonYear, lessonYear.id, new PartitionKey("LessonYear")); } else { LessonStats lessonYear = new(); lessonYear.id = $"{year}"; lessonYear.code = "LessonYear"; lessonYear.pk = "LessonYear"; if (type == 1) lessonYear.lesson = lessYear; else lessonYear.open = lessYear; await cosmosClient.GetContainer("TEAMModelOS", "School").CreateItemAsync(lessonYear, new PartitionKey("LessonYear")); } } if (!string.IsNullOrEmpty(schoolId)) { await redisClient.SortedSetIncrementAsync($"BIStats:Less:{schoolId}:{LessType}:{dateDay}", $"{hour}", num);//一天24小时课例数 有上传 base.josn 小时为单位 await redisClient.SortedSetIncrementAsync($"BIStats:Less:{schoolId}:{LessType}:{year}", $"{days}", num);//一年的课例数量 有上传 base.josn 小时为单位 var scExpDay = await _azureRedis.GetRedisClient(8).KeyTimeToLiveAsync($"BIStats:Less:{schoolId}:{LessType}:{dateDay}"); if (scExpDay == null) await _azureRedis.GetRedisClient(8).KeyExpireAsync($"BIStats:Less:{schoolId}:{LessType}:{dateDay}", expireDay); //设置八天后到期 var scExpYear = await _azureRedis.GetRedisClient(8).KeyTimeToLiveAsync($"BIStats:Less:{schoolId}:{LessType}:{year}"); if (scExpYear == null) await _azureRedis.GetRedisClient(8).KeyExpireAsync($"BIStats:Less:{schoolId}:{LessType}:{year}", expireYear); //设置一年后到期 //保存当天的统计 小时 var dayScCnt = redisClient.SortedSetRangeByScoreWithScores($"BIStats:Less:{schoolId}:{LessType}:{dateDay}"); if (dayScCnt != null && dayScCnt.Length > 0) { double[] daHour = new double[23]; foreach (var item in dayScCnt) { double val = ((double)item.Score); int key = ((int)item.Element); daHour[key] = val; } List lessHours = new(daHour); var lessRes = await cosmosClient.GetContainer("TEAMModelOS", "School").ReadItemStreamAsync($"{dateDay}", new PartitionKey($"LessonHour-{schoolId}")); if (lessRes.StatusCode == System.Net.HttpStatusCode.OK) { using var json = await JsonDocument.ParseAsync(lessRes.Content); LessonStats lessonStats = json.ToObject(); lessonStats.code = $"LessonHour-{schoolId}"; lessonStats.pk = "LessonHour"; if (type == 1) lessonStats.lesson = lessHours; else lessonStats.open = lessHours; await cosmosClient.GetContainer("TEAMModelOS", "School").ReplaceItemAsync(lessonStats, lessonStats.id, new PartitionKey($"LessonHour-{schoolId}")); } else { LessonStats lessonStats = new(); lessonStats.id = $"{dateDay}"; lessonStats.code = $"LessonHour-{schoolId}"; lessonStats.pk = "LessonHour"; if (type == 1) lessonStats.lesson = lessHours; else lessonStats.open = lessHours; await cosmosClient.GetContainer("TEAMModelOS", "School").CreateItemAsync(lessonStats, new PartitionKey($"LessonHour-{schoolId}")); } } //保一年的统计 天 var scYearCnt = redisClient.SortedSetRangeByScoreWithScores($"BILesson:{schoolId}:{LessType}:{year}"); if (scYearCnt != null && scYearCnt.Length > 0) { double[] daYear = new double[yearDays]; foreach (var item in scYearCnt) { double val = ((double)item.Score); int key = ((int)item.Element); daYear[key] = val; } List lessYear = new(daYear); var lessRes = await cosmosClient.GetContainer("TEAMModelOS", "School").ReadItemStreamAsync($"{year}", new PartitionKey($"LessonYear-{schoolId}")); if (lessRes.StatusCode == System.Net.HttpStatusCode.OK) { using var json = await JsonDocument.ParseAsync(lessRes.Content); LessonStats lessonYear = json.ToObject(); lessonYear.code = $"LessonYear-{schoolId}"; lessonYear.pk = "LessonYear"; if (type == 1) lessonYear.lesson = lessYear; else lessonYear.open = lessYear; await cosmosClient.GetContainer("TEAMModelOS", "School").ReplaceItemAsync(lessonYear, lessonYear.id, new PartitionKey($"LessonYear-{schoolId}")); } else { LessonStats lessonYear = new(); lessonYear.id = $"{year}"; lessonYear.code = $"LessonYear-{schoolId}"; lessonYear.pk = "LessonYear"; if (type == 1) lessonYear.lesson = lessYear; else lessonYear.open = lessYear; await cosmosClient.GetContainer("TEAMModelOS", "School").CreateItemAsync(lessonYear, new PartitionKey($"LessonYear-{schoolId}")); } } } } catch (Exception ex) { await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}-SetBICosmosDBStats 课例统计异常 {ex.Message}\n{ex.StackTrace}", GroupNames.成都开发測試群組); } } /// /// 通过时间戳保存开课记录和课例记录统计 /// 统计数据存储在table表中 /// /// table连接池 /// redis连接池 /// 错误消息发送 /// 13位时间戳 /// 数量 /// 类型:0 开课记录 1 课例记录 /// 范围(默认学校范围):学校 school 个人 private /// 学校编码 /// public static async Task SetTableStats(AzureStorageFactory _azureStorage, AzureRedisFactory _azureRedis, DingDing _dingDing, long unix, int num = 1, int type = 0,string scope = "private", string schoolId = null) { try { //SemaphoreSlim slimlock = new(1, 1); //对可同时访问资源或资源池的线程数加以限制 结束 //await slimlock.WaitAsync(); Monitor.TryEnter(unix); //锁对象 DateTimeOffset dateTime = DateTimeOffset.FromUnixTimeMilliseconds(unix); int year, month, day, hour, days; year = dateTime.Year; month = dateTime.Month; day = dateTime.Day; hour = dateTime.Hour; days = dateTime.DayOfYear; var dateDay = dateTime.ToString("yyyyMMdd"); //获取当天的日期 var yearDays = (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) ? 366 : 365; var table = _azureStorage.GetCloudTableClient().GetTableReference("BIStats"); var redisClient = _azureRedis.GetRedisClient(8); DateTime expireDay = DateTime.UtcNow.AddDays(8); //8天后到期 DateTime expireYear = DateTime.UtcNow.AddDays(396); //一年后到期 ////保存当天的统计 小时 //SortedSetEntry[] dayCnt = null; ////保一年的统计 天 //SortedSetEntry[] yearCnt = null; string LessType = "Open"; if (type == 1) LessType = "Lesson"; try { await redisClient.SortedSetIncrementAsync($"BIStats:Less:All:{LessType}:{dateDay}", $"{hour}", num);//一天24小时课例数 有上传 base.josn 小时为单位 await redisClient.SortedSetIncrementAsync($"BIStats:Less:All:{LessType}:{year}", $"{days}", num);//一年的课例数量 有上传 base.josn 小时为单位 var expDay = await _azureRedis.GetRedisClient(8).KeyTimeToLiveAsync($"BIStats:Less:All:{LessType}:{dateDay}"); if (expDay == null) await _azureRedis.GetRedisClient(8).KeyExpireAsync($"BIStats:Less:All:{LessType}:{dateDay}", expireDay); //设置八天后到期 var expYear = await _azureRedis.GetRedisClient(8).KeyTimeToLiveAsync($"BIStats:Less:All:{LessType}:{year}"); if (expYear == null) await _azureRedis.GetRedisClient(8).KeyExpireAsync($"BIStats:Less:All:{LessType}:{year}", expireYear); //设置一年后到期 ////保存当天的统计 小时 //dayCnt = redisClient.SortedSetRangeByScoreWithScores($"BIStats:Less:All:{LessType}:{dateDay}"); ////保一年的统计 天 //yearCnt = redisClient.SortedSetRangeByScoreWithScores($"BIStats:Less:All:{LessType}:{year}"); } catch { } double[] daHour = new double[23]; daHour[hour] = num; string hourStats = string.Join(',', daHour); double[] daYear = new double[yearDays]; daYear[hour] = num; string yearStats = string.Join(',', daYear); LessStats lessHour = table.Get("LessonHour", $"{dateDay}"); if (lessHour != null) { if (type == 1) { if (lessHour.lesson != null) { double[] tempLess = Array.ConvertAll(lessHour.lesson.Split(','), s => double.Parse(s)); tempLess[hour] = tempLess[hour] + num; string strLess = string.Join(',', tempLess); lessHour.lesson = strLess; } else lessHour.lesson = hourStats; } else { if (lessHour.open != null) { double[] tempOpen = Array.ConvertAll(lessHour.open.Split(','), s => double.Parse(s)); tempOpen[hour] = tempOpen[hour] + num; string sOpen = string.Join(',', tempOpen); lessHour.open = sOpen; } else lessHour.open = hourStats; } } else { lessHour = new() { PartitionKey = "LessonHour", RowKey = $"{dateDay}" }; if (type == 1) lessHour.lesson = hourStats; else lessHour.open = hourStats; } await table.SaveOrUpdate(lessHour); LessStats lessYear = table.Get("LessonYear", $"{year}"); if (lessYear != null) { if (type == 1) { if (lessYear.lesson != null) { double[] tempLess = Array.ConvertAll(lessYear.lesson.Split(','), s => double.Parse(s)); tempLess[days] = tempLess[days] + num; string sLess = string.Join(',', tempLess); lessYear.lesson = sLess; } else lessYear.lesson = yearStats; } else { if (lessYear.open != null) { double[] tempOpen = Array.ConvertAll(lessYear.open.Split(','), s => double.Parse(s)); tempOpen[days] = tempOpen[days] + num; string sOpen = string.Join(',', tempOpen); lessYear.open = sOpen; } else lessYear.open = yearStats; } } else { lessYear = new() { PartitionKey = "LessonYear", RowKey = $"{year}" }; if (type == 1) lessYear.lesson = yearStats; else lessYear.open = yearStats; } try { await table.SaveOrUpdate(lessYear); } catch { } //保存学校课例数据 if (!string.IsNullOrEmpty(schoolId) && scope.Equals("school")) { ////保存学校当天的统计 小时 //SortedSetEntry[] dayScCnt = null; ////保学校一年的统计 天 //SortedSetEntry[] scYearCnt = null; try { await redisClient.SortedSetIncrementAsync($"BIStats:Less:{schoolId}:{LessType}:{dateDay}", $"{hour}", num);//一天24小时课例数 有上传 base.josn 小时为单位 await redisClient.SortedSetIncrementAsync($"BIStats:Less:{schoolId}:{LessType}:{year}", $"{days}", num);//一年的课例数量 有上传 base.josn 小时为单位 var scExpDay = await _azureRedis.GetRedisClient(8).KeyTimeToLiveAsync($"BIStats:Less:{schoolId}:{LessType}:{dateDay}"); if (scExpDay == null) await _azureRedis.GetRedisClient(8).KeyExpireAsync($"BIStats:Less:{schoolId}:{LessType}:{dateDay}", expireDay); //设置八天后到期 var scExpYear = await _azureRedis.GetRedisClient(8).KeyTimeToLiveAsync($"BIStats:Less:{schoolId}:{LessType}:{year}"); if (scExpYear == null) await _azureRedis.GetRedisClient(8).KeyExpireAsync($"BIStats:Less:{schoolId}:{LessType}:{year}", expireYear); //设置一年后到期 ////保存学校当天的统计 小时 //dayScCnt = redisClient.SortedSetRangeByScoreWithScores($"BIStats:Less:{schoolId}:{LessType}:{dateDay}"); ////保学校一年的统计 天 //scYearCnt = redisClient.SortedSetRangeByScoreWithScores($"BIStats:Less:{schoolId}:{LessType}:{year}"); }catch { } double[] daScHour = new double[23]; daScHour[hour] = num; string hourScStats = string.Join(',', daScHour); double[] daScYear = new double[yearDays]; daScYear[hour] = num; string strYearSc = string.Join(',', daScYear); LessStats lessScHour = table.Get("LessonHour", $"{dateDay}-{schoolId}"); if (lessScHour != null) { if (type == 1) { if (lessScHour.lesson != null) { double[] tempLess = Array.ConvertAll(lessScHour.lesson.Split(','), s => double.Parse(s)); tempLess[hour] = tempLess[hour] + num; string strLess = string.Join(',', tempLess); lessScHour.lesson = strLess; } else lessScHour.lesson = hourScStats; } else { if (lessScHour.open != null) { double[] tempOpen = Array.ConvertAll(lessScHour.open.Split(','), s => double.Parse(s)); tempOpen[hour] = tempOpen[hour] + num; string sOpen = string.Join(',', tempOpen); lessScHour.open = sOpen; } else lessScHour.open = hourScStats; } } else { lessScHour = new() { PartitionKey = "LessonHour", RowKey = $"{dateDay}-{schoolId}" }; if (type == 1) lessScHour.lesson = hourScStats; else lessScHour.open = hourScStats; } try { await table.SaveOrUpdate(lessScHour); } catch { } LessStats lessScYear = table.Get("LessonYear", $"{year}-{schoolId}"); if (lessScYear != null) { if (type == 1) { if (lessScYear.lesson != null) { double[] tempLess = Array.ConvertAll(lessScYear.lesson.Split(','), s => double.Parse(s)); tempLess[days] = tempLess[days] + num; string sLess = string.Join(',', tempLess); lessScYear.lesson = sLess; } else lessScYear.lesson = strYearSc; } else { if (lessScYear.open != null) { double[] tempOpen = Array.ConvertAll(lessScYear.open.Split(','), s => double.Parse(s)); tempOpen[days] = tempOpen[days] + num; string sOpen = string.Join(',', tempOpen); lessScYear.open = sOpen; } else lessScYear.open = strYearSc; } } else { lessScYear = new() { PartitionKey = "LessonYear", RowKey = $"{year}" }; if (type == 1) lessScYear.lesson = strYearSc; else lessScYear.open = strYearSc; } try { await table.SaveOrUpdate(lessScYear); } catch { } //if (dayScCnt != null && dayScCnt.Length > 0) //{ // double[] daHourSc = new double[23]; // foreach (var item in dayScCnt) // { // double val = ((double)item.Score); // int key = ((int)item.Element); // daHourSc[key] = val; // } // string hoursStatsSc = string.Join(",", daHourSc); // LessStats lessStatsSc = table.Get("LessonHour", $"{dateDay}-{schoolId}"); // if (lessStatsSc != null) // { // if (type == 1) // lessStatsSc.lesson = hoursStatsSc; // else // lessStatsSc.open = hoursStatsSc; // } // else // { // lessStatsSc = new() { PartitionKey = "LessonHour", RowKey = $"{dateDay}-{schoolId}" }; // if (type == 1) // lessStatsSc.lesson = hoursStatsSc; // else // lessStatsSc.open = hoursStatsSc; // lessStatsSc.time = dateDay; // lessStatsSc.school = schoolId; // } // try // { // await table.SaveOrUpdate(lessStatsSc); // } // catch { } //} //if (scYearCnt != null && scYearCnt.Length > 0) //{ // double[] daYearSc = new double[yearDays]; // foreach (var item in scYearCnt) // { // double val = ((double)item.Score); // int key = ((int)item.Element); // daYearSc[key] = val; // } // string tempStatsSc = string.Join(",", daYearSc); // LessStats lessStatsSc = table.Get("LessonYear", $"{year}-{schoolId}"); // if (lessStatsSc != null) // { // if (type == 1) // lessStatsSc.lesson = tempStatsSc; // else // lessStatsSc.open = tempStatsSc; // } // else // { // lessStatsSc = new() { PartitionKey = "LessonYear", RowKey = $"{year}-{schoolId}" }; // if (type == 1) // lessStatsSc.lesson = tempStatsSc; // else // lessStatsSc.open = tempStatsSc; // lessStatsSc.time = $"{year}"; // lessStatsSc.school = schoolId; // } // try // { // await table.SaveOrUpdate(lessStatsSc); // } // catch { } //} } Monitor.Enter(unix); //锁对象 结束 //slimlock.Release(); // 对可同时访问资源或资源池的线程数加以限制 结束 } catch (Exception ex) { await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}-SetBITableStats BI记录课例统计数据异常!参数:{unix}|{num}|{type}|{scope}|{schoolId} ;错误信息:{ex.Message}\n{ex.StackTrace}", GroupNames.成都开发測試群組); } } } }