123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930 |
- using ICSharpCode.SharpZipLib.GZip;
- using IES.ExamLibrary.Models;
- using IES.ExamServer.DI;
- using IES.ExamServer.DI.SignalRHost;
- using IES.ExamServer.Filters;
- using IES.ExamServer.Helper;
- using IES.ExamServer.Helpers;
- using IES.ExamServer.Models;
- using IES.ExamServer.Services;
- using Microsoft.AspNetCore.Http;
- using Microsoft.AspNetCore.Mvc;
- using Microsoft.AspNetCore.SignalR;
- using Microsoft.Extensions.Caching.Memory;
- using Microsoft.Extensions.Configuration;
- using System;
- using System.Diagnostics.Eventing.Reader;
- using System.Diagnostics.Metrics;
- using System.Linq.Expressions;
- using System.Net.Http;
- using System.Net.Http.Json;
- using System.Security.Cryptography.X509Certificates;
- using System.Text.Json;
- using System.Text.Json.Nodes;
- using System.Text.RegularExpressions;
- using static IES.ExamServer.Services.ManageService;
- using static System.Reflection.Metadata.BlobBuilder;
- namespace IES.ExamServer.Controllers
- {
- [ApiController]
- [Route("manage")]
- public class ManageController : BaseController
- {
- private readonly IConfiguration _configuration;
- private readonly IHttpClientFactory _httpClientFactory;
- private readonly IMemoryCache _memoryCache;
- private readonly ILogger<ManageController> _logger;
- private readonly LiteDBFactory _liteDBFactory;
- private readonly CenterServiceConnectionService _connectionService;
- private readonly int DelayMicro = 10;//微观数据延迟
- private readonly int DelayMacro = 100;//宏观数据延迟
- private readonly IHubContext<SignalRExamServerHub> _signalRExamServerHub;
- private readonly DataQueue _dataQueue;
- public ManageController(LiteDBFactory liteDBFactory, ILogger<ManageController> logger, IConfiguration configuration,
- IHttpClientFactory httpClientFactory, IMemoryCache memoryCache, CenterServiceConnectionService connectionService,
- IHubContext<SignalRExamServerHub> signalRExamServerHub, DataQueue dataQueue)
- {
- _logger = logger;
- _configuration=configuration;
- _httpClientFactory=httpClientFactory;
- _memoryCache=memoryCache;
- _liteDBFactory=liteDBFactory;
- _connectionService=connectionService;
- _signalRExamServerHub=signalRExamServerHub;
- _dataQueue=dataQueue;
- }
- /// <summary>
- /// 导出数据
- ///通过线上回传数据需要鉴权验证等操作。
- ///通过离线包回传数据需要加密操作
- /// </summary>
- /// <param name="json"></param>
- /// <returns></returns>
- [HttpPost("export-evaluation-result")]
- [AuthToken("admin", "teacher", "visitor")]
- public IActionResult ExportEvaluationResult(JsonNode json)
- {
- string evaluationId = $"{json["evaluationId"]}";
- string shortCode = $"{json["shortCode"]}";
- string openCode = $"{json["openCode"]}";
- string? loginToken = HttpContext.GetXAuth("AuthToken");
- EvaluationClient? evaluationClient = _liteDBFactory.GetLiteDatabase().GetCollection<EvaluationClient>()
- .FindOne(x => x.id!.Equals(evaluationId) && x.openCode!.Equals(openCode));
- //已作答
- int answerCount = 0;
- //未作答
- int absentCount = 0;
- List<SubjectPushData> subjectPushDatas = new List<SubjectPushData>();
- List<SubjectPushData> subjectPushNew = new List<SubjectPushData>();
- if (evaluationClient!=null)
- {
- var datas = _liteDBFactory.GetLiteDatabase().GetCollection<SubjectPushData>()
- .Find(x => !string.IsNullOrWhiteSpace(x.evaluationId) && x.evaluationId!.Equals(evaluationId)).ToList();
- if (datas.IsNotEmpty())
- {
- subjectPushDatas.AddRange(datas);
- }
- var data = _liteDBFactory.GetLiteDatabase().GetCollection<EvaluationStudentResult>()
- .Find(x => !string.IsNullOrWhiteSpace(x.evaluationId) && x.evaluationId.Equals(evaluationId));
- if (data.IsNotEmpty())
- {
- foreach (var item in data)
- {
- foreach (var subjectResult in item.subjectResults)
- {
- if (subjectResult.finished==1)
- {
- SubjectPushData subjectPushData = new SubjectPushData(subjectResult, item);
- subjectPushData.loginToken=loginToken;
- if (!datas.Exists(x => x.id!.Equals(subjectPushData.id)))
- {
- //数据丢失导致的推送SubjectPushData 没有被保存的问题
- subjectPushDatas.Add(subjectPushData);
- subjectPushNew.Add(subjectPushData);
- }
- }
- else { absentCount++; }
- }
- }
- }
- if (subjectPushNew.IsNotEmpty())
- {
- _liteDBFactory.GetLiteDatabase().GetCollection<SubjectPushData>().Upsert(subjectPushNew);
- }
- }
- answerCount=subjectPushDatas.Count(x => x.finished==1 && x.answers.IsNotEmpty());
- absentCount+=subjectPushDatas.Count()-answerCount;
- //TODO 以及增加 作答文件的相关数据。
- return Ok(new { answerCount, absentCount, subjectPushDatas });
- }
- /// <summary>
- /// 手动推送
- ///通过线上回传数据需要鉴权验证等操作。
- ///通过离线包回传数据需要加密操作
- /// </summary>
- /// <param name="json"></param>
- /// <returns></returns>
- [HttpPost("manual-push")]
- [AuthToken("admin", "teacher", "visitor")]
- public async Task<IActionResult> ManualPush(JsonNode json)
- {
- string evaluationId = $"{json["evaluationId"]}";
- //string shortCode = $"{json["shortCode"]}";
- string openCode = $"{json["openCode"]}";
- string deviceId = $"{json["deviceId"]}";
- EvaluationClient? evaluationClient = _liteDBFactory.GetLiteDatabase().GetCollection<EvaluationClient>()
- .FindOne(x => x.id!.Equals(evaluationId) && x.openCode!.Equals(openCode));
- int count = 0;
- string? loginToken = HttpContext.GetXAuth("AuthToken");
- if (evaluationClient!=null)
- {
- var datas = _liteDBFactory.GetLiteDatabase().GetCollection<SubjectPushData>()
- .Find(x => !string.IsNullOrWhiteSpace(x.evaluationId) && x.evaluationId!.Equals(evaluationId)).ToList();
- if (datas.IsNotEmpty())
- {
- foreach (var item in datas)
- {
- if (item.pushed<=1 && item.finished==1)
- {
- item.order=count;
- item.loginToken=loginToken;
- count++;
- //_logger.LogInformation($"推送数据加入队列=>>序号:{subjectPushData.order}--学号:{item.studentId}--姓名:{item.studentName}--科目:{subjectResult.subjectName}");
- await _dataQueue.TryAddAsync(item);
-
- }
- }
- }
- var data = _liteDBFactory.GetLiteDatabase().GetCollection<EvaluationStudentResult>()
- .Find(x =>!string.IsNullOrWhiteSpace(x.evaluationId) && x.evaluationId.Equals(evaluationId) );
- List<SubjectPushData> subjectPushDatas = new List<SubjectPushData>();
- if (data.IsNotEmpty())
- {
- foreach (var item in data)
- {
- foreach (var subjectResult in item.subjectResults)
- {
- if ( subjectResult.finished==1)
- {
-
- SubjectPushData subjectPushData= new SubjectPushData(subjectResult, item);
- subjectPushData.order=count;
- subjectPushData.loginToken=loginToken;
- if (!datas.Exists(x => x.id!.Equals(subjectPushData.id)))
- {
- //数据丢失导致的推送SubjectPushData 没有被保存的问题
- subjectPushDatas.Add(subjectPushData);
- //未被推送的数据
- if (subjectPushData.pushed<=1 && subjectPushData.finished==1)
- {
- count++;
- subjectPushData.pushed=1;
- await _dataQueue.TryAddAsync(subjectPushData);
- }
- }
-
- }
- }
- }
- }
- if (subjectPushDatas.IsNotEmpty())
- {
- _liteDBFactory.GetLiteDatabase().GetCollection<SubjectPushData>().Upsert(subjectPushDatas);
- }
- }
- return Ok(new { code=200, message="推送成功!", count });
- }
- /// <summary>
- /// 清理缓存,列出缓存占用空间,type =list列出,type=clear清理,不能清理近期及正在激活的数据,并且提示清理中暂未上传或者导出的数据。
- /// </summary>
- /// <param name="json"></param>
- /// <returns></returns>
- [HttpPost("clean-cache")]
- [AuthToken("admin", "teacher", "visitor")]
- public async Task<IActionResult> CleanCache(JsonNode json)
- {
-
- return Ok();
- }
- //C#.NET 6 后端与前端流式通信
- //https://www.doubao.com/chat/collection/687687510791426?type=Thread
- //下载日志记录:1.步骤,检查,2.获取描述信息,3.分类型,4下载文件,5.前端处理,6.返回结果 , 正在下载...==> [INFO]https://www.doubao.com/chat/collection/687687510791426?type=Thread [Size=180kb] Ok...
- //进度条 展示下载文件总大小和已下载,末尾展示 文件总个数和已下载个数
- //https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/8.0.7/signalr.min.js
- /* int data = 0,blob=0, groupList=0
- {
- "evaluationId":"idssss",
- "shortCode":"1234567890",
- "ownerId":"hbcn/tmdid",
- "data":1,
- "blob":1,
- "groupList":1
- }
- */
- //如果要访问中心,则需要教师登录联网。
- [HttpPost("download-package-music")]
- [AuthToken("admin", "teacher", "visitor")]
- public async Task<IActionResult> DownloadPackageMusic(JsonNode json)
- {
- return Ok();
- }
- [HttpPost("download-package")]
- [AuthToken("admin", "teacher", "visitor")]
- public async Task<IActionResult> DownloadPackage(JsonNode json)
- {
- var token = GetAuthTokenInfo();
- //检查试卷文件完整性
- List<string> successMsgs = new List<string>();
- List<string> errorMsgs = new List<string>();
- EvaluationCheckFileResult result = new EvaluationCheckFileResult() { successMsgs=successMsgs, errorMsgs=errorMsgs };
- if (token.scope.Equals(ExamConstant.ScopeTeacher) || token.scope.Equals(ExamConstant.ScopeVisitor))
- {
- if (_connectionService.centerIsConnected)
- {
- Teacher? teacher = _liteDBFactory.GetLiteDatabase().GetCollection<Teacher>().FindOne(x => x.id!.Equals(token.id));
- string id = $"{json["evaluationId"]}";
- string shortCode = $"{json["shortCode"]}";
- string deviceId = $"{json["deviceId"]}";
- EvaluationClient? evaluationClient = _liteDBFactory.GetLiteDatabase().GetCollection<EvaluationClient>().FindOne(x => x.id!.Equals(id) && x.shortCode!.Equals(shortCode));
-
- if (teacher != null && evaluationClient!= null)
- {
- int msg_status = Constant._Message_status_info;
- string msg_content = msg_status.Equals(Constant._Message_status_success) ? "成功" : "失败";
- await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, Constant._Message_grant_type_download_file,
- new MessageContent { dataId = evaluationClient.id, dataName = evaluationClient.name, messageType = Constant._Message_type_message, status = 0, step=1, content = "检测云端数据是否匹配..." });
- var dataInfo= await GetEvaluationFromCenter(GetXAuthToken(), _configuration, _httpClientFactory, shortCode, evaluationClient.id!);
-
- if (dataInfo.centerCode.Equals("200")&& dataInfo.evaluationCloud!=null)
- {
- await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, Constant._Message_grant_type_download_file,
- new MessageContent { dataId = evaluationClient.id, dataName = evaluationClient.name, messageType = Constant._Message_type_message, status = Constant._Message_status_success, step = 1, content = $"云端数据检测结果:{dataInfo.centerMsg},状态:{dataInfo.centerCode}" },true);
- string? CenterUrl = _configuration.GetValue<string>("ExamServer:CenterUrl");
- var client = _httpClientFactory.CreateClient();
- if (client.DefaultRequestHeaders.Contains(Constant._X_Auth_AuthToken))
- {
- client.DefaultRequestHeaders.Remove(Constant._X_Auth_AuthToken);
- }
- client.DefaultRequestHeaders.Add(Constant._X_Auth_AuthToken, teacher.x_auth_token);
- HttpResponseMessage message = await client.PostAsJsonAsync($"{CenterUrl}/blob/sas-read", new { containerName = $"{dataInfo.evaluationCloud.ownerId}" });
- string sas = string.Empty;
- string url = string.Empty;
- string cnt = string.Empty;
- if (message.IsSuccessStatusCode)
- {
- //url sas timeout name
- string content = await message.Content.ReadAsStringAsync();
- JsonNode? jsonNode = content.ToObject<JsonNode>();
- if (jsonNode != null)
- {
- sas = $"{jsonNode["sas"]}";
- cnt = $"{jsonNode["name"]}";
- url = $"{jsonNode["url"]}";
- msg_status = Constant._Message_status_success;
- }
- else
- {
- msg_status = Constant._Message_status_error;
- }
- }
- else
- {
- msg_status = Constant._Message_status_error;
-
- }
- msg_content = msg_status.Equals(Constant._Message_status_success) ? "成功" : "失败";
- await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, Constant._Message_grant_type_download_file,
- new MessageContent { dataId = evaluationClient.id, dataName = evaluationClient.name, messageType = Constant._Message_type_message, status = msg_status, step = 1, content = $"获取云端下载授权=>{msg_content}" }, true);
- var httpClient = _httpClientFactory.CreateClient();
- string packagePath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "package");
- string evaluationPath = Path.Combine(packagePath, dataInfo.evaluationCloud.id!);
- //删除文件夹
- FileHelper.DeleteFolder(evaluationPath);
- string evaluationDataPath = Path.Combine(evaluationPath, "data");
- if (!Directory.Exists(evaluationDataPath))
- {
- Directory.CreateDirectory(evaluationDataPath);
- }
-
- await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, Constant._Message_grant_type_download_file,
- new MessageContent { dataId = evaluationClient.id, dataName = evaluationClient.name, messageType = Constant._Message_type_message, step = 1, finish=1,status = Constant._Message_status_info, content = $"下载前清理资源" });
- string evaluationData = string.Empty;
- {
- //evaluation
- string evaluationUrl = $"{url}/{cnt}/package/{json["evaluationId"]}/data/evaluation.json?{sas}";
- HttpResponseMessage dataMessage = await httpClient.GetAsync(evaluationUrl);
- if (dataMessage.IsSuccessStatusCode)
- {
- var content = await dataMessage.Content.ReadAsStringAsync();
- evaluationData=content;
- string path_evaluation = Path.Combine(evaluationDataPath, "evaluation.json");
- await System.IO.File.WriteAllTextAsync(path_evaluation, content);
- successMsgs.Add("评测信息文件evaluation.json文件下载成功!");
- await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, Constant._Message_grant_type_download_file,
- new MessageContent { dataId = evaluationClient.id, dataName = evaluationClient.name, messageType = Constant._Message_type_message, step = 2, finish = 1, status = Constant._Message_status_success, content = $"评测信息文件evaluation.json文件下载成功!" });
- }
- else
- {
- errorMsgs.Add("评测信息文件evaluation.json文件下载失败!");
- await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, Constant._Message_grant_type_download_file,
- new MessageContent { dataId = evaluationClient.id, dataName = evaluationClient.name, messageType = Constant._Message_type_message, step = 2, finish = 1, status = Constant._Message_status_error, content = $"评测信息文件evaluation.json文件下载失败!" }, true);
- }
- }
- //{
- // //source.json
- // string sourceUrl = $"{url}/{cnt}/package/{json["evaluationId"]}/data/source.json?{sas}";
- // HttpResponseMessage dataMessage = await httpClient.GetAsync(sourceUrl);
- // if (dataMessage.IsSuccessStatusCode)
- // {
- // var content = await dataMessage.Content.ReadAsStringAsync();
- // string path_source = Path.Combine(evaluationDataPath, "source.json");
- // await System.IO.File.WriteAllTextAsync(path_source, content);
- // successMsgs.Add("评测数据原始文件source.json文件下载成功!");
- // await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, Constant._Message_grant_type_download_file,
- // new MessageContent { dataId = evaluationClient.id, dataName = evaluationClient.name, messageType = Constant._Message_type_message, status = Constant._Message_status_success, content = $"评测数据原始文件source.json文件下载成功!" });
- // }
- // else
- // {
- // errorMsgs.Add("评测数据原始文件source.json文件下载失败!");
- // await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, Constant._Message_grant_type_download_file,
- // new MessageContent { dataId = evaluationClient.id, dataName = evaluationClient.name, messageType = Constant._Message_type_message, status = Constant._Message_status_error, content = $"评测数据原始文件source.json文件下载失败!" }, true);
- // }
- //}
- {
- //grouplist.json
- string grouplistUrl = $"{url}/{cnt}/package/{json["evaluationId"]}/data/grouplist.json?{sas}";
- HttpResponseMessage groupListMessage = await httpClient.GetAsync(grouplistUrl);
- if (groupListMessage.IsSuccessStatusCode)
- {
- var content = await groupListMessage.Content.ReadAsStringAsync();
- string path_groupList = Path.Combine(evaluationDataPath, "grouplist.json");
- await System.IO.File.WriteAllTextAsync(path_groupList, content);
- successMsgs.Add("评测名单grouplist.json文件下载成功!");
- await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, Constant._Message_grant_type_download_file,
- new MessageContent { dataId = evaluationClient.id, dataName = evaluationClient.name, messageType = Constant._Message_type_message, step = 3, finish = 1, status = Constant._Message_status_success, content = $"评测名单grouplist.json文件下载成功!" });
- }
- else
- {
- errorMsgs.Add("评测名单grouplist.json文件下载失败!");
- await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, Constant._Message_grant_type_download_file,
- new MessageContent { dataId = evaluationClient.id, dataName = evaluationClient.name, messageType = Constant._Message_type_message, step = 3, finish = 1, status = Constant._Message_status_error, content = $"评测名单grouplist.json文件下载失败!" }, true);
- }
- }
- {
-
- //下载试卷文件
- List<EvaluationExam>? evaluationExams = evaluationData.ToObject<JsonNode>()?["evaluationExams"]?.ToObject<List<EvaluationExam>>();
- int blobCount = evaluationExams!.SelectMany(x => x.papers).SelectMany(x=>x.blobs).Count();
- int currCount = 0;
- foreach (var evaluationExam in evaluationExams!)
- {
- foreach (var evaluationPaper in evaluationExam.papers)
- {
- string path_paper = Path.Combine(evaluationPath, $"papers/{evaluationPaper.paperId}");
- if (!Directory.Exists(path_paper))
- {
- Directory.CreateDirectory(path_paper);
- }
- //最多开启10个线程并行下载
- var parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = 5 };
- // 使用 Parallel.ForEachAsync 并行处理每个 blob
- await Parallel.ForEachAsync(evaluationPaper.blobs, parallelOptions, async (blob, cancellationToken) =>
- {
- currCount++;
- double size = Math.Round(blob.size * 1.0 / 1024 / 1024, 2);
- string? fileName = Path.GetFileName(blob.path);
- try
- {
- // 下载 Blob 文件到本地
- //httpClient.Timeout = TimeSpan.FromSeconds(300);
-
- HttpResponseMessage blobMessage = await httpClient.GetAsync($"{url}/{cnt}/{blob.path}?{sas}", cancellationToken);
-
- if (blobMessage.IsSuccessStatusCode)
- {
- byte[] bytes = await blobMessage.Content.ReadAsByteArrayAsync(cancellationToken);
- string? extension = Path.GetExtension(blob.path);
- if (extension!=null)
- {
- if (extension.Equals(extension.ToUpper()))
- {
- string? fileNameWithoutExtension = Path.GetFileNameWithoutExtension(blob.path);
- await System.IO.File.WriteAllBytesAsync(Path.Combine(path_paper, $"{fileNameWithoutExtension!}_1{extension}"), bytes, cancellationToken);
- }
- else
- {
-
- await System.IO.File.WriteAllBytesAsync(Path.Combine(path_paper, fileName!), bytes, cancellationToken);
- }
- }
- await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, Constant._Message_grant_type_download_file,
- new MessageContent { dataId = evaluationClient.id, dataName = evaluationClient.name, messageType = Constant._Message_type_message, step = 4, status = Constant._Message_status_success, content = $"[进度:{currCount}/{blobCount}][大小:{size}kb]{evaluationExam.subjectName},{evaluationPaper.paperName},{fileName}文件下载成功。" });
- }
- else
- {
- string? error = await blobMessage.Content.ReadAsStringAsync(cancellationToken);
- errorMsgs.Add($"{evaluationExam.subjectName},{evaluationPaper.paperName},{blob.path}文件下载失败,{blobMessage.StatusCode},{error}");
- await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, Constant._Message_grant_type_download_file,
- new MessageContent { dataId = evaluationClient.id, dataName = evaluationClient.name, messageType = Constant._Message_type_message, step = 4, status = Constant._Message_status_error, content = $"[进度:{currCount}/{blobCount}][大小:{size}kb]{evaluationExam.subjectName},{evaluationPaper.paperName},{fileName}文件下载失败,{blobMessage.StatusCode},{error}" }, true);
- // Console.WriteLine($"Error downloading {blob.path},{blobMessage.StatusCode},{error}");
- }
- }
- catch (Exception ex)
- {
- errorMsgs.Add($"{evaluationExam.subjectName},{evaluationPaper.paperName},{blob.path}文件下载错误,{ex.Message}");
- await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, Constant._Message_grant_type_download_file,
- new MessageContent { dataId = evaluationClient.id, dataName = evaluationClient.name, messageType = Constant._Message_type_message, step = 4, status = Constant._Message_status_error, content = $"[进度:{currCount}/{blobCount}][大小:{size}kb]{evaluationExam.subjectName},{evaluationPaper.paperName},{fileName}文件下载错误,{ex.Message}" }, true);
- // 处理异常
- //Console.WriteLine($"Error downloading {blob.path}: {ex.Message}");
- }
- });
- }
- }
- }
- _liteDBFactory.GetLiteDatabase().GetCollection<EvaluationClient>().Upsert(dataInfo.evaluationCloud!);
- await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, Constant._Message_grant_type_download_file,
- new MessageContent { dataId = evaluationClient.id, dataName = evaluationClient.name, messageType = Constant._Message_type_message, step = 4, finish = 1, status = Constant._Message_status_info, content = $"评测试卷下载完成" });
- (successMsgs, errorMsgs) = await ManageService.CheckFile(dataInfo.evaluationCloud!, successMsgs, errorMsgs, _signalRExamServerHub, _memoryCache, _logger, deviceId, evaluationPath, Constant._Message_grant_type_download_file,step:5);
-
- await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, Constant._Message_grant_type_download_file,
- new MessageContent { dataId = evaluationClient.id, dataName = evaluationClient.name, messageType = Constant._Message_type_message, step = 5, finish = 1, status = Constant._Message_status_info, content = $"评测完整性验证完成" });
- await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, Constant._Message_grant_type_download_file,
- new MessageContent { dataId = evaluationClient.id, dataName = evaluationClient.name, messageType = Constant._Message_type_message, step = 6, status = Constant._Message_status_info, content = $"正在创建压缩包,请稍等..." });
- //下载完成后,对数据进行检查,然后在加密压缩。
- string zipPath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "zip");
- if (!Directory.Exists(zipPath))
- {
- Directory.CreateDirectory(zipPath);
- }
- string zipFilePath = Path.Combine(zipPath, $"{dataInfo.evaluationCloud.id}-{dataInfo.evaluationCloud.blobHash}.zip");
- var zipInfo = ZipHelper.CreatePasswordProtectedZip(evaluationPath, zipFilePath, dataInfo.evaluationCloud.openCode!);
- if (zipInfo.res)
- {
- successMsgs.Add("评测数据压缩包创建成功!");
- await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, Constant._Message_grant_type_download_file,
- new MessageContent { dataId = evaluationClient.id, dataName = evaluationClient.name, messageType = Constant._Message_type_message, step = 6, finish = 1, status = Constant._Message_status_success, content = $"评测数据压缩包创建成功!" });
- }
- else
- {
- errorMsgs.Add("评测数据压缩包创建失败!");
- await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, Constant._Message_grant_type_download_file,
- new MessageContent { dataId = evaluationClient.id, dataName = evaluationClient.name, messageType = Constant._Message_type_message, step = 6, finish = 1, status = Constant._Message_status_error, content = $"评测数据压缩包创建失败!" }, true);
- }
- }
- else {
- string content = $"云端数据检测结果:{dataInfo.centerMsg},状态:{dataInfo.centerCode}";
- errorMsgs.Add(content);
- await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, Constant._Message_grant_type_download_file,
- new MessageContent { dataId = evaluationClient.id, dataName = evaluationClient.name, messageType = Constant._Message_type_message, step = 1, finish = 1, status = Constant._Message_status_error, content = content }, true);
- }
- }
- else
- {
- string teacherMsg = teacher==null ? "未找到教师信息" : "";
- string evaluationMsg = evaluationClient==null ? "未找到评测信息" : "";
- errorMsgs.Add($"用户信息或未找到评测信息!{teacherMsg}{evaluationMsg}");
- }
- }
- else {
- errorMsgs.Add($"云端数据中心未连接");
- }
- }
- else
- {
- errorMsgs.Add($"请使用教师或访客账号登录!");
- }
- result.checkError=result.errorMsgs.Count();
- result.checkSuccess=result.successMsgs.Count();
- result.checkTotal=result.checkSuccess+result.checkError;
- if (result.errorMsgs.Count()==0)
- {
- return Ok(new { code = 200, msg = "下载成功!", result });
- }
- else {
- return Ok(new { code = 1, msg = "下载失败!", result });
- }
- }
- /// <summary>
- /// 输入开卷码,查询评测信息,并返回数据。
- /// </summary>
- /// <param name="json"></param>
- /// <returns></returns>
- [HttpPost("open-evaluation")]
- [AuthToken("admin", "teacher", "visitor")]
- public async Task<IActionResult> OpenEvaluation(JsonNode json)
- {
- string deviceId = $"{json["deviceId"]}";
- string evaluationId = $"{json["evaluationId"]}";
- string openCode = $"{json["openCode"]}";
- string shortCode = $"{json["shortCode"]}";
- var token = GetAuthTokenInfo();
- List<string> successMsgs = new List<string>();
- List<string> errorMsgs = new List<string>();
- EvaluationCheckFileResult result = new EvaluationCheckFileResult() { successMsgs=successMsgs, errorMsgs =errorMsgs };
- if (!string.IsNullOrEmpty(openCode) && !string.IsNullOrEmpty(evaluationId))
- {
- EvaluationClient? evaluationLocal = _liteDBFactory.GetLiteDatabase().GetCollection<EvaluationClient>().FindOne(x => x.id!.Equals(evaluationId) && x.openCode!.Equals(openCode) && x.shortCode!.Equals(shortCode));
- if (evaluationLocal!=null)
- {
- //ManageService.CheckData(evaluationLocal, null, successMsgs, errorMsgs, _liteDBFactory);
- await _signalRExamServerHub.SendMessage(_memoryCache, _logger, deviceId, Constant._Message_grant_type_check_file,
- new MessageContent { dataId=evaluationLocal.id, dataName=evaluationLocal.name, messageType=Constant._Message_type_message, status=0, content="开始检查评测信息文件.." });
- //判断文件包的压缩包是否存在。
- string zipPath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "zip");
- //校验本地文件数据
- string packagePath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "package");
- string evaluationPath = Path.Combine(packagePath, evaluationLocal.id!);
- if (System.IO.File.Exists(Path.Combine(zipPath, $"{evaluationLocal.id}-{evaluationLocal.blobHash}.zip")))
- {
- string key = $"{Constant._KeyEvaluationZipExtract}:{evaluationId}:{openCode}";
- successMsgs.Add("加载评测试卷文件包!");
- //string keyData = _memoryCache.Get<string>(key);
- //if (!string.IsNullOrWhiteSpace(keyData))
- //{
- //}
- //else {
-
- //}
- //删除文件夹
- FileHelper.DeleteFolder(evaluationPath);
- if (!Directory.Exists(evaluationPath))
- {
- Directory.CreateDirectory(evaluationPath);
- }
- //解压文件包
- var extractRes = await ZipHelper.ExtractPasswordProtectedZip(Path.Combine(zipPath, $"{evaluationLocal.id}-{evaluationLocal.blobHash}.zip"), evaluationPath, evaluationLocal.openCode!, _signalRExamServerHub,_memoryCache,_logger,deviceId,evaluationLocal);
- if (extractRes.res)
- {
- _memoryCache.Set(key,key,TimeSpan.FromSeconds(30));
- successMsgs.Add("评测试卷文件包解压成功!");
- (successMsgs, errorMsgs) = await ManageService.CheckFile(evaluationLocal, successMsgs, errorMsgs, _signalRExamServerHub, _memoryCache, _logger, deviceId, evaluationPath, Constant._Message_grant_type_check_file);
- }
- else {
- errorMsgs.Add("评测试卷文件包解压失败!");
- //return Ok(new { code = 3, msg = "评测试卷文件包解压失败!" });
- }
- }
- else {
- errorMsgs.Add("评测试卷文件包不存在!");
- //return Ok(new { code = 3, msg = "评测试卷文件包不存在!" });
- }
- }
- else {
- errorMsgs.Add("未找到评测信息!");
- // return Ok(new { code = 2, msg = "未找到评测信息!" });
- }
- }
- else
- {
- errorMsgs.Add("评测ID或提取码均未填写!");
- // return Ok(new { code = 1, msg = "评测ID或提取码均未填写!" });
- }
- result.checkTotal = result.errorMsgs.Count() + result.successMsgs.Count();
- result.checkError= result.errorMsgs.Count();
- result.checkSuccess = result.successMsgs.Count();
- if (result.errorMsgs.Count()==0)
- {
- return Ok(new { code = 200, msg = "校验成功!", result });
- }
- else
- {
- return Ok(new { code = 1, msg = "校验失败!", result });
- }
- }
- /// <summary>
- /// 检查数据包是否有更新,zip是否存在,数据文件是否完整,名单文件是否完整。
- /// 如果有智音模块,则需要检查智音音乐缓存是否完整。
- /// </summary>
- /// <param name="json"></param>
- /// <returns></returns>
- [HttpPost("check-evaluation")]
- [AuthToken("admin", "teacher", "visitor")]
- public async Task<IActionResult> CheckEvaluation(JsonNode json)
- {
- string shortCode = $"{json["shortCode"]}";
- string evaluationId = $"{json["evaluationId"]}";
- string checkCenter = $"{json["checkCenter"]}";
- string deviceId = $"{json["deviceId"]}";
- string centerCode = string.Empty, centerMsg = string.Empty;
- Expression<Func<EvaluationClient, bool>> predicate = x => true;
- var token = GetAuthTokenInfo();
- if (!string.IsNullOrEmpty(shortCode) || !string.IsNullOrEmpty(evaluationId))
- {
- if (!string.IsNullOrEmpty(shortCode))
- {
- predicate= predicate.And(x => !string.IsNullOrWhiteSpace(x.shortCode) && x.shortCode.Equals(shortCode));
- }
- if (!string.IsNullOrWhiteSpace(evaluationId))
- {
- predicate= predicate.And(x => x.id!.Equals(evaluationId));
- }
- }
- else
- {
- return Ok(new { code = 400, msg = "评测ID或提取码均未填写!" });
- }
- IEnumerable<EvaluationClient>? evaluationClients = _liteDBFactory.GetLiteDatabase().GetCollection<EvaluationClient>().Find(predicate);
- EvaluationClient? evaluationLocal = null;
- EvaluationClient? evaluationCloud = null;
- if (evaluationClients!=null && evaluationClients.Count()>0)
- {
- evaluationLocal= evaluationClients.First();
- }
- List<string> successMsgs = new List<string>();
- List<string> errorMsgs = new List<string>();
- //从数据中心搜索
- if ("1".Equals($"{checkCenter}"))
- {
- if (_connectionService.centerIsConnected)
- {
- if (token.scope.Equals(ExamConstant.ScopeTeacher) || token.scope.Equals(ExamConstant.ScopeVisitor))//由于已经绑定学校,访客教师也可以访问中心。
- {
- Teacher? teacher = _liteDBFactory.GetLiteDatabase().GetCollection<Teacher>().FindOne(x => x.id!.Equals(token.id));
- if (teacher != null)
- {
- (evaluationCloud, centerCode, centerMsg)= await ManageService.GetEvaluationFromCenter(GetXAuthToken(), _configuration,_httpClientFactory,shortCode,evaluationId);
- }
- else
- {
- centerCode = $"401";
- centerMsg = "当前登录账号未找到";
- }
- }
- }
- else
- {
- centerCode = $"404";
- centerMsg = "云端数据中心未连接";
- }
- if (centerCode.Equals("200"))
- {
- successMsgs.Add($"云端数据检测结果:{centerMsg},状态:{centerCode}");
- }
- else
- {
- errorMsgs.Add($"云端数据检测结果:{centerMsg},状态:{centerCode}");
- }
- }
- EvaluationCheckDataResult checkDataResult;
- (checkDataResult, evaluationLocal) = ManageService.CheckData( evaluationLocal, evaluationCloud, successMsgs, errorMsgs, _liteDBFactory);
- Dictionary<string, object?>? evaluation = null;
- if (evaluationLocal!=null)
- {
- string zipPath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "zip");
- //判断文件包的压缩包是否存在。
- if (!System.IO.File.Exists(Path.Combine(zipPath, $"{evaluationLocal.id}-{evaluationLocal.blobHash}.zip")))
- {
- checkDataResult.zip =1;
- checkDataResult.blob=1;
- checkDataResult.data=1;
- checkDataResult.groupList=1;
- checkDataResult.blobSize=evaluationLocal.blobSize;
- checkDataResult.dataSize=evaluationLocal.dataSize;
- checkDataResult.studentCount=evaluationLocal.studentCount;
- errorMsgs.Add($"评测文件包压缩包不存在,需要重新下载评测!");
- }
- var properties = evaluationLocal.GetType().GetProperties();
- evaluation = new Dictionary<string, object?>();
- foreach (var property in properties)
- {
- if (!property.Name.Equals("openCode"))
- {
- evaluation[property.Name] = property.GetValue(evaluationLocal);
- }
- }
- }
- return Ok(new
- {
- code = 200,
- evaluation = evaluation,
- result = checkDataResult
- });
- }
- /// <summary>
- /// 获取当前评测的开考设置信息
- /// </summary>
- /// <param name="json"></param>
- /// <returns></returns>
- [HttpPost("list-evaluation-round")]
- [AuthToken("admin", "teacher", "visitor")]
- public IActionResult ListEvaluationRound(JsonNode json)
- {
- string evaluationId = $"{json["evaluationId"]}";
- string openCode = $"{json["openCode"]}";
- string shortCode = $"{json["shortCode"]}";
- EvaluationClient? evaluationClient = _liteDBFactory.GetLiteDatabase().GetCollection<EvaluationClient>()
- .FindOne(x => x.id!.Equals(evaluationId) && x.shortCode!.Equals(shortCode) && x.openCode!.Equals(openCode));
- if (evaluationClient!=null)
- {
- IEnumerable<EvaluationRoundSetting>? settings = _liteDBFactory.GetLiteDatabase().GetCollection<EvaluationRoundSetting>().Find(x => x.evaluationId!.Equals(evaluationClient.id)).OrderByDescending(x => x.activate).ThenByDescending(x => x.startline).ThenByDescending(x => x.createTime);
- if (settings.IsNotEmpty())
- {
- return Ok(new { code = 200, msg = "OK", settings = settings });
- }
- else
- {
- return Ok(new { code = 2, msg = "未设置开考信息!" });
- }
- }
- else
- {
- return Ok(new { code = 1, msg = "未找到评测信息!" });
- }
-
- }
- /// <summary>
- /// 获取当前评测的开考设置信息
- /// </summary>
- /// <param name="json"></param>
- /// <returns></returns>
- [HttpPost("load-evaluation-round")]
- [AuthToken("admin", "teacher", "visitor")]
- public IActionResult LoadEvaluationRound(JsonNode json)
- {
- string evaluationId = $"{json["evaluationId"]}";
- string openCode = $"{json["openCode"]}";
- string shortCode = $"{json["shortCode"]}";
- string settingId = $"{json["settingId"]}";
- EvaluationClient? evaluationClient = _liteDBFactory.GetLiteDatabase().GetCollection<EvaluationClient>()
- .FindOne(x => x.id!.Equals(evaluationId) && x.shortCode!.Equals(shortCode) && x.openCode!.Equals(openCode));
- EvaluationRoundSetting? setting = null;
- if (evaluationClient!=null)
- {
- setting = _liteDBFactory.GetLiteDatabase().GetCollection<EvaluationRoundSetting>().FindOne(x =>x.id!.Equals(settingId) && x.evaluationId!.Equals(evaluationClient.id));
- if (setting!=null)
- {
- IEnumerable<EvaluationStudentResult>? results = null;
- var members = _liteDBFactory.GetLiteDatabase().GetCollection<EvaluationMember>().Find(x => x.evaluationId!.Equals(evaluationClient.id) && x.roundId!.Equals(setting.id));
- //并获取学生的作答信息
- //顺便返回本轮的学生名单
- if (members!=null && members.Count()>0)
- {
- results = _liteDBFactory.GetLiteDatabase().GetCollection<EvaluationStudentResult>()
- .Find(x => members.Select(x => x.id).Contains(x.studentId)&&!string.IsNullOrWhiteSpace(x.evaluationId) && x.evaluationId.Equals(evaluationClient.id));
- if (results.Count()==members.Count())
- {
- return Ok(new { code = 200, setting, results });
- }
- else {
- return Ok(new { code = 200,msg="学生作答信息数量不匹配", setting, results });
- }
- }
- else
- {
- return Ok(new { code = 200,msg="未分配学生名单,或名单没有学生!", setting , results });
- }
- }
- else
- {
- return Ok(new { code = 2, msg = "未设置开考信息!" });
- }
- }
- else {
- return Ok(new { code = 1, msg = "未找到评测信息!" });
- }
- }
-
- /// <summary>
- /// 设置评测开考信息(本轮名单,计时规则,分配试卷等)
- /// </summary>
- /// <param name="json"></param>
- /// <returns></returns>
- [HttpPost("setting-evaluation-round")]
- [AuthToken("admin", "teacher", "visitor")]
- public IActionResult SettingEvaluationRound(JsonNode json)
- {
- EvaluationRoundSetting? setting = json.ToObject<EvaluationRoundSetting>();
- if (setting!=null)
- {
- var db = _liteDBFactory.GetLiteDatabase();
- var collection = db.GetCollection<EvaluationClient>() ;
- //&& x.openCode!.Equals($"{json["openCode"]}")&& x.shortCode!.Equals($"{json["shortCode"]}")
- string shortCode = $"{json["shortCode"]}";
- string openCode = $"{json["openCode"]}";
- EvaluationClient ? evaluationClient = collection.FindOne(x => x.id!.Equals(setting.evaluationId)
- && !string.IsNullOrWhiteSpace(x.shortCode) && x.shortCode.Equals(x.shortCode)
- && !string.IsNullOrWhiteSpace(x.openCode) && x.openCode.Equals(openCode) );
- if (evaluationClient!=null)
- {
- IEnumerable<EvaluationRoundSetting> settings = _liteDBFactory.GetLiteDatabase().GetCollection<EvaluationRoundSetting>().Find(x => x.evaluationId!.Equals(evaluationClient.id));
- if (settings != null && settings.Count() > 0)
- {
- var datas = settings.ToList();
- foreach (EvaluationRoundSetting item in datas)
- {
- item.activate = 0;
- }
- _liteDBFactory.GetLiteDatabase().GetCollection<EvaluationRoundSetting>().Upsert(datas);
- }
- /// 判断是否包含所有分组
- bool isAllContained = setting.groupList.All(x => evaluationClient.grouplist.Any(y => y.id == x.id));
- if (isAllContained && evaluationClient.grouplist.IsNotEmpty())
- {
- var ids= setting.groupList.Select(x => x.id).OrderBy(x=>x);
- //增加排序,保证id的唯一性
- setting.id=ShaHashHelper.GetSHA1($"{evaluationClient.id}_{string.Join("", ids) }");
- setting.createTime= DateTimeOffset.Now.ToUnixTimeMilliseconds();
-
- _liteDBFactory.GetLiteDatabase().GetCollection<EvaluationRoundSetting>().Upsert(setting);
- _liteDBFactory.GetLiteDatabase().GetCollection<EvaluationClient>().Upsert(evaluationClient);
- /// 分配试卷
- var(roundStudentPapers, members, results, code, msg)= ManageService.AssignStudentPaper(evaluationClient, setting,_connectionService, _liteDBFactory, _logger);
- //var (roundStudentPapers, members, results,code,msg) = AssignStudentPaper(evaluationClient, setting);
- return Ok(new { code = code, msg =msg , setting, results, roundStudentPapers });
- }
- else
- {
- return Ok(new { code = 3, msg = "开考名单不在当前评测中!" });
- }
- }
- else { return Ok(new { code = 2, msg = "未找到评测,请确认评测ID、提取码、开卷码是否正确!" }); }
- }
- else
- {
- return Ok(new { code = 1, msg = "未完善设置信息!" });
- }
-
- }
-
- /// <summary>
- /// 加载本地的活动列表
- /// </summary>
- /// <param name="json"></param>
- /// <returns></returns>
- [HttpPost("list-local-evaluation")]
- [AuthToken("admin", "teacher", "visitor")]
- public IActionResult ListLocalEvaluation(JsonNode json)
- {
- if (!string.IsNullOrWhiteSpace($"{_connectionService.serverDevice?.school?.id}"))
- {
- string code = $"{_connectionService!.serverDevice!.school!.id}";
- IEnumerable<EvaluationClient>? evaluationClients = _liteDBFactory.GetLiteDatabase().GetCollection<EvaluationClient>().Find(x => x.ownerId!.Equals(code)).OrderByDescending(x => x.stime);
- if (evaluationClients != null)
- {
- var result = evaluationClients.Select(client =>
- {
- var properties = client.GetType().GetProperties();
- var anonymousObject = new Dictionary<string, object?>();
- foreach (var property in properties)
- {
- if (!property.Name.Equals("openCode"))
- {
- anonymousObject[property.Name] = property.GetValue(client);
- }
- }
- return anonymousObject;
- });
- return Ok(new { code = 200, evaluation = result });
- }
- else
- {
- return Ok(new { code = 200, evaluation = new List<EvaluationClient>() });
- }
- }
- else
- {
- return Ok(new { code = 200, evaluation = new List<EvaluationClient>() });
- }
-
-
- }
- }
- }
|