123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508 |
- using Azure.Cosmos;
- using Microsoft.AspNetCore.Http;
- using Microsoft.AspNetCore.Mvc;
- using System;
- using System.Collections.Generic;
- using System.IdentityModel.Tokens.Jwt;
- using System.Linq;
- using System.Text;
- using System.Text.Json;
- using System.Threading.Tasks;
- using TEAMModelOS.Models.Dto;
- using TEAMModelOS.SDK.Models;
- using TEAMModelOS.SDK;
- using TEAMModelOS.SDK.Context.Constant.Common;
- using TEAMModelOS.SDK.DI;
- using TEAMModelOS.SDK.DI.AzureCosmos.Inner;
- using TEAMModelOS.SDK.Extension;
- using TEAMModelOS.SDK.Helper.Common.CollectionHelper;
- using TEAMModelOS.SDK.Helper.Common.StringHelper;
- using TEAMModelOS.Models;
- using Microsoft.Extensions.Options;
- using TEAMModelOS.SDK.Models.Cosmos;
- using Microsoft.AspNetCore.Authorization;
- using TEAMModelOS.Filter;
- using StackExchange.Redis;
- using TEAMModelOS.SDK.Models.Cosmos.Common.Inner;
- using TEAMModelOS.Services.Common;
- using Azure.Messaging.ServiceBus;
- using Microsoft.Extensions.Configuration;
- namespace TEAMModelOS.Controllers.Learn
- {
- /// <summary>
- /// 投票活动
- /// </summary>
- [ProducesResponseType(StatusCodes.Status200OK)]
- [ProducesResponseType(StatusCodes.Status400BadRequest)]
- //[Authorize(Roles = "IES5")]
- [Route("common/vote")]
- [ApiController]
- public class VoteController : ControllerBase
- {
- private readonly AzureRedisFactory _azureRedis;
- private readonly AzureCosmosFactory _azureCosmos;
- private readonly SnowflakeId _snowflakeId;
- private readonly AzureServiceBusFactory _serviceBus;
- private readonly DingDing _dingDing;
- private readonly Option _option;
- private readonly AzureStorageFactory _azureStorage;
- public IConfiguration _configuration { get; set; }
- public VoteController(AzureCosmosFactory azureCosmos, AzureServiceBusFactory serviceBus, SnowflakeId snowflakeId, DingDing dingDing, IOptionsSnapshot<Option> option,
- AzureRedisFactory azureRedis, AzureStorageFactory azureStorage, IConfiguration configuration)
- {
- _azureCosmos = azureCosmos;
- _serviceBus = serviceBus;
- _snowflakeId = snowflakeId;
- _dingDing = dingDing;
- _option = option?.Value;
- _azureRedis = azureRedis;
- _azureStorage = azureStorage;
- _configuration = configuration;
- }
- /// <summary>
- /// 新增 或 修改投票活动
- /// </summary>
- /// <param name="request"></param>
- /// <returns></returns>
- [ProducesDefaultResponseType]
- [HttpPost("upsert")]
- [AuthToken(Roles = "teacher,admin")]
- public async Task<IActionResult> Upsert(Vote request)
- {
- try
- {
- //新增Vote
- var client = _azureCosmos.GetCosmosClient();
- request.code = request.pk + "-" + request.code;
- request.ttl = -1;
- long now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
- request.createTime = now;
-
- //如果设置的时间是小于当前时间则立即发布
- if (request.startTime <= 0) {
- request.startTime = now;
- }
- if (string.IsNullOrEmpty(request.id))
- {
-
- request.id = Guid.NewGuid().ToString();
- if (request.startTime > now)
- {
- request.progress = "pending";
- }
- else {
- request.progress = "going";
- }
- var messageBlob = new ServiceBusMessage();
- if (request.scope.Equals("school"))
- {
- request.size = await _azureStorage.GetBlobContainerClient(request.school).GetBlobsSize($"vote/{request.id}");
- messageBlob = new ServiceBusMessage(new { id = Guid.NewGuid().ToString(), progress = "insert", root = $"vote/{request.id}", name = $"{request.school}" }.ToJsonString());
- }
- else
- {
- request.size = await _azureStorage.GetBlobContainerClient(request.creatorId).GetBlobsSize($"vote/{request.id}");
- messageBlob = new ServiceBusMessage(new { id = Guid.NewGuid().ToString(), progress = "insert", root = $"vote/{request.id}", name = $"{request.creatorId}" }.ToJsonString());
- }
- messageBlob.ApplicationProperties.Add("name", "BlobRoot");
- var ActiveTask = _configuration.GetValue<string>("Azure:ServiceBus:ActiveTask");
- await _serviceBus.GetServiceBusClient().SendMessageAsync(ActiveTask, messageBlob);
- request = await client.GetContainer("TEAMModelOS", "Common").CreateItemAsync(request, new PartitionKey($"{request.code}"));
- }
- else
- {
- var response = await client.GetContainer("TEAMModelOS", "Common").ReadItemStreamAsync(request.id, new PartitionKey($"{request.code}"));
- var messageBlob = new ServiceBusMessage();
- if (request.scope.Equals("school"))
- {
- request.size = await _azureStorage.GetBlobContainerClient(request.school).GetBlobsSize($"vote/{request.id}");
- messageBlob = new ServiceBusMessage(new { id = Guid.NewGuid().ToString(), progress = "update", root = $"vote/{request.id}", name = $"{request.school}" }.ToJsonString());
- }
- else
- {
- request.size = await _azureStorage.GetBlobContainerClient(request.creatorId).GetBlobsSize($"vote/{request.id}");
- messageBlob = new ServiceBusMessage(new { id = Guid.NewGuid().ToString(), progress = "update", root = $"vote/{request.id}", name = $"{request.creatorId}" }.ToJsonString());
- }
- messageBlob.ApplicationProperties.Add("name", "BlobRoot");
- var ActiveTask = _configuration.GetValue<string>("Azure:ServiceBus:ActiveTask");
- await _serviceBus.GetServiceBusClient().SendMessageAsync(ActiveTask, messageBlob);
- if (response.Status == 200)
- {
- using var json = await JsonDocument.ParseAsync(response.ContentStream);
- var info = json.ToObject<Vote>();
- if (info.progress.Equals("going"))
- {
- return Ok(new { v = "活动正在进行中" });
- }
- if (request.startTime > now)
- {
- request.progress = "pending";
- }
- else
- {
- request.progress = "going";
- }
- request.progress = info.progress;
- request = await client.GetContainer("TEAMModelOS", "Common").ReplaceItemAsync(request, info.id, new PartitionKey($"{info.code}"));
- }
- else
- {
- if (request.startTime > now)
- {
- request.progress = "pending";
- }
- else
- {
- request.progress = "going";
- }
- request = await client.GetContainer("TEAMModelOS", "Common").CreateItemAsync(request, new PartitionKey($"{request.code}"));
- }
- }
- return Ok(new { vote = request });
- }
- catch (Exception e)
- {
- await _dingDing.SendBotMsg($"OS,{_option.Location},common/vote/save()\n{e.Message}", GroupNames.醍摩豆服務運維群組);
- return BadRequest(e.StackTrace);
- }
- }
- /// <summary>
- /// 查询投票活动,用于列表,编辑,查看
- /// </summary>
- /// <data>
- ///Vote-学校/教师编码 活动分区 !"code":"hbcn"/1606285227
- ///时间筛选范围开始时间 默认30天之前 ?"stime":1608274766154
- ///时间筛选范围结束时间 默认当前时间 ?"etime":1608274766666
- ///每页大小 ?"count":10/null/Undefined
- ///分页Token ?"continuationToken":Undefined/null/"[{\"token\":\"+RID:~omxMAP3ipcSEEwAAAAAAAA==#RT:2#TRC:20#ISV:2#IEO:65551#QCF:1#FPC:AYQTAAAAAAAAiRMAAAAAAAA=\",\"range\":{\"min\":\"\",\"max\":\"FF\"}}]"
- /// 当前状态 ?"progress":Undefined/null/"" 表示两种状态都要查询/ "going"/"finish" 表示查询进行中/ 或者已完成 学生端只能查询正在进行或已经结束 going 已发布|finish 已结束
- /// </data>
- /// <param name="request"></param>
- /// <returns></returns>
- [ProducesDefaultResponseType]
- [HttpPost("find")]
- [AuthToken(Roles = "teacher,admin")]
- public async Task<IActionResult> Find(JsonElement requert)
- {
- try
- {
- //必须有学校或者教师编码
- if (!requert.TryGetProperty("code", out JsonElement code)) return BadRequest();
- //开始时间,默认最近三十天
- var stimestamp = DateTimeOffset.UtcNow.AddDays(-30).ToUnixTimeMilliseconds();
- if (requert.TryGetProperty("stime", out JsonElement stime)) {
- if (!stime.ValueKind.Equals(JsonValueKind.Undefined)&&stime.TryGetInt64(out long data))
- {
- stimestamp = data;
- };
- };
- //默认当前时间
- var etimestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
- if (requert.TryGetProperty("etime", out JsonElement etime))
- {
- if (!etime.ValueKind.Equals(JsonValueKind.Undefined)&&etime.TryGetInt64(out long data))
- {
- etimestamp = data;
- };
- };
- var progresssql = "";
- if (!requert.TryGetProperty("progress", out JsonElement progress))
- {
- if (!progress.ValueKind.Equals(JsonValueKind.Undefined) && !progress.ValueKind.Equals(JsonValueKind.Null) && progress.ValueKind.Equals(JsonValueKind.String))
- {
- progresssql = $" and c.progress='{progresssql}' ";
- }
- }
- string continuationToken = null;
- //默认不指定返回大小
- int? topcout=null;
- if (requert.TryGetProperty("count", out JsonElement jcount)) {
- if(!jcount.ValueKind.Equals(JsonValueKind.Undefined) && jcount.TryGetInt32(out int data))
- {
- topcout = data;
- }
- };
- //是否需要进行分页查询,默认不分页
- bool iscontinuation = false;
- //如果指定了返回大小
- if (requert.TryGetProperty("continuationToken", out JsonElement continuation))
- {
- //指定了cancellationToken 表示需要进行分页
- if (!continuation.ValueKind.Equals(JsonValueKind.Null) && !continuation.ValueKind.Equals(JsonValueKind.Undefined))
- {
- continuationToken = continuation.GetString();
- iscontinuation = true;
- }
- };
- List<object> votes = new List<object>();
- var client = _azureCosmos.GetCosmosClient();
-
- var query =$"select c.id,c.name,c.code,c.startTime,c.endTime,c.progress from c where c.createTime >= {stimestamp} and c.createTime <= {etimestamp} {progresssql } ";
- await foreach (var item in client.GetContainer("TEAMModelOS", "Common").GetItemQueryStreamIterator(queryText: query,
- requestOptions: new QueryRequestOptions() {MaxItemCount = topcout, PartitionKey = new PartitionKey($"Vote-{code}") }))
- {
- using var json = await JsonDocument.ParseAsync(item.ContentStream);
- if (json.RootElement.TryGetProperty("_count", out JsonElement count) && count.GetUInt16() > 0)
- {
- foreach (var obj in json.RootElement.GetProperty("Documents").EnumerateArray())
- {
- votes.Add(obj.ToObject<JsonElement>());
- }
- //如果需要分页则跳出
- if (iscontinuation) {
- continuationToken = item.GetContinuationToken();
- break;
- }
- }
- }
- return Ok(new { votes, continuationToken });
- }
- catch (Exception ex)
- {
- await _dingDing.SendBotMsg($"OS,{_option.Location},common/vote/find()\n{ex.StackTrace}", GroupNames.醍摩豆服務運維群組);
- return BadRequest(ex.StackTrace);
- }
- }
- ///<summary>
- /// 查询投票活动,用于创建者列表,编辑,查看,投票人员查看
- /// </summary>
- /// <data>
- /// ! "id":"3c075347-75ef-4bcb-ae03-68678d02d5ef",
- /// ! "code":"Vote-hbcn"/"code":"Vote-1606285227"
- /// </data>
- /// <param name="request"></param>
- /// <returns></returns>
- [ProducesDefaultResponseType]
- [HttpPost("find-id")]
- [AuthToken(Roles = "teacher,admin,student")]
- public async Task<IActionResult> FindById(JsonElement requert)
- {
- Vote vote = null;
- //活动id
- if (!requert.TryGetProperty("id", out JsonElement id)) return BadRequest();
- //活动分区
- if (!requert.TryGetProperty("code", out JsonElement code)) return BadRequest();
- try
- {
- var client = _azureCosmos.GetCosmosClient();
-
- vote = await client.GetContainer("TEAMModelOS", "Common").ReadItemAsync<Vote>(id.GetString(), new PartitionKey($"{code}"));
- if (vote != null)
- {
-
- return Ok(new { vote, status = 200 });
- }
- else
- {
- return Ok(new { vote, status = 404 });
- }
- }
- catch (Exception ex)
- {
- await _dingDing.SendBotMsg($"OS,{_option.Location},common/vote/find-id()\n{ex.Message}\n{id}\n{code}", GroupNames.醍摩豆服務運維群組);
- return Ok(new { vote,status=404 });
- }
- }
- /// <summary>
- /// 删除投票活动 TODO 使用ttl删除,并处理相关事务逻辑
- /// </summary>
- /// <param name="request"></param>
- /// <returns></returns>
- [ProducesDefaultResponseType]
- [HttpPost("delete")]
- [AuthToken(Roles = "admin,teacher")]
- public async Task<IActionResult> Delete(JsonElement request)
- {
- try
- {
- var (userid, _, _,school) = HttpContext.GetAuthTokenInfo();
- if (!request.TryGetProperty("id", out JsonElement id)) return BadRequest();
- if (!request.TryGetProperty("code", out JsonElement code)) return BadRequest();
- var client = _azureCosmos.GetCosmosClient();
-
- Vote vote =await client.GetContainer("TEAMModelOS", "Common").ReadItemAsync<Vote>(id.GetString(), new PartitionKey($"{code}") );
- bool flag = false;
- //必须是本人或者这个学校的管理者才能删除
- if (vote.creatorId == userid)
- {
- flag = true;
- }
- else {
- if (vote.scope == "school"&& vote.school.Equals(school)) {
- flag = true;
- }
- }
- if (flag)
- {
- //使用ttl删除,并处理相关事务逻辑
- vote.ttl = 1;
- vote.status = 404;
- vote = await client.GetContainer("TEAMModelOS", "Common").UpsertItemAsync(vote, new PartitionKey($"{vote.code}"));
- await _dingDing.SendBotMsg($"{_option.Location}-投票活动【{vote.name}-{vote.id}-ttl={vote.ttl}】正在操作", GroupNames.成都开发測試群組);
- _azureRedis.GetRedisClient(8).KeyDelete($"Vote:Record:{vote.id}");
- _azureRedis.GetRedisClient(8).KeyDelete($"Vote:Count:{vote.id}");
- await _dingDing.SendBotMsg($"{_option.Location}-投票活动【{vote.name}-{vote.id}】被删除", GroupNames.成都开发測試群組);
- //删除blob 相关资料
- await _azureStorage.GetBlobServiceClient().DelectBlobs(code.ToString(), $"vote/{id}");
- //通知删除信息
- var messageBlob = new ServiceBusMessage(new { id = Guid.NewGuid().ToString(), progress = "delete", root = $"vote/{id}", name = $"{code}" }.ToJsonString());
- messageBlob.ApplicationProperties.Add("name", "BlobRoot");
- var ActiveTask = _configuration.GetValue<string>("Azure:ServiceBus:ActiveTask");
- await _serviceBus.GetServiceBusClient().SendMessageAsync(ActiveTask, messageBlob);
- return Ok(new { flag });
- }
- else {
- return Ok(new { flag });
- }
- }
- catch (Exception e)
- {
- return BadRequest(e.StackTrace);
- }
- }
- /// <summary>
- /// 投票记录 当活动没结算且没有BlobUrl时则调用此接口
- /// </summary>
- /// <redis>
- /// 投票活动选项计数器 使用SortedSet(有序集合)ZSET 数据集合 使用命令 ZINCRBY key:"Vote:Count:AAA",value:"A",score:1
- /// 投票活动 投票记录 使用Hash(哈希表)使用命令 key:"Vote:Record:AAA",feild:15283771540-20210105,value:"{"opt":["A","C","A"],"time":1608274766154}"
- /// </redis>
- /// <param name="request">
- /// !"id":"aaaa"
- /// !"code":"Vote-hbcn"/"code":"Vote-1606285227"
- /// </param>
- /// <returns>
- /// </returns>
- [ProducesDefaultResponseType]
- [HttpPost("record")]
- [AuthToken(Roles = "teacher,admin,student")]
- public async Task<IActionResult> Record(JsonElement request)
- {
- if (!request.TryGetProperty("id", out JsonElement id))
- {
- return BadRequest();
- }
- //活动分区
- if (!request.TryGetProperty("code", out JsonElement code))
- {
- return BadRequest();
- }
- //获取投票活动的所有投票记录
- var records = await _azureRedis.GetRedisClient(8).HashGetAllAsync($"Vote:Record:{id}");
- //获取投票活动的选项及投票数
- var counts = _azureRedis.GetRedisClient(8).SortedSetRangeByScoreWithScores($"Vote:Count:{id}");
- List<dynamic> options = new List<dynamic>();
- if (counts != null && counts.Length > 0)
- {
- foreach (var count in counts)
- {
- options.Add(new { code = count.Element.ToString(), count = (int)count.Score });
- }
- }
- List<JsonElement> res = new List<JsonElement>();
- foreach (var rcd in records)
- {
- var value = rcd.Value.ToString().ToObject<JsonElement>();
- res.Add(value);
- }
- return Ok(new { options, records= res});
- }
- /// <summary>
- /// 个人已投票记录查询
- /// </summary>
- /// <param name="request"></param>
- /// <returns></returns>
- [ProducesDefaultResponseType]
- [HttpPost("decided")]
- [AuthToken(Roles = "teacher,student")]
- public async Task<IActionResult> Decided(JsonElement request)
- {
- var (userid, _, _, _) = HttpContext.GetAuthTokenInfo();
- if (!request.TryGetProperty("id", out JsonElement id))
- {
- return BadRequest();
- }
- //活动分区
- if (!request.TryGetProperty("code", out JsonElement code))
- {
- return BadRequest();
- }
- //获取投票活动的所有投票记录
- var records = await _azureRedis.GetRedisClient(8).HashGetAllAsync($"Vote:Record:{id}:{userid}");
- List<JsonElement> res = new List<JsonElement>();
- foreach (var rcd in records)
- {
- var value = rcd.Value.ToString().ToObject<JsonElement>();
- res.Add(value);
- }
- return Ok(new { records= res });
- }
- /// <summary>
- /// 投票
- /// </summary>
- /// <redis>
- /// 投票活动选项计数器 使用SortedSet(有序集合)ZSET 数据集合 使用命令 ZINCRBY key:"Vote:Count:AAA",value:"A",score:1
- /// 投票活动 投票记录 使用Hash(哈希表)使用命令 key:"Vote:Record:AAA",feild:15283771540-20210105,value:"{"opt":["A","C","A"],"time":1608274766154}"
- /// </redis>
- /// <param name="request">
- /// !"id":"aaaa"
- /// !"code":"Vote-hbcn"/"code":"Vote-1606285227"
- /// !"option":{"A":5,"B":6}/{"1":5,"2":8}
- /// </param>
- /// <returns>
- /// msgid=0投票失败,1投票成功,2不在时间范围内,3不在发布范围内,4投票周期内重复投票,5周期内的可投票数不足
- ///
- /// </returns>
- [ProducesDefaultResponseType]
- [HttpPost("decide")]
- [AuthToken(Roles = "teacher,student")]
- public async Task<IActionResult> Decide(JsonElement request)
- {
- var (userid, _, _, _) = HttpContext.GetAuthTokenInfo();
- int msgid = await ActivityStudentService.Decide(request, _azureCosmos, _azureRedis, userid);
- return Ok(new { msgid });
- }
- /// <summary>
- /// 投票
- /// </summary>
- /// <redis>
- /// 投票活动选项计数器 使用SortedSet(有序集合)ZSET 数据集合 使用命令 ZINCRBY key:"Vote:Count:AAA",value:"A",score:1
- /// 投票活动 投票记录 使用Hash(哈希表)使用命令 key:"Vote:Record:AAA",feild:15283771540-20210105,value:"{"opt":["A","C","A"],"time":1608274766154}"
- /// </redis>
- /// <param name="request">
- /// !"id":"aaaa"
- /// !"code":"Vote-hbcn"/"code":"Vote-1606285227"
- /// !"option":{"A":5,"B":6}/{"1":5,"2":8}
- /// !"userid":"15283771540"
- /// </param>
- /// <returns>
- /// msgid=0投票失败,1投票成功,2不在时间范围内,3不在发布范围内,4投票周期内重复投票,5周期内的可投票数不足,6未设置投票项
- /// </returns>
- [ProducesDefaultResponseType]
- [HttpPost("decide-mock")]
- public async Task<IActionResult> DecideMock(JsonElement request)
- {
- //var (userid, _, _, _) = HttpContext.GetAuthTokenInfo();
- //活动id
- if (request.TryGetProperty("userid", out JsonElement userid))
- {
- int msgid = await ActivityStudentService.Decide(request, _azureCosmos, _azureRedis, $"{userid}");
- return Ok(new { msgid });
- }
- else { return Ok(new { msgid = 0 }); }
- }
- }
- }
|