123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428 |
- using Azure.Cosmos;
- using Microsoft.AspNetCore.Http;
- using Microsoft.AspNetCore.Mvc;
- using Microsoft.CodeAnalysis.Options;
- using Microsoft.Extensions.Options;
- using System;
- using System.Collections.Generic;
- using System.IdentityModel.Tokens.Jwt;
- using System.Linq;
- using System.Text.Json;
- using System.Threading.Tasks;
- using TEAMModelOS.Models;
- using TEAMModelOS.Models.CommonInfo;
- using TEAMModelOS.Models.SchoolInfo;
- using TEAMModelOS.Models.StudentInfo;
- using TEAMModelOS.SDK;
- using TEAMModelOS.SDK.Context.Constant.Common;
- using TEAMModelOS.SDK.DI;
- using TEAMModelOS.SDK.Extension;
- using TEAMModelOS.SDK.Helper.Common.CollectionHelper;
- using TEAMModelOS.SDK.Helper.Common.StringHelper;
- namespace TEAMModelOS.Controllers
- {
- [ProducesResponseType(StatusCodes.Status200OK)]
- [ProducesResponseType(StatusCodes.Status400BadRequest)]
- //[Authorize(Roles = "IES5")
- [Route("school/exam")]
- [ApiController]
- public class ExamController : ControllerBase
- {
- private readonly AzureCosmosFactory _azureCosmos;
- private readonly SnowflakeId _snowflakeId;
- private readonly AzureServiceBusFactory _serviceBus;
- private readonly DingDing _dingDing;
- private readonly Option _option;
- public ExamController(AzureCosmosFactory azureCosmos, AzureServiceBusFactory serviceBus, SnowflakeId snowflakeId,
- DingDing dingDing,
- IOptionsSnapshot<Option> option)
- {
- _azureCosmos = azureCosmos;
- _serviceBus = serviceBus;
- _snowflakeId = snowflakeId;
- _dingDing = dingDing;
- _option = option?.Value;
- }
- /// <summary>
- /// 保存考试信息
- /// </summary>
- /// <param name="request"></param>
- /// <returns></returns>
- [ProducesDefaultResponseType]
- //[AuthToken(Roles = "Teacher")]
- [HttpPost("save")]
- public async Task<BaseResponse> Save(ExamInfo request)
- {
- ResponseBuilder builder = ResponseBuilder.custom();
-
- if (string.IsNullOrEmpty(request.id))
- {
- request.id = _snowflakeId.NextId()+"";
- request.status = 100;
- // await _azureCosmos.SaveOrUpdate(request.@params);
- }
- if (request.publish.Equals("0"))
- {
- request.startTime = new DateTimeOffset(DateTime.UtcNow).ToUnixTimeMilliseconds();
- request.status = 200;
- }
- else if (request.publish.Equals("1"))
- {
- //设定开始时间
- string msgId = _snowflakeId.NextId() + "";
- long SequenceNumber = await _serviceBus.GetServiceBusClient().SendLeamMessage<ExamInfo>(Constants.TopicName, request.id, request.code, request.startTime, 200, msgId);
- request.sequenceNumber = SequenceNumber;
- }
- if (request.status == 0)
- {
- if (request.startTime < new DateTimeOffset(DateTime.UtcNow).ToUnixTimeMilliseconds()) request.status = 200;
- else request.status = 100;
- }
- await _azureCosmos.SaveOrUpdate(request);
- //设定结束时间
- string msgEndId = _snowflakeId.NextId() + "";
- await _serviceBus.GetServiceBusClient().SendLeamMessage<ExamInfo>(Constants.TopicName, request.id, request.code, request.endTime, 300, msgEndId);
- return builder.Data(request).build();
- }
- /// <summary>
- /// 删除
- /// </summary>
- /// <param name="request"></param>
- /// <returns></returns>
- [ProducesDefaultResponseType]
- //[AuthToken(Roles = "Teacher")]
- [HttpPost("delete")]
- public async Task<BaseResponse> Delete(IdPk request)
- {
- ResponseBuilder builder = ResponseBuilder.custom();
- IdPk items = await _azureCosmos.DeleteAsync<ExamInfo>(request.id, request.pk);
- await _azureCosmos.DeleteAll<Paper>(new Dictionary<string, object>() { { "code", request.id } });
- return builder.Data(items).build();
- }
- /// <summary>
- /// 查询考试信息
- /// </summary>
- /// <param name="request"></param>
- /// <returns></returns>
- [ProducesDefaultResponseType]
- //[AuthToken(Roles = "Teacher")]
- [HttpPost("find")]
- public async Task<IActionResult> Find(JsonElement requert)
- {
- //ResponseBuilder builder = ResponseBuilder.custom();
- //if (!requert.TryGetProperty("id_token", out JsonElement id_token)) return BadRequest();
- if (!requert.TryGetProperty("school_code", out JsonElement school_code)) return BadRequest();
- //var jwt = new JwtSecurityToken(id_token.GetString());
- //if (!jwt.Payload.Iss.Equals("account.teammodel", StringComparison.Ordinal)) return BadRequest();
- //var id = jwt.Payload.Sub;
- var client = _azureCosmos.GetCosmosClient();
- List<object> examInfo = new List<object>();
- var query = $"select * from c ";
- await foreach (var item in client.GetContainer("TEAMModelOSTemp", "School").GetItemQueryStreamIterator(queryText: query, requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"ExamInfo-{school_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())
- {
- examInfo.Add(obj.ToObject<object>());
- }
- }
- }
- return Ok(new { examInfo });
- /*if (StringHelper.getKeyCount(request) > 0)
- {
- return builder.Data(await _azureCosmos.FindByDict<ExamInfo>(request)).build();
- }
- else {
- return builder.build();
- }*/
- }
- /// <summary>
- /// 教师阅卷
- /// </summary>
- /// <param name="request"></param>
- /// <returns></returns>
- [ProducesDefaultResponseType]
- //[AuthToken(Roles = "Teacher")]
- [HttpPost("marking")]
- public async Task<BaseResponse> Marking(ExamRecord request)
- {
- ResponseBuilder builder = ResponseBuilder.custom();
- //判断是否每一个题目都有分数
- List<ExamInfo> exams = await _azureCosmos.FindByDict<ExamInfo>(new Dictionary<string, object> { { "id", request.examCode } });
- if (exams.IsNotEmpty())
- {
- ExamInfo examInfo = exams[0];
- //提交答案时间必须是状态已发布,且时间在起止时间内
- if ( examInfo.status == 300 &&
- examInfo.endTime <= new DateTimeOffset(DateTime.UtcNow).ToUnixTimeMilliseconds())
- {
- return builder.Data(await _azureCosmos.SaveOrUpdate(request)).build();
- }
- else
- {
- return builder.Error(ResponseCode.FAILED, "请在作答时间段内提交答案!").build();
- }
- }
- else
- {
- return builder.Error(ResponseCode.DATA_EXIST, "考试不存在!").build();
- }
- }
- /// <summary>
- /// 学生回答问题
- /// </summary>
- /// <param name="request"></param>
- /// <returns></returns>
- [ProducesDefaultResponseType]
- //[AuthToken(Roles = "Teacher")]
- [HttpPost("upsert-all-record")]
- public async Task<BaseResponse> upsertRecord(List<ExamRecord> request)
- {
- ResponseBuilder builder = ResponseBuilder.custom();
- if (request.IsNotEmpty())
- {
- //要先处理状态,判断卷子是否存在,并判断卷子归属的考试是否允许再次提交
- List<ExamInfo> exams = await _azureCosmos.FindByDict<ExamInfo>(new Dictionary<string, object> { { "id", request[0].examCode } });
- if (exams.IsNotEmpty())
- {
-
- return builder.Data(await _azureCosmos.SaveOrUpdateAll(request)).build();
- }
- else
- {
- return builder.Error(ResponseCode.DATA_EXIST, "考试不存在!").build();
-
- }
- }
- else {
- return builder.Error(ResponseCode.PARAMS_ERROR, "作答数据为空!").build();
- }
- }
- /// <summary>
- /// 学生回答问题
- /// </summary>
- /// <param name="request"></param>
- /// <returns></returns>
- [ProducesDefaultResponseType]
- //[AuthToken(Roles = "Teacher")]
- [HttpPost("upsert-record")]
- public async Task<BaseResponse> upsertRecord(ExamRecord request)
- {
- ResponseBuilder builder = ResponseBuilder.custom();
- //要先处理状态,判断卷子是否存在,并判断卷子归属的考试是否允许再次提交
- List<ExamInfo> exams= await _azureCosmos.FindByDict<ExamInfo>(new Dictionary<string, object> { { "id", request.examCode } });
- if (exams.IsNotEmpty())
- {
- ExamInfo examInfo = exams[0];
- //提交答案时间必须是状态已发布,且时间在起止时间内
- if (examInfo.startTime <= new DateTimeOffset(DateTime.UtcNow).ToUnixTimeMilliseconds() && examInfo.status == 200 &&
- examInfo.endTime >= new DateTimeOffset(DateTime.UtcNow).ToUnixTimeMilliseconds())
- {
- return builder.Data(await _azureCosmos.SaveOrUpdate(request)).build();
- }
- else
- {
- return builder.Error(ResponseCode.FAILED, "请在作答时间段内提交答案!").build();
- }
- }
- else {
- return builder.Error(ResponseCode.DATA_EXIST, "考试不存在!").build();
- }
- }
- /// <summary>
- /// 查询作答摘要信息
- /// </summary>
- /// <param name="request"></param>
- /// <returns></returns>
- [ProducesDefaultResponseType]
- //[AuthToken(Roles = "Teacher")]
- [HttpPost("find-summary-record")]
- public async Task<IActionResult> findSummaryRecord(JsonElement requert)
- {
- //ResponseBuilder builder = ResponseBuilder.custom();
- //var (id, school) = HttpContext.GetAuthTokenInfo();
- try {
- if (!requert.TryGetProperty("id_token", out JsonElement id_token)) return BadRequest();
- if (!requert.TryGetProperty("school_code", out JsonElement school_code)) return BadRequest();
- var jwt = new JwtSecurityToken(id_token.GetString());
- if (!jwt.Payload.Iss.Equals("account.teammodel", StringComparison.Ordinal)) return BadRequest();
- var id = jwt.Payload.Sub;
- var client = _azureCosmos.GetCosmosClient();
- // 如果只有学生id则返回学生参加过的考试 只返回相关摘要信息
- if (StringHelper.getKeyCount(requert) == 1 && requert.TryGetProperty("code", out JsonElement code))
- {
- //string pk = typeof(ExamRecord).Name +"-"+ code.ToString();
- //List<string> props = new List<string> { "id", "code", "examCode", "status", "mark", "score" };
- //List<ExamRecord> examRecords = await _azureCosmos.FindByDict<ExamRecord>(request, props);
- //return builder.Data(examRecords).Extend(new Dictionary<string, object> { { "props", props } }).build();
- List<object> props = new List<object>();
- await foreach (var item in client.GetContainer("TEAMModelOSTemp", "Student").GetItemQueryStreamIterator(queryText: $"select c.id, c.code, c.examCode, c.status ,c.mark, c.score from c where c.id = {id}", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"ExamRecord-{school_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())
- {
- props.Add(obj.ToObject<object>());
- }
- }
- }
- return Ok(new { props });
- }
- else
- {
- if (requert.TryGetProperty("examCode", out JsonElement _))
- {
- //List<string> props = new List<string> { "id", "code", "examCode", "status", "mark", "score" };
- //List<ExamRecord> examRecords = await _azureCosmos.FindByDict<ExamRecord>(request, props);
- //return builder.Data(examRecords).Extend(new Dictionary<string, object> { { "props", props } }).build();
- List<object> props = new List<object>();
- await foreach (var item in client.GetContainer("TEAMModelOSTemp", "Student").GetItemQueryStreamIterator(queryText: $"select c.id, c.code, c.examCode, c.status ,c.mark, c.score from c ", requestOptions: new QueryRequestOptions() { PartitionKey = new PartitionKey($"ExamRecord-{school_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())
- {
- props.Add(obj.ToObject<object>());
- }
- }
- }
- return Ok(new { props });
- }
- }
- return Ok();
- } catch (Exception ex) {
- await _dingDing.SendBotMsg($"CoreAPI2,{_option.Location},exam/findSummaryRecord()\n{ex.Message}", GroupNames.醍摩豆服務運維群組);
- return BadRequest();
- }
-
- }
- /// <summary>
- /// 查询单个作答信息 试卷id ,
- /// </summary>
- /// <param name="request"></param>
- /// <returns></returns>
- [ProducesDefaultResponseType]
- //[AuthToken(Roles = "Teacher")]
- [HttpPost("find-by-idPk")]
- public async Task<BaseResponse> findByIdPk(IdPk request)
- {
- ResponseBuilder builder = ResponseBuilder.custom();
- ExamRecord record = await _azureCosmos.FindByIdPk<ExamRecord>(request.id,request.pk);
- if (record != null) {
- //处理客观题自动阅卷
- Paper paper = await _azureCosmos.FindByIdPk<Paper>(record.id, record.examCode);
- //允许自动对客观题阅卷及,及阅卷状态大于2 已完成.
- if (paper.markConfig != null && paper.markConfig.auto && record.mark<2) {
- // TODO 需要处理已经打分的作答,以防止手动改分后被冲掉。
- if (paper.answers.IsNotEmpty() && record.answers.IsNotEmpty()) {
- autoMark(paper.item, paper.answers, record.answers, paper.markConfig);
- }
- }
- }
- return builder.Data(record).build();
- }
- #region private
- private List<Answer> autoMark(List<ItemInfo> items ,List<Answer> stdAnswers, List<Answer> stuAnswers ,MarkConfig markConfig) {
- int size = stuAnswers.Count;
- for (int i = 0; i < size; i++) {
- //如果当前题目已经打分则直接跳过。
- if (stuAnswers[i].score > 0) {
- stuAnswers[i].mark = 1;
- continue;
- }
- //客观题
- if (stuAnswers[i].type.Equals("single") || stuAnswers[i].type.Equals("multiple") || stuAnswers[i].type.Equals("judge")) {
- stuAnswers[i].mark = 1;
- //多选题单独处理
- if (stuAnswers[i].type.Equals("multiple"))
- {
- List<string> stuAns = stuAnswers[i].ans;
- Answer stdAnswer = stdAnswers.Where(x =>x.num== stuAnswers[i].num).FirstOrDefault();
- if (stdAnswer != null) {
- //处理多选答案是否有选错的 选错的则直接0分 ,如果少选则处理部分分数
- bool right = true;
- List<string> rightStr = new List<string>();
- if (stuAns.IsNotEmpty())
- {
- foreach (string stuAn in stuAns)
- {
- if (!stdAnswer.ans.Contains(stuAn))
- {
- right = false;
- break;
- }
- else {
- rightStr.Add(stuAn);
- }
- }
- }
- else {
- right = false;
- }
- if (right && rightStr.IsNotEmpty() && rightStr.Count != stdAnswer.ans.Count)
- {
- if (markConfig.type == 1)
- {
- //1多选漏选不得分
- stuAnswers[i].score = 0;
- }
- else if(markConfig.type==3){
- stuAnswers[i].score=Math.Floor((double)(1.0 * rightStr.Count / stdAnswer.ans.Count * stdAnswer.score));
- if (stuAnswers[i].score == 0) {
- stuAnswers[i].score = 1;
- }
- }
- else if (markConfig.type == 4)
- {
- stuAnswers[i].score = markConfig.score;
- }
- else
- { //2多选漏选得一半的分数(默认)
- stuAnswers[i].score = stdAnswer.score/2;
- }
- }
- else {
- //选错不得分
- stuAnswers[i].score = 0;
- }
- }
- }
- else {
- List<string> stuAns = stuAnswers[i].ans;
- Answer stdAnswer = stdAnswers.Where(x => x.num == stuAnswers[i].num).FirstOrDefault();
- if (stdAnswer != null&&stdAnswer.ans.IsNotEmpty()&& stuAns.IsNotEmpty()) {
- if (stuAns[0].Equals(stdAnswer.ans[0]))
- {
- stuAnswers[i].score = stdAnswer.score;
- }
- else {
- stuAnswers[i].score = 0;
- }
- }
- }
- }
- }
- return stuAnswers;
- }
- #endregion
- }
- }
|