CrazyIter_Bin 3 months ago
parent
commit
4b7dacce68

+ 4 - 0
TEAMModelOS.Extension/IES.Exam/IES.ExamLibrary/Models/EvaluationCommon.cs

@@ -337,6 +337,10 @@ namespace IES.ExamServer.Models
         /// 本地路径
         /// </summary>
         public string? local { get; set; }
+        /// <summary>
+        /// 题目数量
+        /// </summary>
+        public int questionCount { get; set; }
     }
     public class EvaluationPaper: SubjectExamPaper
     {

+ 47 - 5
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Controllers/ManageController.cs

@@ -50,8 +50,46 @@ namespace IES.ExamServer.Controllers
             _connectionService=connectionService;
             _signalRExamServerHub=signalRExamServerHub;
         }
+        /// <summary>
+        /// 导出数据
+        ///通过线上回传数据需要鉴权验证等操作。
+        ///通过离线包回传数据需要加密操作
+        /// </summary>
+        /// <param name="json"></param>
+        /// <returns></returns>
+        [HttpPost("export-evaluation-result")]
+        [AuthToken("admin", "teacher", "visitor")]
+        public async Task<IActionResult> ExportEvaluationResult(JsonNode json) 
+        {
+            string id = $"{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(id) && x.openCode!.Equals(openCode)&& x.shortCode!.Equals(shortCode));
+            return Ok();
+        }
+        /// <summary>
+        /// 手动推送
         ///通过线上回传数据需要鉴权验证等操作。
         ///通过离线包回传数据需要加密操作
+        /// </summary>
+        /// <param name="json"></param>
+        /// <returns></returns>
+        [HttpPost("manual-push")]
+        [AuthToken("admin", "teacher", "visitor")]
+        public async Task<IActionResult> ManualPush(JsonNode json)
+        {
+            string id = $"{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(id) && x.openCode!.Equals(openCode)&& x.shortCode!.Equals(shortCode));
+            return Ok();
+        }
+
+
 
         /// <summary>
         /// 清理缓存,列出缓存占用空间,type =list列出,type=clear清理,不能清理近期及正在激活的数据,并且提示清理中暂未上传或者导出的数据。
@@ -62,6 +100,8 @@ namespace IES.ExamServer.Controllers
         [AuthToken("admin", "teacher", "visitor")]
         public async Task<IActionResult> CleanCache(JsonNode json)
         {
+           
+
             return Ok();
         }
 
@@ -791,6 +831,7 @@ namespace IES.ExamServer.Controllers
                                             subjectName=subject.subjectName,
                                             paperId=studentPaper.paperId,
                                             paperName=studentPaper.paperName,
+                                            questionCount=studentPaper.questionCount,
                                             id=id,
                                         });
                                         // 移动到下一个试卷
@@ -837,9 +878,8 @@ namespace IES.ExamServer.Controllers
                                         type= evaluationClient.type,
                                         pid= evaluationClient.pid,
                                     };
-                                    var studentPapers = roundStudentPapers.FindAll(x => !string.IsNullOrWhiteSpace(x.studentId) &&  x.studentId.Equals(member.id)
-                                                            &&!string.IsNullOrWhiteSpace(x.evaluationId)  && x.evaluationId.Equals(evaluationClient.id));
-
+                                    var studentPapers = roundStudentPapers.FindAll(x => !string.IsNullOrWhiteSpace(x.studentId) 
+                                    &&  x.studentId.Equals(member.id) &&!string.IsNullOrWhiteSpace(x.evaluationId)  && x.evaluationId.Equals(evaluationClient.id));
                                     if (studentPapers.IsNotEmpty())
                                     {
                                         foreach (var studentPaper in studentPapers)
@@ -854,6 +894,7 @@ namespace IES.ExamServer.Controllers
                                                 subjectName = studentPaper.subjectName,
                                                 paperId = studentPaper.paperId,
                                                 paperName = studentPaper.paperName,
+                                                questionCount=studentPaper.questionCount,
                                                 createTime=now
                                             });
                                         }
@@ -866,8 +907,8 @@ namespace IES.ExamServer.Controllers
                                     studentResult.studentName = member.name;
                                     studentResult.classId = member.classId;
                                     studentResult.className = member.className;
