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;
namespace TEAMModelOS.Controllers.Learn
{
///
/// 投票活动
///
[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 VoteController(AzureCosmosFactory azureCosmos, AzureServiceBusFactory serviceBus, SnowflakeId snowflakeId, DingDing dingDing, IOptionsSnapshot option,
AzureRedisFactory azureRedis, AzureStorageFactory azureStorage)
{
_azureCosmos = azureCosmos;
_serviceBus = serviceBus;
_snowflakeId = snowflakeId;
_dingDing = dingDing;
_option = option?.Value;
_azureRedis = azureRedis;
_azureStorage = azureStorage;
}
///
/// 新增 或 修改投票活动
///
///
///
[ProducesDefaultResponseType]
[HttpPost("upsert")]
public async Task Upsert(Vote request)
{
try
{
//新增Vote
var client = _azureCosmos.GetCosmosClient();
request.code = request.pk + "-" + request.code;
long now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
request.createTime = now;
if (string.IsNullOrEmpty(request.id))
{
request.id = Guid.NewGuid().ToString();
if (request.startTime > now)
{
request.progress = "pending";
}
else {
request.progress = "going";
}
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}"));
if (response.Status == 200)
{
using var json = await JsonDocument.ParseAsync(response.ContentStream);
var info = json.ToObject();
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);
}
}
///
/// 查询投票活动,用于列表,编辑,查看
///
///
///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 已结束
///
///
///
[ProducesDefaultResponseType]
[HttpPost("find")]
public async Task 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 votes = new List();
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());
}
//如果需要分页则跳出
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);
}
}
///
/// 查询投票活动,用于创建者列表,编辑,查看,投票人员查看
///
///
/// ! "id":"3c075347-75ef-4bcb-ae03-68678d02d5ef",
/// ! "code":"Vote-hbcn"/"code":"Vote-1606285227"
///
///
///
[ProducesDefaultResponseType]
[HttpPost("find-id")]
public async Task FindById(JsonElement requert)
{
try
{
var client = _azureCosmos.GetCosmosClient();
//活动id
if (!requert.TryGetProperty("id", out JsonElement id)) return BadRequest();
//活动分区
if (!requert.TryGetProperty("code", out JsonElement code)) return BadRequest();
Vote vote = await client.GetContainer("TEAMModelOS", "Common").ReadItemAsync(id.GetString(), new PartitionKey($"{code}"));
if (vote != null)
{
return Ok(new { vote });
}
else
{
return BadRequest("id,code不存在!");
}
}
catch (Exception ex)
{
await _dingDing.SendBotMsg($"OS,{_option.Location},common/vote/find-id()\n{ex.Message}", GroupNames.醍摩豆服務運維群組);
return BadRequest(ex.StackTrace);
}
}
///
/// 删除投票活动 TODO 使用ttl删除,并处理相关事务逻辑
///
///
///
[ProducesDefaultResponseType]
[HttpPost("delete")]
[AuthToken(Roles = "admin,teacher")]
public async Task 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(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}"));
var adid = vote.id;
var adcode = $"Activity-{vote.school}";
ActivityData data = null;
try
{
if (vote.scope == "school")
{
data = await client.GetContainer("TEAMModelOS", "School").ReadItemAsync(adid, new Azure.Cosmos.PartitionKey($"{adcode}"));
}
else if (vote.scope == "private")
{
data = await client.GetContainer("TEAMModelOS", "Teacher").ReadItemAsync(adid, new Azure.Cosmos.PartitionKey($"{adcode}"));
}
}
catch
{
data = null;
}
await _dingDing.SendBotMsg($"投票活动【{vote.name}-{vote.id}-ttl={vote.ttl}】正在操作", GroupNames.成都开发測試群組);
_azureRedis.GetRedisClient(8).KeyDelete($"Vote:Record:{vote.id}");
_azureRedis.GetRedisClient(8).KeyDelete($"Vote:Count:{vote.id}");
if (data != null)
{
data.ttl = 1;
if (vote.scope == "school")
{
data = await client.GetContainer("TEAMModelOS", "School").DeleteItemAsync(adid, new Azure.Cosmos.PartitionKey($"{data.code}"));
}
else if (vote.scope == "private")
{
data = await client.GetContainer("TEAMModelOS", "Teacher").DeleteItemAsync(adid, new Azure.Cosmos.PartitionKey($"{data.code}"));
}
}
await _dingDing.SendBotMsg($"投票活动【{vote.name}-{vote.id}】被删除", GroupNames.成都开发測試群組);
return Ok(new { flag });
}
else {
return Ok(new { flag });
}
}
catch (Exception e)
{
return BadRequest(e.StackTrace);
}
}
///
/// 投票记录 当活动没结算且没有BlobUrl时则调用此接口
///
///
/// 投票活动选项计数器 使用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}"
///
///
/// !"id":"aaaa"
/// !"code":"Vote-hbcn"/"code":"Vote-1606285227"
///
///
///
[ProducesDefaultResponseType]
[HttpPost("record")]
//[AuthToken(Roles = "teacher,student")]
public async Task 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 options = new List();
if (counts != null && counts.Length > 0)
{
foreach (var count in counts)
{
options.Add(new { code = count.Element.ToString(), count = (int)count.Score });
}
}
List res = new List();
foreach (var rcd in records)
{
var value = rcd.Value.ToString().ToObject();
res.Add(value);
}
return Ok(new { options, records= res});
}
///
/// 个人已投票记录查询
///
///
///
[ProducesDefaultResponseType]
[HttpPost("decided")]
[AuthToken(Roles = "teacher,student")]
public async Task 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 res = new List();
foreach (var rcd in records)
{
var value = rcd.Value.ToString().ToObject();
res.Add(value);
}
return Ok(new { records= res });
}
///
/// 投票
///
///
/// 投票活动选项计数器 使用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}"
///
///
/// !"id":"aaaa"
/// !"code":"Vote-hbcn"/"code":"Vote-1606285227"
/// !"option":{"A":5,"B":6}/{"1":5,"2":8}
///
///
/// msgid=0投票失败,1投票成功,2不在时间范围内,3不在发布范围内,4投票周期内重复投票,5周期内的可投票数不足
///
///
[ProducesDefaultResponseType]
[HttpPost("decide")]
[AuthToken(Roles = "teacher,student")]
public async Task Decide(JsonElement request)
{
var (userid, _, _, _) = HttpContext.GetAuthTokenInfo();
int msgid = await ActivityStudentService.Decide(request, _azureCosmos, _azureRedis, userid);
return Ok(new { msgid });
}
///
/// 投票
///
///
/// 投票活动选项计数器 使用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}"
///
///
/// !"id":"aaaa"
/// !"code":"Vote-hbcn"/"code":"Vote-1606285227"
/// !"option":{"A":5,"B":6}/{"1":5,"2":8}
/// !"userid":"15283771540"
///
///
/// msgid=0投票失败,1投票成功,2不在时间范围内,3不在发布范围内,4投票周期内重复投票,5周期内的可投票数不足,6未设置投票项
///
[ProducesDefaultResponseType]
[HttpPost("decide-mock")]
public async Task 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 }); }
}
}
}