DebateController.cs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412
  1. using Azure.Cosmos;
  2. using Microsoft.AspNetCore.Http;
  3. using Microsoft.AspNetCore.Mvc;
  4. using Microsoft.Extensions.Options;
  5. using System;
  6. using System.Collections.Generic;
  7. using System.Linq;
  8. using System.Text;
  9. using System.Text.Json;
  10. using System.Threading.Tasks;
  11. using TEAMModelOS.Models;
  12. using TEAMModelOS.SDK.DI;
  13. using TEAMModelOS.SDK;
  14. using TEAMModelOS.SDK.Models;
  15. using TEAMModelOS.SDK.Extension;
  16. using TEAMModelOS.Filter;
  17. using HTEXLib.COMM.Helpers;
  18. using Microsoft.AspNetCore.Authorization;
  19. namespace TEAMModelOS.Controllers
  20. {
  21. [ProducesResponseType(StatusCodes.Status200OK)]
  22. [ProducesResponseType(StatusCodes.Status400BadRequest)]
  23. [Route("school/debate")]
  24. [ApiController]
  25. public class DebateController: ControllerBase
  26. {
  27. private readonly AzureCosmosFactory _azureCosmos;
  28. private readonly DingDing _dingDing;
  29. private readonly Option _option;
  30. public DebateController(AzureCosmosFactory azureCosmos, SnowflakeId snowflakeId, DingDing dingDing, IOptionsSnapshot<Option> option)
  31. {
  32. _azureCosmos = azureCosmos;
  33. _dingDing = dingDing;
  34. _option = option?.Value; ;
  35. }
  36. /// <summary>
  37. /// 查询话题
  38. /// </summary>
  39. /// <param name="request"></param>
  40. /// <returns></returns>
  41. [ProducesDefaultResponseType]
  42. //[AuthToken(Roles = "teacher")]
  43. [HttpPost("find-id")]
  44. [AuthToken(Roles = "teacher,admin,student")]
  45. #if !DEBUG
  46. [Authorize(Roles = "IES")]
  47. #endif
  48. public async Task<IActionResult> FindId(JsonElement request)
  49. {
  50. try
  51. {
  52. if (!request.TryGetProperty("debateId", out JsonElement _debateId)) { return BadRequest(); }
  53. if (!request.TryGetProperty("debateCode", out JsonElement _debateCode)) { return BadRequest(); }
  54. if (!request.TryGetProperty("scope", out JsonElement _scope)) { return BadRequest(); }
  55. string debateCode = $"{_scope}".Equals("school") ? $"{_debateCode}".StartsWith("Debate-") ? $"{_debateCode}" : $"Debate-{_debateCode}" : "Debate";
  56. List<dynamic> debates = new List<dynamic>();
  57. Debate debate = await _azureCosmos.GetCosmosClient().GetContainer("TEAMModelOS", "School").ReadItemAsync<Debate>($"{_debateId}", new PartitionKey($"{debateCode}"));
  58. return Ok(new { debate = debate , replyCount = debate.replies!=null ?debate.replies.Count:0});
  59. }
  60. catch (Exception ex)
  61. {
  62. await _dingDing.SendBotMsg($"OS,{_option.Location},DebateController,school/debate/FindId\n{ex.Message}\n{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
  63. return BadRequest();
  64. }
  65. }
  66. /// <summary>
  67. /// 删除话题
  68. /// </summary>
  69. /// <param name="request"></param>
  70. /// <returns></returns>
  71. [ProducesDefaultResponseType]
  72. //[AuthToken(Roles = "teacher")]
  73. [HttpPost("delete")]
  74. #if !DEBUG
  75. [AuthToken(Roles = "teacher,admin,student")]
  76. [Authorize(Roles = "IES")]
  77. #endif
  78. public async Task<IActionResult> Delete(JsonElement request)
  79. {
  80. try
  81. {
  82. if (!request.TryGetProperty("debateId", out JsonElement _debateId)) { return BadRequest(); }
  83. if (!request.TryGetProperty("debateCode", out JsonElement _debateCode)) { return BadRequest(); }
  84. if (!request.TryGetProperty("scope", out JsonElement _scope)) { return BadRequest(); }
  85. string debateCode =$"{_scope}".Equals("school")? $"{_debateCode}".StartsWith("Debate-") ? $"{_debateCode}" : $"Debate-{_debateCode}": "Debate";
  86. await _azureCosmos.GetCosmosClient().GetContainer("TEAMModelOS", "School").DeleteItemStreamAsync($"{_debateId}", new PartitionKey($"{debateCode}"));
  87. return Ok(new { status=200});
  88. }
  89. catch (Exception ex)
  90. {
  91. await _dingDing.SendBotMsg($"OS,{_option.Location},DebateController,school/debate/delete\n{ex.Message}\n{ex.StackTrace}{request.ToJsonString()}", GroupNames.醍摩豆服務運維群組);
  92. return BadRequest();
  93. }
  94. }
  95. /// <summary>
  96. /// 查询所有话题
  97. /// </summary>
  98. /// <param name="request"></param>
  99. /// <returns></returns>
  100. [ProducesDefaultResponseType]
  101. //[AuthToken(Roles = "teacher")]
  102. [HttpPost("find")]
  103. [AuthToken(Roles = "teacher,admin,student")]
  104. #if !DEBUG
  105. [Authorize(Roles = "IES")]
  106. #endif
  107. public async Task<IActionResult> Find(JsonElement request) {
  108. try
  109. {
  110. List<dynamic> debates = new List<dynamic>();
  111. request.TryGetProperty("source", out JsonElement _source);
  112. request.TryGetProperty("tmdid", out JsonElement _tmdid);
  113. request.TryGetProperty("userType", out JsonElement _userType);
  114. request.TryGetProperty("keyWord", out JsonElement _keyWord);
  115. request.TryGetProperty("comid", out JsonElement _comid);
  116. string debateCode = "Debate";
  117. if (request.TryGetProperty("code", out JsonElement _code) && !string.IsNullOrWhiteSpace($"{_code}"))
  118. {
  119. debateCode= $"{_code}".StartsWith("Debate-") ? $"{_code}" : $"Debate-{_code}";
  120. }
  121. StringBuilder stringBuilder = new StringBuilder($"select c.id,c.code,c.tmdid,c.userType,c.tmdname,c.title,c.comment,c.time,c.comid,c.likeCount,c.school,c.wordCount,c.timeoutReply,c.expire,c.source,c.pk, ARRAY_LENGTH( c.replies ) as replyCount from c where c.pk='Debate' ");
  122. if (!string.IsNullOrEmpty($"{_source}")) {
  123. stringBuilder.Append($" and c.source='{_source}'");
  124. }
  125. if (!string.IsNullOrEmpty($"{_tmdid}") && !string.IsNullOrWhiteSpace($"{_userType}"))
  126. {
  127. stringBuilder.Append($" and c.tmdid='{_tmdid}' and c.userType='{_userType}'");
  128. }
  129. if (!string.IsNullOrEmpty($"{_keyWord}"))
  130. {
  131. stringBuilder.Append($" and contains(c.title,'{_keyWord}') ");
  132. }
  133. if (!string.IsNullOrEmpty($"{_comid}"))
  134. {
  135. stringBuilder.Append($" and c.comid='{_comid}'");
  136. }
  137. stringBuilder.Append("order by c.time desc ");
  138. await foreach (var item in _azureCosmos.GetCosmosClient().GetContainer("TEAMModelOS", "School")
  139. .GetItemQueryIterator<dynamic>(queryText: stringBuilder.ToString(), requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey(debateCode) }))
  140. {
  141. debates.Add(item);
  142. }
  143. return Ok(debates);
  144. }
  145. catch (Exception ex)
  146. {
  147. await _dingDing.SendBotMsg($"OS,{_option.Location},school/debate/find\n{ex.Message}\n{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
  148. return BadRequest();
  149. }
  150. }
  151. /*
  152. {
  153. "opt":"add"
  154. "debateId": "1fcdb1d6-de51-40d5-bfa0-193c3929e50c",
  155. "debateCode":"Debate-hbcn",
  156. "reply":{
  157. "id":null,
  158. "pid":"话题的id 1fcdb1d6-de51-40d5-bfa0-193c3929e50c",
  159. "tmdid":"回复者的id",
  160. "tmdname":"回复者的昵称",
  161. "comment":"回复内容",
  162. "atTmdid":"被回复的id",
  163. "atTmdname":"被回复的name"
  164. }
  165. }
  166. {
  167. "opt":"del"
  168. "debateId": "1fcdb1d6-de51-40d5-bfa0-193c3929e50c",
  169. "debateCode":"Debate-hbcn",
  170. "reply":{
  171. "id":"uuid"
  172. }
  173. }
  174. */
  175. /// <summary>
  176. /// 回复话题
  177. /// </summary>
  178. /// <param name="request"></param>
  179. /// <returns></returns>
  180. [ProducesDefaultResponseType]
  181. [HttpPost("reply")]
  182. [Authorize(Roles = "IES")]
  183. [AuthToken(Roles = "admin,teacher,student")]
  184. public async Task<IActionResult> reply(JsonElement request) {
  185. var (userid, _, _, school) = HttpContext.GetAuthTokenInfo();
  186. if (!request.TryGetProperty("opt", out JsonElement _opt)) { return BadRequest(); }
  187. if (!request.TryGetProperty("debateId", out JsonElement _debateId)) { return BadRequest(); }
  188. if (!request.TryGetProperty("debateCode", out JsonElement _debateCode)) { return BadRequest(); }
  189. if (!request.TryGetProperty("scope", out JsonElement _scope )) { return BadRequest(); }
  190. DebateReply reply = null;
  191. string debateCode = $"{_debateCode}".StartsWith("Debate-") ? $"{_debateCode}" : $"Debate-{_debateCode}";
  192. if (_scope.Equals("private")) {
  193. debateCode="Debate";
  194. }
  195. if (!string.IsNullOrEmpty($"{_debateId}") && !string.IsNullOrEmpty($"{debateCode}") && !string.IsNullOrEmpty($"{_opt}"))
  196. {
  197. switch ($"{_opt}")
  198. {
  199. case "add":
  200. try
  201. {
  202. if (!request.TryGetProperty("reply", out JsonElement _reply)) { return BadRequest(); }
  203. Debate debate = await _azureCosmos.GetCosmosClient().GetContainer("TEAMModelOS", "School").ReadItemAsync<Debate>($"{_debateId}", new PartitionKey($"{debateCode}"));
  204. reply = _reply.ToObject<DebateReply>();
  205. reply.pid = string.IsNullOrEmpty(reply.pid) ? $"{_debateId}" : reply.pid;
  206. reply.id = string.IsNullOrEmpty(reply.id) ? Guid.NewGuid().ToString() : reply.id;
  207. reply.time = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
  208. debate.replies.Add(reply);
  209. debate.likeCount = debate.replies.SelectMany(z => z.likes).Count();
  210. await _azureCosmos.GetCosmosClient().GetContainer("TEAMModelOS", "School").ReplaceItemAsync<Debate>(debate, $"{_debateId}", new PartitionKey($"{debateCode}"));
  211. return Ok(new { reply , replyCount = debate.replies.Count });
  212. }
  213. catch (CosmosException ex)
  214. {
  215. return BadRequest();
  216. }
  217. case "like":
  218. try
  219. {
  220. Debate debate = await _azureCosmos.GetCosmosClient().GetContainer("TEAMModelOS", "School").ReadItemAsync<Debate>($"{_debateId}", new PartitionKey($"{debateCode}"));
  221. if (!request.TryGetProperty("likeData", out JsonElement _likeData)) { return BadRequest(); }
  222. LikeRcd likeRcd = _likeData.ToObject<LikeRcd>();
  223. if (likeRcd != null && !string.IsNullOrWhiteSpace(likeRcd.replyId))
  224. {
  225. var replyData = debate.replies.Find(x => $"{likeRcd.replyId}".Equals(x.id));
  226. var like = replyData.likes.FindAll(c => !string.IsNullOrWhiteSpace(c.tmdid) && c.tmdid.Equals(likeRcd.tmdid) && !string.IsNullOrWhiteSpace(c.userType) && c.userType.Equals(likeRcd.userType));
  227. if (like.IsEmpty())
  228. {
  229. replyData.likes.Add(likeRcd);
  230. }
  231. debate.likeCount = debate.replies.SelectMany(z => z.likes).Count();
  232. await _azureCosmos.GetCosmosClient().GetContainer("TEAMModelOS", "School").ReplaceItemAsync<Debate>(debate, $"{_debateId}", new PartitionKey($"{debateCode}"));
  233. return Ok(new { reply, replyCount = debate.replies.Count });
  234. }
  235. else {
  236. return BadRequest();
  237. }
  238. }
  239. catch (CosmosException ex)
  240. {
  241. return BadRequest();
  242. }
  243. case "unlike":
  244. try
  245. {
  246. Debate debate = await _azureCosmos.GetCosmosClient().GetContainer("TEAMModelOS", "School").ReadItemAsync<Debate>($"{_debateId}", new PartitionKey($"{debateCode}"));
  247. if (!request.TryGetProperty("likeData", out JsonElement _likeData)) { return BadRequest(); }
  248. LikeRcd likeRcd = _likeData.ToObject<LikeRcd>();
  249. if (likeRcd != null && !string.IsNullOrWhiteSpace(likeRcd.replyId))
  250. {
  251. var replyData = debate.replies.Find(x => $"{likeRcd.replyId}".Equals(x.id) );
  252. replyData.likes.RemoveAll(c => !string.IsNullOrWhiteSpace(c.tmdid) && c.tmdid.Equals(likeRcd.tmdid) && !string.IsNullOrWhiteSpace(c.userType) && c.userType.Equals(likeRcd.userType));
  253. debate.likeCount = debate.replies.SelectMany(z => z.likes).Count();
  254. await _azureCosmos.GetCosmosClient().GetContainer("TEAMModelOS", "School").ReplaceItemAsync<Debate>(debate, $"{_debateId}", new PartitionKey($"{debateCode}"));
  255. return Ok(new { reply, replyCount = debate.replies.Count });
  256. }
  257. else
  258. {
  259. return BadRequest();
  260. }
  261. }
  262. catch (CosmosException ex)
  263. {
  264. return BadRequest();
  265. }
  266. case "del":
  267. try
  268. {
  269. Debate debate = await _azureCosmos.GetCosmosClient().GetContainer("TEAMModelOS", "School").ReadItemAsync<Debate>($"{_debateId}", new PartitionKey($"{debateCode}"));
  270. if (!request.TryGetProperty("replyId", out JsonElement _replyId)) { return BadRequest(); }
  271. var findall = debate.replies.FindAll(x => $"{_replyId}" .Equals(x.id));
  272. if (findall.IsNotEmpty())
  273. {
  274. foreach (var find in findall)
  275. {
  276. if (find.tmdid .Equals(userid)||debate.tmdid .Equals(userid))
  277. {
  278. debate.replies.Remove(find);
  279. }
  280. else {
  281. //不能删除别人的
  282. return Ok(new { status = 2, replyCount = debate.replies.Count });
  283. }
  284. }
  285. debate.likeCount = debate.replies.SelectMany(z => z.likes).Count();
  286. await _azureCosmos.GetCosmosClient().GetContainer("TEAMModelOS", "School").ReplaceItemAsync<Debate>(debate, $"{_debateId}", new PartitionKey($"{debateCode}"));
  287. //删除成功
  288. return Ok(new { status = 0 , replyCount = debate.replies.Count });
  289. }
  290. else {
  291. return Ok(new { status = 0, replyCount = debate.replies.Count });
  292. }
  293. }
  294. catch (CosmosException ex)
  295. { //删除失败,话题不存在
  296. return Ok(new { status=1});
  297. }
  298. default:
  299. return BadRequest("opt only is del or add");
  300. }
  301. }
  302. else { return BadRequest(); }
  303. }
  304. /*
  305. {
  306. "comid":"uuid",
  307. "source":"normal",
  308. "debate":
  309. {
  310. "id":"uuid",
  311. "code":"hbcn",
  312. "tmdid":"153445tmdid",
  313. "tmdname":"醍摩豆。昵称",
  314. "title":"本次教研活动有哪些建议?",
  315. "comment":"请大家踊跃讨论!",
  316. "time":0,
  317. "school":"hbcn",
  318. "wordCount":500,//字数限制
  319. "timeoutReply":false,
  320. "expire":11255522
  321. }
  322. }
  323. */
  324. /// <summary>
  325. /// 创建话题
  326. /// </summary>
  327. /// <param name="request"></param>
  328. /// <returns></returns>
  329. [ProducesDefaultResponseType]
  330. [HttpPost("upsert")]
  331. [AuthToken(Roles = "teacher,admin,student")]
  332. #if !DEBUG
  333. [Authorize(Roles = "IES")]
  334. #endif
  335. public async Task<IActionResult> upsert(JsonElement request) {
  336. var (tid, tname, _, tschool) = HttpContext.GetAuthTokenInfo();
  337. /// 评论/话题的统一id,用于被关联
  338. request.TryGetProperty("comid", out JsonElement _comid);
  339. /// 创建来源
  340. if (!request.TryGetProperty("source", out JsonElement _source)) { return BadRequest("source param is null!"); }
  341. request.TryGetProperty("debate", out JsonElement _debate);
  342. string comid = $"{_comid}";
  343. ///如果位空则表示来源并非 技能点话题或者作品成果话题 或者以及其他已经定义的业务类型关联的comid,为保证数据完整性,则需要重新生成。
  344. if (string.IsNullOrEmpty(comid)) {
  345. comid = Guid.NewGuid().ToString();
  346. }
  347. Debate debate= _debate.ToObject<Debate>();
  348. debate.comid = comid;
  349. debate.source = $"{_source}";
  350. if ($"{_source}".Equals("normal") || $"{_source}".Equals("ability")) {
  351. debate.openType = 1;
  352. }
  353. else { debate.openType = 0; }
  354. debate.pk = "Debate";
  355. if (string.IsNullOrEmpty(debate.code))
  356. {
  357. return BadRequest("debate.code param is null!");
  358. }
  359. else {
  360. debate.code = debate.code.StartsWith("Debate-") ? debate.code : $"Debate-{debate.code}";
  361. }
  362. ///个人的不建立分区键
  363. if (debate.code.Contains(tid)) {
  364. debate.scope="private";
  365. debate.code="Debate";
  366. }
  367. if (!string.IsNullOrWhiteSpace(tschool) && debate.code.Contains(tschool))
  368. {
  369. debate.scope="school";
  370. }
  371. if (string.IsNullOrEmpty(debate.id)) {
  372. debate.id = Guid.NewGuid().ToString();
  373. debate.time= DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
  374. await _azureCosmos.GetCosmosClient().GetContainer("TEAMModelOS", "School").CreateItemAsync(debate, new PartitionKey(debate.code));
  375. }
  376. else {
  377. try
  378. {
  379. Debate _DBdebate = await _azureCosmos.GetCosmosClient().GetContainer("TEAMModelOS", "School").ReadItemAsync<Debate>(debate.id, new PartitionKey(debate.code));
  380. ///保证记录回复记录和点赞记录正常
  381. debate.replies = _DBdebate.replies;
  382. debate.likeCount = _DBdebate.likeCount;
  383. await _azureCosmos.GetCosmosClient().GetContainer("TEAMModelOS", "School").ReplaceItemAsync(debate, debate.id, new PartitionKey(debate.code));
  384. }
  385. catch (CosmosException ex) {
  386. if (ex.Status == 404) {
  387. await _azureCosmos.GetCosmosClient().GetContainer("TEAMModelOS", "School").CreateItemAsync(debate, new PartitionKey(debate.code));
  388. }
  389. }
  390. }
  391. return Ok(new { debate= debate,now= DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() });
  392. }
  393. }
  394. }