-                                    var studentPapers = roundStudentPapers.FindAll(x => !string.IsNullOrWhiteSpace(x.studentId) &&  x.studentId.Equals(member.id)
-                                                          &&!string.IsNullOrWhiteSpace(x.evaluationId)  && x.evaluationId.Equals(evaluationClient.id));
+                                    var studentPapers = roundStudentPapers.FindAll(x => !string.IsNullOrWhiteSpace(x.studentId)
+                                    &&  x.studentId.Equals(member.id) &&!string.IsNullOrWhiteSpace(x.evaluationId)  && x.evaluationId.Equals(evaluationClient.id));
                                     if (studentPapers.IsNotEmpty())
                                     {
                                         foreach (var studentPaper in studentPapers)
@@ -886,6 +927,7 @@ namespace IES.ExamServer.Controllers
                                                     subjectName = studentPaper.subjectName,
                                                     paperId = studentPaper.paperId,
                                                     paperName = studentPaper.paperName,
+                                                    questionCount= studentPaper.questionCount,
                                                     createTime=now
                                                 };
                                             }

+ 45 - 11
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Controllers/StudentController.cs

@@ -88,21 +88,55 @@ namespace IES.ExamServer.Controllers
                     }
                     if (!string.IsNullOrWhiteSpace(subjectId)  && !string.IsNullOrWhiteSpace(examId) && !string.IsNullOrWhiteSpace(paperId)&& costTime>0)
                     {
-                        var subjectResult = studentResult.subjectResults.Where(x => subjectId.Equals(x.subjectId) && examId.Equals(x.examId) && paperId.Equals(x.paperId)).FirstOrDefault();
-                        if (subjectResult!=null)
+                       
+                        if (answers!.IsNotEmpty())
                         {
-                            if (answers!=null) 
+                            var subjectExams= evaluationClient.subjects.FindAll(x => examId.Equals(x.examId)&& subjectId.Equals(x.subjectId));
+                            var papers= subjectExams.FirstOrDefault()?.papers.FindAll(x => paperId.Equals(x.paperId));
+                            if (papers.IsNotEmpty())
                             {
-                                subjectResult.answers=answers!;
+                                if (answers!.Count()!=papers!.First()?.questionCount) 
+                                {
+                                    return Ok(new { msg = "提交的答案数量与试卷题目数量不匹配。", code = 5 });
+                                }
+                            }
+                            string subjectResultId = ShaHashHelper.GetSHA1(evaluationClient.id+examId+subjectId+token.id);
+                            var subjectResult = studentResult.subjectResults.Where(x => subjectResultId.Equals(x.id) &&  subjectId.Equals(x.subjectId)
+                                    && examId.Equals(x.examId) && paperId.Equals(x.paperId)).FirstOrDefault();
+                            if (subjectResult!=null)
+                            {
+                                subjectResult.answers=answers;
+                                subjectResult.finished=1;
+                                subjectResult.costTime=costTime;
+                                subjectResult.submitTime=now;
+                                subjectResult.pushed=0;//强制重新推送
+                            }
+                            EvaluationSubjectResult result = new EvaluationSubjectResult()
+                            {
+                                id = subjectResultId,
+                                evaluationId = evaluationId,
+                                examId = examId,
+                                examName = subjectResult?.examName,
+                                subjectId = subjectId,
+                                subjectName = subjectResult?.subjectName,
+                                paperId =paperId,
+                                paperName = subjectResult?.paperName,
+                                createTime=now,
+                                finished=1,
+                                costTime=costTime,
+                                submitTime=now,
+                                answers=answers,
+                                questionCount=papers.IsNotEmpty()? papers!.First().questionCount : 0,
+                                pushed=0//强制重新推送
+                            };
+                            _liteDBFactory.GetLiteDatabase().GetCollection<EvaluationSubjectResult>().Upsert(result);
+
+                            if (_connectionService!.centerIsConnected)
+                            {
+
                             }
-                            subjectResult.finished=1;
-                            subjectResult.costTime=costTime;
-                            subjectResult.submitTime=now;
-                        }
-                        if (_connectionService!.centerIsConnected) 
-                        {
-                        
                         }
+                       
                     }
                     return Ok(new { code = 200, studentResult = studentResult, msg = "提交成功!" });
                 }

