IESHttpTrigger.cs 65 KB

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