HiTAControlller.cs 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686
  1. using Azure.Core;
  2. using Azure.Cosmos;
  3. using Azure.Storage.Blobs.Models;
  4. using DocumentFormat.OpenXml.Office2010.Excel;
  5. using DocumentFormat.OpenXml.Spreadsheet;
  6. using DocumentFormat.OpenXml.Wordprocessing;
  7. using HTEXLib.COMM.Helpers;
  8. using Microsoft.AspNetCore.Authorization;
  9. using Microsoft.AspNetCore.DataProtection;
  10. using Microsoft.AspNetCore.Hosting;
  11. using Microsoft.AspNetCore.Http;
  12. using Microsoft.AspNetCore.Mvc;
  13. using Microsoft.Azure.Amqp.Framing;
  14. using Microsoft.Extensions.Configuration;
  15. using Microsoft.Extensions.Hosting;
  16. using Microsoft.Extensions.Options;
  17. using Microsoft.OData.UriParser;
  18. using Newtonsoft.Json.Linq;
  19. using OfficeOpenXml.FormulaParsing.LexicalAnalysis;
  20. using StackExchange.Redis;
  21. using System;
  22. using System.Collections.Generic;
  23. using System.ComponentModel.DataAnnotations;
  24. using System.Dynamic;
  25. using System.IdentityModel.Tokens.Jwt;
  26. using System.Linq;
  27. using System.Net.Http;
  28. using System.Text.Json;
  29. using System.Threading.Tasks;
  30. using TEAMModelOS.Filter;
  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.Dtos;
  37. using TEAMModelOS.SDK.Models.Service;
  38. using TEAMModelOS.SDK.Services;
  39. using TEAMModelOS.Services;
  40. using Top.Api;
  41. using static TEAMModelOS.SDK.Services.BlobService;
  42. namespace TEAMModelOS.Controllers.Client
  43. {
  44. [Route("hita")]
  45. [ApiController]
  46. public class HiTAControlller : ControllerBase
  47. {
  48. private readonly AzureStorageFactory _azureStorage;
  49. private readonly AzureRedisFactory _azureRedis;
  50. private readonly AzureCosmosFactory _azureCosmos;
  51. private readonly DingDing _dingDing;
  52. private readonly Option _option;
  53. private readonly SnowflakeId _snowflakeId;
  54. private readonly IConfiguration _configuration;
  55. private readonly IPSearcher _searcher;
  56. private readonly HttpTrigger _httpTrigger;
  57. private readonly CoreAPIHttpService _coreAPIHttpService;
  58. private readonly IWebHostEnvironment _environment;
  59. public readonly IHttpClientFactory _httpClientFactory;
  60. public HiTAControlller(IWebHostEnvironment environment,
  61. CoreAPIHttpService coreAPIHttpService,
  62. AzureStorageFactory azureStorage,
  63. AzureRedisFactory azureRedis,
  64. AzureCosmosFactory azureCosmos,
  65. DingDing dingDing,
  66. SnowflakeId snowflakeId,
  67. IOptionsSnapshot<Option> option, IConfiguration configuration, IPSearcher searcher, HttpTrigger httpTrigger, IHttpClientFactory httpClientFactory)
  68. {
  69. _azureStorage = azureStorage;
  70. _azureRedis = azureRedis;
  71. _azureCosmos = azureCosmos;
  72. _dingDing = dingDing;
  73. _snowflakeId = snowflakeId;
  74. _option = option?.Value;
  75. _configuration = configuration;
  76. _searcher = searcher;
  77. _httpTrigger = httpTrigger;
  78. _coreAPIHttpService = coreAPIHttpService;
  79. _environment = environment; _httpClientFactory = httpClientFactory;
  80. }
  81. public class HiTAJoinSchool
  82. {
  83. public string type { get; set; }
  84. //[Required(ErrorMessage = "{0} 必须填写")]
  85. public string id { get; set; }
  86. //[Required(ErrorMessage = "{0} 必须填写")]
  87. public string name { get; set; }
  88. //[Required(ErrorMessage = "{0} 必须填写")]
  89. public string pic { get; set; }
  90. public string school { get; set; }
  91. public long ts { get; set; }
  92. public string id_token { get; set; }
  93. public string code { get; set; }
  94. }
  95. /// <summary>
  96. ///
  97. /// </summary>
  98. /// <param name="json"></param>
  99. /// <returns></returns>
  100. [ProducesDefaultResponseType]
  101. [HttpPost("school-join")]
  102. //[Authorize(Roles = "HiTA")]
  103. public async Task<IActionResult> SchoolJoin(HiTAJoinSchool join)
  104. {
  105. try
  106. {
  107. Dictionary<string, object> dict = new Dictionary<string, object>();
  108. Dictionary<string, object> qure = new Dictionary<string, object>();
  109. foreach (var a in HttpContext.Request.Query)
  110. {
  111. qure.Add(a.Key, a.Value);
  112. }
  113. foreach (var a in HttpContext.Request.Headers)
  114. {
  115. dict.Add(a.Key, a.Value);
  116. }
  117. //await _dingDing.SendBotMsg(join.ToJsonString()+ dict.ToJsonString(), GroupNames.成都开发測試群組);
  118. if (!string.IsNullOrWhiteSpace(join.id_token) && !string.IsNullOrWhiteSpace(join.school) && join.school.Contains("login:"))
  119. {
  120. var jwt = new JwtSecurityToken(join.id_token);
  121. //await _dingDing.SendBotMsg(join.ToJsonString(), GroupNames.成都开发測試群組);
  122. var id = jwt.Payload.Sub;
  123. await _azureRedis.GetRedisClient(8).StringSetAsync($"HiTA:Login:{join.school.Replace("login:", "")}", id, expiry: new TimeSpan(0, 0, 10));
  124. return Ok(new { name = ",关闭弹窗以获取登录信息", school = "tmd" });
  125. }
  126. if (!string.IsNullOrWhiteSpace(join.id_token) && !string.IsNullOrWhiteSpace(join.school))
  127. {
  128. var jwt = new JwtSecurityToken(join.id_token);
  129. //await _dingDing.SendBotMsg(join.ToJsonString(), GroupNames.成都开发測試群組);
  130. var id = jwt.Payload.Sub;
  131. string school = join.school;
  132. //權限token
  133. jwt.Payload.TryGetValue("name", out object name);
  134. //jwt.Payload.TryGetValue("picture", out object picture);
  135. long ts = join.ts;
  136. (int code, string msg, object data) res = await SchoolService.JoinScool(school, id, $"{name}", "", ts, _azureCosmos, _azureStorage, _coreAPIHttpService, _dingDing, _option, _configuration, _environment);
  137. if (res.data != null)
  138. {
  139. return Ok(res.data);
  140. }
  141. else
  142. {
  143. return Ok(new { error = res.code, res.msg });
  144. }
  145. }
  146. else
  147. {
  148. return Ok(new { error = 400, msg = "参数错误" });
  149. }
  150. }
  151. catch (Exception ex)
  152. {
  153. await _dingDing.SendBotMsg($"{ex.Message},{ex.StackTrace}", GroupNames.成都开发測試群組);
  154. return Ok(new { error = 400, msg = "参数错误" });
  155. }
  156. }
  157. [ProducesDefaultResponseType]
  158. [HttpGet("get-school-knowledge")]
  159. public async Task<IActionResult> GetSchoolKnowledge([FromQuery] HiTAJoinSchool join)
  160. {
  161. string sql = $"select value c from c where c.subjectId='{join.id}' and c.periodId='{join.code}' ";
  162. var reslut= await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.School).GetList<Knowledge>(sql, $"Knowledge-{join.school}-{join.id}");
  163. if (reslut.list.IsNotEmpty()) {
  164. return Ok(new { code = 200, points= reslut.list.First().points, blocks = reslut.list.First().blocks});
  165. }
  166. return Ok(new { code=1});
  167. }
  168. [ProducesDefaultResponseType]
  169. [HttpPost("refresh-access-token")]
  170. [AuthToken(Roles = "teacher,admin")]
  171. public async Task<IActionResult> RefreshAccessToken(JsonElement json) {
  172. if (!json.TryGetProperty("refresh-token", out JsonElement _refreshtoken))
  173. {
  174. return Ok(new { code = 1, msg = "刷新失败!" });
  175. }
  176. try {
  177. var refreshtoken = $"{_refreshtoken}".Replace("Bearer ", "");
  178. var clientID = _configuration.GetValue<string>("HaBookAuth:CoreService:clientID");
  179. var clientSecret = _configuration.GetValue<string>("HaBookAuth:CoreService:clientSecret");
  180. var authorizationJwt = new JwtSecurityToken(refreshtoken);
  181. if (authorizationJwt.Payload.Azp.Equals(clientID))
  182. {
  183. //检查旧的Token验证是否通过
  184. JwtSecurityToken securityToken = await CoreTokenExtensions.Validate(refreshtoken, _option.Location.Replace("-Dep", "").Replace("-Test", ""), $"{authorizationJwt.Payload["tid"]}", _configuration);
  185. if (securityToken!=null)
  186. {
  187. //重新生成新的
  188. var token = await CoreTokenExtensions.CreateAccessToken(clientID, clientSecret, _option.Location.Replace("-Dep", "").Replace("-Test", ""));
  189. return Ok(new { code = 200, access_token = token.AccessToken, token_type = token.TokenType, expires_in = token.ExpiresOn });
  190. }
  191. }
  192. }
  193. catch (Exception ex) { return Ok(new { code = 1, msg = "刷新失败!" }); }
  194. return Ok(new { code = 1, msg = "刷新失败!" });
  195. }
  196. [ProducesDefaultResponseType]
  197. [HttpGet("get-school-data")]
  198. public async Task<IActionResult> GetSchoolData([FromQuery] HiTAJoinSchool join)
  199. {
  200. IList<IdCodeNameCount> periodSubjects = new List<IdCodeNameCount>();
  201. School school= await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.School).ReadItemAsync<School>(join.school, new PartitionKey("Base"));
  202. foreach(var period in school.period)
  203. {
  204. foreach (var subject in period.subjects) {
  205. string name = subject.name;
  206. if (school.period.Count>1) {
  207. name=$"{period.name} {subject.name}";
  208. }
  209. string key = $"Knowledge:Count:{school.id}-{subject.id}";
  210. int pcount = 0;
  211. var value= _azureRedis.GetRedisClient(8).HashGet(key,period.id) ;
  212. if (value!=default && !value.IsNullOrEmpty) {
  213. var json = value.ToString().ToObject<JsonElement>();
  214. if (json.TryGetProperty("pcount", out JsonElement _pcount) && int.TryParse($"{_pcount}",out pcount)) {
  215. }
  216. }
  217. periodSubjects.Add(new IdCodeNameCount {
  218. periodId=period.id,
  219. periodName=period.name,
  220. periodType =period.periodType,
  221. subjectId=subject.id,
  222. subjectName=subject.name,
  223. name=name,
  224. count = pcount
  225. });
  226. }
  227. }
  228. var id_token = new JwtSecurityToken($"{join.id_token}");
  229. var auth_token = string.Empty;
  230. var response = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "School").ReadItemStreamAsync(id_token.Payload.Sub, new PartitionKey($"Teacher-{join.school}"));
  231. if (response.Status == 200) {
  232. id_token.Payload.TryGetValue("name", out object name);
  233. id_token.Payload.TryGetValue("picture", out object picture);
  234. SchoolTeacher schoolTeacher = JsonDocument.Parse(response.ContentStream).RootElement.ToObject<SchoolTeacher>();
  235. auth_token = JwtAuthExtension.CreateAuthToken(_option.HostName, id_token.Payload.Sub, $"{name}",$"{picture}", _option.JwtSecretKey, Website: "IES", scope: Constant.ScopeTeacher, schoolID: join.school, areaId: school.areaId, standard: school.standard, roles: schoolTeacher.roles.ToArray(), permissions:schoolTeacher.permissions.ToArray(), expire: 1);
  236. }
  237. await SystemService.RecordAccumulateData(_azureRedis, _dingDing, new SDK.Models.Dtos.Accumulate { client="hita", count=1, id=school.id, key="teacher_login", name=school.name, scope="school", target=school.id });
  238. return Ok(new { periodSubjects,auth_token });
  239. }
  240. [ProducesDefaultResponseType]
  241. [HttpGet("check-login")]
  242. public async Task<IActionResult> CheckLogin([FromQuery] HiTAJoinSchool join)
  243. {
  244. var data = await _azureRedis.GetRedisClient(8).StringGetAsync($"HiTA:Login:{join.code}");
  245. if (data.HasValue)
  246. {
  247. var id = data.ToString();
  248. var location = _option.Location;
  249. var clientID = _configuration.GetValue<string>("HaBookAuth:CoreService:clientID");
  250. TmdidImplicit implicit_token = await _coreAPIHttpService.Implicit(new Dictionary<string, string>()
  251. {
  252. { "grant_type", "implicit" },
  253. { "client_id",clientID },
  254. { "account",id },
  255. { "nonce",Guid.NewGuid().ToString()}
  256. }, location, _configuration);
  257. IEnumerable<dynamic> schools= new List<dynamic>();
  258. var response = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, Constant.Teacher).ReadItemStreamAsync(id, new PartitionKey("Base"));
  259. if (response.Status==200) {
  260. Teacher teacher= JsonDocument.Parse(response.Content).RootElement.ToObject<Teacher>();
  261. schools= teacher.schools.Where(x=>x.status.Equals("join")).Select(x => new { id = x.schoolId, name = x.name });
  262. }
  263. object name = null, picture = null;
  264. var jwt = new JwtSecurityToken(implicit_token.id_token);
  265. jwt.Payload.TryGetValue("name", out name);
  266. jwt.Payload.TryGetValue("picture", out picture);
  267. jwt.Payload.TryGetValue("lang", out object _jwtlang);
  268. (string ip, string region) = await LoginService.LoginIp(HttpContext, _searcher);
  269. TeacherInfo teacherInfo= await TeacherService.TeacherInfoLite(_azureCosmos, $"{name}", $"{picture}", $"{jwt.Payload.Sub}", _azureStorage, _option, _azureRedis, "", _httpTrigger, $"{_jwtlang}");
  270. string x_auth_token = string.Empty;
  271. if (teacherInfo!=null) {
  272. x_auth_token= teacherInfo.auth_token;
  273. }
  274. return Ok(new { implicit_token ,schools, x_auth_token });
  275. }
  276. return Ok();
  277. }
  278. /// <summary>
  279. /// 扫码加入学校 //已被school-join 替代
  280. /// </summary>
  281. /// <param name="json"></param>
  282. /// <returns></returns>
  283. [ProducesDefaultResponseType]
  284. [HttpGet("join-school")]
  285. //[Authorize(Roles = "HiTA")]
  286. public async Task<IActionResult> ScanCodeJoinSchool([FromQuery] HiTAJoinSchool join)
  287. {
  288. Dictionary<string, object> dict = new Dictionary<string, object>();
  289. Dictionary<string, object> qure = new Dictionary<string, object>();
  290. foreach (var a in HttpContext.Request.Query)
  291. {
  292. qure.Add(a.Key, a.Value);
  293. }
  294. foreach (var a in HttpContext.Request.Headers)
  295. {
  296. dict.Add(a.Key, a.Value);
  297. }
  298. if (!string.IsNullOrWhiteSpace(join.type)&& join.type.Equals("login") && !string.IsNullOrWhiteSpace(join.code))
  299. {
  300. string idd = string.Empty;
  301. if (!string.IsNullOrWhiteSpace(join.id))
  302. {
  303. idd=join.id;
  304. }
  305. else {
  306. var jwt = new JwtSecurityToken(join.id_token);
  307. //await _dingDing.SendBotMsg(join.ToJsonString(), GroupNames.成都开发測試群組);
  308. idd = jwt.Payload.Sub;
  309. }
  310. await _azureRedis.GetRedisClient(8).StringSetAsync($"HiTA:Login:{join.code}", idd,expiry: new TimeSpan(0,0,30));
  311. return Ok(new { name= ",关闭弹窗以获取登录信息" ,school="tmd"});
  312. }
  313. await _dingDing.SendBotMsg(join.ToJsonString()+ dict.ToJsonString(), GroupNames.成都开发測試群組);
  314. string school = join.school;
  315. string id = join.id;
  316. string name = join.name;
  317. string picture = join.pic;
  318. long ts = join.ts;
  319. (int code ,string msg ,object data )res= await SchoolService. JoinScool(school, id, name, picture, ts,_azureCosmos,_azureStorage,_coreAPIHttpService,_dingDing,_option,_configuration,_environment);
  320. if (res.data != null)
  321. {
  322. return Ok(res.data);
  323. }
  324. else {
  325. return Ok(new { res.code,res.msg});
  326. }
  327. //return Ok(new { schoolTeacher.roles, schoolTeacher.status, school = $"{school}", schoolInfo.name, schoolInfo.picture });
  328. }
  329. //[Authorize(Roles = "HiTA")]
  330. [ProducesResponseType(StatusCodes.Status200OK)]
  331. [ProducesResponseType(StatusCodes.Status400BadRequest)]
  332. [ProducesDefaultResponseType]
  333. [HttpPost("get-teacher-info")]
  334. public async Task<IActionResult> GetTeacherInfo()
  335. {
  336. try
  337. {
  338. string id_token = HttpContext.GetXAuth("IdToken");
  339. (string ip, string region) = await LoginService.LoginIp(HttpContext, _searcher);
  340. if (string.IsNullOrEmpty(id_token)) return BadRequest();
  341. var jwt = new JwtSecurityToken(id_token);
  342. if (!jwt.Payload.Iss.Equals("account.teammodel", StringComparison.OrdinalIgnoreCase)) return BadRequest();
  343. var id = jwt.Payload.Sub;
  344. var clientc = _azureCosmos.GetCosmosClient();
  345. var clientr = _azureRedis.GetRedisClient(8);
  346. var response = await clientc.GetContainer(Constant.TEAMModelOS, "Teacher").ReadItemStreamAsync(id, new PartitionKey("Base"));
  347. if (response.Status == 200)
  348. {
  349. var jsonsc = await JsonDocument.ParseAsync(response.ContentStream);
  350. //學校資訊
  351. List<dynamic> schools = new List<dynamic>();
  352. if (jsonsc.RootElement.TryGetProperty("schools", out JsonElement value))
  353. {
  354. foreach (var obj in value.EnumerateArray())
  355. {
  356. string schoolCodeNow = $"{obj.GetProperty("schoolId")}";
  357. var clientss = _azureStorage.GetBlobContainerClient(schoolCodeNow);
  358. //Blob使用狀況
  359. //UsedBlob schoolUsedBlob = await BlobService.GetBlobUsed(clientc, clientss, clientr, "school", schoolCodeNow);
  360. (long usedSize, long teach, long total, long surplus, Dictionary<string, double?> catalog) schoolUsedBlob = await BlobService.GetSurplusSpace(schoolCodeNow, "school", _option.Location, _azureCosmos, _azureRedis, _azureStorage, _dingDing,_httpTrigger);
  361. //管理者名單
  362. List<Dictionary<string, string>> adminList = new List<Dictionary<string, string>>();
  363. string querySchool = $"SELECT c.id, c.name FROM c WHERE ARRAY_CONTAINS(c.roles, 'admin', true)";
  364. await foreach (var item in clientc.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryStreamIterator(querySchool, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Teacher-{schoolCodeNow}") }))
  365. {
  366. using var json = await JsonDocument.ParseAsync(item.ContentStream);
  367. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  368. {
  369. foreach (var objadmin in json.RootElement.GetProperty("Documents").EnumerateArray())
  370. {
  371. Dictionary<string, string> adminRow = new Dictionary<string, string>();
  372. adminRow.Add("id", objadmin.GetProperty("id").GetString());
  373. adminRow.Add("name", objadmin.GetProperty("name").GetString());
  374. adminList.Add(adminRow);
  375. }
  376. }
  377. }
  378. //老師在此學校的課程
  379. List<dynamic> coursesch = new List<dynamic>();
  380. var query = $"SELECT c.id, c.name, c.no FROM c JOIN schedule IN c.schedule WHERE schedule.teacherId = '{id}'";
  381. await foreach (var item in clientc.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryStreamIterator(queryText: query, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Course-{schoolCodeNow}") }))
  382. {
  383. var jsoncs = await JsonDocument.ParseAsync(item.ContentStream);
  384. if (jsoncs.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  385. {
  386. foreach (var obj2 in jsoncs.RootElement.GetProperty("Documents").EnumerateArray())
  387. {
  388. dynamic courseExtobj = new ExpandoObject();
  389. courseExtobj.id = obj2.GetProperty("id");
  390. courseExtobj.name = obj2.GetProperty("name");
  391. courseExtobj.no = obj2.GetProperty("no");
  392. coursesch.Add(courseExtobj);
  393. }
  394. }
  395. }
  396. //學校資料生成
  397. dynamic schoolExtobj = new ExpandoObject();
  398. var schoolJson = await clientc.GetContainer(Constant.TEAMModelOS, "School").ReadItemStreamAsync($"{obj.GetProperty("schoolId")}", new PartitionKey("Base"));
  399. var school = await JsonDocument.ParseAsync(schoolJson.ContentStream);
  400. try
  401. {
  402. schoolExtobj.schoolId = obj.GetProperty("schoolId");
  403. schoolExtobj.name = school.RootElement.GetProperty("name");
  404. schoolExtobj.region = school.RootElement.GetProperty("region");
  405. schoolExtobj.province = school.RootElement.GetProperty("province");
  406. schoolExtobj.city = school.RootElement.GetProperty("city");
  407. schoolExtobj.status = obj.GetProperty("status");
  408. schoolExtobj.picture = school.RootElement.GetProperty("picture");
  409. schoolExtobj.size = new ExpandoObject();
  410. schoolExtobj.size.used = schoolUsedBlob.usedSize + schoolUsedBlob.teach * 1073741824;
  411. long ssize = (school.RootElement.TryGetProperty("size", out JsonElement ssizeJson)) ? ssizeJson.GetInt32() : 0;
  412. schoolExtobj.size.total = ssize * 1073741824;
  413. schoolExtobj.size.avaliable = schoolExtobj.size.total - schoolExtobj.size.used;
  414. schoolExtobj.admin = adminList;
  415. schoolExtobj.courses = coursesch;
  416. schools.Add(schoolExtobj);
  417. }
  418. catch (Exception)
  419. {
  420. await _dingDing.SendBotMsg($"IES5,{_option.Location},HiTA/Debug()\nJson:{schoolJson}\n schoolExtobj:{schoolExtobj}", GroupNames.醍摩豆服務運維群組);
  421. }
  422. }
  423. }
  424. //預設學校
  425. string defaultschool = null;
  426. if (jsonsc.RootElement.TryGetProperty("defaultSchool", out JsonElement defaultSchoolJson) && !defaultSchoolJson.ValueKind.Equals(JsonValueKind.Null))
  427. {
  428. defaultschool = defaultSchoolJson.GetString();
  429. }
  430. //老師個人課程
  431. List<dynamic> courses = new List<dynamic>();
  432. await foreach (var item in clientc.GetContainer(Constant.TEAMModelOS, "Teacher").GetItemQueryStreamIterator(queryText: $"SELECT c.id, c.name, c.no FROM c ", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Course-{id}") }))
  433. {
  434. var jsontcs = await JsonDocument.ParseAsync(item.ContentStream);
  435. if (jsontcs.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  436. {
  437. foreach (var obj in jsontcs.RootElement.GetProperty("Documents").EnumerateArray())
  438. {
  439. dynamic courseExtobj = new ExpandoObject();
  440. courseExtobj.id = obj.GetProperty("id");
  441. courseExtobj.name = obj.GetProperty("name");
  442. courseExtobj.no = obj.GetProperty("no");
  443. courses.Add(courseExtobj);
  444. }
  445. }
  446. }
  447. //老師個人空間 ※老師可用的空間 = (Teacher)Base.size + (School)各校的Teacher-{schoolCode}
  448. dynamic size = new ExpandoObject();
  449. size.resource = 0;
  450. size.records = 0;
  451. size.used = 0;
  452. size.total = 0;
  453. var clientst = _azureStorage.GetBlobContainerClient(id);
  454. ////個人可用空間
  455. if (jsonsc.RootElement.TryGetProperty("size", out JsonElement teaSizeTotalJson) && teaSizeTotalJson.ValueKind.Equals(JsonValueKind.Number))
  456. {
  457. size.total += teaSizeTotalJson.GetInt32();
  458. }
  459. ////學校分派給老師的空間
  460. int GsizeFromSchool = 0;
  461. string querySizeFromSchool = $"SELECT sum(c.size) as size FROM c WHERE CONTAINS(c.code, 'Teacher-') AND c.id = '{id}'";
  462. await foreach (var item in clientc.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryStreamIterator(querySizeFromSchool, requestOptions: new QueryRequestOptions() { }))
  463. {
  464. var json = await JsonDocument.ParseAsync(item.ContentStream);
  465. foreach (var elmt in json.RootElement.GetProperty("Documents").EnumerateArray())
  466. {
  467. if (elmt.TryGetProperty("size", out JsonElement sizeJson) && sizeJson.ValueKind.Equals(JsonValueKind.Number))
  468. {
  469. GsizeFromSchool = sizeJson.GetInt32();
  470. break;
  471. }
  472. }
  473. }
  474. size.total += GsizeFromSchool;
  475. size.total = Convert.ToInt64(size.total) * 1073741824;
  476. ////個人空間使用狀況
  477. (long usedSize, long teach, long total, long surplus, Dictionary<string, double?> catalog) teacherUsedBlob = await BlobService.GetSurplusSpace(id, "private", _option.Location, _azureCosmos, _azureRedis, _azureStorage, _dingDing,_httpTrigger);
  478. // UsedBlob teacherUsedBlob = await BlobService.GetBlobUsed(clientc, clientst, clientr, "private", id);
  479. size.used = teacherUsedBlob.usedSize;
  480. foreach (KeyValuePair<string, double?> blobCatalog in teacherUsedBlob.catalog)
  481. {
  482. if (blobCatalog.Key.Equals("records"))
  483. {
  484. size.records = blobCatalog.Value;
  485. }
  486. else
  487. {
  488. size.resource += blobCatalog.Value;
  489. }
  490. }
  491. //資源數
  492. int resCount = 0; //教材數
  493. int itemCount = 0; //題目數
  494. int paperCount = 0; //試卷數
  495. //取得老師題目數
  496. await foreach (var itemC in clientc.GetContainer(Constant.TEAMModelOS, "Teacher").GetItemQueryStreamIterator(queryText: $"SELECT COUNT(1) AS count FROM c WHERE c.pid = null", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Item-{id}") }))
  497. {
  498. using var json = await JsonDocument.ParseAsync(itemC.ContentStream);
  499. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  500. {
  501. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  502. {
  503. itemCount += obj.GetProperty("count").GetInt32();
  504. }
  505. }
  506. }
  507. //取得老師試卷數
  508. await foreach (var paperC in clientc.GetContainer(Constant.TEAMModelOS, "Teacher").GetItemQueryStreamIterator(queryText: $"SELECT COUNT(1) AS count FROM c WHERE c.scope = 'private'", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Paper-{id}") }))
  509. {
  510. using var json = await JsonDocument.ParseAsync(paperC.ContentStream);
  511. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  512. {
  513. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  514. {
  515. paperCount += obj.GetProperty("count").GetInt32();
  516. }
  517. }
  518. }
  519. List<BlobCount> blogCountList = await BloblogCount(clientc, "private", id, "", "");
  520. foreach (BlobCount blobCountRow in blogCountList)
  521. {
  522. switch (blobCountRow.type)
  523. {
  524. case "doc":
  525. case "image":
  526. case "res":
  527. case "video":
  528. case "audio":
  529. case "other":
  530. resCount += blobCountRow.count;
  531. break;
  532. }
  533. }
  534. //進行中的活動數
  535. int activityCount = 0;
  536. List<string> examIdList = new List<string>();
  537. await foreach (var actitem in clientc.GetContainer(Constant.TEAMModelOS, "Common").GetItemQueryStreamIterator(queryText: $"SELECT COUNT(1) AS count FROM c where (c.status<>404 or IS_DEFINED(c.status) = false ) and c.progress = 'going'", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Exam-{id}") }))
  538. {
  539. using var json = await JsonDocument.ParseAsync(actitem.ContentStream);
  540. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  541. {
  542. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  543. {
  544. activityCount += obj.GetProperty("count").GetInt32();
  545. }
  546. }
  547. }
  548. await foreach (var actitem in clientc.GetContainer(Constant.TEAMModelOS, "Common").GetItemQueryStreamIterator(queryText: $"SELECT COUNT(1) AS count FROM c where (c.status<>404 or IS_DEFINED(c.status) = false ) and c.progress = 'going'", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Homework-{id}") }))
  549. {
  550. using var json = await JsonDocument.ParseAsync(actitem.ContentStream);
  551. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  552. {
  553. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  554. {
  555. activityCount += obj.GetProperty("count").GetInt32();
  556. }
  557. }
  558. }
  559. //await foreach (var actitem in clientc.GetContainer(Constant.TEAMModelOS, "Common").GetItemQueryStreamIterator(queryText: $"SELECT COUNT(1) AS count FROM c WHERE c.progress = 'going'", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Vote-{id}") }))
  560. //{
  561. // using var json = await JsonDocument.ParseAsync(actitem.ContentStream);
  562. // if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  563. // {
  564. // foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  565. // {
  566. // activityCount += obj.GetProperty("count").GetInt32();
  567. // }
  568. // }
  569. //}
  570. //await foreach (var actitem in clientc.GetContainer(Constant.TEAMModelOS, "Common").GetItemQueryStreamIterator(queryText: $"SELECT COUNT(1) AS count FROM c WHERE c.progress = 'going'", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Survey-{id}") }))
  571. //{
  572. // using var json = await JsonDocument.ParseAsync(actitem.ContentStream);
  573. // if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  574. // {
  575. // foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  576. // {
  577. // activityCount += obj.GetProperty("count").GetInt32();
  578. // }
  579. // }
  580. //}
  581. //用户在线记录
  582. try
  583. {
  584. _ = _httpTrigger.RequestHttpTrigger(new { school = defaultschool, scope = $"{Constant.ScopeTeacher}", id = $"{id}", ip = $"{ip}", expire = 1 }, _option.Location, "online-record");
  585. }
  586. catch { }
  587. await SystemService.RecordAccumulateData(_azureRedis, _dingDing, new SDK.Models.Dtos.Accumulate { client="hita", count=1, id="ies", key="tmd_login", name="醍摩豆账号登录", scope="ies", target="ies" });
  588. return Ok(new { schools, defaultschool, courses, size, resCount, itemCount, paperCount, activityCount });
  589. }
  590. else //無此老師
  591. {
  592. return BadRequest();
  593. }
  594. }
  595. catch (Exception ex)
  596. {
  597. await _dingDing.SendBotMsg($"IES5,{_option.Location},HiTA/GetTeachInfo()\n{ex.Message}\n{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
  598. return BadRequest();
  599. }
  600. }
  601. //[Authorize(Roles = "HiTA")]
  602. [ProducesResponseType(StatusCodes.Status200OK)]
  603. [ProducesResponseType(StatusCodes.Status400BadRequest)]
  604. [ProducesDefaultResponseType]
  605. [HttpPost("get-school-list")]
  606. public async Task<IActionResult> GetSchoolListByRegion(JsonElement request)
  607. {
  608. try
  609. {
  610. //參數取得
  611. if (!request.TryGetProperty("regionName", out JsonElement regionNameJson)) return BadRequest();
  612. if (!request.TryGetProperty("provinceName", out JsonElement provinceNameJson)) return BadRequest();
  613. if (!request.TryGetProperty("cityName", out JsonElement cityNameJson)) return BadRequest();
  614. if (string.IsNullOrWhiteSpace(Convert.ToString(regionNameJson))) return BadRequest();
  615. string regionName = Convert.ToString(regionNameJson);
  616. string provinceName = (provinceNameJson.ValueKind.Equals(JsonValueKind.Null)) ? null : Convert.ToString(provinceNameJson);
  617. string cityName = (cityNameJson.ValueKind.Equals(JsonValueKind.Null)) ? null : Convert.ToString(cityNameJson);
  618. regionName = regionName.Replace("地區", "").Replace("地区", "");
  619. if (!string.IsNullOrWhiteSpace(provinceName)) provinceName = provinceName.Replace("省", "");
  620. if (!string.IsNullOrWhiteSpace(cityName)) cityName = cityName.Replace("市", "");
  621. //省 市
  622. //取得學校資訊
  623. List<SchoolSimple> schoolList = new List<SchoolSimple>();
  624. var clientc = _azureCosmos.GetCosmosClient();
  625. string querySchoolSelect = $"SELECT c.id, c.name FROM c";
  626. string querySchoolWhere1 = $"CONTAINS(c.region, '{regionName}')"; //依據地理資料(國省市)
  627. querySchoolWhere1 += (provinceName != null) ? $" AND CONTAINS(c.province, '{provinceName}')" : $" AND IS_NULL(c.province)";
  628. querySchoolWhere1 += (cityName != null) ? $" AND CONTAINS(c.city, '{cityName}')" : $" AND IS_NULL(c.city)";
  629. string querySchoolWhere2 = "IS_NULL(c.region)"; //無任何地理資料
  630. string querySchoolWhere = $" WHERE ({querySchoolWhere1}) OR ({querySchoolWhere2})";
  631. string querySchool = $"{querySchoolSelect}{querySchoolWhere}";
  632. await foreach (var item in clientc.GetContainer(Constant.TEAMModelOS, "School").GetItemQueryStreamIterator(querySchool, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Base") }))
  633. {
  634. using var json = await JsonDocument.ParseAsync(item.ContentStream);
  635. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  636. {
  637. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  638. {
  639. SchoolSimple sch = new SchoolSimple();
  640. sch.id = Convert.ToString(obj.GetProperty("id"));
  641. sch.name = Convert.ToString(obj.GetProperty("name"));
  642. schoolList.Add(sch);
  643. }
  644. }
  645. }
  646. return Ok(schoolList);
  647. }
  648. catch (Exception ex)
  649. {
  650. await _dingDing.SendBotMsg($"IES5,{_option.Location},HiTA/GetSchoolListByRegion()\n{ex.Message}\n{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
  651. return BadRequest();
  652. }
  653. }
  654. private class SchoolSimple
  655. {
  656. public string id { get; set; }
  657. public string name { get; set; }
  658. }
  659. }
  660. }