IESHttpTrigger.cs 64 KB

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