IESHttpTrigger.cs 66 KB

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