IESHttpTrigger.cs 65 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191
  1. using Azure.Cosmos;
  2. using Azure.Storage.Blobs.Models;
  3. using Azure.Storage.Sas;
  4. using DocumentFormat.OpenXml.Bibliography;
  5. using DocumentFormat.OpenXml.Drawing.Wordprocessing;
  6. using DocumentFormat.OpenXml.Office2010.Excel;
  7. using DocumentFormat.OpenXml.Office2013.Excel;
  8. using DocumentFormat.OpenXml.Spreadsheet;
  9. using HTEXLib;
  10. using HTEXLib.COMM.Helpers;
  11. using HTEXLib.Models;
  12. using Microsoft.AspNetCore.Components;
  13. using Microsoft.Azure.Cosmos.Table;
  14. using Microsoft.Azure.Functions.Worker;
  15. using Microsoft.Azure.Functions.Worker.Http;
  16. using Microsoft.Extensions.Configuration;
  17. using Microsoft.Extensions.Hosting;
  18. using Microsoft.Extensions.Options;
  19. using OpenXmlPowerTools;
  20. using StackExchange.Redis;
  21. using System;
  22. using System.Collections.Generic;
  23. using System.Dynamic;
  24. using System.IO;
  25. using System.Linq;
  26. using System.Net;
  27. using System.Net.Http;
  28. using System.Net.Http.Json;
  29. using System.Reflection;
  30. using System.Security.Policy;
  31. using System.Text;
  32. using System.Text.Json;
  33. using System.Threading;
  34. using System.Threading.Tasks;
  35. using System.Web;
  36. using TEAMModelOS.Models;
  37. using TEAMModelOS.SDK;
  38. using TEAMModelOS.SDK.Context.Constant;
  39. using TEAMModelOS.SDK.DI;
  40. using TEAMModelOS.SDK.Extension;
  41. using TEAMModelOS.SDK.Models;
  42. using TEAMModelOS.SDK.Models.Dtos;
  43. using TEAMModelOS.SDK.Models.Service;
  44. using TEAMModelOS.SDK.Models.Table;
  45. using static TEAMModelOS.SDK.Models.Teacher;
  46. using static TEAMModelOS.SDK.Services.ActivityStudentService;
  47. using static TEAMModelOS.SDK.Services.BlobService;
  48. using Path = System.IO.Path;
  49. namespace TEAMModelOS.FunctionV4
  50. {
  51. public class IESHttpTrigger
  52. {
  53. private readonly AzureCosmosFactory _azureCosmos;
  54. private readonly DingDing _dingDing;
  55. private readonly AzureStorageFactory _azureStorage;
  56. private readonly AzureRedisFactory _azureRedis;
  57. private readonly IHttpClientFactory _httpClient;
  58. private readonly Option _option;
  59. private readonly CoreAPIHttpService _coreAPIHttpService;
  60. private readonly IConfiguration _configuration;
  61. public IESHttpTrigger( AzureCosmosFactory azureCosmos, DingDing dingDing, CoreAPIHttpService coreAPIHttpService, AzureStorageFactory azureStorage
  62. , AzureRedisFactory azureRedis, IHttpClientFactory httpClient, IOptionsSnapshot<Option> option,
  63. IConfiguration configuration)
  64. {
  65. _azureCosmos = azureCosmos;
  66. _dingDing = dingDing;
  67. _azureStorage = azureStorage;
  68. _azureRedis = azureRedis;
  69. _httpClient = httpClient;
  70. _coreAPIHttpService = coreAPIHttpService;
  71. _option = option?.Value;
  72. _configuration = configuration;
  73. }
  74. /// <summary>
  75. /// </summary>
  76. /// <param name="req"></param>
  77. /// <param name="log"></param>
  78. /// <returns></returns>
  79. [Function("upsert-student-portrait")]
  80. public async Task<HttpResponseData> UpsertStudentPortrait([HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)] HttpRequestData req)
  81. {
  82. string data = await new StreamReader(req.Body).ReadToEndAsync();
  83. var json = JsonDocument.Parse(data).RootElement;
  84. var response = req.CreateResponse(HttpStatusCode.OK);
  85. var responseData = await OpenApiService.UpsertStudentPortrait(_azureCosmos, _dingDing,_azureRedis, json);
  86. await response.WriteAsJsonAsync(new {data = responseData });
  87. return response;
  88. }
  89. /// <summary>
  90. /// </summary>
  91. /// <param name="req"></param>
  92. /// <param name="log"></param>
  93. /// <returns></returns>
  94. [Function("system-info-function")]
  95. public async Task<HttpResponseData> SystemInfo([HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)] HttpRequestData req)
  96. {
  97. var response = req.CreateResponse(HttpStatusCode.OK);
  98. Type attr = this.GetType();
  99. string currentDirectory = Path.GetDirectoryName(attr.Assembly.Location);
  100. Assembly assembly = Assembly.LoadFrom($"{currentDirectory}\\TEAMModelOS.FunctionV4.dll");
  101. var description = assembly.GetCustomAttribute<AssemblyDescriptionAttribute>().Description;
  102. //var v1 = Assembly.GetEntryAssembly().GetName().Version;
  103. //var v2 = Assembly.GetEntryAssembly().GetCustomAttribute<AssemblyFileVersionAttribute>().Version;
  104. // var description = Assembly.GetEntryAssembly().GetCustomAttribute<AssemblyDescriptionAttribute>().Description;
  105. var version = Assembly.GetEntryAssembly().GetCustomAttribute<AssemblyFileVersionAttribute>().Version;
  106. long nowtime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
  107. //Console.WriteLine($"Assembly.GetEntryAssembly().GetName().Version: " +
  108. // $"{Assembly.GetEntryAssembly().GetName().Version}");5.2107.12.1
  109. //Console.WriteLine($"Assembly.GetEntryAssembly().GetCustomAttribute<AssemblyFileVersionAttribute>().Version:" +
  110. // $"{Assembly.GetEntryAssembly().GetCustomAttribute<AssemblyFileVersionAttribute>().Version}");5.2107.12.1
  111. //Console.WriteLine($"Assembly.GetEntryAssembly().GetCustomAttribute<AssemblyInformationalVersionAttribute>().InformationalVersion:" +
  112. // $"{Assembly.GetEntryAssembly().GetCustomAttribute<AssemblyInformationalVersionAttribute>().InformationalVersion}");5.2107.12
  113. await response.WriteAsJsonAsync(new { version, description, nowtime });
  114. return response;
  115. }
  116. /// <summary>
  117. /// 空间不足发送通知
  118. /// </summary>
  119. /// <param name="req"></param>
  120. /// <param name="log"></param>
  121. /// <returns></returns>
  122. [Function("surplus-space-notify")]
  123. public async Task<HttpResponseData> SurplusSpaceNotify([HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)] HttpRequestData req) {
  124. var response = req.CreateResponse(HttpStatusCode.OK);
  125. string msg = "";
  126. try
  127. {
  128. string data = await new StreamReader(req.Body).ReadToEndAsync();
  129. var json = JsonDocument.Parse(data).RootElement;
  130. json.TryGetProperty("name", out JsonElement _name);
  131. json.TryGetProperty("scope", out JsonElement _scope);
  132. json.TryGetProperty("percent", out JsonElement _percent);
  133. double percent= _percent.GetDouble();
  134. string name = _name.ToString();
  135. string scope = _scope.ToString();
  136. int tag = 11;
  137. if (percent <= 10 && percent > 5)
  138. {
  139. tag = 10;
  140. }
  141. else if (percent <= 5 && percent > 0)
  142. {
  143. tag = 5;
  144. }
  145. else if (percent <= 0)
  146. {
  147. tag = 0;
  148. }
  149. List<IdNameCode> ids = new List<IdNameCode>();
  150. Teacher teacher = null;
  151. School school = null;
  152. if (scope.Equals("school", StringComparison.OrdinalIgnoreCase))
  153. {
  154. school = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.School).ReadItemAsync<School>(name, new PartitionKey("Base"));
  155. string sql = $"select value c from c where c.code='Teacher-{name}' and c.status='join' and array_contains(c.roles,'admin') ";
  156. List<SchoolTeacher> adminTeachers = new List<SchoolTeacher>();
  157. await foreach (var item in _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.School)
  158. .GetItemQueryIterator<SchoolTeacher>(queryText: sql, requestOptions: new QueryRequestOptions { PartitionKey = new PartitionKey($"Teacher-{name}") }))
  159. {
  160. adminTeachers.Add(item);
  161. }
  162. if (adminTeachers.IsNotEmpty())
  163. {
  164. string sqlAdmin = $"select c.id,c.lang as code ,c.name from c where c.id in ({string.Join(",", adminTeachers.Select(z => $"'{z.id}'"))}) ";
  165. await foreach (var item in _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher)
  166. .GetItemQueryIterator<IdNameCode>(queryText: sqlAdmin, requestOptions: new QueryRequestOptions { PartitionKey = new PartitionKey($"Base") }))
  167. {
  168. ids.Add(item);
  169. }
  170. }
  171. }
  172. else
  173. {
  174. teacher= await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher).ReadItemAsync<Teacher>(name, new PartitionKey("Base"));
  175. ids.Add(new IdNameCode
  176. {
  177. id = teacher.id,
  178. name = teacher.name,
  179. code = teacher.lang
  180. });
  181. }
  182. //如果已经扩容请忽略此通知!
  183. string key = scope.Equals("school", StringComparison.OrdinalIgnoreCase) ? $"Blob:Space:School:Notify:{name}" : $"Blob:Space:Private:Notify:{name}";
  184. foreach (var idnamecode in ids)
  185. {
  186. string filed = $"{idnamecode.id}-{tag}";
  187. BlobSpaceNotify? blobSpaceNotify = null;
  188. RedisValue value = await _azureRedis.GetRedisClient(8).HashGetAsync(key, filed);
  189. if (value != default && !value.IsNullOrEmpty)
  190. {
  191. blobSpaceNotify = value.ToString().ToObject<BlobSpaceNotify>();
  192. }
  193. if (tag < 11)
  194. {
  195. if (blobSpaceNotify == null)
  196. {
  197. if ("school".Equals(scope, StringComparison.OrdinalIgnoreCase))
  198. {
  199. _coreAPIHttpService.PushNotify(new List<IdNameCode> { idnamecode}, $"blob-space-school-notify", Constant.NotifyType_IES5_Management,
  200. new Dictionary<string, object> { { "tmdname", idnamecode.name }, { "schoolName", school.name }, { "percent", $"{tag}" }, { "schoolId", $"{school.id}" }, { "tmdid", idnamecode.id } },
  201. $"{Environment.GetEnvironmentVariable("Option:Location")}", _configuration, _dingDing, "");
  202. }
  203. else
  204. {
  205. _coreAPIHttpService.PushNotify(new List<IdNameCode> { idnamecode }, $"blob-space-private-notify", Constant.NotifyType_IES5_Management,
  206. new Dictionary<string, object> { { "tmdname", idnamecode.name }, { "percent", $"{tag}" }, { "tmdid", idnamecode.id } },
  207. $"{Environment.GetEnvironmentVariable("Option:Location")}", _configuration, _dingDing, "");
  208. }
  209. blobSpaceNotify = new BlobSpaceNotify { id = idnamecode.id, tag = tag, containerName = name, scope = scope, notifyIndex = Guid.NewGuid().ToString() };
  210. await _azureRedis.GetRedisClient(8).HashSetAsync(key, filed, blobSpaceNotify.ToJsonString());
  211. await _azureRedis.GetRedisClient(8).KeyExpireAsync(key, new TimeSpan(hours:7*24,minutes:0,seconds:0));
  212. }
  213. else {
  214. //已经发送过的不在提交
  215. }
  216. }
  217. else
  218. {
  219. if (blobSpaceNotify != null)
  220. {
  221. //撤销
  222. var index = blobSpaceNotify.notifyIndex;
  223. await _azureRedis.GetRedisClient(8).HashDeleteAsync(key, filed);
  224. _coreAPIHttpService.CancelNotify(index, $"{Environment.GetEnvironmentVariable("Option:Location")}", _configuration);
  225. }
  226. }
  227. }
  228. }
  229. catch (Exception ex) {
  230. await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")},空间不足,通知发送处理异常{ex.Message}\n{ex.StackTrace}", GroupNames.成都开发測試群組);
  231. }
  232. return response;
  233. }
  234. /// <summary>
  235. /// 区级艺术评价变更,异步同步已开启数据同步的学校。
  236. /// </summary>
  237. /// <param name="req"></param>
  238. /// <param name="log"></param>
  239. /// <returns></returns>
  240. [Function("area-artsetting-change")]
  241. public async Task<HttpResponseData> AreaArtSettingChange([HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)] HttpRequestData req) {
  242. var response = req.CreateResponse(HttpStatusCode.OK);
  243. string msg = "";
  244. try {
  245. string data = await new StreamReader(req.Body).ReadToEndAsync();
  246. var json = JsonDocument.Parse(data).RootElement;
  247. json.TryGetProperty("areaId", out JsonElement _areaId);
  248. string schoolSQL = $"select value c from c where c.areaId='{_areaId}'";
  249. List<School> schools = new List<School>();
  250. await foreach (var item in _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.School)
  251. .GetItemQueryIterator<School>(queryText: schoolSQL, requestOptions: new QueryRequestOptions { PartitionKey = new PartitionKey($"Base") }))
  252. {
  253. schools.Add(item);
  254. }
  255. ArtSetting artSetting = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Normal).ReadItemAsync<ArtSetting>($"{_areaId}", new PartitionKey("ArtSetting"));
  256. foreach (var school in schools)
  257. {
  258. msg = school.ToJsonString();
  259. List<Period> periods = new List<Period>();
  260. var hastype_period = school.period.Where(p => !string.IsNullOrWhiteSpace(p.periodType));
  261. if (hastype_period.Any())
  262. {
  263. periods.AddRange(hastype_period);
  264. }
  265. var nottype_period = school.period.Where(p => string.IsNullOrWhiteSpace(p.periodType));
  266. if (nottype_period!=null && nottype_period.Count()>0)
  267. {
  268. foreach (var period in nottype_period)
  269. {
  270. if (period.name.Contains("小学"))
  271. {
  272. period.periodType= "primary";
  273. }
  274. if (period.name.Contains("初中"))
  275. {
  276. period.periodType = "junior";
  277. }
  278. if (period.name.Contains("高中"))
  279. {
  280. period.periodType = "senior";
  281. }
  282. if (string.IsNullOrWhiteSpace(period.periodType) && school.period.Count == 1)
  283. {
  284. if (school.name.Contains("小学"))
  285. {
  286. period.periodType = "primary";
  287. }
  288. if (school.name.Contains("初中"))
  289. {
  290. period.periodType = "junior";
  291. }
  292. if (school.name.Contains("高中"))
  293. {
  294. period.periodType = "senior";
  295. }
  296. }
  297. if (!string.IsNullOrWhiteSpace(period.periodType))
  298. {
  299. periods.Add(period);
  300. }
  301. }
  302. }
  303. foreach (var period in periods)
  304. {
  305. var dimension = artSetting.dimensions.FindAll(x => x.type.Intersect(new List<string> { period.periodType }).Any());
  306. var bindIds = period.subjects.Where(s => !string.IsNullOrWhiteSpace(s.bindId)).Select(x => x.bindId);
  307. //该学段未同步学科的。
  308. var unBindIds = dimension.Where(z => !string.IsNullOrWhiteSpace(z.subjectBind)).Select(x => x.subjectBind).ToHashSet().Except(bindIds);
  309. if (unBindIds.Any() && unBindIds.Count()>0)
  310. {
  311. //尝试寻找同名学科且没有设置bindId的
  312. foreach (var unBindId in unBindIds)
  313. {
  314. var subjects = artSetting.dimensions.FindAll(d => !string.IsNullOrWhiteSpace(d.subjectBind) && !string.IsNullOrWhiteSpace(d.subject) && d.subjectBind.Equals(unBindId))?.Select(m => m.subject);
  315. if (subjects != null)
  316. {
  317. foreach (var subject in subjects)
  318. {
  319. //获取同名学科,且没绑定的
  320. var sub = period.subjects.FindAll(sub => sub.name.Contains(subject) && string.IsNullOrWhiteSpace(sub.bindId));
  321. if (sub.IsNotEmpty())
  322. {
  323. sub[0].bindId = unBindId;
  324. }
  325. else
  326. {
  327. period.subjects.Add(new Subject { id = Guid.NewGuid().ToString(), name = subject, bindId = unBindId, type = 1 });
  328. }
  329. break;
  330. }
  331. }
  332. }
  333. }
  334. var period_subjects = period.subjects.Where(s => !string.IsNullOrWhiteSpace(s.bindId));
  335. foreach (var subject in period_subjects)
  336. {
  337. var dim = dimension.Where(x => x.subjectBind.Equals(subject.bindId));
  338. if (dim.Any())
  339. {
  340. Knowledge old = null;
  341. string sql = $"select value(c) from c where c.periodId = '{period.id}'";
  342. string pk = $"Knowledge-{school.id}-{subject.id}";
  343. await foreach (var item in _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "School").
  344. GetItemQueryIterator<Knowledge>(queryText: sql, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey(pk) }))
  345. {
  346. old = item;
  347. break;
  348. }
  349. //同步知识块。
  350. if (old != null)
  351. {
  352. bool change = false;
  353. //如果之前的是1 来源于区级,后面因区级删除,应该还原为0。
  354. var oldBlocks = old.blocks.Select(x => x.name).ToHashSet();
  355. var dimBlocks = dim.SelectMany(d => d.blocks);
  356. //增加的
  357. var addBlocks = dimBlocks.Except(oldBlocks);
  358. //减少的
  359. var cutBlocks = oldBlocks.Except(dimBlocks);
  360. foreach (var add in addBlocks)
  361. {
  362. old.blocks.Add(new Block { name = add, source = 1 });
  363. change = true;
  364. }
  365. //减少的还原为0
  366. if (cutBlocks.Any())
  367. {
  368. old.blocks.ForEach(ob => {
  369. if (cutBlocks.Contains(ob.name))
  370. {
  371. ob.source = 0;
  372. change = true;
  373. }
  374. });
  375. }
  376. foreach (var db in dimBlocks) {
  377. old.blocks.ForEach(ob => {
  378. if (db.Equals(ob.name))
  379. {
  380. ob.source = 1;
  381. change = true;
  382. }
  383. });
  384. }
  385. if (change)
  386. {
  387. await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.School).ReplaceItemAsync(old, old.id, new PartitionKey(old.code));
  388. }
  389. var count = new { pcount = old.points != null ? old.points.Count : 0, bcount = old.blocks != null ? old.blocks.Count : 0 };
  390. //处理知识点,知识块计数问题
  391. await _azureRedis.GetRedisClient(8).HashSetAsync($"Knowledge:Count:{old.owner}-{old.subjectId}", old.periodId, count.ToJsonString());
  392. }
  393. else
  394. {
  395. var blocks = dim.SelectMany(x => x.blocks).Select(bs => new Block { name = bs, source = 1 });
  396. if (blocks.Any())
  397. {
  398. var _new = new Knowledge
  399. {
  400. id = Guid.NewGuid().ToString(),
  401. pk = "Knowledge",
  402. code = pk,
  403. owner = school.id,
  404. periodId = period.id,
  405. subjectId = subject.id,
  406. blocks = blocks.ToList()
  407. };
  408. await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.School).CreateItemAsync(_new, new PartitionKey(_new.code));
  409. var count = new { pcount = _new.points != null ? _new.points.Count : 0, bcount = _new.blocks != null ? _new.blocks.Count : 0 };
  410. //处理知识点,知识块计数问题
  411. await _azureRedis.GetRedisClient(8).HashSetAsync($"Knowledge:Count:{_new.owner}-{_new.subjectId}", _new.periodId, count.ToJsonString());
  412. }
  413. }
  414. }
  415. }
  416. }
  417. await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.School).ReplaceItemAsync(school, school.id, new PartitionKey(school.code));
  418. }
  419. return response;
  420. } catch (Exception ex) {
  421. await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")},area-artsetting-change,{ex.Message}\n{ex.StackTrace}\n{msg}", GroupNames.醍摩豆服務運維群組);
  422. }
  423. return response;
  424. }
  425. /// <summary>
  426. /// 行政班,学生毕业状态变更。
  427. /// </summary>
  428. /// <param name="req"></param>
  429. /// <param name="log"></param>
  430. /// <returns></returns>
  431. [Function("graduate-change")]
  432. public async Task<HttpResponseData> GraduateChange([HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)] HttpRequestData req) {
  433. var response = req.CreateResponse(HttpStatusCode.OK);
  434. dynamic jsondata =new ExpandoObject() ;
  435. try {
  436. string data = await new StreamReader(req.Body).ReadToEndAsync();
  437. var json = JsonDocument.Parse(data).RootElement;
  438. jsondata = json;
  439. //await _dingDing.SendBotMsg( "毕业状态变更:"+json.ToJsonString(), GroupNames.成都开发測試群組);
  440. string schoolId = null;
  441. if (json.TryGetProperty("schoolId", out JsonElement _schoolId))
  442. {
  443. schoolId = $"{_schoolId}";
  444. }
  445. if (string.IsNullOrEmpty(schoolId))
  446. {
  447. return response;
  448. }
  449. //计算毕业的
  450. if (json.TryGetProperty("graduate_classes", out JsonElement _graduate_classes))
  451. {
  452. List<Class> graduate_classes = _graduate_classes.ToObject<List<Class>>();
  453. if (graduate_classes.IsNotEmpty())
  454. {
  455. var ids = graduate_classes.Where(x => !string.IsNullOrWhiteSpace(x.id)).Select(x => $"'{x.id}'");
  456. List<Student> students = new List<Student>();
  457. string sql = $"select value c from c where (c.graduate = 0 or IS_DEFINED(c.graduate) = false) and c.classId in ({string.Join(",", ids)})";
  458. await foreach (var item in _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Student)
  459. .GetItemQueryIterator<Student>(queryText: sql, requestOptions: new QueryRequestOptions { PartitionKey = new PartitionKey($"Base-{schoolId}") }))
  460. {
  461. item.graduate = 1;
  462. students.Add(item);
  463. }
  464. foreach (var item in students)
  465. {
  466. item.graduate = 1;
  467. await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Student).ReplaceItemAsync<Student>(item, item.id, new PartitionKey($"Base-{schoolId}"));
  468. }
  469. foreach (var item in graduate_classes)
  470. {
  471. item.graduate = 1;
  472. await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.School).ReplaceItemAsync<Class>(item, item.id, new PartitionKey($"Class-{schoolId}"));
  473. }
  474. }
  475. }
  476. //未毕业的
  477. if (json.TryGetProperty("cancel_graduate_classes", out JsonElement _cancel_graduate_classes))
  478. {
  479. List<Class> cancel_graduate_classes = _cancel_graduate_classes.ToObject<List<Class>>();
  480. if (cancel_graduate_classes.IsNotEmpty())
  481. {
  482. var ids = cancel_graduate_classes.Where(x => !string.IsNullOrWhiteSpace(x.id)).Select(x => $"'{x.id}'");
  483. List<Student> students = new List<Student>();
  484. string sql = $"select value c from c where c.graduate =1 and c.classId in ({string.Join(",", ids)})";
  485. await foreach (var item in _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Student)
  486. .GetItemQueryIterator<Student>(queryText: sql, requestOptions: new QueryRequestOptions { PartitionKey = new PartitionKey($"Base-{schoolId}") }))
  487. {
  488. item.graduate = 0;
  489. students.Add(item);
  490. }
  491. foreach (var item in students)
  492. {
  493. item.graduate = 0;
  494. await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Student).ReplaceItemAsync<Student>(item, item.id, new PartitionKey($"Base-{schoolId}"));
  495. }
  496. foreach (var item in cancel_graduate_classes)
  497. {
  498. item.graduate = 0;
  499. await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.School).ReplaceItemAsync<Class>(item, item.id, new PartitionKey($"Class-{schoolId}"));
  500. }
  501. }
  502. }
  503. } catch (Exception ex) {
  504. await _dingDing.SendBotMsg($"graduate-change,{ex.Message}\n{ex.StackTrace}\n{jsondata.ToJsonString()}",GroupNames.醍摩豆服務運維群組);
  505. }
  506. return response;
  507. }
  508. /// <summary>
  509. /// 数据推送接口
  510. /// </summary>
  511. /// <param name="req"></param>
  512. /// <param name="log"></param>
  513. /// <returns></returns>
  514. [Function("lesson-tag-change")]
  515. public async Task<HttpResponseData> LessonTagChange([HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)] HttpRequestData req) {
  516. var response = req.CreateResponse(HttpStatusCode.OK);
  517. string data = await new StreamReader(req.Body).ReadToEndAsync();
  518. var json = JsonDocument.Parse(data).RootElement;
  519. List<TagOldNew> old_new = null;
  520. string school = null;
  521. if (json.TryGetProperty("school", out JsonElement _school))
  522. {
  523. school = _school.GetString();
  524. }
  525. if (json.TryGetProperty("old_new", out JsonElement _old_new))
  526. {
  527. old_new = _old_new.ToObject<List<TagOldNew>>();
  528. }
  529. if (old_new.IsNotEmpty() && !string.IsNullOrWhiteSpace(school))
  530. {
  531. foreach (var on in old_new)
  532. {
  533. List<LessonRecord> lessonRecords = new List<LessonRecord>();
  534. string sql = $"select value(c) from c where array_contains(c.category,'{on._old}') ";
  535. await foreach (var item in _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "School").GetItemQueryIterator<LessonRecord>
  536. (queryText: sql.ToString(), requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"LessonRecord-{_school}") }))
  537. {
  538. lessonRecords.Add(item);
  539. }
  540. lessonRecords.ForEach(item =>
  541. {
  542. //修改标签
  543. if (!string.IsNullOrWhiteSpace(on._new))
  544. {
  545. for (int i = 0; i < item.category.Count; i++)
  546. {
  547. if (item.category[i].Equals(on._old))
  548. {
  549. item.category[i] = on._new;
  550. }
  551. }
  552. }
  553. else
  554. {
  555. //表示删除标签
  556. item.category.RemoveAll(x => x.Equals(on._old));
  557. }
  558. });
  559. foreach (var item in lessonRecords)
  560. {
  561. await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "School").ReplaceItemAsync(item, item.id, new PartitionKey(item.code));
  562. }
  563. }
  564. }
  565. await response.WriteAsJsonAsync(new { data = json });
  566. return response;
  567. }
  568. /// <summary>
  569. /// 数据推送接口
  570. /// </summary>
  571. /// <param name="req"></param>
  572. /// <param name="log"></param>
  573. /// <returns></returns>
  574. [Function("knowledge-change")]
  575. public async Task<HttpResponseData> KnowledgeChange([HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)] HttpRequestData req)
  576. {
  577. var response = req.CreateResponse(HttpStatusCode.OK);
  578. string data = await new StreamReader(req.Body).ReadToEndAsync();
  579. var json = JsonDocument.Parse(data).RootElement;
  580. List<TagOldNew> old_new = null;
  581. string school = null;
  582. if (json.TryGetProperty("school", out JsonElement _school))
  583. {
  584. school = _school.GetString();
  585. }
  586. if (json.TryGetProperty("old_new", out JsonElement _old_new))
  587. {
  588. old_new = _old_new.ToObject<List<TagOldNew>>();
  589. }
  590. if (old_new.IsNotEmpty() && !string.IsNullOrWhiteSpace(school))
  591. {
  592. foreach (var on in old_new)
  593. {
  594. List<ItemInfo> items = new List<ItemInfo>();
  595. string sql = $"select value(c) from c where array_contains(c.knowledge,'{on._old}') ";
  596. await foreach (var item in _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "School").GetItemQueryIterator<ItemInfo>
  597. (queryText: sql.ToString(), requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Item-{_school}") }))
  598. {
  599. items.Add(item);
  600. }
  601. items.ForEach(item =>
  602. {
  603. //修改知识点
  604. if (!string.IsNullOrEmpty(on._new))
  605. {
  606. for (int i = 0; i < item.knowledge.Count; i++)
  607. {
  608. if (item.knowledge[i].Equals(on._old))
  609. {
  610. item.knowledge[i] = on._new;
  611. }
  612. }
  613. }
  614. else
  615. {
  616. //表示删除知识点
  617. item.knowledge.RemoveAll(x => x.Equals(on._old));
  618. }
  619. });
  620. foreach (var item in items)
  621. {
  622. ItemBlob itemBlob = null;
  623. try
  624. {
  625. BlobDownloadInfo blobDownloadResult = await _azureStorage.GetBlobContainerClient($"{school}").GetBlobClient($"/item/{item.id}/{item.id}.json").DownloadAsync();
  626. if (blobDownloadResult != null)
  627. {
  628. var blob = JsonDocument.Parse(blobDownloadResult.Content);
  629. itemBlob = blob.RootElement.ToObject<ItemBlob>();
  630. itemBlob.exercise.knowledge = item.knowledge;
  631. await _azureStorage.GetBlobContainerClient($"{school}").UploadFileByContainer(itemBlob.ToJsonString(), "item", $"{item.id}/{item.id}.json", true);
  632. }
  633. }
  634. catch (Exception ex)
  635. {
  636. itemBlob = null;
  637. }
  638. await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "School").ReplaceItemAsync(item, item.id, new PartitionKey(item.code));
  639. }
  640. }
  641. }
  642. await response.WriteAsJsonAsync(new { data = json });
  643. return response;
  644. }
  645. /// <summary>
  646. /// 在线人数记录
  647. /// </summary>
  648. /// <param name="msg"></param>
  649. /// <returns></returns>
  650. [Function("online-record")]
  651. public async Task<HttpResponseData> OnlineRecord([HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)] HttpRequestData req)
  652. {
  653. var response = req.CreateResponse(HttpStatusCode.OK);
  654. string data = await new StreamReader(req.Body).ReadToEndAsync();
  655. var json = JsonDocument.Parse(data).RootElement;
  656. try
  657. {
  658. string school = null;
  659. string scope = null;
  660. string id = null;
  661. string ip = null;
  662. int expire = 1;
  663. if (json.TryGetProperty("school", out JsonElement _school))
  664. school = _school.GetString();
  665. if (json.TryGetProperty("scope", out JsonElement _scope))
  666. scope = _scope.GetString();
  667. if (json.TryGetProperty("id", out JsonElement _id))
  668. id = _id.GetString();
  669. if (json.TryGetProperty("ip", out JsonElement _ip))
  670. ip = _ip.GetString();
  671. if (json.TryGetProperty("expire", out JsonElement _expire))
  672. expire = _expire.GetInt32();
  673. var table = _azureStorage.GetCloudTableClient().GetTableReference("IESLogin");
  674. var cosmosClient = _azureCosmos.GetCosmosClient();
  675. DateTimeOffset dateTime = DateTimeOffset.UtcNow;
  676. var dateHour = dateTime.ToString("yyyyMMddHH"); //获取当天的小时
  677. var dateDay = dateTime.ToString("yyyyMMdd"); //获取当天的日期
  678. var dateMonth = dateTime.ToString("yyyyMM");//获取当月的日期
  679. var currentHour = dateTime.Hour; //当前小时
  680. var currentDay = dateTime.Day; //当前天
  681. long Expire = dateTime.AddHours(expire).ToUnixTimeMilliseconds(); //token到期时间
  682. long now = dateTime.ToUnixTimeMilliseconds(); //当前时间戳
  683. DateTime hour = DateTime.UtcNow.AddHours(25); //25小时到期
  684. DateTime month = DateTime.UtcNow.AddDays(32); //一个月到期
  685. var delTbHour = dateTime.AddHours(-168).ToString("yyyyMMddHH"); //168小时前
  686. var delTbDay = dateTime.AddDays(-180).ToString("yyyyMMdd"); //180天前
  687. switch (scope)
  688. {
  689. case "teacher":
  690. try
  691. {
  692. Teacher teacher = await cosmosClient.GetContainer("TEAMModelOS", "Teacher").ReadItemAsync<Teacher>(id, new PartitionKey("Base"));
  693. teacher.loginInfos = new List<LoginInfo>() { new LoginInfo { expire = Expire, ip = ip, time = now } };
  694. await cosmosClient.GetContainer("TEAMModelOS", "Teacher").ReplaceItemAsync<Teacher>(teacher, teacher.id, new PartitionKey("Base"));
  695. }
  696. catch { }
  697. break;
  698. case "student":
  699. try
  700. {
  701. Student student = await cosmosClient.GetContainer("TEAMModelOS", Constant.Student).ReadItemAsync<Student>(id, new PartitionKey($"Base-{school}"));
  702. student.loginInfos = new List<LoginInfo>() { new LoginInfo { expire = Expire, ip = ip, time = now } };
  703. await cosmosClient.GetContainer("TEAMModelOS", Constant.Student).ReplaceItemAsync<Student>(student, student.id, new PartitionKey($"Base-{school}"));
  704. string key = $"Login:School:{school}:student-day:{dateDay}";
  705. //记录一个学校每天每个学生登录的次数
  706. await _azureRedis.GetRedisClient(8).SortedSetIncrementAsync(key, student.id, 1);
  707. //获取key到期的时间
  708. await _azureRedis.GetRedisClient(8).KeyExpireAsync(key, hour); //设置到期时间25小时
  709. }
  710. catch (Exception ex ){ await _dingDing.SendBotMsg($"{ex.Message}{ex.StackTrace}", GroupNames.成都开发測試群組); }
  711. break;
  712. case "tmduser":
  713. try
  714. {
  715. TmdUser tmdUser = await cosmosClient.GetContainer("TEAMModelOS", Constant.Student).ReadItemAsync<TmdUser>(id, new PartitionKey("Base"));
  716. tmdUser.loginInfos = new List<LoginInfo>() { new LoginInfo { expire = Expire, ip = ip, time = now } };
  717. await cosmosClient.GetContainer("TEAMModelOS", Constant.Student).ReplaceItemAsync<TmdUser>(tmdUser, tmdUser.id, new PartitionKey("Base"));
  718. }
  719. catch { }
  720. break;
  721. }
  722. //天
  723. SortedSetEntry[] dayCnt = null;
  724. //月
  725. SortedSetEntry[] monthCnt = null;
  726. try
  727. {
  728. await _azureRedis.GetRedisClient(8).SortedSetIncrementAsync($"Login:IES:{scope}:{dateDay}", $"{currentHour}", 1);//一天24小时 小时为单位
  729. await _azureRedis.GetRedisClient(8).SortedSetIncrementAsync($"Login:IES:{scope}:{dateMonth}", $"{currentDay}", 1); //一天的累计 天为单位
  730. var resDay = await _azureRedis.GetRedisClient(8).KeyTimeToLiveAsync($"Login:IES:{scope}:{dateDay}");
  731. if (resDay == null)
  732. await _azureRedis.GetRedisClient(8).KeyExpireAsync($"Login:IES:{scope}:{dateDay}", hour); //设置到期时间
  733. var rspMonth = await _azureRedis.GetRedisClient(8).KeyTimeToLiveAsync($"Login:IES:{scope}:{dateMonth}");
  734. if (rspMonth == null)
  735. await _azureRedis.GetRedisClient(8).KeyExpireAsync($"Login:IES:{scope}:{dateMonth}", month); //设置到期时间
  736. //保存当前小时统计
  737. dayCnt = _azureRedis.GetRedisClient(8).SortedSetRangeByScoreWithScores($"Login:IES:{scope}:{dateDay}");
  738. //保存当前的统计数据
  739. monthCnt = _azureRedis.GetRedisClient(8).SortedSetRangeByScoreWithScores($"Login:IES:{scope}:{dateMonth}");
  740. }
  741. catch { }
  742. if (dayCnt != null && dayCnt.Length > 0)
  743. {
  744. List<HourLogin> hourLogins = new();
  745. foreach (var dCnt in dayCnt)
  746. {
  747. if (((int)dCnt.Element) == currentHour)
  748. {
  749. var tphourLogins = await table.QueryWhereString<HourLogin>($"PartitionKey eq 'HourLogin' and RowKey eq '{dateHour}'");
  750. if (tphourLogins.Count > 0)
  751. {
  752. foreach (var hourLogin in tphourLogins)
  753. {
  754. if (scope.Equals("teacher"))
  755. hourLogin.Teacher = (int)dCnt.Score;
  756. else if (scope.Equals("student"))
  757. hourLogin.Student = (int)dCnt.Score;
  758. else
  759. hourLogin.TmdUser = (int)dCnt.Score;
  760. hourLogins.Add(hourLogin);
  761. }
  762. }
  763. else
  764. {
  765. HourLogin hourLogin = new() { PartitionKey = $"HourLogin", RowKey = dateHour, Hour = currentHour };
  766. if (scope.Equals("teacher"))
  767. {
  768. hourLogin.Teacher = 1;
  769. hourLogin.Student = 0;
  770. hourLogin.TmdUser = 0;
  771. }
  772. else if (scope.Equals("student"))
  773. {
  774. hourLogin.Teacher = 0;
  775. hourLogin.Student = 1;
  776. hourLogin.TmdUser = 0;
  777. }
  778. else
  779. {
  780. hourLogin.Teacher = 0;
  781. hourLogin.Student = 0;
  782. hourLogin.TmdUser = 1;
  783. }
  784. hourLogins.Add(hourLogin);
  785. }
  786. }
  787. }
  788. await table.SaveOrUpdateAll(hourLogins); //保存和更新保存当前小时登录次数
  789. }
  790. if (monthCnt != null && monthCnt.Length > 0)
  791. {
  792. List<DayLogin> dayLogins = new();
  793. foreach (var mCnt in monthCnt)
  794. {
  795. if (((int)mCnt.Element) == currentDay)
  796. {
  797. //保存当天的峰值
  798. var tbDays = await table.QueryWhereString<DayLogin>($"PartitionKey eq 'DayLogin' and RowKey eq '{dateDay}'");
  799. if (tbDays.Count > 0)
  800. {
  801. foreach (var dayLogin in tbDays)
  802. {
  803. if (scope.Equals("teacher"))
  804. dayLogin.Teacher = (int)mCnt.Score;
  805. else if (scope.Equals("student"))
  806. dayLogin.Student = (int)mCnt.Score;
  807. else
  808. dayLogin.TmdUser = (int)mCnt.Score;
  809. dayLogins.Add(dayLogin);
  810. }
  811. }
  812. else
  813. {
  814. //保存当月每天的峰值
  815. DayLogin dayLogin = new() { PartitionKey = $"DayLogin", RowKey = dateDay, Day = currentDay };
  816. if (scope.Equals("teacher"))
  817. {
  818. dayLogin.Teacher = 1;
  819. dayLogin.Student = 0;
  820. dayLogin.TmdUser = 0;
  821. }
  822. else if (scope.Equals("student"))
  823. {
  824. dayLogin.Teacher = 0;
  825. dayLogin.Student = 1;
  826. dayLogin.TmdUser = 0;
  827. }
  828. else
  829. {
  830. dayLogin.Teacher = 0;
  831. dayLogin.Student = 0;
  832. dayLogin.TmdUser = 1;
  833. }
  834. dayLogins.Add(dayLogin);
  835. }
  836. }
  837. }
  838. await table.SaveOrUpdateAll(dayLogins);// 保存当月每天在线数据
  839. }
  840. string tbHourSql = $"PartitionKey eq 'HourLogin' and RowKey le '{delTbHour}'";
  841. string tbDaySql = $"PartitionKey eq 'DayLogin' and RowKey le '{delTbDay}'";
  842. try
  843. {
  844. await table.DeleteStringWhere<HourLogin>(rowKey: tbHourSql); //删除168小时前的数据
  845. await table.DeleteStringWhere<DayLogin>(rowKey: tbDaySql); //删除180天前的数据
  846. }
  847. catch { }
  848. if (!string.IsNullOrWhiteSpace(school))
  849. {
  850. //天
  851. SortedSetEntry[] scDayCnt = null;
  852. //月
  853. SortedSetEntry[] scMonthCnt = null;
  854. try
  855. {
  856. await _azureRedis.GetRedisClient(8).SortedSetIncrementAsync($"Login:School:{school}:{scope}:{dateDay}", $"{currentHour}", 1);//当天当前小时在线人加1
  857. await _azureRedis.GetRedisClient(8).SortedSetIncrementAsync($"Login:School:{school}:{scope}:{dateMonth}", $"{currentDay}", 1); //当天的在线加1
  858. var reScDay = await _azureRedis.GetRedisClient(8).KeyTimeToLiveAsync($"Login:School:{school}:{scope}:{dateDay}");
  859. if (reScDay == null)
  860. await _azureRedis.GetRedisClient(8).KeyExpireAsync($"Login:School:{school}:{scope}:{dateDay}", hour); //设置到期时间
  861. var reScMonth = await _azureRedis.GetRedisClient(8).KeyTimeToLiveAsync($"Login:School:{school}:{scope}:{dateMonth}");
  862. if (reScMonth == null)
  863. await _azureRedis.GetRedisClient(8).KeyExpireAsync($"Login:School:{school}:{scope}:{dateMonth}", month); //设置到期时间
  864. //保存学校当天每小时的
  865. scDayCnt = _azureRedis.GetRedisClient(8).SortedSetRangeByScoreWithScores($"Login:School:{school}:{scope}:{dateDay}");
  866. //学校天峰值
  867. scMonthCnt = _azureRedis.GetRedisClient(8).SortedSetRangeByScoreWithScores($"Login:School:{school}:{scope}:{dateMonth}");
  868. }
  869. catch { }
  870. if (scDayCnt != null && scDayCnt.Length > 0)
  871. {
  872. List<HourLoginSchool> hourLoginSchools = new();
  873. foreach (var scDCnt in scDayCnt)
  874. {
  875. if (((int)scDCnt.Element) == currentHour)
  876. {
  877. var tmpHour = await table.QueryWhereString<HourLoginSchool>($"PartitionKey eq 'HourLogin-{school}' and RowKey eq '{dateHour}'");
  878. if (tmpHour.Count > 0)
  879. {
  880. foreach (var hLoginSc in tmpHour)
  881. {
  882. if (scope.Equals("teacher"))
  883. hLoginSc.Teacher = (int)scDCnt.Score;
  884. else if (scope.Equals("student"))
  885. hLoginSc.Student = (int)scDCnt.Score;
  886. else
  887. hLoginSc.TmdUser = (int)scDCnt.Score;
  888. hourLoginSchools.Add(hLoginSc);
  889. }
  890. }
  891. else
  892. {
  893. //学校小时峰值
  894. HourLoginSchool hourLoginSc = new() { PartitionKey = $"HourLogin-{school}", RowKey = dateHour, Hour = currentHour, School = school };
  895. if (scope.Equals("teacher"))
  896. {
  897. hourLoginSc.Teacher = 1;
  898. hourLoginSc.Student = 0;
  899. hourLoginSc.TmdUser = 0;
  900. }
  901. else if (scope.Equals("student"))
  902. {
  903. hourLoginSc.Teacher = 0;
  904. hourLoginSc.Student = 1;
  905. hourLoginSc.TmdUser = 0;
  906. }
  907. else
  908. {
  909. hourLoginSc.Teacher = 0;
  910. hourLoginSc.Student = 0;
  911. hourLoginSc.TmdUser = 1;
  912. }
  913. hourLoginSchools.Add(hourLoginSc);
  914. }
  915. }
  916. }
  917. await table.SaveOrUpdateAll(hourLoginSchools);
  918. }
  919. if (scMonthCnt != null && scMonthCnt.Length > 0)
  920. {
  921. List<DayLoginSchool> DayLoginSchools = new();
  922. foreach (var scMCnt in scMonthCnt)
  923. {
  924. if (((int)scMCnt.Element) == currentDay)
  925. {
  926. var tempDays = await table.QueryWhereString<DayLoginSchool>($"PartitionKey eq 'DayLogin-{school}' and RowKey eq '{dateDay}'");
  927. if (tempDays.Count > 0)
  928. {
  929. foreach (var dLoginSc in tempDays)
  930. {
  931. if (scope.Equals("teacher"))
  932. dLoginSc.Teacher = (int)scMCnt.Score;
  933. else if (scope.Equals("student"))
  934. dLoginSc.Student = (int)scMCnt.Score;
  935. else
  936. dLoginSc.TmdUser = (int)scMCnt.Score;
  937. DayLoginSchools.Add(dLoginSc);
  938. }
  939. }
  940. else
  941. {
  942. //学校天峰值
  943. DayLoginSchool dayLoginSc = new() { PartitionKey = $"DayLogin-{school}", RowKey = dateDay, Day = currentDay, School = school };
  944. if (scope.Equals("teacher"))
  945. {
  946. dayLoginSc.Teacher = 1;
  947. dayLoginSc.Student = 0;
  948. dayLoginSc.TmdUser = 0;
  949. }
  950. else if (scope.Equals("student"))
  951. {
  952. dayLoginSc.Teacher = 0;
  953. dayLoginSc.Student = 1;
  954. dayLoginSc.TmdUser = 0;
  955. }
  956. else
  957. {
  958. dayLoginSc.Teacher = 0;
  959. dayLoginSc.Student = 0;
  960. dayLoginSc.TmdUser = 1;
  961. }
  962. DayLoginSchools.Add(dayLoginSc);
  963. }
  964. }
  965. }
  966. await table.SaveOrUpdateAll(DayLoginSchools);//保存学校当月在线数据
  967. }
  968. string tbScHourSql = $"PartitionKey eq 'HourLogin-{school}' and RowKey le '{delTbHour}'";
  969. List<HourLogin> scHourLog = await table.QueryWhereString<HourLogin>(tbScHourSql);
  970. if (scHourLog.Count > 0)
  971. try
  972. {
  973. //await table.DeleteStringWhere<HourLogin>(tbScHourSql); //删除学校168小时前的数据
  974. await table.DeleteAll(scHourLog);
  975. }
  976. catch { }
  977. string tbScDaySql = $"PartitionKey eq 'DayLogin-{school}' and RowKey le '{delTbDay}'";
  978. List<DayLogin> scDayLog = await table.QueryWhereString<DayLogin>(tbScDaySql);
  979. if (scDayLog.Count > 0)
  980. try
  981. {
  982. //await table.DeleteStringWhere<DayLogin>(tbScDaySql); //删除学校180天前的数据
  983. await table.DeleteAll(scDayLog);
  984. }
  985. catch { }
  986. }
  987. await response.WriteAsJsonAsync(new { data = json });
  988. return response;
  989. }
  990. catch (Exception ex)
  991. {
  992. await _dingDing.SendBotMsg($"{Environment.GetEnvironmentVariable("Option:Location")}-online-record 人数记录异常{ex.Message}\n{ex.StackTrace}", GroupNames.成都开发測試群組);
  993. await response.WriteAsJsonAsync(new { data = json });
  994. return response;
  995. }
  996. }
  997. /// <summary>
  998. /// 艺术评测报告生成
  999. /// </summary>
  1000. /// <param name="msg"></param>
  1001. /// <returns></returns>
  1002. //[Function("gen-art-pdf")]
  1003. public async Task<HttpResponseData> GenArtPDF([HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)] HttpRequestData req) {
  1004. var response = req.CreateResponse(HttpStatusCode.OK);
  1005. try {
  1006. string data = await new StreamReader(req.Body).ReadToEndAsync();
  1007. var json = JsonDocument.Parse(data).RootElement;
  1008. json.TryGetProperty("studentPdfs", out JsonElement _studentPdfs);
  1009. json.TryGetProperty("artResults", out JsonElement _artResults);
  1010. json.TryGetProperty("schoolCode", out JsonElement _schoolCode);
  1011. List<ArtStudentPdf> studentPdfs = _studentPdfs.Deserialize<List<ArtStudentPdf>>();
  1012. List<StudentArtResult> artResults = _artResults.Deserialize<List<StudentArtResult>>();
  1013. List<Task<string>> uploads = new List<Task<string>>();
  1014. studentPdfs.ForEach(x => {
  1015. x.blob = $"art/{x.artId}/report/{x.studentId}.json";
  1016. uploads.Add(_azureStorage.GetBlobContainerClient($"{_schoolCode}").UploadFileByContainer(x.ToJsonString(), "art", $"{x.artId}/report/{x.studentId}.json", true));
  1017. });
  1018. var uploadJsonUrls = await Task.WhenAll(uploads);
  1019. var list = uploadJsonUrls.ToList();
  1020. List<string> urls = new List<string>();
  1021. (string uri, string sas) = _azureStorage.GetBlobContainerSAS($"{_schoolCode}", Azure.Storage.Sas.BlobContainerSasPermissions.Read);
  1022. studentPdfs.ForEach(x => {
  1023. string atrUrl = "https://teammodelos.blob.core.chinacloudapi.cn/0-public/art-report-template/report.html";
  1024. var s = _azureStorage.GetBlobSAS($"{_schoolCode}", x.blob, BlobSasPermissions.Read);
  1025. s = $"{HttpUtility.UrlEncode($"{s}", Encoding.UTF8)}";
  1026. string url = $"{atrUrl}?url={s}&pdfpath={x.artId}/report/{x.studentId}";
  1027. urls.Add(url);
  1028. //var a = list.Find(l => l.Contains(x.studentId));
  1029. //if (a != null) {
  1030. // a = $"{HttpUtility.UrlEncode($"{a}?{sas}", Encoding.UTF8)}";
  1031. // string url = $"{atrUrl}?url={a}&pdfpath={x.artId}/report/{x.studentId}";
  1032. // urls.Add(url);
  1033. //}
  1034. });
  1035. string env = "release";
  1036. if (Environment.GetEnvironmentVariable("Option:Location").Contains("Test", StringComparison.CurrentCultureIgnoreCase) ||
  1037. Environment.GetEnvironmentVariable("Option:Location").Contains("Dep", StringComparison.CurrentCultureIgnoreCase))
  1038. {
  1039. env = "develop";
  1040. }
  1041. int psize = 20;
  1042. List<string> resUrls = new List<string>();
  1043. if (urls.Count <= psize)
  1044. {
  1045. var screenshot = new ScreenshotDto
  1046. {
  1047. width = 1080,
  1048. height = 1920,
  1049. urls = urls,
  1050. fileNameKey = "pdfpath",
  1051. cnt = $"{_schoolCode}",
  1052. root = "art",
  1053. pagesize = 5,
  1054. env = env
  1055. };
  1056. var st = screenshot.ToJsonString();
  1057. await _dingDing.SendBotMsg($"艺术评测报告生成中:\n{st}", GroupNames.成都开发測試群組);
  1058. var httpResponse = await _httpClient.CreateClient().PostAsJsonAsync("http://cdhabook.teammodel.cn:8805/screen/screenshot-pdf",
  1059. screenshot
  1060. );
  1061. if (httpResponse.StatusCode == HttpStatusCode.OK)
  1062. {
  1063. JsonElement json_res = await httpResponse.Content.ReadFromJsonAsync<JsonElement>();
  1064. resUrls = json_res.GetProperty("urls").Deserialize<List<string>>();
  1065. await UpdatePdfBlob(artResults, resUrls);
  1066. }
  1067. }
  1068. else {
  1069. int pages = (urls.Count + psize) / psize;
  1070. for (int i = 0; i < pages; i++) {
  1071. var lists = urls.Skip(i * psize).Take(psize).ToList();
  1072. var screenshot = new ScreenshotDto
  1073. {
  1074. width = 1080,
  1075. height = 1920,
  1076. urls = lists,
  1077. fileNameKey = "pdfpath",
  1078. cnt = $"{_schoolCode}",
  1079. root = "art",
  1080. pagesize = 5,
  1081. env = env
  1082. };
  1083. //var st = screenshot.ToJsonString();
  1084. // await _dingDing.SendBotMsg($"艺术评测报告生成中:\n{st}", GroupNames.成都开发測試群組);
  1085. var httpResponse = await _httpClient.CreateClient().PostAsJsonAsync("http://cdhabook.teammodel.cn:8805/screen/screenshot-pdf",
  1086. screenshot
  1087. );
  1088. if (httpResponse.StatusCode == HttpStatusCode.OK)
  1089. {
  1090. JsonElement json_res = await httpResponse.Content.ReadFromJsonAsync<JsonElement>();
  1091. resUrls = json_res.GetProperty("urls").Deserialize<List<string>>();
  1092. await UpdatePdfBlob(artResults, resUrls);
  1093. }
  1094. }
  1095. }
  1096. await response.WriteAsJsonAsync(new { data = new { count = studentPdfs.Count, resUrls } });
  1097. } catch (Exception ex) {
  1098. await _dingDing.SendBotMsg($"{ex.Message}{ex.StackTrace}", GroupNames.成都开发測試群組);
  1099. }
  1100. return response;
  1101. }
  1102. //https://teammodeltest.blob.core.chinacloudapi.cn/hbcn/art/e864a924-e644-42dc-87c4-e970e6185065/report/202201001.pdf
  1103. private async Task UpdatePdfBlob(List<StudentArtResult> studentArts ,List<string> urls)
  1104. {
  1105. List<Task<ItemResponse<StudentArtResult>>> artResults =new List<Task<ItemResponse<StudentArtResult>>>();
  1106. urls.ForEach(z => {
  1107. var res = studentArts.FindAll(x => z.Contains(x.artId) && z.Contains($"{x.studentId}"));
  1108. if (res.Any()) {
  1109. if (string.IsNullOrWhiteSpace(res[0].blob)) {
  1110. res[0].blob = $"art/{res[0].artId}/report/{res[0].studentId}.pdf";
  1111. artResults.Add(_azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Student).ReplaceItemAsync(res[0], res[0].id, new PartitionKey(res[0].code)));
  1112. }
  1113. }
  1114. });
  1115. if (artResults.Any())
  1116. {
  1117. await Task.WhenAll(artResults);
  1118. }
  1119. }
  1120. public class ScreenshotDto
  1121. {
  1122. public int width { get; set; } = 1920;
  1123. public int height { get; set; } = 1080;
  1124. public string? url { get; set; }
  1125. /// <summary>
  1126. /// 批量地址
  1127. /// </summary>
  1128. public List<string> urls { get; set; } = new List<string>();
  1129. /// <summary>
  1130. /// 提取参数的唯一id作为文件名
  1131. /// </summary>
  1132. public string? fileNameKey { get; set; }
  1133. /// <summary>
  1134. /// 存在哪个容器里
  1135. /// </summary>
  1136. public string? cnt { get; set; }
  1137. public int delay { get; set; }
  1138. public int pagesize { get; set; } = 5;
  1139. public string? root { get; set; }
  1140. public string? env { get; set; } = "release";
  1141. public string? msgId { get; set; }
  1142. }
  1143. }
  1144. }