TeacherService.cs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  1. using Azure.Cosmos;
  2. using Azure.Storage.Blobs.Models;
  3. using Azure.Storage.Sas;
  4. using Microsoft.AspNetCore.Http;
  5. using Microsoft.AspNetCore.Mvc;
  6. using Microsoft.Extensions.Options;
  7. using System;
  8. using System.Collections.Generic;
  9. using System.Dynamic;
  10. using System.IdentityModel.Tokens.Jwt;
  11. using System.IO;
  12. using System.Linq;
  13. using System.Text.Json;
  14. using System.Threading.Tasks;
  15. using TEAMModelOS.Models;
  16. using TEAMModelOS.SDK.Models;
  17. using TEAMModelOS.SDK.DI;
  18. using TEAMModelOS.SDK.Extension;
  19. using TEAMModelOS.SDK.Filter;
  20. using TEAMModelOS.SDK.Models.Cosmos;
  21. using HTEXLib.COMM.Helpers;
  22. using TEAMModelOS.SDK.Models.Service;
  23. using Microsoft.Extensions.Configuration;
  24. using System.Net.Http;
  25. using TEAMModelOS.SDK;
  26. using static TEAMModelOS.SDK.Models.Teacher;
  27. namespace TEAMModelOS.Services
  28. {
  29. public static class TeacherService
  30. {
  31. public static async Task<TeacherInfo> TeacherInfo(AzureCosmosFactory _azureCosmos, Teacher teacher, string name, string picture, string id,
  32. AzureStorageFactory _azureStorage, Option _option,AzureRedisFactory _azureRedis,string ip, HttpTrigger _httpTrigger,string lang )
  33. {
  34. List<object> schools = new List<object>();
  35. List<AreaDto> areas = new List<AreaDto>();
  36. string defaultschool = null;
  37. string defaultschoolPeriod = null;
  38. //TODO 取得Teacher 個人相關數據(課程清單、虛擬教室清單、歷史紀錄清單等),學校數據另外API處理,多校切換時不同
  39. var client = _azureCosmos.GetCosmosClient();
  40. int total = 0;
  41. int tsize = 0;
  42. List<Area> areasDbs = new List<Area>();
  43. List<AreaSetting> areaSettings = new List<AreaSetting>();
  44. try
  45. {
  46. if (teacher == null)
  47. {
  48. teacher = await client.GetContainer(Constant.TEAMModelOS, "Teacher").ReadItemAsync<Teacher>(id, new PartitionKey("Base"));
  49. if (!string.IsNullOrWhiteSpace(lang) && string.IsNullOrWhiteSpace(teacher.lang)) {
  50. teacher.lang = lang;
  51. }
  52. teacher.name = $"{name}";
  53. teacher.picture = $"{picture}";
  54. }
  55. else
  56. {
  57. name = teacher.name;
  58. picture = teacher.picture;
  59. id = teacher.id;
  60. }
  61. ///教师的个人空间
  62. tsize = teacher.size;
  63. ///教师的总空间 包含 个人空间和学校赠送的空间累加
  64. total = teacher.size;
  65. HashSet<string> areaIds = new HashSet<string>();
  66. if (teacher.areas.IsNotEmpty())
  67. {
  68. teacher.areas.ForEach(x => {
  69. areaIds.Add(x.areaId);
  70. });
  71. }
  72. if (teacher.schools.IsNotEmpty())
  73. {
  74. teacher.schools.ForEach(x => {
  75. if (!string.IsNullOrEmpty(x.areaId))
  76. {
  77. areaIds.Add(x.areaId);
  78. }
  79. });
  80. }
  81. if (areaIds.Count > 0)
  82. {
  83. string queryText = $"select value(c) from c where c.id in ({string.Join(",", areaIds.Select(x => $"'{x}'"))})";
  84. await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "Normal").GetItemQueryIterator<Area>(queryText: queryText, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("Base-Area") }))
  85. {
  86. areasDbs.Add(item);
  87. }
  88. await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "Normal").GetItemQueryIterator<AreaSetting>(queryText: queryText, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey("AreaSetting") }))
  89. {
  90. areaSettings.Add(item);
  91. }
  92. }
  93. if (teacher.areas.IsNotEmpty())
  94. {
  95. foreach (var areat in teacher.areas)
  96. {
  97. Area area = areasDbs.Find(x => x.id.Equals(areat.areaId));
  98. AreaSetting setting = null;
  99. if (area != null)
  100. {
  101. setting = areaSettings.Find(x => x.id.Equals(areat.areaId));
  102. }
  103. int access = 0;
  104. if (setting != null && !string.IsNullOrWhiteSpace(setting.accessConfig))
  105. {
  106. access = 1;
  107. }
  108. //if (setting != null)
  109. //{
  110. // setting.accessConfig = null;
  111. //}
  112. areas.Add(new AreaDto { areaId = area.id, name = area.name, standard = area.standard, standardName = area.standardName, setting = setting, access = access });
  113. }
  114. }
  115. //检查是否有加入学校,如果加入学校,则当个人空间size是0G的时候,则免费获得一个G空间,但无论加入多少个学校,只能获取一次 1G的免费空间。没有加入学校则默认0G空间,除非自己购买空间
  116. List<SchoolTeacher> schoolTeachers= new List<SchoolTeacher>();
  117. if (teacher.schools.IsNotEmpty())
  118. {
  119. foreach (var sc in teacher.schools)
  120. {
  121. string statusNow = sc.status != null ? sc.status : "";
  122. if (statusNow.Equals("join") || statusNow.Equals("invite") || statusNow.Equals("request"))
  123. {
  124. dynamic schoolExtobj = new ExpandoObject();
  125. schoolExtobj.schoolId = sc.schoolId;
  126. schoolExtobj.name = sc.name;
  127. schoolExtobj.status = sc.status;
  128. schoolExtobj.time = sc.time;
  129. schoolExtobj.picture = sc.picture;
  130. schoolExtobj.areaId = $"{sc.areaId}";
  131. Area area = null;
  132. int access = 0;
  133. if (!string.IsNullOrEmpty($"{sc.areaId}"))
  134. {
  135. area = areasDbs.Find(x => x.id.Equals(sc.areaId));
  136. AreaSetting setting = null;
  137. if (area != null)
  138. {
  139. setting = areaSettings.Find(x => x.id.Equals(sc.areaId));
  140. }
  141. if (setting != null && !string.IsNullOrWhiteSpace(setting.accessConfig))
  142. {
  143. access = 1;
  144. }
  145. }
  146. if (area != null)
  147. {
  148. schoolExtobj.area = new { area.name, area.id, area.institution, area.provName, area.code, area.cityCode, area.cityName, area.standard, area.provCode, area.pk, access };
  149. }
  150. else
  151. {
  152. schoolExtobj.area = area;
  153. }
  154. var sctch = await client.GetContainer(Constant.TEAMModelOS, "School").ReadItemStreamAsync(id, new PartitionKey($"Teacher-{sc.schoolId}"));
  155. if (sctch.Status == 200 && sctch != null && sctch.ContentStream != null)
  156. {
  157. var jsonDoc = await JsonDocument.ParseAsync(sctch.ContentStream);
  158. SchoolTeacher schoolTeacher = jsonDoc.RootElement.ToObject<SchoolTeacher>();
  159. if (!string.IsNullOrWhiteSpace(schoolTeacher.periodId))
  160. {
  161. schoolExtobj.periodId = schoolTeacher.periodId;
  162. }
  163. else
  164. {
  165. var schoolbase = await client.GetContainer(Constant.TEAMModelOS, "School").ReadItemStreamAsync(sc.schoolId, new PartitionKey($"Base"));
  166. if (schoolbase.Status == 200) {
  167. var schoolJsonDoc = await JsonDocument.ParseAsync(schoolbase.ContentStream);
  168. School schoolBase = schoolJsonDoc.RootElement.ToObject<School>();
  169. if (schoolBase.period.IsNotEmpty()) {
  170. schoolExtobj.periodId = schoolBase.period[0].id;
  171. schoolTeacher.periodId = schoolBase.period[0].id;
  172. await client.GetContainer(Constant.TEAMModelOS, "School").ReplaceItemAsync(schoolTeacher, id, new PartitionKey($"Teacher-{sc.schoolId}"));
  173. }
  174. }
  175. }
  176. if (schoolTeacher.name == null || schoolTeacher.picture == null || !schoolTeacher.name.Equals($"{name}") || !schoolTeacher.picture.Equals($"{picture}"))
  177. {
  178. schoolTeacher.name = $"{name}";
  179. schoolTeacher.picture = $"{picture}";
  180. await client.GetContainer(Constant.TEAMModelOS, "School").ReplaceItemAsync(schoolTeacher, id, new PartitionKey($"Teacher-{sc.schoolId}"));
  181. }
  182. if (jsonDoc.RootElement.TryGetProperty("size", out JsonElement _size) && _size.ValueKind.Equals(JsonValueKind.Number))
  183. {
  184. total += _size.GetInt32();
  185. schoolExtobj.size = _size.GetInt32();
  186. }
  187. else { schoolExtobj.size = 0; }
  188. schoolTeachers.Add(schoolTeacher);
  189. }
  190. else
  191. {
  192. schoolExtobj.size = 0;
  193. }
  194. //if (statusNow.Equals("join"))
  195. //{
  196. // await TmdUserService.JoinSchool(client, teacher.id, teacher.picture, teacher.name, sc.schoolId, sc.name);
  197. //}
  198. schools.Add(schoolExtobj);
  199. }
  200. }
  201. //如果包含任何申请,邀请,加入学校的记录 且个人空间未分配2G 则默认分配个人空间至2G.
  202. if (teacher.size < 2 && teacher.schools.Count > 0)
  203. {
  204. teacher.size = 2;
  205. }
  206. //如果未包含任何申请,邀请,加入学校的记录 且 个人空间没有分配1G 则默认赠送一个G
  207. if (teacher.schools.Count == 0 && teacher.size < 1)
  208. {
  209. teacher.size = 1;
  210. }
  211. }
  212. if (string.IsNullOrEmpty(teacher.defaultSchool) && teacher.schools.IsNotEmpty())
  213. {
  214. var tech = teacher.schools.FindAll(x => x.status.Equals("join"));
  215. if (tech.IsNotEmpty())
  216. {
  217. teacher.defaultSchool = teacher.schools[0].schoolId;
  218. }
  219. }
  220. if (!string.IsNullOrEmpty(teacher.defaultSchool))
  221. {
  222. if (teacher.schools.IsNotEmpty())
  223. {
  224. var tech = teacher.schools.FindAll(x => x.status.Equals("join") && x.schoolId.Equals(teacher.defaultSchool));
  225. if (!tech.IsNotEmpty())
  226. {
  227. var techde = teacher.schools.FindAll(x => x.status.Equals("join"));
  228. if (techde.IsNotEmpty())
  229. {
  230. teacher.defaultSchool = techde[0].schoolId;
  231. }
  232. else
  233. {
  234. teacher.defaultSchool = null;
  235. }
  236. }
  237. }
  238. else
  239. {
  240. teacher.defaultSchool = null;
  241. }
  242. }
  243. var sctecher= schoolTeachers.Find(z => z.id.Equals(teacher.id) && z.code.Equals($"Teacher-{teacher.defaultSchool}"));
  244. if (sctecher != null)
  245. {
  246. defaultschoolPeriod = sctecher.periodId;
  247. }
  248. else {
  249. var sctch = await client.GetContainer(Constant.TEAMModelOS, "School").ReadItemStreamAsync(id, new PartitionKey($"Teacher-{teacher.defaultSchool}"));
  250. if (sctch.Status == 200 && sctch != null && sctch.ContentStream != null) {
  251. var jsonDoc = await JsonDocument.ParseAsync(sctch.ContentStream);
  252. SchoolTeacher schoolTeacher = jsonDoc.RootElement.ToObject<SchoolTeacher>();
  253. defaultschoolPeriod = schoolTeacher.periodId;
  254. }
  255. }
  256. teacher.defaultschoolPeriod= defaultschoolPeriod;
  257. await client.GetContainer(Constant.TEAMModelOS, "Teacher").ReplaceItemAsync<Teacher>(teacher, id, new PartitionKey("Base"));
  258. //預設學校ID
  259. defaultschool = teacher.defaultSchool;
  260. }
  261. catch (CosmosException ex)
  262. {
  263. if (ex.Status == 404 && teacher == null)
  264. {
  265. //如果沒有,則初始化Teacher基本資料到Cosmos
  266. teacher = new Teacher
  267. {
  268. createTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
  269. id = id,
  270. pk = "Base",
  271. code = "Base",
  272. name = name?.ToString(),
  273. picture = picture?.ToString(),
  274. //创建账号并第一次登录IES5则默认赠送1G
  275. size = 1,
  276. defaultSchool = null,
  277. schools = new List<Teacher.TeacherSchool>(),
  278. };
  279. var container = _azureStorage.GetBlobContainerClient(id);
  280. await container.CreateIfNotExistsAsync(PublicAccessType.None); //嘗試創建Teacher私有容器,如存在則不做任何事,保障容器一定存在
  281. teacher = await _azureCosmos.GetCosmosClient().GetContainer(Constant.TEAMModelOS, "Teacher").CreateItemAsync<Teacher>(teacher, new PartitionKey("Base"));
  282. total = teacher.size;
  283. tsize = teacher.size;
  284. }
  285. }
  286. catch (Exception ex)
  287. {
  288. throw new Exception($"{ex.Message}\n{ex.StackTrace}");
  289. }
  290. //私人課程
  291. List<object> courses = new List<object>();
  292. await foreach (var item in client.GetContainer(Constant.TEAMModelOS, "Teacher").GetItemQueryStreamIterator(queryText: $"select value(c) from c ", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Course-{id}") }))
  293. {
  294. using var json = await JsonDocument.ParseAsync(item.ContentStream);
  295. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  296. {
  297. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  298. {
  299. courses.Add(obj.ToObject<object>());
  300. }
  301. }
  302. }
  303. List<string> roles = new List<string>() { "teacher" };
  304. Area areaa = null;
  305. if (areas.Count > 0)
  306. {
  307. roles.Add("area");
  308. if (!string.IsNullOrEmpty($"{areas[0]}"))
  309. {
  310. areaa = areasDbs.Find(x => x.Equals(areas[0].areaId));
  311. }
  312. }
  313. //換取AuthToken,提供給前端
  314. var auth_token = JwtAuthExtension.CreateAuthToken(_option.HostName, id, name?.ToString(), picture?.ToString(), _option.JwtSecretKey, Website: "IES", scope: Constant.ScopeTeacher, standard: areaa != null ? areaa.standard : "", roles: roles.ToArray(), expire: 1);
  315. //用户在线记录
  316. try
  317. {
  318. _ = _httpTrigger.RequestHttpTrigger(new { school = defaultschool, scope = $"{Constant.ScopeTeacher}", id=$"{id}", ip=$"{ip}", expire=1 }, _option.Location, "online-record");
  319. }
  320. catch { }
  321. //取得Teacher Blob 容器位置及SAS
  322. await _azureStorage.GetBlobContainerClient(id).CreateIfNotExistsAsync(PublicAccessType.None); //嘗試創建Teacher私有容器,如存在則不做任何事,保障容器一定存在
  323. var (blob_uri, blob_sas) = _azureStorage.GetBlobContainerSAS(id, BlobContainerSasPermissions.Write | BlobContainerSasPermissions.Read | BlobContainerSasPermissions.List | BlobContainerSasPermissions.Delete);
  324. var (osblob_uri, osblob_sas) = roles.Contains("area") ? _azureStorage.GetBlobContainerSAS("teammodelos", BlobContainerSasPermissions.Write | BlobContainerSasPermissions.Read | BlobContainerSasPermissions.List | BlobContainerSasPermissions.Delete) : _azureStorage.GetBlobContainerSAS("teammodelos", BlobContainerSasPermissions.Read | BlobContainerSasPermissions.List);
  325. return new TeacherInfo
  326. {
  327. auth_token = auth_token,
  328. blob_uri = blob_uri,
  329. blob_sas = blob_sas,
  330. schools = schools,
  331. defaultschool = defaultschool,
  332. defaultschoolPeriod=defaultschoolPeriod,
  333. courses = courses,
  334. total = total,
  335. osblob_sas = osblob_sas,
  336. osblob_uri = osblob_uri,
  337. tsize = tsize,
  338. areas = areas,
  339. teacher = teacher
  340. };
  341. }
  342. }
  343. public record AreaDto
  344. {
  345. //areaId = area.id, name = area.name, area.standard, area.standardName
  346. public string areaId { get; set; }
  347. public string name { get; set; }
  348. public string standard { get; set; }
  349. public string standardName { get; set; }
  350. //public string accessConfig { get; set; }
  351. public AreaSetting setting { get; set; }
  352. public int access { get; set; } = 0;
  353. }
  354. public record TeacherInfo
  355. {
  356. public string auth_token { get; set; }
  357. public string blob_uri { get; set; }
  358. public string blob_sas { get; set; }
  359. public List<object> schools { get; set; } = new List<object>();
  360. public string defaultschool { get; set; }
  361. public string defaultschoolPeriod { get; set; }
  362. public List<object> courses { get; set; } = new List<object>();
  363. public int total { get; set; }
  364. public string osblob_uri { get; set; }
  365. public string osblob_sas { get; set; }
  366. public int tsize { get; set; }
  367. public List<AreaDto> areas { get; set; } = new List<AreaDto>();
  368. public Teacher teacher { get; set; }
  369. }
  370. }