ShareController.cs 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507
  1. using Azure.Cosmos;
  2. using HTEXLib.COMM.Helpers;
  3. using Microsoft.AspNetCore.Http;
  4. using Microsoft.AspNetCore.Mvc;
  5. using Microsoft.Extensions.Configuration;
  6. using Microsoft.Extensions.Options;
  7. using System;
  8. using System.Collections.Generic;
  9. using System.Linq;
  10. using System.Text;
  11. using System.Text.Json;
  12. using System.Threading.Tasks;
  13. using TEAMModelOS.Filter;
  14. using TEAMModelOS.Models;
  15. using TEAMModelOS.SDK.DI;
  16. using TEAMModelOS.SDK.Extension;
  17. using TEAMModelOS.SDK.Models;
  18. using TEAMModelOS.SDK.Models.Cosmos;
  19. using TEAMModelOS.SDK.Models.Cosmos.Common;
  20. using TEAMModelOS.SDK.Models.Service;
  21. using TEAMModelOS.Services.Common;
  22. namespace TEAMModelOS.Controllers
  23. {
  24. [ProducesResponseType(StatusCodes.Status200OK)]
  25. [ProducesResponseType(StatusCodes.Status400BadRequest)]
  26. //[Authorize(Roles = "IES5")]
  27. [Route("teacher/share")]
  28. [ApiController]
  29. public class ShareController: ControllerBase
  30. {
  31. private readonly AzureCosmosFactory _azureCosmos;
  32. private readonly SnowflakeId _snowflakeId;
  33. private readonly DingDing _dingDing;
  34. private readonly Option _option;
  35. private readonly IConfiguration _configuration;
  36. private readonly NotificationService _notificationService;
  37. public ShareController(AzureCosmosFactory azureCosmos, SnowflakeId snowflakeId, DingDing dingDing, IOptionsSnapshot<Option> option, IConfiguration configuration, NotificationService notificationService)
  38. {
  39. _azureCosmos = azureCosmos;
  40. _snowflakeId = snowflakeId;
  41. _dingDing = dingDing;
  42. _option = option?.Value;
  43. _configuration = configuration;
  44. _notificationService = notificationService;
  45. }
  46. /*
  47. {
  48. "school": "学校编码",
  49. "scope":"school|private",
  50. "tmdInfo":["tmdid":"id1","tmdname":"name1"],
  51. "coedit":true,
  52. "share":true,
  53. "issuer":"权限颁发者tmdid",
  54. "opt":"add/del/edit"
  55. "syllabusId":"分享的课纲章节id",
  56. "syllabusName":"章节名称",
  57. "volumeId":"册别id",
  58. "volumeName":"册别name"
  59. }
  60. */
  61. /// <summary>
  62. /// 分享及邀请共编,并设置TTL过期时间
  63. /// </summary>
  64. /// <param name="request"></param>
  65. /// <returns></returns>
  66. [ProducesDefaultResponseType]
  67. [HttpPost("to")]
  68. // [AuthToken(Roles = "Teacher")]
  69. public async Task<IActionResult> To(ShareData request) {
  70. // var (id, _, _, _) = HttpContext.GetAuthTokenInfo();
  71. try {
  72. var client = _azureCosmos.GetCosmosClient();
  73. //需要判断id== req.issuer 才能进行授权操作
  74. if (request.scope.Equals("school"))
  75. {
  76. Syllabus syllabusD = await client.GetContainer("TEAMModelOS", "School").ReadItemAsync<Syllabus>(request.syllabusId, new PartitionKey($"Syllabus-{request.volumeId}"));
  77. if (request.opt == "del")
  78. {
  79. if (syllabusD.auth.IsNotEmpty())
  80. {
  81. List<SyllabusAuth> syllabusAuths = new List<SyllabusAuth>();
  82. syllabusD.auth.ForEach(x=> {
  83. if (request.tmdInfo.Select(tmd=>tmd.tmdid).Contains(x.tmdid)) {
  84. syllabusAuths.Add(x);
  85. }
  86. }) ;
  87. syllabusAuths.ForEach(x => {
  88. syllabusD.auth.Remove(x);
  89. });
  90. await client.GetContainer("TEAMModelOS", "School").ReplaceItemAsync<Syllabus>(syllabusD, request.syllabusId, new PartitionKey($"Syllabus-{request.volumeId}"));
  91. request.tmdInfo.ForEach(async x => {
  92. await client.GetContainer("TEAMModelOS", "Teacher").DeleteItemAsync<Share>(request.syllabusId, new PartitionKey($"Share-{x.tmdid}"));
  93. });
  94. }
  95. }
  96. else if (request.opt.Equals("add") || request.opt.Equals("edit"))
  97. {
  98. (Syllabus syllabus, List<Share> shares) = DoAuth(request, syllabusD);
  99. shares.ForEach(async x=> {
  100. await client.GetContainer("TEAMModelOS", "Teacher").UpsertItemAsync<Share>(x, new PartitionKey($"{x.code}"));
  101. //发送共编或分享通知
  102. if (x.coedit == true) {
  103. Notification notification = new Notification
  104. {
  105. hubName = "hita",
  106. type = "msg",
  107. from = $"ies5:{request.school}",
  108. to = new List<string>() { x.code.Replace("Share-","") },
  109. label = $"coedit_syllabus",
  110. body = new { biz = "coedit", tmdid = x.issuer, schoolcode = $"{request.school}", status = 1, time = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() }.ToJsonString(),
  111. expires = DateTimeOffset.UtcNow.AddDays(7).ToUnixTimeSeconds()
  112. };
  113. var url = _configuration.GetValue<string>("HaBookAuth:CoreService:sendnotification");
  114. var clientID = _configuration.GetValue<string>("HaBookAuth:CoreService:clientID");
  115. var clientSecret = _configuration.GetValue<string>("HaBookAuth:CoreService:clientSecret");
  116. var location = _option.Location;
  117. var code = await _notificationService.SendNotification(clientID, clientSecret, location, url, notification);
  118. }
  119. if (x.share == true)
  120. {
  121. Notification notification = new Notification
  122. {
  123. hubName = "hita",
  124. type = "msg",
  125. from = $"ies5:{request.school}",
  126. to = new List<string>() { x.code.Replace("Share-", "") },
  127. label = $"share_syllabus",
  128. body = new { biz = "share", tmdid = x.issuer, schoolcode = $"{request.school}", status = 1, time = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() }.ToJsonString(),
  129. expires = DateTimeOffset.UtcNow.AddDays(7).ToUnixTimeSeconds()
  130. };
  131. var url = _configuration.GetValue<string>("HaBookAuth:CoreService:sendnotification");
  132. var clientID = _configuration.GetValue<string>("HaBookAuth:CoreService:clientID");
  133. var clientSecret = _configuration.GetValue<string>("HaBookAuth:CoreService:clientSecret");
  134. var location = _option.Location;
  135. var code = await _notificationService.SendNotification(clientID, clientSecret, location, url, notification);
  136. }
  137. });
  138. await client.GetContainer("TEAMModelOS", "School").UpsertItemAsync<Syllabus>(syllabus, new PartitionKey($"Syllabus-{request.volumeId}"));
  139. }
  140. }
  141. else if (request.scope.Equals("private"))
  142. {
  143. Syllabus syllabusD = await client.GetContainer("TEAMModelOS", "Teacher").ReadItemAsync<Syllabus>(request.syllabusId, new PartitionKey($"Syllabus-{request.volumeId}"));
  144. if (request.opt == "del")
  145. {
  146. if (syllabusD.auth.IsNotEmpty())
  147. {
  148. List<SyllabusAuth> syllabusAuths = new List<SyllabusAuth>();
  149. syllabusD.auth.ForEach(x => {
  150. if (request.tmdInfo.Select(tmd=>x.tmdid).Contains(x.tmdid))
  151. {
  152. syllabusAuths.Add(x);
  153. }
  154. });
  155. syllabusAuths.ForEach(x => {
  156. syllabusD.auth.Remove(x);
  157. });
  158. await client.GetContainer("TEAMModelOS", "Teacher").ReplaceItemAsync<Syllabus>(syllabusD, request.syllabusId, new PartitionKey($"Syllabus-{request.volumeId}"));
  159. request.tmdInfo.ForEach(async x => {
  160. await client.GetContainer("TEAMModelOS", "Teacher").DeleteItemAsync<Share>(request.syllabusId, new PartitionKey($"Share-{x.tmdid}"));
  161. });
  162. }
  163. }
  164. else if (request.opt.Equals("add") || request.opt.Equals("edit"))
  165. {
  166. (Syllabus vlm, List<Share> shares) = DoAuth(request, syllabusD);
  167. shares.ForEach(async x => {
  168. await client.GetContainer("TEAMModelOS", "Teacher").UpsertItemAsync<Share>(x, new PartitionKey($"{x.code}"));
  169. });
  170. await client.GetContainer("TEAMModelOS", "Teacher").UpsertItemAsync<Syllabus>(syllabusD, new PartitionKey($"Syllabus-{request.volumeId}"));
  171. }
  172. }
  173. return Ok(new { code=200});
  174. }
  175. catch (Exception ex ) {
  176. await _dingDing.SendBotMsg($"OS,{_option.Location},teacher/share/to\n{ex.Message}{ex.StackTrace}", GroupNames.成都开发測試群組);
  177. }
  178. return Ok(new { code = 500 });
  179. }
  180. private (Syllabus, List<Share>) DoAuth(ShareData request, Syllabus syllabus)
  181. {
  182. long now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
  183. List<Share> shares = new List<Share>();
  184. request.tmdInfo.ForEach(xmd => {
  185. var share = new Share
  186. {
  187. id = request.syllabusId,
  188. volumeId = request.volumeId,
  189. volumeName = request.volumeName,
  190. syllabusName = request.syllabusName,
  191. code = $"Share-{xmd.tmdid}",
  192. pk = "Share",
  193. ttl = -1,
  194. issuer = request.issuer,
  195. createTime = now,
  196. school = request.school,
  197. scope = request.scope,
  198. coedit = request.coedit,
  199. share = request.share,
  200. cagree = 0,
  201. sagree = 0
  202. };
  203. shares.Add(share);
  204. });
  205. if (syllabus.auth.IsNotEmpty())
  206. {
  207. request.tmdInfo.ForEach(xmd => {
  208. bool flag = false;
  209. int indx = 0;
  210. for (int index = 0; index < syllabus.auth.Count; index++)
  211. {
  212. if (syllabus.auth[index].tmdid == xmd.tmdid)
  213. {
  214. flag = true;
  215. indx = index;
  216. break;
  217. }
  218. }
  219. ///更新位置上的授权信息
  220. if (flag)
  221. {
  222. syllabus.auth[indx] = new SyllabusAuth
  223. {
  224. tmdid = xmd.tmdid,
  225. tmdname = xmd.tmdname,
  226. coedit = request.coedit,
  227. share = request.share,
  228. };
  229. }
  230. //新增
  231. else
  232. {
  233. syllabus.auth.Add(new SyllabusAuth
  234. {
  235. tmdid = xmd.tmdid,
  236. tmdname = xmd.tmdname,
  237. coedit = request.coedit,
  238. share = request.share,
  239. });
  240. }
  241. });
  242. }
  243. else
  244. {
  245. request.tmdInfo.ForEach(xmd => {
  246. syllabus.auth = new List<SyllabusAuth>() {
  247. new SyllabusAuth {
  248. tmdid = xmd.tmdid,
  249. tmdname = xmd.tmdname,
  250. coedit=request.coedit,
  251. share=request.share,
  252. }
  253. };
  254. });
  255. }
  256. return (syllabus, shares);
  257. }
  258. /// <summary>
  259. /// {"code":"教师编码","id":"章节id","agree":1共编同意,2共编拒绝,3分享同意,4分享拒绝}
  260. /// 教师拉取自己收到的分享及共编
  261. /// </summary>
  262. /// <param name="request"></param>
  263. /// <returns></returns>
  264. [ProducesDefaultResponseType]
  265. [HttpPost("agree")]
  266. // [AuthToken(Roles = "Teacher")]
  267. public async Task<IActionResult> Agree(JsonElement request) {
  268. try {
  269. if (!request.TryGetProperty("agree", out JsonElement agree)) { return BadRequest(); }
  270. if (!request.TryGetProperty("code", out JsonElement code)) { return BadRequest(); }
  271. if (!request.TryGetProperty("id", out JsonElement id)) { return BadRequest(); }
  272. var client = _azureCosmos.GetCosmosClient();
  273. Share share= await client.GetContainer("TEAMModelOS", "Teacher").ReadItemAsync<Share>($"{id}", new PartitionKey($"Share-{code}"));
  274. if (agree.ValueKind.Equals(JsonValueKind.Number))
  275. {
  276. int agreeNum = agree.GetInt32();
  277. if (agreeNum == 1)
  278. {
  279. share.cagree = 1;
  280. }
  281. else if (agreeNum == 2)
  282. {
  283. share.cagree = 2;
  284. }
  285. else if (agreeNum == 3)
  286. {
  287. share.sagree = 1;
  288. }
  289. else if (agreeNum == 4)
  290. {
  291. share.sagree = 2;
  292. }
  293. else
  294. {
  295. return Ok(new { status = 500 });
  296. }
  297. await client.GetContainer("TEAMModelOS", "Teacher").ReplaceItemAsync<Share>(share, $"{id}", new PartitionKey($"Share-{code}"));
  298. Syllabus syllabus = null;
  299. if (share.scope == "school")
  300. {
  301. syllabus = await client.GetContainer("TEAMModelOS", "School").ReadItemAsync<Syllabus>($"{id}", new PartitionKey($"Syllabus-{share.volumeId}"));
  302. }
  303. else if (share.scope == "private")
  304. {
  305. syllabus = await client.GetContainer("TEAMModelOS", "Teacher").ReadItemAsync<Syllabus>($"{id}", new PartitionKey($"Syllabus-{share.volumeId}"));
  306. }
  307. if (syllabus.auth.IsNotEmpty())
  308. {
  309. syllabus.auth.ForEach(x =>
  310. {
  311. if (x.tmdid == $"{code}")
  312. {
  313. if (agreeNum == 1)
  314. {
  315. x.cagree = 1;
  316. }
  317. else if (agreeNum == 2)
  318. {
  319. x.cagree = 2;
  320. }
  321. else if (agreeNum == 3)
  322. {
  323. x.sagree = 1;
  324. }
  325. else if (agreeNum == 4)
  326. {
  327. x.sagree = 2;
  328. }
  329. }
  330. });
  331. }
  332. if (share.scope == "school")
  333. {
  334. syllabus = await client.GetContainer("TEAMModelOS", "School").ReplaceItemAsync<Syllabus>(syllabus, $"{id}", new PartitionKey($"Syllabus-{share.volumeId}"));
  335. }
  336. else if (share.scope == "private")
  337. {
  338. syllabus = await client.GetContainer("TEAMModelOS", "Teacher").ReplaceItemAsync<Syllabus>(syllabus, $"{id}", new PartitionKey($"Syllabus-{share.volumeId}"));
  339. }
  340. return Ok(new { status = 200 });
  341. }
  342. else
  343. {
  344. return Ok(new { status = 500 });
  345. }
  346. } catch (Exception ex)
  347. {
  348. await _dingDing.SendBotMsg($"OS,{_option.Location},teacher/share/agree()\n{ex.Message}{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
  349. return Ok(new { status = 500 });
  350. }
  351. }
  352. /// <summary>
  353. /// {"code":"教师编码","type":"coedit/share"}
  354. /// 教师拉取自己收到的分享及共编
  355. /// </summary>
  356. /// <param name="request"></param>
  357. /// <returns></returns>
  358. [ProducesDefaultResponseType]
  359. [HttpPost("find")]
  360. // [AuthToken(Roles = "Teacher")]
  361. public async Task<IActionResult> Find(JsonElement request)
  362. {
  363. try
  364. {
  365. List<Share> shares = new List<Share>();
  366. if (!request.TryGetProperty("code", out JsonElement code)) { return BadRequest(); }
  367. if (!request.TryGetProperty("type", out JsonElement type)) { return BadRequest(); }
  368. request.TryGetProperty("id", out JsonElement id);
  369. var client = _azureCosmos.GetCosmosClient();
  370. StringBuilder queryText = new StringBuilder("select value(c) from c");
  371. if (type.ValueKind.Equals(JsonValueKind.String) && type.GetString() == "coedit")
  372. {
  373. queryText.Append(" where c.coedit=true");
  374. }
  375. else if (type.ValueKind.Equals(JsonValueKind.String) && type.GetString() == "share")
  376. {
  377. queryText.Append(" where c.share=true");
  378. }
  379. else {
  380. await _dingDing.SendBotMsg($"OS,{_option.Location},teacher/share/find()\n type== coedit|share 参数必选一个", GroupNames.醍摩豆服務運維群組);
  381. return BadRequest();
  382. }
  383. if (id.ValueKind.Equals(JsonValueKind.String) && !string.IsNullOrEmpty(id.GetString())) {
  384. queryText.Append($" and c.id='{id}'");
  385. }
  386. await foreach (var item in client.GetContainer("TEAMModelOS", "Teacher").GetItemQueryIterator<Share>(queryText: queryText.ToString(),
  387. requestOptions: new QueryRequestOptions() { PartitionKey = new Azure.Cosmos.PartitionKey($"Share-{code}") }))
  388. {
  389. shares.Add(item);
  390. }
  391. var sharesGp = shares.GroupBy(x => new {id= x.volumeId ,code=x.scope=="school"?x.school:x.issuer}).Select(y=>new { id=y.Key.id,code=y.Key.code,list=y.ToList()});
  392. return Ok(new { shares = sharesGp });
  393. }
  394. catch (Exception ex)
  395. {
  396. await _dingDing.SendBotMsg($"OS,{_option.Location},teacher/share/find()\n{ex.Message}{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
  397. return BadRequest();
  398. }
  399. }
  400. public record ShareView
  401. {
  402. public string scope { get; set; }
  403. // public string code { get; set; }
  404. public string school { get; set; }
  405. public string issuer { get; set; }
  406. public string volumeId { get; set; }
  407. public List<string> syllabusId { get; set; }
  408. }
  409. /*
  410. {
  411. "scope": "school/private",
  412. "school": "学校编码",
  413. "issuer": "权限颁发者",
  414. "volumeId": "册别id",
  415. "syllabusId": ["id1课纲章节节点id","id2课纲章节节点id"],
  416. }
  417. */
  418. /// <summary>
  419. /// 查看分享
  420. /// </summary>
  421. /// <param name="request"></param>
  422. /// <returns></returns>
  423. [ProducesDefaultResponseType]
  424. [HttpPost("view-share")]
  425. [AuthToken(Roles = "teacher")]
  426. public async Task<IActionResult> View(ShareView request)
  427. {
  428. try
  429. {
  430. List<SyllabusTreeNode> treeNodes = new List<SyllabusTreeNode>();
  431. Volume volume;
  432. var client = _azureCosmos.GetCosmosClient();
  433. string code = null;
  434. List<string> sid = new List<string>();
  435. request.syllabusId.ForEach(x => { sid.Add($"'{x}'"); });
  436. var sidSql= string.Join(",", sid);
  437. if (request.scope == "school")
  438. {
  439. code = request.school;
  440. var queryslt = $"SELECT value(c) FROM c where c.id in ({sidSql})";
  441. await foreach (var item in client.GetContainer("TEAMModelOS", "School").GetItemQueryIterator<Syllabus>(queryText: queryslt, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Syllabus-{request.volumeId}") }))
  442. {
  443. List<SyllabusTree> trees = SyllabusService.ListToTree(item.children);
  444. SyllabusTreeNode tree = new SyllabusTreeNode() { id = item.id, scope = item.scope, trees = trees, volumeId = item.volumeId, auth = item.auth };
  445. treeNodes.Add(tree);
  446. }
  447. volume = await client.GetContainer("TEAMModelOS", "School").ReadItemAsync<Volume>(request.volumeId, new PartitionKey($"Volume-{code}"));
  448. }
  449. else if (request.scope == "private")
  450. {
  451. code = request.issuer;
  452. var queryslt = $"SELECT value(c) FROM c where c.id in ({sidSql})";
  453. await foreach (var item in client.GetContainer("TEAMModelOS", "Teacher").GetItemQueryIterator<Syllabus>(queryText: queryslt, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"Syllabus-{request.volumeId}") }))
  454. {
  455. List<SyllabusTree> trees = SyllabusService.ListToTree(item.children);
  456. SyllabusTreeNode tree = new SyllabusTreeNode() { id = item.id, scope = item.scope, trees = trees, volumeId = item.volumeId, auth = item.auth };
  457. treeNodes.Add(tree);
  458. }
  459. volume = await client.GetContainer("TEAMModelOS", "Teacher").ReadItemAsync<Volume>(request.volumeId, new PartitionKey($"Volume-{code}"));
  460. }
  461. else
  462. {
  463. return BadRequest();
  464. }
  465. return Ok(new { volume,tree= treeNodes });
  466. }
  467. catch (Exception ex) {
  468. await _dingDing.SendBotMsg($"OS,{_option.Location},teacher/share/view()\n{ex.Message}", GroupNames.醍摩豆服務運維群組);
  469. return BadRequest();
  470. }
  471. }
  472. /// <summary>
  473. ///二维码扫码方式
  474. /// </summary>
  475. /// <param name="request"></param>
  476. /// <returns></returns>
  477. [ProducesDefaultResponseType]
  478. [HttpPost("qrcode")]
  479. //[AuthToken(Roles = "Teacher")]
  480. public async Task<IActionResult> Qrcode(Favorite request)
  481. {
  482. return Ok();
  483. }
  484. }
  485. }