+ 0 - 145
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/DI/AnswerPushService.cs

@@ -1,145 +0,0 @@
-using IES.ExamServer.Models;
-using LiteDB;
-using System.Net.Http;
-using System.Text;
-using System.Threading.Channels;
-
-namespace IES.ExamServer.DI
-{
-    public class AnswerPushService: BackgroundService
-    {
-        private readonly DataQueue _dataQueue;
-        private readonly IHttpClientFactory _httpClientFactory;
-        private readonly LiteDBFactory _liteDBFactory;
-        private readonly CenterServiceConnectionService _connectionService;
-        public AnswerPushService(DataQueue dataQueue, IHttpClientFactory httpClientFactory, LiteDBFactory liteDBFactory, CenterServiceConnectionService connectionService)
-        {
-            
-            _dataQueue = dataQueue;
-            _httpClientFactory = httpClientFactory;
-            _liteDBFactory = liteDBFactory;
-            _connectionService = connectionService;
-
-        }
-        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
-        {
-            ///数据中心链接了,开始推送数据
-            if (_connectionService.centerIsConnected) 
-            {
-                // 启动时加载未推送的数据
-                await LoadUnpushedDataAsync(stoppingToken);
-            }
-            while (!stoppingToken.IsCancellationRequested)
-            {
-                // 从 Channel 中读取数据
-                if (await _dataQueue.Reader.WaitToReadAsync(stoppingToken))
-                {
-                    while (_dataQueue.Reader.TryRead(out var data))
-                    {
-
-                        await PushSubjectResultDataToCloudAsync(data, stoppingToken);
-                        await PushMusicAIResultDataToCloudAsync(data, stoppingToken);
-                    }
-                }
-            }
-
-        }
-        private async Task PushMusicAIResultDataToCloudAsync(EvaluationStudentResult data, CancellationToken cancellationToken)
-        {
-
-            if (_connectionService.centerIsConnected)
-            {
-                try
-                {
-                    //  var json = JsonSerializer.Serialize(data);
-                    //var content = new StringContent(json, Encoding.UTF8, "application/json");
-                    var httpClient = _httpClientFactory.CreateClient();
-                    var response = await httpClient.PostAsJsonAsync("", data, cancellationToken);
-
-                    if (response.IsSuccessStatusCode)
-                    {
-                        MarkDataAsPushed(data);
-                        Console.WriteLine($"Data {data.id} pushed successfully.");
-                    }
-                    else
-                    {
-                        Console.WriteLine($"Failed to push data {data.id}. Retrying...");
-                        // 推送失败,重新加入队列
-                        await _dataQueue.Writer.WriteAsync(data, cancellationToken);
-                    }
-                }
-                catch (Exception ex)
-                {
-                    Console.WriteLine($"Error pushing data {data.id}: {ex.Message}");
-                    // 推送失败,重新加入队列
-                    await _dataQueue.Writer.WriteAsync(data, cancellationToken);
-                }
-            }
-        }
-        private async Task PushSubjectResultDataToCloudAsync(EvaluationStudentResult data, CancellationToken cancellationToken)
-        {
-            if (_connectionService.centerIsConnected) 
-            {
-                try
-                {
-                    //  var json = JsonSerializer.Serialize(data);
-                    //var content = new StringContent(json, Encoding.UTF8, "application/json");
-                    var httpClient = _httpClientFactory.CreateClient();
-                    var response = await httpClient.PostAsJsonAsync("", data, cancellationToken);
-
-                    if (response.IsSuccessStatusCode)
-                    {
-                        MarkDataAsPushed(data);
-                        Console.WriteLine($"Data {data.id} pushed successfully.");
-                    }
-                    else
-                    {
-                        Console.WriteLine($"Failed to push data {data.id}. Retrying...");
-                        // 推送失败,重新加入队列
-                        await _dataQueue.Writer.WriteAsync(data, cancellationToken);
-                    }
-                }
-                catch (Exception ex)
-                {
-                    Console.WriteLine($"Error pushing data {data.id}: {ex.Message}");
-                    // 推送失败,重新加入队列
-                    await _dataQueue.Writer.WriteAsync(data, cancellationToken);
-                }
-            }
-        }
-        private void MarkDataAsPushed(EvaluationStudentResult data)
-        {
-            var collection = _liteDBFactory.GetLiteDatabase().GetCollection<EvaluationStudentResult>();
-            data.pushed = 1;
-            collection.Upsert(data);
-        }
-        private async Task LoadUnpushedDataAsync(CancellationToken cancellationToken)
-        {
-            var collection = _liteDBFactory.GetLiteDatabase().GetCollection<EvaluationStudentResult>();
-            var unpushedData = collection.Find(x => x.pushed!=1);
-
-            // 将未推送的数据写入 Channel
-            await _dataQueue.WriteBatchAsync(unpushedData, cancellationToken);
-        }
-    }
-
-    public class DataQueue
-    {
-        private readonly Channel<EvaluationStudentResult> _channel;
-        public DataQueue() {
-            // 创建一个无界 Channel
-            _channel = Channel.CreateUnbounded<EvaluationStudentResult>();
-        }
-        public ChannelWriter<EvaluationStudentResult> Writer => _channel.Writer;
-        public ChannelReader<EvaluationStudentResult> Reader => _channel.Reader;
-
-        // 批量写入数据
-        public async Task WriteBatchAsync(IEnumerable<EvaluationStudentResult> dataList, CancellationToken cancellationToken = default)
-        {
-            foreach (var data in dataList)
-            {
-                await _channel.Writer.WriteAsync(data, cancellationToken);
-            }
-        }
-    }
-}

