ActivityService.cs 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text.Json;
  5. using System.Threading.Tasks;
  6. using TEAMModelOS.SDK.Models.Cosmos;
  7. using TEAMModelOS.SDK.Extension;
  8. using Azure.Cosmos;
  9. using TEAMModelOS.SDK.DI;
  10. using HTEXLib.COMM.Helpers;
  11. using System.Text;
  12. using TEAMModelOS.SDK.Models;
  13. using TEAMModelOS.SDK.Models.Cosmos.Common.Inner;
  14. using StackExchange.Redis;
  15. namespace TEAMModelOS.Services.Common
  16. {
  17. public static class ActivityService
  18. {
  19. /// <summary>
  20. /// 活动委托
  21. /// </summary>
  22. /// <param name="data"></param>
  23. /// <returns></returns>
  24. delegate dynamic DoActivityTips(ActivityData data, AzureCosmosFactory _azureCosmos,string id, AzureRedisFactory _azureRedis);
  25. public static async Task<int> Decide(JsonElement request,AzureCosmosFactory _azureCosmos,AzureRedisFactory _azureRedis,string userid ) {
  26. DateTimeOffset now = DateTimeOffset.UtcNow;
  27. long curr = now.ToUnixTimeMilliseconds();
  28. byte msgid = 0;//0投票失败,1投票成功,2不在时间范围内,3不在发布范围内,4投票周期内重复投票,5周期内的可投票数不足,6未设置投票项
  29. //活动id
  30. if (!request.TryGetProperty("id", out JsonElement id)) {
  31. return msgid;
  32. }
  33. //活动分区
  34. if (!request.TryGetProperty("code", out JsonElement code)) {
  35. return msgid;
  36. }
  37. List<string> option = new List<string>();
  38. if (request.TryGetProperty("option", out JsonElement joption))
  39. {
  40. option = joption.ToObject<List<string>>();
  41. if (option.IsEmpty())
  42. {
  43. msgid = 6;
  44. return msgid;
  45. }
  46. }
  47. else
  48. {
  49. return msgid;
  50. }
  51. try
  52. {
  53. //1.再次检查投票
  54. var client = _azureCosmos.GetCosmosClient();
  55. Vote vote = null;
  56. ///TODO 检查是否在投票范围内,包括在tmdids 及班级 但是需要处理认证金钥中的班级问题
  57. await foreach (var item in client.GetContainer("TEAMModelOS", "Common").GetItemQueryIterator<Vote>(queryText: $"select c.progress,c.times,c.voteNum,c.startTime,c.endTime from c where c.id = '{id}'",
  58. requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"{code}") }))
  59. {
  60. vote = item;
  61. break;
  62. }
  63. if (vote != null)
  64. {
  65. //判断投票时间是否在起止时间内
  66. if (curr >= vote.startTime && curr <= vote.endTime)
  67. {
  68. string Field = "";
  69. RedisValue value;
  70. switch (vote.times) {
  71. case "once":
  72. // //如果是只能投票一次的活动则直接获取Redis的第一条 只能投一次
  73. Field = $"{userid}-once";
  74. RedisValue[] values = _azureRedis.GetRedisClient(8).HashValues($"Vote:Record:{vote.id}_{vote.code}");
  75. if (values != null && values.Length > 0)
  76. {
  77. msgid = await VoteIng(vote, values[0], msgid, option, Field, curr, _azureRedis);
  78. }
  79. else
  80. {
  81. msgid = 1;
  82. }
  83. break;
  84. case "day": //周期内每天
  85. Field = $"{userid}-day-{now.ToString("yyyyMMdd")}";
  86. value = _azureRedis.GetRedisClient(8).HashGet($"Vote:Record:{vote.id}_{vote.code}", Field);
  87. msgid = await VoteIng(vote, value, msgid, option, Field, curr, _azureRedis);
  88. break;
  89. case "week": //自然周
  90. Field = $"{userid}-week-{now.ToString("yyyy")}{GetWeek(now)}";
  91. value = _azureRedis.GetRedisClient(8).HashGet($"Vote:Record:{vote.id}_{vote.code}", Field);
  92. msgid = await VoteIng(vote, value, msgid, option, Field, curr, _azureRedis);
  93. break;
  94. case "month": //月份
  95. Field = $"{userid}-month-{now.ToString("yyyyMM")}";
  96. value = _azureRedis.GetRedisClient(8).HashGet($"Vote:Record:{vote.id}_{vote.code}", Field);
  97. msgid = await VoteIng(vote, value, msgid, option, Field, curr, _azureRedis);
  98. break;
  99. case "year"://年份
  100. Field = $"{userid}-year-{now.ToString("yyyy")}";
  101. value = _azureRedis.GetRedisClient(8).HashGet($"Vote:Record:{vote.id}_{vote.code}", Field);
  102. msgid = await VoteIng(vote, value, msgid, option, Field, curr, _azureRedis);
  103. break;
  104. }
  105. }
  106. else
  107. {
  108. msgid = 2;
  109. }
  110. }
  111. }
  112. catch (Exception e)
  113. {
  114. }
  115. return msgid;
  116. }
  117. public static async Task<byte> VoteIng(Vote vote, RedisValue value, byte msgid, List<string> option, string Field, long curr, AzureRedisFactory _azureRedis)
  118. {
  119. if (!value.IsNullOrEmpty)
  120. {
  121. VoteRecord record=value.ToString().ToObject<VoteRecord>();
  122. //处理记录投票+当前设置的投票是否小于等于周期内最大投票数
  123. if (record.opt.Count + option.Count <= vote.voteNum)
  124. {
  125. record.opt.AddRange(option);
  126. record.time = curr;
  127. //保存投票记录
  128. bool status = await _azureRedis.GetRedisClient(8).HashSetAsync($"Vote:Record:{vote.id}_{vote.code}", Field, record.ToJsonString());
  129. //当前投票分组计数存入活动的Redis
  130. var group_opt= option.GroupBy(x => x);
  131. foreach (var opt in group_opt) {
  132. await _azureRedis.GetRedisClient(8).SortedSetIncrementAsync($"Vote:Count:{vote.id}_{vote.code}", opt.Key, opt.Count());
  133. }
  134. if (status)
  135. {
  136. msgid = 1;
  137. }
  138. }
  139. else
  140. {
  141. msgid = 5;
  142. }
  143. }
  144. else
  145. {
  146. //保存投票记录
  147. VoteRecord record = new VoteRecord { opt = option, time = curr };
  148. bool status = await _azureRedis.GetRedisClient(8).HashSetAsync($"Vote:Record:{vote.id}_{vote.code}", Field, record.ToJsonString());
  149. //当前投票分组计数存入活动的Redis
  150. var group_opt = option.GroupBy(x => x);
  151. foreach (var opt in group_opt)
  152. {
  153. await _azureRedis.GetRedisClient(8).SortedSetIncrementAsync($"Vote:Count:{vote.id}_{vote.code}", opt.Key, opt.Count());
  154. }
  155. if (status)
  156. {
  157. msgid = 1;
  158. }
  159. }
  160. return msgid;
  161. }
  162. /// <summary>
  163. /// 获取时间的在当年的第几周
  164. /// </summary>
  165. /// <param name="dt"></param>
  166. /// <returns></returns>
  167. public static int GetWeek(DateTimeOffset dt)
  168. {
  169. DateTimeOffset time = Convert.ToDateTime(dt.ToString("yyyy") + "-01-01");
  170. TimeSpan ts = dt - time;
  171. int iii = (int)time.DayOfWeek;
  172. int day = int.Parse(ts.TotalDays.ToString("F0"));
  173. if (iii == 0)
  174. {
  175. day--;
  176. }
  177. else
  178. {
  179. day = day - (7 - iii) - 1;
  180. }
  181. int week = ((day + 7) / 7) + 1;
  182. return week;
  183. }
  184. /// <summary>
  185. ///
  186. /// </summary>
  187. /// <param name="containerId">容器</param>
  188. /// <param name="requert"></param>
  189. /// <param name="id">登录者ID</param>
  190. /// <param name="_azureCosmos"></param>
  191. /// <returns></returns>
  192. public static async Task<(List<ActivityData> datas, string continuationToken)> FindByRole(string containerId, JsonElement requert, string id, AzureCosmosFactory _azureCosmos,AzureRedisFactory _azureRedis)
  193. {
  194. //开始时间,默认最近三十天
  195. var stimestamp = DateTimeOffset.UtcNow.AddDays(-30).ToUnixTimeMilliseconds();
  196. if (!requert.TryGetProperty("stime", out JsonElement stime))
  197. {
  198. if (stime.TryGetInt64(out long data))
  199. {
  200. stimestamp = data;
  201. }
  202. }
  203. if (!requert.TryGetProperty("type", out JsonElement type))
  204. {
  205. //if (stime.TryGetInt64(out long data))
  206. //{
  207. // stimestamp = data;
  208. //}
  209. }
  210. //默认当前时间, 未开始的不能查询
  211. var etimestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
  212. //if (!requert.TryGetProperty("etime", out JsonElement etime))
  213. //{
  214. // if (etime.TryGetInt64(out long data))
  215. // {
  216. // etimestamp = data;
  217. // };
  218. //};
  219. string continuationToken = null;
  220. //默认不指定返回大小
  221. int? topcout = null;
  222. if (!requert.TryGetProperty("count", out JsonElement jcount))
  223. {
  224. if (jcount.TryGetInt32(out int data))
  225. {
  226. topcout = data;
  227. }
  228. }
  229. //是否需要进行分页查询,默认不分页
  230. bool iscontinuation = false;
  231. //如果指定了返回大小
  232. if (!requert.TryGetProperty("continuationToken", out JsonElement continuation))
  233. {
  234. //指定了cancellationToken 表示需要进行分页
  235. if (!continuation.ValueKind.Equals(JsonValueKind.Null) && continuation.ValueKind.Equals(JsonValueKind.String))
  236. {
  237. continuationToken = continuation.GetString();
  238. iscontinuation = true;
  239. }
  240. }
  241. //班级
  242. List<string> classes=null;
  243. if (!requert.TryGetProperty("classes", out JsonElement jclasses))
  244. {
  245. if (jclasses.ValueKind is JsonValueKind.Array ) {
  246. classes = jclasses.ToObject<List<string>>();
  247. }
  248. }
  249. string query = null;
  250. if (classes.IsNotEmpty() )
  251. {
  252. StringBuilder insql =new StringBuilder();
  253. classes.ForEach( x => { insql.Append($"'{x}',"); });
  254. string sql = insql.ToString();
  255. sql = sql.ToString().Substring(0, sql.Length - 2);
  256. query = $" SELECT distinct value c FROM c JOIN A0 IN c.classes join A1 in c.tmdids where c.startTime >= {stimestamp} and c.startTime <= {etimestamp} and c.pk='Activity' and (A0 in('{sql}') or A1 in ('{id}')) ";
  257. }
  258. else {
  259. query = $" SELECT distinct value c FROM c join A1 in c.tmdids where c.startTime >= {stimestamp} and c.startTime <= {etimestamp} and c.pk='Activity' and A1 ='{id}' ";
  260. }
  261. List<ActivityData> datas = new List<ActivityData>();
  262. var client = _azureCosmos.GetCosmosClient();
  263. await foreach (var item in client.GetContainer("TEAMModelOS", containerId).GetItemQueryStreamIterator(query, continuationToken: continuationToken, requestOptions: new QueryRequestOptions() { MaxItemCount = topcout, PartitionKey = new PartitionKey($"Common-{id}") }))
  264. {
  265. using var json = await JsonDocument.ParseAsync(item.ContentStream);
  266. if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
  267. {
  268. foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
  269. {
  270. datas.Add(obj.ToObject<ActivityData>());
  271. }
  272. //如果需要分页则跳出
  273. if (iscontinuation)
  274. {
  275. continuationToken = item.GetContinuationToken();
  276. break;
  277. }
  278. }
  279. }
  280. bool tips = false;
  281. if (!requert.TryGetProperty("tips", out JsonElement jtips))
  282. {
  283. //指定了cancellationToken 表示需要进行分页
  284. if (!jtips.ValueKind.Equals(JsonValueKind.Null) && !jtips.ValueKind.Equals(JsonValueKind.True))
  285. {
  286. tips = jtips.GetBoolean();
  287. }
  288. }
  289. if (tips)
  290. {
  291. DoActivityTips activityTips;
  292. dynamic res = default;
  293. foreach (var data in datas)
  294. {
  295. switch (data.type)
  296. {
  297. //投票
  298. case "vote":
  299. activityTips = DoVoteTips;
  300. //msgid, //0不能投票,1可以投票,2不在时间范围内,3周期内的可投票数不足
  301. //voteCount 可用投票数
  302. res = activityTips(data, _azureCosmos, id, _azureRedis);
  303. break;
  304. //问卷
  305. case "survey":
  306. //msgid 0 已作答, 1未作答,2,未完成
  307. activityTips = DoSurveyTips;
  308. res = activityTips(data, _azureCosmos, id, _azureRedis);
  309. break;
  310. //评测
  311. case "exam":
  312. //msgid 0 已作答, 1未作答,2,未完成, 用时间控制 相关发布状态,并且展示相应的结果
  313. activityTips = DoExamTips;
  314. res = activityTips(data, _azureCosmos, id, _azureRedis);
  315. break;
  316. //学习活动
  317. case "learn":
  318. //msgid 0 已完成, 1未开始,2,未完成
  319. activityTips = DoLearnTips;
  320. res = activityTips(data, _azureCosmos, id, _azureRedis);
  321. break;
  322. //作业活动
  323. case "homework":
  324. //msgid 0 已作答, 1未作答,2,未完成,3已批改,且有错误,4已批改,已完成
  325. //index:0,1,5 错误题序
  326. activityTips = DoHomeworkTips;
  327. res = activityTips(data, _azureCosmos, id, _azureRedis);
  328. break;
  329. default: break;
  330. }
  331. }
  332. }
  333. return (datas, continuationToken);
  334. }
  335. private async static Task<dynamic> DoVoteTips(ActivityData commonData , AzureCosmosFactory _azureCosmos,string userid,AzureRedisFactory _azureRedis)
  336. {
  337. Vote vote=null;
  338. var client = _azureCosmos.GetCosmosClient();
  339. await foreach (var item in client.GetContainer("TEAMModelOS", "Common").GetItemQueryIterator<Vote>($"select value c from c where c.id='{commonData.id}'",
  340. requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey(commonData.scode) }))
  341. {
  342. vote = item;
  343. break;
  344. }
  345. byte msgid = 0;
  346. int voteCount = 0;
  347. if (vote != null) {
  348. DateTimeOffset now = DateTimeOffset.UtcNow;
  349. long curr = now.ToUnixTimeMilliseconds();
  350. //判断投票时间是否在起止时间内
  351. if (curr >= vote.startTime && curr <= vote.endTime)
  352. {
  353. string Field = "";
  354. RedisValue value = default;
  355. switch (vote.times)
  356. {
  357. case "once":
  358. // //如果是只能投票一次的活动则直接获取Redis的第一条 只能投一次
  359. Field = $"{userid}-once";
  360. RedisValue[] values = _azureRedis.GetRedisClient(8).HashValues($"Vote:Record:{vote.id}_{vote.code}");
  361. if (values != null && values.Length>0)
  362. {
  363. value = values[0];
  364. }
  365. break;
  366. case "day": //周期内每天
  367. Field = $"{userid}-day-{now.ToString("yyyyMMdd")}";
  368. value = _azureRedis.GetRedisClient(8).HashGet($"Vote:Record:{vote.id}_{vote.code}", Field);
  369. break;
  370. case "week": //自然周
  371. Field = $"{userid}-week-{now.ToString("yyyy")}{GetWeek(now)}";
  372. value = _azureRedis.GetRedisClient(8).HashGet($"Vote:Record:{vote.id}_{vote.code}", Field);
  373. break;
  374. case "month": //月份
  375. Field = $"{userid}-month-{now.ToString("yyyyMM")}";
  376. value = _azureRedis.GetRedisClient(8).HashGet($"Vote:Record:{vote.id}_{vote.code}", Field);
  377. break;
  378. case "year"://年份
  379. Field = $"{userid}-year-{now.ToString("yyyy")}";
  380. value = _azureRedis.GetRedisClient(8).HashGet($"Vote:Record:{vote.id}_{vote.code}", Field);
  381. break;
  382. }
  383. if (value != default && value.IsNullOrEmpty)
  384. {
  385. List<string> opt = null;
  386. JsonElement record = value.ToString().ToObject<JsonElement>();
  387. if (record.TryGetProperty("opt", out JsonElement jopt)) {
  388. opt = jopt.ToObject<List<string>>();
  389. }
  390. if (opt != null)
  391. {
  392. //处理记录投票是否小于等于周期内最大投票数
  393. if (opt.Count <= vote.voteNum)
  394. {
  395. voteCount = vote.voteNum - opt.Count;
  396. msgid = 1;
  397. }
  398. else
  399. {
  400. //3周期内的可投票数不足
  401. msgid = 3;
  402. voteCount = 0;
  403. }
  404. }
  405. else {
  406. msgid = 1;
  407. voteCount = vote.voteNum;
  408. }
  409. }
  410. else {
  411. //未投票,可以投票
  412. msgid = 1;
  413. voteCount = vote.voteNum;
  414. }
  415. }
  416. else
  417. {
  418. msgid = 2;
  419. voteCount = 0;
  420. }
  421. }
  422. return new {msgid,voteCount };
  423. }
  424. private static dynamic DoHomeworkTips(ActivityData commonData, AzureCosmosFactory _azureCosmos, string id, AzureRedisFactory _azureRedis)
  425. {
  426. return null;
  427. }
  428. private static dynamic DoLearnTips(ActivityData commonData, AzureCosmosFactory _azureCosmos, string id, AzureRedisFactory _azureRedis)
  429. {
  430. return null;
  431. }
  432. private static dynamic DoExamTips(ActivityData commonData, AzureCosmosFactory _azureCosmos, string id, AzureRedisFactory _azureRedis)
  433. {
  434. return null;
  435. }
  436. private static dynamic DoSurveyTips(ActivityData commonData, AzureCosmosFactory _azureCosmos, string id, AzureRedisFactory _azureRedis)
  437. {
  438. return null;
  439. }
  440. }
  441. }