ExamController.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Threading.Tasks;
  5. using Microsoft.AspNetCore.Mvc;
  6. using TEAMModelOS.Models;
  7. using TEAMModelOS.SDK.Context.Exception;
  8. using TEAMModelOS.SDK;
  9. using TEAMModelOS.SDK.Helper.Common.CollectionHelper;
  10. using TEAMModelOS.SDK.DI;
  11. using TEAMModelOS.Service.Models;
  12. using TEAMModelOS.SDK.Context.Constant.Common;
  13. using TEAMModelOS.Service.Services.Learn.Implements;
  14. using System.Text.Json;
  15. namespace TEAMModelOS.Controllers
  16. {
  17. [Route("api/[controller]")]
  18. [ApiController]
  19. public class ExamController : BaseController
  20. {
  21. private readonly SnowflakeId SnowflakeId;
  22. private readonly AzureCosmosFactory cosmosDBV3Repository;
  23. private readonly AzureServiceBusFactory _serviceBus;
  24. public ExamController(AzureCosmosFactory _cosmosDBV3Repository, AzureServiceBusFactory serviceBus, SnowflakeId _SnowflakeId)
  25. {
  26. cosmosDBV3Repository = _cosmosDBV3Repository;
  27. _serviceBus = serviceBus;
  28. SnowflakeId = _SnowflakeId;
  29. }
  30. /// <summary>
  31. /// 保存考试信息
  32. /// </summary>
  33. /// <param name="request"></param>
  34. /// <returns></returns>
  35. [HttpPost("save")]
  36. public async Task<BaseResponse> Save(ExamInfo request)
  37. {
  38. ResponseBuilder builder = ResponseBuilder.custom();
  39. if (string.IsNullOrEmpty(request.id))
  40. {
  41. request.id = SnowflakeId.NextId()+"";
  42. request.status = 100;
  43. // await cosmosDBV3Repository.SaveOrUpdate(request.@params);
  44. }
  45. if (request.publish.Equals("0"))
  46. {
  47. request.startTime = new DateTimeOffset(DateTime.UtcNow).ToUnixTimeMilliseconds();
  48. request.status = 200;
  49. }
  50. else if (request.publish.Equals("1"))
  51. {
  52. //设定开始时间
  53. string msgId = SnowflakeId.NextId() + "";
  54. long SequenceNumber = await _serviceBus.SendMessage<ExamInfo>(Constants.TopicName, request.id, request.code, request.startTime, 200, msgId);
  55. request.sequenceNumber = SequenceNumber;
  56. }
  57. if (request.status == 0)
  58. {
  59. if (request.startTime < new DateTimeOffset(DateTime.UtcNow).ToUnixTimeMilliseconds()) request.status = 200;
  60. else request.status = 100;
  61. }
  62. await cosmosDBV3Repository.SaveOrUpdate(request);
  63. //设定结束时间
  64. string msgEndId = SnowflakeId.NextId() + "";
  65. await _serviceBus.SendMessage<ExamInfo>(Constants.TopicName, request.id, request.code, request.endTime, 300, msgEndId);
  66. return builder.Data(request).build();
  67. }
  68. /// <summary>
  69. /// 删除
  70. /// </summary>
  71. /// <param name="request"></param>
  72. /// <returns></returns>
  73. [HttpPost("delete")]
  74. public async Task<BaseResponse> Delete(IdPk request)
  75. {
  76. ResponseBuilder builder = ResponseBuilder.custom();
  77. IdPk items = await cosmosDBV3Repository.DeleteAsync<ExamInfo>(request.id, request.pk);
  78. await cosmosDBV3Repository.DeleteAll<Paper>(new Dictionary<string, object>() { { "code", request.id } });
  79. return builder.Data(items).build();
  80. }
  81. /// <summary>
  82. /// 查询考试信息
  83. /// </summary>
  84. /// <param name="request"></param>
  85. /// <returns></returns>
  86. [HttpPost("find")]
  87. public async Task<BaseResponse> Find(JsonElement request)
  88. {
  89. ResponseBuilder builder = ResponseBuilder.custom();
  90. //Dictionary<string, object> dict = new Dictionary<string, object>();
  91. var emobj = request.EnumerateObject();
  92. int keys = 0;
  93. while (emobj.MoveNext())
  94. {
  95. keys++;
  96. //dict[emobj.Current.Name] = emobj.Current.Value;
  97. }
  98. if (keys > 0)
  99. {
  100. return builder.Data(await cosmosDBV3Repository.FindByDict<ExamInfo>(request)).build();
  101. }
  102. else {
  103. return builder.build();
  104. }
  105. }
  106. /// <summary>
  107. /// 教师阅卷
  108. /// </summary>
  109. /// <param name="request"></param>
  110. /// <returns></returns>
  111. [HttpPost("marking")]
  112. public async Task<BaseResponse> Marking(ExamRecord request)
  113. {
  114. ResponseBuilder builder = ResponseBuilder.custom();
  115. //判断是否每一个题目都有分数
  116. List<ExamInfo> exams = await cosmosDBV3Repository.FindByDict<ExamInfo>(new Dictionary<string, object> { { "id", request.examCode } });
  117. if (exams.IsNotEmpty())
  118. {
  119. ExamInfo examInfo = exams[0];
  120. //提交答案时间必须是状态已发布,且时间在起止时间内
  121. if ( examInfo.status == 300 &&
  122. examInfo.endTime <= new DateTimeOffset(DateTime.UtcNow).ToUnixTimeMilliseconds())
  123. {
  124. return builder.Data(await cosmosDBV3Repository.SaveOrUpdate(request)).build();
  125. }
  126. else
  127. {
  128. return builder.Error(ResponseCode.FAILED, "请在作答时间段内提交答案!").build();
  129. }
  130. }
  131. else
  132. {
  133. return builder.Error(ResponseCode.DATA_EXIST, "考试不存在!").build();
  134. }
  135. }
  136. /// <summary>
  137. /// 学生回答问题
  138. /// </summary>
  139. /// <param name="request"></param>
  140. /// <returns></returns>
  141. [HttpPost("upsertAllRecord")]
  142. public async Task<BaseResponse> upsertRecord(List<ExamRecord> request)
  143. {
  144. ResponseBuilder builder = ResponseBuilder.custom();
  145. if (request.IsNotEmpty())
  146. {
  147. //要先处理状态,判断卷子是否存在,并判断卷子归属的考试是否允许再次提交
  148. List<ExamInfo> exams = await cosmosDBV3Repository.FindByDict<ExamInfo>(new Dictionary<string, object> { { "id", request[0].examCode } });
  149. if (exams.IsNotEmpty())
  150. {
  151. return builder.Data(await cosmosDBV3Repository.SaveOrUpdateAll(request)).build();
  152. }
  153. else
  154. {
  155. return builder.Error(ResponseCode.DATA_EXIST, "考试不存在!").build();
  156. }
  157. }
  158. else {
  159. return builder.Error(ResponseCode.PARAMS_ERROR, "作答数据为空!").build();
  160. }
  161. }
  162. /// <summary>
  163. /// 学生回答问题
  164. /// </summary>
  165. /// <param name="request"></param>
  166. /// <returns></returns>
  167. [HttpPost("upsertRecord")]
  168. public async Task<BaseResponse> upsertRecord(ExamRecord request)
  169. {
  170. ResponseBuilder builder = ResponseBuilder.custom();
  171. //要先处理状态,判断卷子是否存在,并判断卷子归属的考试是否允许再次提交
  172. List<ExamInfo> exams= await cosmosDBV3Repository.FindByDict<ExamInfo>(new Dictionary<string, object> { { "id", request.examCode } });
  173. if (exams.IsNotEmpty())
  174. {
  175. ExamInfo examInfo = exams[0];
  176. //提交答案时间必须是状态已发布,且时间在起止时间内
  177. if (examInfo.startTime <= new DateTimeOffset(DateTime.UtcNow).ToUnixTimeMilliseconds() && examInfo.status == 200 &&
  178. examInfo.endTime >= new DateTimeOffset(DateTime.UtcNow).ToUnixTimeMilliseconds())
  179. {
  180. return builder.Data(await cosmosDBV3Repository.SaveOrUpdate(request)).build();
  181. }
  182. else
  183. {
  184. return builder.Error(ResponseCode.FAILED, "请在作答时间段内提交答案!").build();
  185. }
  186. }
  187. else {
  188. return builder.Error(ResponseCode.DATA_EXIST, "考试不存在!").build();
  189. }
  190. }
  191. /// <summary>
  192. /// 查询作答摘要信息
  193. /// </summary>
  194. /// <param name="request"></param>
  195. /// <returns></returns>
  196. [HttpPost("findSummaryRecord")]
  197. public async Task<BaseResponse> findSummaryRecord(JsonElement request)
  198. {
  199. ResponseBuilder builder = ResponseBuilder.custom();
  200. //Dictionary<string, object> dict = new Dictionary<string, object>();
  201. var emobj = request.EnumerateObject();
  202. int keys = 0;
  203. List<string> skey = new List<string>();
  204. while (emobj.MoveNext())
  205. {
  206. keys++;
  207. skey.Add(emobj.Current.Name);
  208. //dict[emobj.Current.Name] = emobj.Current.Value;
  209. }
  210. //request.TryGetProperty("code",out JsonElement code);
  211. // 如果只有学生id则返回学生参加过的考试 只返回相关摘要信息
  212. if (keys == 1 && skey.Contains("code"))
  213. {
  214. List<string> props = new List<string> { "id", "code", "examCode", "status", "mark", "score" };
  215. List<ExamRecord> examRecords = await cosmosDBV3Repository.FindByDict<ExamRecord>(request, props);
  216. return builder.Data(examRecords).Extend(new Dictionary<string, object> { { "props", props } }).build();
  217. }
  218. else {
  219. if (skey.Contains("examCode"))
  220. {
  221. List<string> props = new List<string> { "id", "code", "examCode", "status", "mark", "score" };
  222. List<ExamRecord> examRecords = await cosmosDBV3Repository.FindByDict<ExamRecord>(request, props);
  223. return builder.Data(examRecords).Extend(new Dictionary<string, object> { { "props", props } }).build();
  224. }
  225. else {
  226. return builder.Error(ResponseCode.PARAMS_ERROR, "参数错误!").build();
  227. }
  228. }
  229. }
  230. /// <summary>
  231. /// 查询单个作答信息 试卷id ,
  232. /// </summary>
  233. /// <param name="request"></param>
  234. /// <returns></returns>
  235. [HttpPost("findByIdPk")]
  236. public async Task<BaseResponse> findByIdPk(IdPk request)
  237. {
  238. ResponseBuilder builder = ResponseBuilder.custom();
  239. ExamRecord record = await cosmosDBV3Repository.FindByIdPk<ExamRecord>(request.id,request.pk);
  240. if (record != null) {
  241. //处理客观题自动阅卷
  242. Paper paper = await cosmosDBV3Repository.FindByIdPk<Paper>(record.id, record.examCode);
  243. //允许自动对客观题阅卷及,及阅卷状态大于2 已完成.
  244. if (paper.markConfig != null && paper.markConfig.auto && record.mark<2) {
  245. // TODO 需要处理已经打分的作答,以防止手动改分后被冲掉。
  246. if (paper.answers.IsNotEmpty() && record.answers.IsNotEmpty()) {
  247. autoMark(paper.item, paper.answers, record.answers, paper.markConfig);
  248. }
  249. }
  250. }
  251. return builder.Data(record).build();
  252. }
  253. public static List<Answer> autoMark(List<ItemInfo> items ,List<Answer> stdAnswers, List<Answer> stuAnswers ,MarkConfig markConfig) {
  254. int size = stuAnswers.Count;
  255. for (int i = 0; i < size; i++) {
  256. //如果当前题目已经打分则直接跳过。
  257. if (stuAnswers[i].score > 0) {
  258. stuAnswers[i].mark = 1;
  259. continue;
  260. }
  261. //客观题
  262. if (stuAnswers[i].type.Equals("Single") || stuAnswers[i].type.Equals("Multiple") || stuAnswers[i].type.Equals("Judge")) {
  263. stuAnswers[i].mark = 1;
  264. //多选题单独处理
  265. if (stuAnswers[i].type.Equals("Multiple"))
  266. {
  267. List<string> stuAns = stuAnswers[i].ans;
  268. Answer stdAnswer = stdAnswers.Where(x =>x.num== stuAnswers[i].num).FirstOrDefault();
  269. if (stdAnswer != null) {
  270. //处理多选答案是否有选错的 选错的则直接0分 ,如果少选则处理部分分数
  271. bool right = true;
  272. List<string> rightStr = new List<string>();
  273. if (stuAns.IsNotEmpty())
  274. {
  275. foreach (string stuAn in stuAns)
  276. {
  277. if (!stdAnswer.ans.Contains(stuAn))
  278. {
  279. right = false;
  280. break;
  281. }
  282. else {
  283. rightStr.Add(stuAn);
  284. }
  285. }
  286. }
  287. else {
  288. right = false;
  289. }
  290. if (right && rightStr.IsNotEmpty() && rightStr.Count != stdAnswer.ans.Count)
  291. {
  292. if (markConfig.type == 1)
  293. {
  294. //1多选漏选不得分
  295. stuAnswers[i].score = 0;
  296. }
  297. else if(markConfig.type==3){
  298. stuAnswers[i].score=Math.Floor((double)(1.0 * rightStr.Count / stdAnswer.ans.Count * stdAnswer.score));
  299. if (stuAnswers[i].score == 0) {
  300. stuAnswers[i].score = 1;
  301. }
  302. }
  303. else if (markConfig.type == 4)
  304. {
  305. stuAnswers[i].score = markConfig.score;
  306. }
  307. else
  308. { //2多选漏选得一半的分数(默认)
  309. stuAnswers[i].score = stdAnswer.score/2;
  310. }
  311. }
  312. else {
  313. //选错不得分
  314. stuAnswers[i].score = 0;
  315. }
  316. }
  317. }
  318. else {
  319. List<string> stuAns = stuAnswers[i].ans;
  320. Answer stdAnswer = stdAnswers.Where(x => x.num == stuAnswers[i].num).FirstOrDefault();
  321. if (stdAnswer != null&&stdAnswer.ans.IsNotEmpty()&& stuAns.IsNotEmpty()) {
  322. if (stuAns[0].Equals(stdAnswer.ans[0]))
  323. {
  324. stuAnswers[i].score = stdAnswer.score;
  325. }
  326. else {
  327. stuAnswers[i].score = 0;
  328. }
  329. }
  330. }
  331. }
  332. }
  333. return stuAnswers;
  334. }
  335. }
  336. }