+ 2 - 1
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/DI/CenterServiceConnectionService.cs

@@ -25,7 +25,8 @@ namespace IES.ExamServer.DI
         ///  }
         /// </summary>
         public string? musicUrl { get; set; }
-       
+        public string? loginToken { get; set; }
+
         public ServerDevice? serverDevice {  get; set; }
         public bool musicIsConnected
         { get;set;

+ 178 - 0
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/DI/SubjectPushService.cs

@@ -0,0 +1,178 @@
+using IES.ExamServer.Helpers;
+using IES.ExamServer.Models;
+using LiteDB;
+using System;
+using System.Net.Http;
+using System.Text;
+using System.Threading.Channels;
+
+namespace IES.ExamServer.DI
+{
+    public class SubjectPushService: BackgroundService
+    {
+        private readonly DataQueue _dataQueue;
+        private readonly IHttpClientFactory _httpClientFactory;
+        private readonly LiteDBFactory _liteDBFactory;
+        private readonly CenterServiceConnectionService _connectionService;
+        private readonly IConfiguration _configuration;
+        public SubjectPushService(DataQueue dataQueue, IHttpClientFactory httpClientFactory, LiteDBFactory liteDBFactory, CenterServiceConnectionService connectionService, IConfiguration configuration)
+        {
+            
+            _dataQueue = dataQueue;
+            _httpClientFactory = httpClientFactory;
+            _liteDBFactory = liteDBFactory;
+            _connectionService = connectionService;
+            _configuration= configuration;
+
+        }
+        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
+        {
+            ///数据中心链接了,开始推送数据
+            if (_connectionService.centerIsConnected) 
+            {
+                // 启动时加载未推送的数据
+                //await LoadUnpushedDataAsync(stoppingToken);
+            }
+            while (!stoppingToken.IsCancellationRequested)
+            {
+                // 从 Channel 中读取数据
+                if (await _dataQueue.Reader.WaitToReadAsync(stoppingToken))
+                {
+                    while (_dataQueue.Reader.TryRead(out var data))
+                    {
+
+                        await PushSubjectResultDataToCloudAsync(data, stoppingToken);
+                       
+                    }
+                }
+            }
+        }
+        
+        private async Task PushSubjectResultDataToCloudAsync((EvaluationStudentResult studentResult, EvaluationSubjectResult subjectResult, string resultId)data, CancellationToken cancellationToken)
+        {
+            if (_connectionService.centerIsConnected) 
+            {
+                try
+                {
+                    //  var json = JsonSerializer.Serialize(data);
+                    //var content = new StringContent(json, Encoding.UTF8, "application/json");
+                    var httpClient = _httpClientFactory.CreateClient();
+
+                    string url = $"{_connectionService.centerUrl}/common/exam/upsert-new-record";
+                    if (httpClient.DefaultRequestHeaders.Contains("X-Auth-AuthToken")) {
+                        httpClient.DefaultRequestHeaders.Remove("X-Auth-AuthToken");
+                    }
+                    httpClient.DefaultRequestHeaders.Add("X-Auth-AuthToken", _connectionService.loginToken);
+                    var response = await httpClient.PostAsJsonAsync(url, new { 
+                        id =data.subjectResult.examId,
+                        answer=data.subjectResult.answers,
+                        subjectId=data.subjectResult.subjectId,
+                        classId=data.studentResult.classId,
+                        ownerId=data.studentResult.ownerId,
+                        paperId=data.subjectResult.paperId,
+                        studentId=data.studentResult.studentId,
+                        studentName=data.studentResult.studentName,
+                    }, cancellationToken);
+
+                    if (response.IsSuccessStatusCode)
+                    {
+                        MarkDataAsPushed(data);
+                        Console.WriteLine($"Data {data.resultId} pushed successfully.");
+                    }
+                    else
+                    {
+                        Console.WriteLine($"Failed to push data {data.resultId}. Retrying...");
+                        // 推送失败,重新加入队列
+                        await _dataQueue.Writer.WriteAsync(data, cancellationToken);
+                    }
+                }
+                catch (Exception ex)
+                {
+                    Console.WriteLine($"Error pushing data {data.resultId}: {ex.Message}");
+                    // 推送失败,重新加入队列
+                    await _dataQueue.Writer.WriteAsync(data, cancellationToken);
+                }
+            }
+        }
+        private void MarkDataAsPushed((EvaluationStudentResult studentResult, EvaluationSubjectResult subjectResult, string resultId) data)
+        {
+            var collection = _liteDBFactory.GetLiteDatabase().GetCollection<EvaluationStudentResult>();
+            EvaluationSubjectResult? subjectResult = data.studentResult.subjectResults.Where(x => x.id!.Equals(data.resultId)).FirstOrDefault();
+            if (subjectResult!=null) 
+            {
+                subjectResult.pushed=1;
+                collection.Upsert(data.studentResult);
+                var collectionSub = _liteDBFactory.GetLiteDatabase().GetCollection<EvaluationSubjectResult>();
+                var unpushedData = collectionSub.FindOne(x => data.resultId.Equals(x.id));
+                if (unpushedData!=null) 
+                {
+                    unpushedData.pushed=1;
+                    _dataQueue.MarkAsProcessed((unpushedData.id!, unpushedData.examId!, unpushedData.evaluationId!, unpushedData.subjectId!,unpushedData, data.studentResult));
+                }
+            }
+            
+        }
+        private async Task LoadUnpushedDataAsync(CancellationToken cancellationToken)
+        {
+            var collection = _liteDBFactory.GetLiteDatabase().GetCollection<EvaluationSubjectResult>();
+            var unpushedData = collection.Find(x => x.pushed!=1);
+            foreach (var data in unpushedData)
+            {
+                var studentResults  = _liteDBFactory.GetLiteDatabase().GetCollection<EvaluationStudentResult>().Find(x => x.subjectResults.Where(x => x.id!.Equals(data.id)).IsNotEmpty()).Distinct();
+                // 将未推送的数据写入 Channel
+                foreach (var studentResult in studentResults)
+                {
+                    await _dataQueue.WriteAsync((studentResult!,data, data.id!), cancellationToken); 
+                }
+            }
+        }
+    }
+
+    public class DataQueue
+    {
+        private readonly Channel<(EvaluationStudentResult studentResult, EvaluationSubjectResult subjectResult, string resultId)> _channel;
+        private readonly HashSet<(string resultId,string examId,string evaluationId,string subjectId, EvaluationSubjectResult subjectResult, EvaluationStudentResult studentResult)> _uniqueIds; // 用于维护唯一性
+
+        public async Task<bool> TryAddAsync((string resultId, string examId, string evaluationId, string subjectId, EvaluationSubjectResult subjectResult, EvaluationStudentResult studentResult) data, CancellationToken cancellationToken = default)
+        {
+            var key = (data.resultId, data.examId,data.evaluationId,data.subjectId,data.subjectResult,data.studentResult); // 复合键
+            lock (_uniqueIds)
+            {
+                if (_uniqueIds.Contains(key))
+                {
+                    return false;
+                }
+                _uniqueIds.Add(key);
+            }
+
+            await _channel.Writer.WriteAsync((data.studentResult,data.subjectResult, data.resultId), cancellationToken);
+            return true;
+        }
+        public DataQueue() {
+            // 创建一个无界 Channel
+            _channel = Channel.CreateUnbounded<(EvaluationStudentResult studentResult, EvaluationSubjectResult subjectResult, string resultId)>();
+            _uniqueIds = new HashSet<(string resultId, string examId, string evaluationId, string subjectId, EvaluationSubjectResult subjectResult, EvaluationStudentResult studentResult)>();
+        }
+        public ChannelWriter<(EvaluationStudentResult studentResult, EvaluationSubjectResult subjectResult, string resultId)> Writer => _channel.Writer;
+        public ChannelReader<(EvaluationStudentResult studentResult, EvaluationSubjectResult subjectResult, string resultId)> Reader => _channel.Reader;
+
+        // 批量写入数据
+        public async Task WriteAsync(( EvaluationStudentResult studentResult, EvaluationSubjectResult subjectResult, string resultId) data, CancellationToken cancellationToken = default)
+        {
+            await _channel.Writer.WriteAsync((data.studentResult,data.subjectResult, data.resultId), cancellationToken);
+        }
+        // 获取当前队列中的未上传数据数量
+        public int GetPendingCount()
+        {
+            return _channel.Reader.Count;
+        }
+        // 从队列中移除数据时,同时从 HashSet 中移除 ID
+        public void MarkAsProcessed((string resultId, string examId, string evaluationId, string subjectId, EvaluationSubjectResult subjectResult, EvaluationStudentResult studentResult) key)
+        {
+            lock (_uniqueIds) // 加锁确保线程安全
+            {
+                _uniqueIds.Remove(key);
+            }
+        }
+    }
+}

+ 5 - 1
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Models/EvaluationRound.cs

@@ -56,6 +56,10 @@
         /// 进行中  剩下的是未开始
         /// </summary>
         public int doingCount { get; set; }
-       
+        /// <summary>
+        /// 乱序作答0 顺序作答,1乱序作答,此字段仅供监考教师设置,如果评测要求是乱序,监考教师则不能修改为顺序作答,只能是从顺序作答改为乱序作答。
+        /// </summary>
+        public int disorder { get; set; }
+
     }
 }

