MonitorServicesBus.cs 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4. using System.Text.Json;
  5. using System.Threading.Tasks;
  6. using Azure.Cosmos;
  7. using Azure.Messaging.ServiceBus;
  8. using Microsoft.Azure.WebJobs;
  9. using Microsoft.Azure.WebJobs.Host;
  10. using Microsoft.Extensions.Logging;
  11. using StackExchange.Redis;
  12. using TEAMModelOS.SDK.DI;
  13. using TEAMModelOS.SDK.Extension;
  14. using TEAMModelOS.SDK;
  15. using TEAMModelOS.SDK.Models;
  16. using TEAMModelOS.SDK.Models.Cosmos;
  17. using TEAMModelOS.SDK.Models.Cosmos.Common;
  18. using TEAMModelOS.Services.Common;
  19. using System.Linq;
  20. using TEAMModelOS.SDK.Models.Service;
  21. using TEAMModelOS.SDK.Models.Cosmos.BI;
  22. using TEAMModelOS.Models;
  23. using Microsoft.Extensions.Options;
  24. using Microsoft.Extensions.Configuration;
  25. using HTEXLib.COMM.Helpers;
  26. namespace TEAMModelFunction
  27. {
  28. public class MonitorServicesBus
  29. {
  30. private readonly AzureCosmosFactory _azureCosmos;
  31. private readonly DingDing _dingDing;
  32. private readonly AzureStorageFactory _azureStorage;
  33. private readonly AzureRedisFactory _azureRedis;
  34. private readonly AzureServiceBusFactory _serviceBus;
  35. private readonly Option _option;
  36. private readonly NotificationService _notificationService;
  37. private readonly IConfiguration _configuration;
  38. public MonitorServicesBus(AzureCosmosFactory azureCosmos, DingDing dingDing, AzureStorageFactory azureStorage, AzureRedisFactory azureRedis, AzureServiceBusFactory serviceBus, IOptionsSnapshot<Option> option, NotificationService notificationService, IConfiguration configuration)
  39. {
  40. _azureCosmos = azureCosmos;
  41. _dingDing = dingDing;
  42. _azureStorage = azureStorage;
  43. _azureRedis = azureRedis;
  44. _serviceBus = serviceBus;
  45. _option = option?.Value;
  46. _notificationService = notificationService;
  47. _configuration = configuration;
  48. }
  49. [FunctionName("Exam")]
  50. public async Task ExamFunc([ServiceBusTrigger("%Azure:ServiceBus:ActiveTask%", "exam", Connection = "Azure:ServiceBus:ConnectionString")] string msg)
  51. {
  52. try
  53. {
  54. var json = JsonDocument.Parse(msg);
  55. json.RootElement.TryGetProperty("id", out JsonElement id);
  56. json.RootElement.TryGetProperty("progress", out JsonElement progress);
  57. json.RootElement.TryGetProperty("code", out JsonElement code);
  58. //Dictionary<string, object> keyValuePairs = mySbMsg.ToObject<Dictionary<string, object>>();
  59. var client = _azureCosmos.GetCosmosClient();
  60. ExamInfo exam = await client.GetContainer(Constant.TEAMModelOS, "Common").ReadItemAsync<ExamInfo>(id.ToString(), new PartitionKey($"{code}"));
  61. exam.progress = progress.ToString();
  62. await client.GetContainer(Constant.TEAMModelOS, "Common").ReplaceItemAsync(exam, id.ToString(), new PartitionKey($"{code}"));
  63. }
  64. catch (CosmosException)
  65. {
  66. }
  67. catch (Exception ex)
  68. {
  69. await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}-ServiceBus,ExamBus()\n{ex.Message}\n{ex.StackTrace}\n{msg}", GroupNames.醍摩豆服務運維群組);
  70. }
  71. }
  72. [FunctionName("Vote")]
  73. public async Task VoteFunc([ServiceBusTrigger("%Azure:ServiceBus:ActiveTask%", "vote", Connection = "Azure:ServiceBus:ConnectionString")] string msg)
  74. {
  75. try
  76. {
  77. var jsonMsg = JsonDocument.Parse(msg);
  78. jsonMsg.RootElement.TryGetProperty("id", out JsonElement id);
  79. jsonMsg.RootElement.TryGetProperty("progress", out JsonElement progress);
  80. jsonMsg.RootElement.TryGetProperty("code", out JsonElement code);
  81. var client = _azureCosmos.GetCosmosClient();
  82. Vote vote = await client.GetContainer(Constant.TEAMModelOS, "Common").ReadItemAsync<Vote>(id.ToString(), new PartitionKey($"{code}"));
  83. vote.progress = progress.ToString();
  84. await client.GetContainer(Constant.TEAMModelOS, "Common").ReplaceItemAsync(vote, id.ToString(), new PartitionKey($"{code}"));
  85. }
  86. catch (CosmosException)
  87. {
  88. }
  89. catch (Exception ex)
  90. {
  91. await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}-ServiceBus,VoteBus()\n{ex.Message}\n{ex.StackTrace}\n{msg}", GroupNames.醍摩豆服務運維群組);
  92. }
  93. }
  94. [FunctionName("Correct")]
  95. public async Task CorrectFunc([ServiceBusTrigger("%Azure:ServiceBus:ActiveTask%", "correct", Connection = "Azure:ServiceBus:ConnectionString")] string msg)
  96. {
  97. try
  98. {
  99. var jsonMsg = JsonDocument.Parse(msg);
  100. jsonMsg.RootElement.TryGetProperty("id", out JsonElement id);
  101. jsonMsg.RootElement.TryGetProperty("progress", out JsonElement progress);
  102. jsonMsg.RootElement.TryGetProperty("code", out JsonElement code);
  103. var client = _azureCosmos.GetCosmosClient();
  104. Correct correct = await client.GetContainer(Constant.TEAMModelOS, "Common").ReadItemAsync<Correct>(id.ToString(), new PartitionKey($"{code}"));
  105. correct.progress = progress.ToString();
  106. await client.GetContainer(Constant.TEAMModelOS, "Common").ReplaceItemAsync(correct, id.ToString(), new PartitionKey($"{code}"));
  107. }
  108. catch (CosmosException)
  109. {
  110. }
  111. catch (Exception ex)
  112. {
  113. await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}-ServiceBus,Correct()\n{ex.Message}\n{ex.StackTrace}\n{msg}", GroupNames.醍摩豆服務運維群組);
  114. }
  115. }
  116. [FunctionName("Survey")]
  117. public async Task SurveyFunc([ServiceBusTrigger("%Azure:ServiceBus:ActiveTask%", "survey", Connection = "Azure:ServiceBus:ConnectionString")] string msg)
  118. {
  119. try
  120. {
  121. var jsonMsg = JsonDocument.Parse(msg);
  122. jsonMsg.RootElement.TryGetProperty("id", out JsonElement id);
  123. jsonMsg.RootElement.TryGetProperty("progress", out JsonElement progress);
  124. jsonMsg.RootElement.TryGetProperty("code", out JsonElement code);
  125. //Dictionary<string, object> keyValuePairs = mySbMsg.ToObject<Dictionary<string, object>>();
  126. var client = _azureCosmos.GetCosmosClient();
  127. Survey survey = await client.GetContainer(Constant.TEAMModelOS, "Common").ReadItemAsync<Survey>(id.ToString(), new PartitionKey($"{code}"));
  128. survey.progress = progress.ToString();
  129. await client.GetContainer(Constant.TEAMModelOS, "Common").ReplaceItemAsync(survey, id.ToString(), new PartitionKey($"{code}"));
  130. }
  131. catch (CosmosException)
  132. {
  133. }
  134. catch (Exception ex)
  135. {
  136. await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}-ServiceBus,SurveyBus()\n{ex.Message}\n{ex.StackTrace}\n{msg}", GroupNames.醍摩豆服務運維群組);
  137. }
  138. }
  139. [FunctionName("Homework")]
  140. public async Task HomeworkFunc([ServiceBusTrigger("%Azure:ServiceBus:ActiveTask%", "homework", Connection = "Azure:ServiceBus:ConnectionString")] string msg)
  141. {
  142. try
  143. {
  144. var jsonMsg = JsonDocument.Parse(msg);
  145. jsonMsg.RootElement.TryGetProperty("id", out JsonElement id);
  146. jsonMsg.RootElement.TryGetProperty("progress", out JsonElement progress);
  147. jsonMsg.RootElement.TryGetProperty("code", out JsonElement code);
  148. var client = _azureCosmos.GetCosmosClient();
  149. Homework homework = await client.GetContainer(Constant.TEAMModelOS, "Common").ReadItemAsync<Homework>(id.ToString(), new PartitionKey($"{code}"));
  150. homework.progress = progress.ToString();
  151. await client.GetContainer(Constant.TEAMModelOS, "Common").ReplaceItemAsync(homework, id.ToString(), new PartitionKey($"{code}"));
  152. }
  153. catch (CosmosException ) {
  154. }
  155. catch (Exception ex)
  156. {
  157. await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}-ServiceBus,Homework()\n{ex.Message}\n{ex.StackTrace}\n{msg}", GroupNames.醍摩豆服務運維群組);
  158. }
  159. }
  160. [FunctionName("Study")]
  161. public async Task StudyFunc([ServiceBusTrigger("%Azure:ServiceBus:ActiveTask%", "study", Connection = "Azure:ServiceBus:ConnectionString")] string msg)
  162. {
  163. try
  164. {
  165. var jsonMsg = JsonDocument.Parse(msg);
  166. jsonMsg.RootElement.TryGetProperty("id", out JsonElement id);
  167. jsonMsg.RootElement.TryGetProperty("progress", out JsonElement progress);
  168. jsonMsg.RootElement.TryGetProperty("code", out JsonElement code);
  169. var client = _azureCosmos.GetCosmosClient();
  170. Study study = await client.GetContainer(Constant.TEAMModelOS, "Common").ReadItemAsync<Study>(id.ToString(), new PartitionKey($"{code}"));
  171. study.progress = progress.ToString();
  172. await client.GetContainer(Constant.TEAMModelOS, "Common").ReplaceItemAsync(study, id.ToString(), new PartitionKey($"{code}"));
  173. }
  174. catch (CosmosException)
  175. {
  176. }
  177. catch (Exception ex)
  178. {
  179. await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}-ServiceBus,Study()\n{ex.Message}\n{ex.StackTrace}\n{msg}", GroupNames.醍摩豆服務運維群組);
  180. }
  181. }
  182. [FunctionName("ExamLite")]
  183. public async Task ExamLiteFunc([ServiceBusTrigger("%Azure:ServiceBus:ActiveTask%", "examlite", Connection = "Azure:ServiceBus:ConnectionString")] string msg)
  184. {
  185. try
  186. {
  187. var jsonMsg = JsonDocument.Parse(msg);
  188. jsonMsg.RootElement.TryGetProperty("id", out JsonElement id);
  189. jsonMsg.RootElement.TryGetProperty("progress", out JsonElement progress);
  190. jsonMsg.RootElement.TryGetProperty("code", out JsonElement code);
  191. var client = _azureCosmos.GetCosmosClient();
  192. ExamLite lite = await client.GetContainer(Constant.TEAMModelOS, "Common").ReadItemAsync<ExamLite>(id.ToString(), new PartitionKey($"{code}"));
  193. lite.progress = progress.ToString();
  194. await client.GetContainer(Constant.TEAMModelOS, "Common").ReplaceItemAsync(lite, id.ToString(), new PartitionKey($"{code}"));
  195. }
  196. catch (CosmosException)
  197. {
  198. }
  199. catch (Exception ex)
  200. {
  201. await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}-ServiceBus,ExamLite()\n{ex.Message}\n{ex.StackTrace}\n{msg}", GroupNames.醍摩豆服務運維群組);
  202. }
  203. }
  204. /// <summary>
  205. /// 根据容器的根目录刷新redis并获取redis的最新使用情况
  206. /// </summary>
  207. /// <param name="msg"></param>
  208. /// <returns></returns>
  209. [FunctionName("BlobRoot")]
  210. public async Task BlobRootFunc([ServiceBusTrigger("%Azure:ServiceBus:ActiveTask%", "blobroot", Connection = "Azure:ServiceBus:ConnectionString")] string msg)
  211. {
  212. try
  213. {
  214. var jsonMsg = JsonDocument.Parse(msg);
  215. if (jsonMsg.RootElement.TryGetProperty("name", out JsonElement _name) && _name.ValueKind == JsonValueKind.String
  216. && jsonMsg.RootElement.TryGetProperty("root", out JsonElement root) && root.ValueKind == JsonValueKind.String)
  217. {
  218. //await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}-ServiceBus,Blob() 容器:触发变更,{jsonMsg.ToJsonString()}",
  219. // GroupNames.成都开发測試群組);
  220. List<Dictionary<string, double?>> list = new List<Dictionary<string, double?>>();
  221. string[] uls = System.Web.HttpUtility.UrlDecode($"{root}", Encoding.UTF8).Split("/");
  222. string u = !string.IsNullOrEmpty(uls[0]) ? uls[0] : uls[1];
  223. string name = $"{_name}";
  224. string lockKey = $"Blob:Lock:{name}:{u}";
  225. bool exist= await _azureRedis.GetRedisClient(8).KeyExistsAsync(lockKey);
  226. if (!exist)
  227. { ///key不存在则正常进行计算
  228. bool condition = false;
  229. TimeSpan timeSpan = new TimeSpan(DateTimeOffset.UtcNow.AddMinutes(5).Ticks);
  230. timeSpan = timeSpan - new TimeSpan(DateTimeOffset.UtcNow.Ticks);
  231. //准备处理Blob刷新时间
  232. long action = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
  233. await _azureRedis.GetRedisClient(8).StringSetAsync(lockKey, action, expiry: timeSpan);
  234. await RefreshBlob(name, u);
  235. //将action 与Redis最新的时间进行比较,如果
  236. var rds = await CheckLockKey(lockKey, action);
  237. condition = rds.condition;
  238. exist = rds.exist;
  239. if (condition || !exist) {
  240. await RefreshBlob(name, u);
  241. }
  242. //使用 CancellationToken
  243. //while (condition || !exist)
  244. //{
  245. //}
  246. }
  247. else {
  248. ///key存在则,则刷新key对应的值
  249. TimeSpan timeSpan = new TimeSpan(DateTimeOffset.UtcNow.AddMinutes(5).Ticks);
  250. timeSpan = timeSpan - new TimeSpan(DateTimeOffset.UtcNow.Ticks);
  251. long now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
  252. await _azureRedis.GetRedisClient(8).StringSetAsync(lockKey, now, expiry: timeSpan);
  253. }
  254. //await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}-ServiceBus,Blob() 容器:{name}使用:{u},文件分类:{list.ToJsonString()}",
  255. // GroupNames.成都开发測試群組);
  256. }
  257. }
  258. catch (Exception ex)
  259. {
  260. await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}-ServiceBus,Blob()\n{ex.Message}\n{ex.StackTrace}\n{msg}", GroupNames.醍摩豆服務運維群組);
  261. }
  262. }
  263. private async Task<(bool condition,bool exist)> CheckLockKey(string lockKey,long nowTime) {
  264. //Redis的最新时间
  265. long newestTime = 0;
  266. RedisValue value = await _azureRedis.GetRedisClient(8).StringGetAsync(lockKey);
  267. if (value != default && !value.IsNullOrEmpty)
  268. {
  269. JsonElement record = value.ToString().ToObject<JsonElement>();
  270. if (record.TryGetInt64(out newestTime))
  271. {
  272. }
  273. }
  274. //说明key已经不存在
  275. if (newestTime == 0)
  276. {
  277. return (false, true);
  278. }
  279. //说明key存在
  280. else {
  281. //说明Redis记录了最新的时间戳
  282. if (nowTime != newestTime)
  283. {
  284. return (true, false);
  285. }
  286. //时间相同,没有被再次记录最新的时间戳
  287. else
  288. {
  289. await _azureRedis.GetRedisClient(8).KeyDeleteAsync(lockKey);
  290. return (false, true);
  291. }
  292. }
  293. }
  294. private async Task RefreshBlob(string name ,string u) {
  295. long statr = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
  296. var client = _azureStorage.GetBlobContainerClient(name);
  297. var size = await client.GetBlobsSize(u);
  298. await _azureRedis.GetRedisClient(8).SortedSetRemoveAsync($"Blob:Catalog:{name}", u);
  299. await _azureRedis.GetRedisClient(8).SortedSetIncrementAsync($"Blob:Catalog:{name}", u, size.HasValue ? size.Value : 0);
  300. var scores = await _azureRedis.GetRedisClient(8).SortedSetRangeByRankWithScoresAsync($"Blob:Catalog:{name}");
  301. double blobsize = 0;
  302. if (scores != default && scores != null)
  303. {
  304. foreach (var score in scores)
  305. {
  306. blobsize = blobsize + score.Score;
  307. }
  308. }
  309. await _azureRedis.GetRedisClient(8).HashSetAsync($"Blob:Record", new RedisValue(name), new RedisValue($"{blobsize}"));
  310. long end = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
  311. long dis = (end - statr)/1000;
  312. long timeout = 10;
  313. if (dis> timeout) {
  314. await _dingDing.SendBotMsg($"ServiceBus,RefreshBlob:空间计算已经超过{timeout}秒\n容器名:{name}\n文件夹:{u}\n计算时长:{dis}", GroupNames.醍摩豆服務運維群組);
  315. }
  316. }
  317. /// <summary>
  318. /// 完善课程变更,StuListChange, originCode是学校编码 则表示名单是学校自定义名单,如果是tmdid则表示醍摩豆的私有名单,scope=school,private。
  319. /// </summary>
  320. /// <data msg>
  321. /// CourseChange
  322. ///// </data>
  323. /// <param name="msg"></param>
  324. /// <returns></returns>
  325. [FunctionName("TeacherTrainChange")]
  326. public async Task TeacherTrainChangeFunc([ServiceBusTrigger("%Azure:ServiceBus:ActiveTask%", "teacher-train-change", Connection = "Azure:ServiceBus:ConnectionString")] string msg) {
  327. try {
  328. // await _dingDing.SendBotMsg($"teacher-train-change\n{msg}",GroupNames.成都开发測試群組);
  329. TeacherTrainChange change = msg.ToObject<TeacherTrainChange>();
  330. if (change.update == null || change.update.Count <= 0 || change.tmdids.IsEmpty())
  331. {
  332. return;
  333. }
  334. var client = _azureCosmos.GetCosmosClient();
  335. string insql = $"where c.id in ({string.Join(",", change.tmdids.Select(x => $"'{x}'"))})";
  336. string selsql = $"select value(c) from c {insql} ";
  337. List<TeacherTrain> teacherTrains = new List<TeacherTrain>();
  338. await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "Teacher").GetItemQueryIterator<TeacherTrain>(queryText: selsql,
  339. requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"TeacherTrain-{change.school}") }))
  340. {
  341. teacherTrains.Add(item);
  342. }
  343. if (change.statistics != 1)
  344. {
  345. List<Task<ItemResponse<TeacherTrain>>> task = new List<Task<ItemResponse<TeacherTrain>>>();
  346. teacherTrains.ForEach(x => {
  347. x.update.UnionWith(change.update);
  348. task.Add(client.GetContainer(Constant.TEAMModelOS, "Teacher").ReplaceItemAsync<TeacherTrain>(x, x.id, new PartitionKey($"TeacherTrain-{change.school}")));
  349. });
  350. await task.TaskPage(5);
  351. var unchange = change.tmdids.Except(teacherTrains.Select(x => x.id));
  352. if (unchange != null)
  353. {
  354. task.Clear();
  355. unchange.ToList().ForEach(x => {
  356. TeacherTrain teacherTrain = new TeacherTrain
  357. {
  358. pk = "TeacherTrain",
  359. id = x,
  360. code = $"TeacherTrain-{change.school}",
  361. tmdid = x,
  362. school = change.school,
  363. update = new HashSet<string> { StatisticsService.TeacherAbility,
  364. StatisticsService.TeacherClass, StatisticsService.OfflineRecord }
  365. };
  366. teacherTrain.update.UnionWith(change.update);
  367. task.Add(client.GetContainer(Constant.TEAMModelOS, "Teacher").UpsertItemAsync<TeacherTrain>(teacherTrain, new PartitionKey($"TeacherTrain-{change.school}")));
  368. });
  369. await task.TaskPage(1);
  370. }
  371. }
  372. else
  373. {
  374. Area area = null;
  375. string sql = $"select value(c) from c where c.standard='{change.standard}'";
  376. await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "Normal").GetItemQueryIterator<Area>(queryText: sql,
  377. requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Base-Area") }))
  378. {
  379. area = item;
  380. }
  381. AreaSetting setting = null;
  382. if (area != null)
  383. {
  384. try
  385. {
  386. //优先找校级
  387. setting = await client.GetContainer(Constant.TEAMModelOS, "School").ReadItemAsync<AreaSetting>(change.school, new PartitionKey("AreaSetting"));
  388. }
  389. catch (CosmosException)
  390. {
  391. try
  392. {
  393. setting = await client.GetContainer(Constant.TEAMModelOS, "Normal").ReadItemAsync<AreaSetting>(area.id, new PartitionKey("AreaSetting"));
  394. }
  395. catch (CosmosException)
  396. {
  397. setting = null;
  398. }
  399. }
  400. }
  401. if (setting == null)
  402. {
  403. setting = new AreaSetting
  404. {
  405. allTime = 50,
  406. classTime = 5,
  407. submitTime = 15,
  408. onlineTime = 20,
  409. offlineTime = 10,
  410. lessonMinutes = 45,
  411. };
  412. }
  413. List<Task<TeacherTrain>> task = new List<Task<TeacherTrain>>();
  414. teacherTrains.ForEach(x => {
  415. x.update.UnionWith(change.update);
  416. task.Add(StatisticsService.StatisticsTeacher(x, setting, area, client, null));
  417. });
  418. await task.TaskPage(1);
  419. var unchange = change.tmdids.Except(teacherTrains.Select(x => x.id));
  420. if (unchange != null)
  421. {
  422. task.Clear();
  423. unchange.ToList().ForEach(x => {
  424. task.Add(StatisticsService.StatisticsTeacher(new TeacherTrain
  425. {
  426. pk = "TeacherTrain",
  427. id = x,
  428. code = $"TeacherTrain-{change.school}",
  429. tmdid = x,
  430. school = change.school,
  431. update = new HashSet<string> { StatisticsService.TeacherAbility,
  432. StatisticsService.TeacherClass, StatisticsService.OfflineRecord }
  433. }, setting, area, client, null));
  434. });
  435. await task.TaskPage(1);
  436. }
  437. }
  438. } catch (Exception ex) {
  439. await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}-研修数据变更,重新统计-TeacherTrainChange\n{msg}\n{ex.Message}\n{ex.StackTrace}", GroupNames.成都开发測試群組);
  440. }
  441. }
  442. /// <summary>
  443. /// 完善课程变更,StuListChange, originCode是学校编码 则表示名单是学校自定义名单,如果是tmdid则表示醍摩豆的私有名单,scope=school,private。
  444. /// </summary>
  445. /// <data msg>
  446. /// CourseChange
  447. ///// </data>
  448. /// <param name="msg"></param>
  449. /// <returns></returns>
  450. [FunctionName("GroupChange")]
  451. public async Task GroupChangeFunc([ServiceBusTrigger("%Azure:ServiceBus:ActiveTask%", "group-change", Connection = "Azure:ServiceBus:ConnectionString")] string msg)
  452. {
  453. var client = _azureCosmos.GetCosmosClient();
  454. try
  455. {
  456. //await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}-名单成员变更-GroupChange\n{msg}", GroupNames.成都开发測試群組);
  457. var jsonMsg = JsonDocument.Parse(msg);
  458. GroupChange groupChange = msg.ToObject<GroupChange>();
  459. //名单变动修改学生课程关联信息
  460. //await StuListService.FixStuCourse(client, stuListChange);
  461. //Vote投票 Survey问卷 Exam评测 Learn学习活动 Homework作业活动
  462. //名单变动修改学生问卷关联信息
  463. await ActivityService.FixActivity(client, _dingDing, groupChange, "Survey");
  464. //名单变动修改学生投票关联信息
  465. await ActivityService.FixActivity(client, _dingDing, groupChange, "Vote");
  466. //名单变动修改学生评测关联信息
  467. await ActivityService.FixActivity(client, _dingDing, groupChange, "Exam");
  468. //名单变动修改学生研修关联信息
  469. await ActivityService.FixActivity(client, _dingDing, groupChange, "Study");
  470. //名单变动修改学生简易评测关联信息
  471. await ActivityService.FixActivity(client, _dingDing, groupChange, "ExamLite");
  472. //TODO学习活动
  473. //await FixActivity(client, stuListChange, "Learn");
  474. //名单变动修改学生作业活动信息
  475. await ActivityService.FixActivity(client, _dingDing, groupChange, "Homework");
  476. if (groupChange.type == null || !groupChange.type.Equals("research") || !groupChange.type.Equals("yxtrain")|| !groupChange.type.Equals("activity"))
  477. {
  478. //课程名单变动修改学生课程关联信息
  479. await ActivityService.FixStuCourse(client, _dingDing, groupChange);
  480. }
  481. }
  482. catch (Exception ex)
  483. {
  484. await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}-GroupChange-GroupChange\n{ex.Message}\n{ex.StackTrace}\n{msg}", GroupNames.成都开发測試群組);
  485. }
  486. }
  487. [FunctionName("ItemCond")]
  488. public async Task ItemCondFunc([ServiceBusTrigger("%Azure:ServiceBus:ItemCondQueue%", Connection = "Azure:ServiceBus:ConnectionString")] string msg)
  489. {
  490. try
  491. {
  492. var client = _azureCosmos.GetCosmosClient();
  493. var jsonMsg = JsonDocument.Parse(msg);
  494. List<ItemCondDto> itemCondDtos = msg.ToObject<List<ItemCondDto>>();
  495. foreach (var itemCondDto in itemCondDtos)
  496. {
  497. if (itemCondDto.scope.Equals("school")) {
  498. ItemCond itemCond = null;
  499. List<ItemInfo> items = new List<ItemInfo>();
  500. var queryslt = $"SELECT c.gradeIds,c.subjectId,c.periodId,c.type,c.level,c.field ,c.scope FROM c where c.periodId='{itemCondDto.filed}' and c.pid= null ";
  501. await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryIterator<ItemInfo>(queryText: queryslt, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Item-{itemCondDto.key}") }))
  502. {
  503. items.Add(item);
  504. }
  505. itemCond = new ItemCond()
  506. {
  507. id = $"{itemCondDto.filed}",
  508. code = $"ItemCond-{itemCondDto.key}",
  509. pk = "ItemCond",
  510. ttl = -1,
  511. count = items.Count,
  512. grades = new List<GradeCount>(),
  513. subjects = new List<SubjectCount>()
  514. };
  515. items.ForEach(z =>
  516. {
  517. if (!string.IsNullOrEmpty(z.type)) {
  518. ItemService.CountItemCond(z, null, itemCond);
  519. }
  520. });
  521. await _azureRedis.GetRedisClient(8).HashSetAsync($"ItemCond:{itemCondDto.key}", $"{itemCondDto.filed}", itemCond.ToJsonString());
  522. }
  523. else
  524. {
  525. ItemCond itemCond = null;
  526. List<ItemInfo> items = new List<ItemInfo>();
  527. var queryslt = $"SELECT c.gradeIds,c.subjectId,c.periodId,c.type,c.level,c.field ,c.scope FROM c where c.pid= null ";
  528. await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "Teacher").GetItemQueryIterator<ItemInfo>(queryText: queryslt, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Item-{itemCondDto.filed}") }))
  529. {
  530. items.Add(item);
  531. }
  532. itemCond = new ItemCond() { id = $"{itemCondDto.filed}", code = $"ItemCond", pk = "ItemCond", ttl = -1, count = items.Count };
  533. items.ForEach(z =>
  534. {
  535. if (!string.IsNullOrEmpty(z.type))
  536. {
  537. ItemService.CountItemCond(z, null, itemCond);
  538. }
  539. });
  540. await _azureRedis.GetRedisClient(8).HashSetAsync($"ItemCond:ItemCond", $"{itemCondDto.filed}", itemCond.ToJsonString());
  541. }
  542. }
  543. }
  544. catch (CosmosException ex )
  545. {
  546. await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}-ServiceBus,ItemCond()\n{ex.Message}\n{ex.StackTrace}\n{msg}", GroupNames.醍摩豆服務運維群組);
  547. }
  548. catch (Exception ex)
  549. {
  550. await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}-ServiceBus,ItemCond()\n{ex.Message}\n{ex.StackTrace}\n{msg}", GroupNames.醍摩豆服務運維群組);
  551. }
  552. }
  553. //更新產品一覽表
  554. [FunctionName("Product")]
  555. public async Task ProductFunc([ServiceBusTrigger("%Azure:ServiceBus:ActiveTask%", "product", Connection = "Azure:ServiceBus:ConnectionString")] string msg, ILogger log)
  556. {
  557. try
  558. {
  559. var jsonMsg = JsonDocument.Parse(msg);
  560. jsonMsg.RootElement.TryGetProperty("method", out JsonElement method);
  561. jsonMsg.RootElement.TryGetProperty("schoolId", out JsonElement schoolId);
  562. jsonMsg.RootElement.TryGetProperty("prodCode", out JsonElement prodCode);
  563. jsonMsg.RootElement.TryGetProperty("prodId", out JsonElement prodId);
  564. var client = _azureCosmos.GetCosmosClient();
  565. string strQuery = string.Empty;
  566. //取得所有學校產品
  567. ////序號
  568. List<SchoolProductSumData> serialsProductSumOrg = new List<SchoolProductSumData>();
  569. strQuery = $"SELECT * FROM c WHERE c.dataType = 'serial'";
  570. await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryStreamIterator(queryText: strQuery, requestOptions: new QueryRequestOptions() { PartitionKey = new Azure.Cosmos.PartitionKey($"Product-{schoolId}") }))
  571. {
  572. using var json = await JsonDocument.ParseAsync(item.ContentStream);
  573. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  574. {
  575. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  576. {
  577. SchoolProductSerial serialInfo = obj.ToObject<SchoolProductSerial>();
  578. SchoolProductSumData serialProd = serialsProductSumOrg.Where(sp => sp.prodCode == serialInfo.prodCode).FirstOrDefault();
  579. if(serialProd == null)
  580. {
  581. SchoolProductSumData serialProdAdd = new SchoolProductSumData();
  582. serialProdAdd.prodCode = serialInfo.prodCode;
  583. serialProdAdd.ids.Add(serialInfo.id);
  584. serialProdAdd.avaliable = serialProdAdd.ids.Count;
  585. serialsProductSumOrg.Add(serialProdAdd);
  586. }
  587. else
  588. {
  589. if(!serialProd.ids.Contains(serialInfo.id))
  590. {
  591. serialProd.ids.Add(serialInfo.id);
  592. }
  593. serialProd.avaliable = serialProd.ids.Count;
  594. }
  595. }
  596. }
  597. }
  598. ////服務
  599. List<SchoolProductSumData> servicesProductSumOrg = new List<SchoolProductSumData>();
  600. long timestampToday = DateTimeOffset.UtcNow.AddSeconds(1).ToUnixTimeSeconds(); //比現實時間延遲1秒
  601. strQuery = $"SELECT * FROM c WHERE c.dataType = 'service' AND c.startDate <= {timestampToday} AND {timestampToday} <= c.endDate AND c.ttl < 0"; //在授權期間、ttl < 0 才取
  602. await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryStreamIterator(queryText: strQuery, requestOptions: new QueryRequestOptions() { PartitionKey = new Azure.Cosmos.PartitionKey($"Product-{schoolId}") }))
  603. {
  604. using var json = await JsonDocument.ParseAsync(item.ContentStream);
  605. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  606. {
  607. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  608. {
  609. SchoolProductService serviceInfo = obj.ToObject<SchoolProductService>();
  610. SchoolProductSumData serviceProd = servicesProductSumOrg.Where(sp => sp.prodCode == serviceInfo.prodCode).FirstOrDefault();
  611. if (serviceProd == null)
  612. {
  613. SchoolProductSumData serviceProdAdd = new SchoolProductSumData();
  614. serviceProdAdd.prodCode = serviceInfo.prodCode;
  615. serviceProdAdd.avaliable = 0;
  616. serviceProdAdd.ids.Add(serviceInfo.id);
  617. serviceProdAdd.avaliable += serviceInfo.number;
  618. servicesProductSumOrg.Add(serviceProdAdd);
  619. }
  620. else
  621. {
  622. if (!serviceProd.ids.Contains(serviceInfo.id))
  623. {
  624. serviceProd.ids.Add(serviceInfo.id);
  625. serviceProd.avaliable += serviceInfo.number;
  626. }
  627. }
  628. }
  629. }
  630. }
  631. ////服務產品特別對應項
  632. if (servicesProductSumOrg.Count > 0)
  633. {
  634. foreach (SchoolProductSumData servicesProductSumOrgRow in servicesProductSumOrg)
  635. {
  636. //更新學校空間
  637. if (servicesProductSumOrgRow.prodCode.Equals("IPALJ6NY"))
  638. {
  639. School school = await client.GetContainer(Constant.TEAMModelOS, "School").ReadItemAsync<School>($"{schoolId}", new PartitionKey("Base"));
  640. school.size = (servicesProductSumOrgRow.avaliable < 1) ? 1 : servicesProductSumOrgRow.avaliable;
  641. await client.GetContainer(Constant.TEAMModelOS, "School").ReplaceItemAsync<School>(school, $"{schoolId}", new PartitionKey("Base"));
  642. }
  643. }
  644. }
  645. ////硬體
  646. List<SchoolProductSumDataHard> hardsProductSumOrg = new List<SchoolProductSumDataHard>();
  647. strQuery = $"SELECT * FROM c WHERE c.dataType = 'hard'";
  648. await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryStreamIterator(queryText: strQuery, requestOptions: new QueryRequestOptions() { PartitionKey = new Azure.Cosmos.PartitionKey($"Product-{schoolId}") }))
  649. {
  650. using var json = await JsonDocument.ParseAsync(item.ContentStream);
  651. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  652. {
  653. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  654. {
  655. SchoolProductHard hardInfo = obj.ToObject<SchoolProductHard>();
  656. SchoolProductSumData hardProd = hardsProductSumOrg.Where(sp => sp.prodCode == hardInfo.prodCode).FirstOrDefault();
  657. if (hardProd == null)
  658. {
  659. SchoolProductSumDataHard hardProdAdd = new SchoolProductSumDataHard();
  660. hardProdAdd.prodCode = hardInfo.prodCode;
  661. hardProdAdd.model = hardInfo.model;
  662. hardProdAdd.ids.Add(hardInfo.id);
  663. hardProdAdd.avaliable = hardProdAdd.ids.Count;
  664. hardsProductSumOrg.Add(hardProdAdd);
  665. }
  666. else
  667. {
  668. if (!hardProd.ids.Contains(hardInfo.id))
  669. {
  670. hardProd.ids.Add(hardInfo.id);
  671. }
  672. hardProd.avaliable = hardProd.ids.Count;
  673. }
  674. }
  675. }
  676. }
  677. //更新學校產品一覽表
  678. SchoolProductSum prodSum = await client.GetContainer(Constant.TEAMModelOS, "School").ReadItemAsync<SchoolProductSum>(schoolId.ToString(), new PartitionKey($"ProductSum"));
  679. prodSum.serial = serialsProductSumOrg;
  680. prodSum.service = servicesProductSumOrg;
  681. prodSum.hard = hardsProductSumOrg;
  682. await client.GetContainer(Constant.TEAMModelOS, "School").ReplaceItemAsync<SchoolProductSum>(prodSum, prodSum.id, new PartitionKey($"{prodSum.code}"));
  683. }
  684. catch (CosmosException ex)
  685. {
  686. await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}-ServiceBus,Product()\n{ex.Message}\n{ex.StackTrace}\n{msg}", GroupNames.醍摩豆服務運維群組);
  687. }
  688. catch (Exception ex)
  689. {
  690. await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}-ServiceBus,Product()\n{ex.Message}\n{ex.StackTrace}\n{msg}", GroupNames.醍摩豆服務運維群組);
  691. }
  692. }
  693. /// <summary>
  694. /// 批量复制文件
  695. /// </summary>
  696. /// <param name="msg"></param>
  697. /// <returns></returns>
  698. [FunctionName("CopyStandardFile")]
  699. public async Task BatchCopyBlobFunc([ServiceBusTrigger("%Azure:ServiceBus:ActiveTask%", "copy-standard-file", Connection = "Azure:ServiceBus:ConnectionString")] string msg)
  700. {
  701. try
  702. {
  703. //await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}-Blob复制文件-CopyStandardFile\n{msg}", GroupNames.成都开发測試群組);
  704. var jsonMsg = JsonDocument.Parse(msg);
  705. BatchCopyFile bIBatchCopyFile = msg.ToObject<BatchCopyFile>();
  706. //批量复制文件
  707. var result = await BatchCopyFileService.CopyFile(_dingDing, _azureStorage, bIBatchCopyFile);
  708. if (result == 200)
  709. {
  710. //发送消息实体
  711. Notification notification = new Notification
  712. {
  713. hubName = "hita",
  714. type = "msg",
  715. from = $"ies5:{_option.Location}:private",
  716. to = bIBatchCopyFile.tmdIds,
  717. label = $"{bIBatchCopyFile.codeKey}_finish",
  718. body = new { location = _option.Location, biz = $"{bIBatchCopyFile.codeKey}", tmdid = $"{bIBatchCopyFile.tmdid}", tmdname = $"{bIBatchCopyFile.tmdName}", status = 1, time = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() }.ToJsonString(),
  719. expires = DateTimeOffset.UtcNow.AddDays(7).ToUnixTimeSeconds()
  720. };
  721. var url = _configuration.GetValue<string>("HaBookAuth:CoreService:sendnotification");
  722. var clientID = _configuration.GetValue<string>("HaBookAuth:CoreService:clientID");
  723. var clientSecret = _configuration.GetValue<string>("HaBookAuth:CoreService:clientSecret");
  724. var location = _option.Location;
  725. await _notificationService.SendNotification(clientID, clientSecret, location, url, notification); //站内发送消息
  726. }
  727. }
  728. catch (Exception ex)
  729. {
  730. await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}-Blob复制文件-CopyStandardFile\n{ex.Message}\n{ex.StackTrace}\n{msg}", GroupNames.醍摩豆服務運維群組);
  731. }
  732. }
  733. }
  734. }