+ 11 - 2
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Models/EvaluationStudent.cs

@@ -145,7 +145,7 @@
         /// 是否推送0 未推送,1推送
         /// </summary>
         public int pushed { get; set; }
-
+        //public HashSet<string> subjectResultIds= new HashSet<string>();
         public HashSet<EvaluationSubjectResult> subjectResults { get; set; } = new HashSet<EvaluationSubjectResult>();
         public EvaluationMusicAIResult? musicAIResult { get; set; }
         public EvaluationVoteResult? voteResult { get; set; }
@@ -196,6 +196,7 @@
         /// 试卷名称
         /// </summary>
         public string? paperName { get; set; }
+        public int questionCount { get; set;}
     }
 
     public abstract class EvaluationResult 
@@ -220,6 +221,10 @@
         public long submitTime { get; set; }
 
         public long createTime { get; set; }
+        /// <summary>
+        /// 是否推送 0 未推送 1 已推送
+        /// </summary>
+        public int pushed { get; set; }
     }
 
     /// <summary>
@@ -279,7 +284,11 @@
         /// <summary>
         /// 学生答案
         /// </summary>
-        public List<List<string?>> answers { get; set; } = new List<List<string?>>();
+        public List<List<string>>? answers { get; set; } = new List<List<string>>();
+        /// <summary>
+        /// 题目数量
+        /// </summary>
+        public int questionCount { get; set; }
     }
     /// <summary>
     /// 投票作答结果

+ 4 - 0
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Program.cs

@@ -67,6 +67,10 @@ namespace IES.ExamServer.Server
             // 蛁聊 ConnectionService 峈等瞰
             builder.Services.AddSingleton<CenterServiceConnectionService>();
             builder.Services.AddSingleton<ServiceInitializer>();
+            // 蛁聊 DataQueue 督昢
+            builder.Services.AddSingleton<DataQueue>();
+            // 蛁聊綴怢督昢
+            builder.Services.AddHostedService<SubjectPushService>();
             builder.Services.AddCors(options =>
             {
                 //options.AddDefaultPolicy(

+ 3 - 9
TEAMModelOS.Extension/IES.Exam/IES.ExamServer/Services/IndexService.cs

@@ -330,25 +330,19 @@ namespace IES.ExamServer.Services
             //{
             //    throw new Exception("未获取到端口信息!");
             //}
-            var networks = device.networks;
+            var networks = device.networks.ToList();
             if (device.networks.IsNotEmpty()) 
             {
                var order=  device.networks.OrderByDescending(x => x.physical).ToList();
                 for (int i=0; i<order.Count();i++) 
                 {
-                    if (i==0)
-                    {
-                        order[i].domain="exam.habook.local";
-                    }
-                    else {
-                        order[i].domain=$"exam{i}.habook.local";
-                    }
+                    order[i].domain="exam.habook.local";
                 }
                 //优先以物理网卡来生成hash,如果没有则以所有网卡生成hash
                 var physical = order.FindAll(x => x.physical==1);
                 if (physical.IsNotEmpty())
                 {
-                    networks.AddRange(physical);
+                    networks=physical;
                 }
             }
             StringBuilder sb= new StringBuilder();

+ 5 - 5
TEAMModelOS.SDK/Models/Service/EvaluationSyncInfoService.cs

@@ -186,7 +186,7 @@ namespace TEAMModelOS.SDK.Models.Service
                                         subjectName=subject.name,
                                         examId=id,
                                         examName=exam.name,
-                                        papers= group.list.Select(x=>new SubjectExamPaper {paperId= x.id,paperName=x.name, blob=x.blob, local=$"package/{evaluationSyncInfo.id}/papers/{x.id}" }).ToList(),
+                                        papers= group.list.Select(x=>new SubjectExamPaper {paperId= x.id,paperName=x.name, questionCount=x.answers.IsNotEmpty()?x.answers.Count():0, blob=x.blob, local=$"package/{evaluationSyncInfo.id}/papers/{x.id}" }).ToList(),
                                     } );
 
                                     EvaluationExam evaluationExam = new EvaluationExam()
@@ -201,7 +201,7 @@ namespace TEAMModelOS.SDK.Models.Service
                                         scope=scope,
                                         stime=stime,
                                         etime=etime,
-                                        papers= group.list.Select(x => new EvaluationPaper { paperId= x.id, paperName=x.name, blob=x.blob, local=$"package/{evaluationSyncInfo.id}/papers/{x.id}", point=x.point,knowledge=x.knowledge,type=x.type,field=x.field }).ToList(),
+                                        papers= group.list.Select(x => new EvaluationPaper { paperId= x.id, paperName=x.name, questionCount=x.answers.IsNotEmpty() ? x.answers.Count() : 0, blob=x.blob, local=$"package/{evaluationSyncInfo.id}/papers/{x.id}", point=x.point,knowledge=x.knowledge,type=x.type,field=x.field }).ToList(),
                                     };
                                     evaluationExams.Add(evaluationExam);
                                 }
@@ -256,7 +256,7 @@ namespace TEAMModelOS.SDK.Models.Service
                                         var subjectSync = evaluationSyncInfo.subjects.Find(x => x.examId.Equals(item.acId) && x.subjectId.Equals(item.subject));
                                         if (subjectSync!=null)
                                         {
-                                            subjectSync.papers= papers?.Select(x => new SubjectExamPaper { paperId=x.id, paperName=x.name, blob=x.blob, local=$"package/{evaluationSyncInfo.id}/papers/{x.id}" }).ToList();
+                                            subjectSync.papers= papers?.Select(x => new SubjectExamPaper { paperId=x.id, paperName=x.name, questionCount=x.answers.IsNotEmpty() ? x.answers.Count() : 0, blob=x.blob, local=$"package/{evaluationSyncInfo.id}/papers/{x.id}" }).ToList();
                                             subjectSync.subjectName=subject.name;
                                         }
                                         else {
@@ -267,7 +267,7 @@ namespace TEAMModelOS.SDK.Models.Service
                                                 examId=item.acId,
                                                 examName=exam.name,
                                                 disorder=item.isOrder.HasValue ? item.isOrder.Value : 0,
-                                                papers = papers?.Select(x => new SubjectExamPaper { paperId=x.id, paperName=x.name, blob=x.blob, local=$"package/{evaluationSyncInfo.id}/papers/{x.id}" }).ToList()
+                                                papers = papers?.Select(x => new SubjectExamPaper { paperId=x.id, paperName=x.name, questionCount=x.answers.IsNotEmpty() ? x.answers.Count() : 0, blob=x.blob, local=$"package/{evaluationSyncInfo.id}/papers/{x.id}" }).ToList()
                                             });
                                         }
                                         dataTime= dataTime<exam._ts*1000 ? exam._ts*1000 : dataTime;
@@ -285,7 +285,7 @@ namespace TEAMModelOS.SDK.Models.Service
                                             stime=stime,
                                             etime=etime,
                                             disorder=item.isOrder.HasValue?item.isOrder.Value:0,
-                                            papers= papers.Select(x => new EvaluationPaper { paperId= x.id, paperName=x.name, blob=x.blob, local=$"package/{evaluationSyncInfo.id}/papers/{x.id}", point=x.point, knowledge=x.knowledge, type=x.type, field=x.field }).ToList(),
+                                            papers= papers.Select(x => new EvaluationPaper { paperId= x.id, paperName=x.name, blob=x.blob, questionCount=x.answers.IsNotEmpty() ? x.answers.Count() : 0, local=$"package/{evaluationSyncInfo.id}/papers/{x.id}", point=x.point, knowledge=x.knowledge, type=x.type, field=x.field }).ToList(),
                                         };
                                         evaluationExams.Add(evaluationExam);
                                     }

+ 6 - 6
TEAMModelOS/Controllers/Common/ExamController.cs

@@ -1603,12 +1603,12 @@ namespace TEAMModelOS.Controllers
         /// <param name="request"></param>
         /// <returns></returns>
         [ProducesDefaultResponseType]
-        [Authorize(Roles = "IES")]
-        [AuthToken(Roles = "student,teacher")]
+        //[Authorize(Roles = "IES")]
+        [AuthToken(Roles = "teacher,visitor")]
         [HttpPost("upsert-new-record")]
         public async Task<IActionResult> upsertNewRecord(JsonElement request)
         {
-
+            await _dingDing.SendBotMsg($"局域网评测端推送学生作答数据:\n{request.ToJsonString()}", GroupNames.成都开发測試群組);
             //评测活动Id
             if (!request.TryGetProperty("id", out JsonElement id)) return BadRequest();
             //学生作答结果
@@ -1621,8 +1621,8 @@ namespace TEAMModelOS.Controllers
             if (!request.TryGetProperty("ownerId", out JsonElement ownerId)) return BadRequest();
             if (!request.TryGetProperty("paperId", out JsonElement paperId)) return BadRequest();
             if (!request.TryGetProperty("studentId", out JsonElement studentId)) return BadRequest();
-
-            var (userId, name, picture, school) = HttpContext.GetAuthTokenInfo();
+            if (!request.TryGetProperty("studentName", out JsonElement studentName)) return BadRequest();
+           // var (userId, name, picture, school) = HttpContext.GetAuthTokenInfo();
             try
             {
                 var client = _azureCosmos.GetCosmosClient();
@@ -1823,7 +1823,7 @@ namespace TEAMModelOS.Controllers
                     if (!isAns) {
                         if (request.TryGetProperty("artId", out JsonElement artId) && request.TryGetProperty("quotaId", out JsonElement quotaId))
                         {
-                            await getArtInfoAsync(client, artId.GetString(), school, result.sum[newIndex], id.GetString(), subjectId.GetString(), quotaId.GetString(), userId, picture, name, userType, new List<string> { classId.GetString()});
+                            await getArtInfoAsync(client, artId.GetString(), $"{ownerId}", result.sum[newIndex], id.GetString(), subjectId.GetString(), quotaId.GetString(), $"{studentId}", null, $"{studentName}", userType, new List<string> { classId.GetString()});
                         };                  
                     }
                 }