Browse Source

Merge branch 'test' of http://110.188.24.182:10023/Fengwo/hotline into test

田爽 2 weeks ago
parent
commit
a6bdef14a5

+ 17 - 0
src/Hotline.Api/Controllers/Bigscreen/DataScreenController.cs

@@ -705,5 +705,22 @@ namespace Hotline.Api.Controllers.Bigscreen
                  .ToListAsync();
             return _mapper.Map<List<OrderSecondaryHandlingDto>>(quer);
         }
+
+        /// <summary>
+        /// 110来源工单
+        /// </summary>
+        /// <returns></returns>
+        [AllowAnonymous]
+        [HttpGet("order-soure-police-query")]
+        public async Task<List<OrderDto>> OrderSourePoliceDetailQuery()
+        {
+            var list = await _orderRepository
+               .Queryable(false, false, false)
+               .Where(x => x.Source==ESource.Police110 && x.CreationTime.Date == DateTime.Now.Date)
+               .OrderByDescending(x => x.CreationTime)
+               .Take(50)
+               .ToListAsync();
+            return _mapper.Map<List<OrderDto>>(list);
+        }
     }
 }

+ 20 - 1
src/Hotline.Api/Controllers/IPPbxController.cs

@@ -73,6 +73,7 @@ namespace Hotline.Api.Controllers
         private readonly ICallTelClient _callTelClient;
         private readonly IRepository<TelOperation> _telOperationRepository;
         private readonly IOptionsSnapshot<AppConfiguration> _appOptions;
+        private readonly IRepository<VoiceEvaluationButton> _voiceEvaluationButtonRepository;
 
         public IPPbxController(IMapper mapper, IUserDomainService userDomainService,
             ISessionContext sessionContext, IRepository<TrCallRecord> trCallRecordRepository,
@@ -92,7 +93,8 @@ namespace Hotline.Api.Controllers
             IIPPbxApplication iPPbxApplication,
             ICallTelClient callTelClient,
             IRepository<TelOperation> telOperationRepository,
-           IOptionsSnapshot<AppConfiguration> appOptions)
+           IOptionsSnapshot<AppConfiguration> appOptions,
+           IRepository<VoiceEvaluationButton> voiceEvaluationButtonRepository)
         {
             _mapper = mapper;
             _userDomainService = userDomainService;
@@ -122,6 +124,7 @@ namespace Hotline.Api.Controllers
             _callTelClient = callTelClient;
             _telOperationRepository = telOperationRepository;
             _appOptions = appOptions;
+            _voiceEvaluationButtonRepository = voiceEvaluationButtonRepository;
         }
 
         #region 添添呼
@@ -692,6 +695,22 @@ namespace Hotline.Api.Controllers
 
         }
 
+        /// <summary>
+        /// 兴唐语音评价接口
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [AllowAnonymous]
+        [StaffNoSessionContextFilter()]
+        [HttpPost("voiceevaluation/button")]
+        [LogFilterAlpha("兴唐语音评价通知")]
+        public async Task VoiceEvaluationButton(VoiceEvaluationButtonDto dto)
+        {
+            var data = _mapper.Map<VoiceEvaluationButton>(dto);
+            await _voiceEvaluationButtonRepository.AddAsync(data, HttpContext.RequestAborted);
+
+        }
+
         #endregion
 
         #region 通话记录(对内)

+ 2 - 0
src/Hotline.Api/Realtimes/RealtimeMethods.cs

@@ -75,6 +75,8 @@
         public static string OrderHandlingDetail = "OrderHandlingDetail";
 
         public static string OrderSecondaryHandlingDetail = "OrderSecondaryHandlingDetail";
+
+        public static string OrderSourePoliceDetail = "OrderSourePoliceDetail";
         #endregion
 
         #region 司法大屏

+ 12 - 3
src/Hotline.Api/Realtimes/RealtimeService.cs

@@ -129,7 +129,7 @@ public class RealtimeService : IRealtimeService, IScopeDependency
     /// <param name="id"></param>
     /// <param name="cancellationToken"></param>
     /// <returns></returns>
-    public async Task CircularRecoordAsync(string id,string title,string sendContent, CancellationToken cancellationToken)
+    public async Task CircularRecoordAsync(string id, string title, string sendContent, CancellationToken cancellationToken)
     {
         //获取所有需要推送的
         var record = await _circularRecordRepository.Queryable().Where(x => x.Id == id).FirstAsync();
@@ -138,7 +138,7 @@ public class RealtimeService : IRealtimeService, IScopeDependency
         {
             //个人数量
             await SendToUserAsync(record.UserId, RealtimeMethods.CircularRecord,
-                new CircularRecoordDto() { CircularType = Share.Enums.Article.ECircularType.Person, Count = record.RecordCount,SendContent = sendContent,Title= title },
+                new CircularRecoordDto() { CircularType = Share.Enums.Article.ECircularType.Person, Count = record.RecordCount, SendContent = sendContent, Title = title },
                 cancellationToken);
         }
         else
@@ -152,7 +152,7 @@ public class RealtimeService : IRealtimeService, IScopeDependency
                     //数量
                     await SendToUserAsync(user.Id, RealtimeMethods.CircularRecord,
                         new CircularRecoordDto()
-                        { CircularType = Share.Enums.Article.ECircularType.Org, Count = record.RecordCount,SendContent = sendContent,Title = title },
+                        { CircularType = Share.Enums.Article.ECircularType.Org, Count = record.RecordCount, SendContent = sendContent, Title = title },
                         cancellationToken);
 
                 }
@@ -228,6 +228,15 @@ public class RealtimeService : IRealtimeService, IScopeDependency
     /// <returns></returns>
     public Task OrderSecondaryHandlingDetailAsync(object obj, CancellationToken cancellationToken) =>
         SendToGroupAsync(RealtimeGroupNames.BigDataScreen, RealtimeMethods.OrderSecondaryHandlingDetail, obj, cancellationToken);
+
+    /// <summary>
+    /// 推送110来源工单
+    /// </summary>
+    /// <param name="obj"></param>
+    /// <param name="cancellationToken"></param>
+    /// <returns></returns>
+    public Task OrderSourePoliceDetailAsync(object obj, CancellationToken cancellationToken) =>
+        SendToGroupAsync(RealtimeGroupNames.BigDataScreen, RealtimeMethods.OrderSourePoliceDetail, obj, cancellationToken);
     #endregion
 
     /// <summary>

+ 15 - 0
src/Hotline.Application/Bigscreen/DataScreenRefreshService.cs

@@ -127,6 +127,21 @@ namespace Hotline.Application.Bigscreen
                 }
                 catch { }
 
+                try
+                {
+                    var list = await _orderRepository
+                       .Queryable(false, false, false)
+                       .Where(x => x.Source == ESource.Police110 && x.StartTime.Value.Date == DateTime.Now.Date)
+                       .OrderByDescending(x => x.StartTime)
+                       .Take(50)
+                       .ToListAsync();
+                    var orderlist = _mapper.Map<List<OrderDto>>(list);
+
+                    await realtimeService.OrderSourePoliceDetailAsync(orderlist, stoppingToken);
+                }
+                catch { }
+
+
                 await Task.Delay(times);
             }
         }

+ 62 - 20
src/Hotline.Application/Exam/Service/ExamManages/UserExamService.cs

@@ -42,6 +42,7 @@ using DocumentFormat.OpenXml.Office2013.Excel;
 using Hotline.Share.Enums.Exams;
 using DocumentFormat.OpenXml.Wordprocessing;
 using Hotline.Repository.SqlSugar.Exam.Repositories.ExamManages;
+using Hotline.Exams.Questions;
 
 namespace Hotline.Application.Exam.Service.ExamManages
 {
@@ -90,17 +91,24 @@ namespace Hotline.Application.Exam.Service.ExamManages
         public async Task<ExamQuestionDto> GetExamQuestionDto(ExamQuestionRequest examQuestionRequest)
         {
             var expression = examQuestionRequest.GetExpression();
-            var quesetion = await new ExamRepository<Exams.ExamManages.ExamQuestionBak>(_uow, _dataPermissionFilterBuilder, _serviceProvider).Queryable().Where(expression).FirstAsync();
+            var question = await new ExamRepository<Exams.ExamManages.ExamQuestionBak>(_uow, _dataPermissionFilterBuilder, _serviceProvider).Queryable().Where(expression).FirstAsync();
 
-            if (quesetion != null)
+            if (question != null)
             {
-                var examQuestionDto = _mapper.Map<ExamQuestionDto>(quesetion);
+                var examQuestionDto = _mapper.Map<ExamQuestionDto>(question);
+
+                var questionScore = await new ExamRepository<ExamQuestionScoreBak>(_uow, _dataPermissionFilterBuilder, _serviceProvider).GetAsync(x=>x.QuestionType == question.QuestionType && x.ExamManageId == examQuestionRequest.ExamId);
+
+                if (questionScore != null)
+                {
+                    examQuestionDto.Score = questionScore.Score;
+                }               
 
                 if (examQuestionDto.QuestionType.CheckSelectType())
                 {
-                    var questionOptions = await new ExamRepository<ExamQuestionOptionsBak>(_uow, _dataPermissionFilterBuilder, _serviceProvider).Queryable().Where(x => x.ExamQuestionId == quesetion.Id).ToListAsync();
+                    var questionOptions = await new ExamRepository<ExamQuestionOptionsBak>(_uow, _dataPermissionFilterBuilder, _serviceProvider).Queryable().Where(x => x.ExamQuestionId == question.Id).ToListAsync();
 
-                    List<ExamUserExamItemOptions> userItemItemOptions = await GetUserExteamItemOptios(quesetion);
+                    List<ExamUserExamItemOptions> userItemItemOptions = await GetUserExteamItemOptios(question);
 
                     if (questionOptions != null)
                     {
@@ -143,7 +151,7 @@ namespace Hotline.Application.Exam.Service.ExamManages
             var userExamItemRepository = new ExamRepository<ExamUserExamItem>(_uow, _dataPermissionFilterBuilder, _serviceProvider);
 
             var userExamItemTable = userExamItemRepository.Queryable().Where(i => i.QuestionId == examQuestionRequest.QuestionId);
-            var userExamTable = _repository.Queryable().Where(u => u.UserId == _sessionContext.UserId);
+            var userExamTable = _repository.Queryable().Where(u => u.UserId == _sessionContext.UserId && u.ExamId == examQuestionRequest.ExamId);
             var examAnswerTable = examAnswerRepository.Queryable();
 
             var examAnswers = await examAnswerTable.InnerJoin(userExamItemTable, (e, i) => e.UserExamItemId == i.Id)
@@ -163,7 +171,7 @@ namespace Hotline.Application.Exam.Service.ExamManages
             var userItemItemOptions = await userExamItemOptionsTable
                 .InnerJoin(userExamItemTable, (o, u) => o.UserExamItemId == u.Id)
                 .InnerJoin(userExamTable, (o, u, e) => u.UserExamId == e.Id)
-                .Where((o, u, e) => u.QuestionId == quesetion.QuestionId && e.UserId == _sessionContext.UserId).
+                .Where((o, u, e) => u.QuestionId == quesetion.QuestionId && e.UserId == _sessionContext.UserId && e.ExamStatus == EExamStatus.Executing).
                 Select((o, u, e) => o).ToListAsync();
             return userItemItemOptions;
         }
@@ -296,9 +304,9 @@ namespace Hotline.Application.Exam.Service.ExamManages
             }
         }
 
-        public async Task<StartExamViewResponse> ExamAsync(UpdateUserExamItemDto addUserExamItemDto, CancellationToken cancellationToken)
+        public async Task<StartExamViewResponse> ExamAsync(UpdateUserExamItemDto updateUserExamItemDto, CancellationToken cancellationToken)
         {
-            var userExam = await new ExamRepository<ExamUserExam>(_uow, _dataPermissionFilterBuilder, _serviceProvider).Queryable().Where(x => x.Id == addUserExamItemDto.UserExamId).FirstAsync();
+            var userExam = await new ExamRepository<ExamUserExam>(_uow, _dataPermissionFilterBuilder, _serviceProvider).Queryable().Where(x => x.Id == updateUserExamItemDto.UserExamId).FirstAsync();
 
             var startExamViewResponse = await CheckExamValid(userExam, cancellationToken);
 
@@ -309,18 +317,19 @@ namespace Hotline.Application.Exam.Service.ExamManages
                 return startExamViewResponse;
             }
 
-            var hasUserExamItem = _userExamItemRepository.Queryable().Where(x => x.UserExamId == addUserExamItemDto.UserExamId && x.QuestionId == addUserExamItemDto.QuestionId).Any();
+            var hasUserExamItem = _userExamItemRepository.Queryable().Where(x => x.UserExamId == updateUserExamItemDto.UserExamId && x.QuestionId == updateUserExamItemDto.QuestionId).Any();
 
             if (!hasUserExamItem)
             {
+                var addUserExamItemDto = _mapper.Map<UpdateUserExamItemDto, AddUserExamItemDto>(updateUserExamItemDto);
                 await AddExamAsync(_userExamItemRepository, addUserExamItemDto, cancellationToken);
             }
             else
             {
-                await UpdateExamAsync(_userExamItemRepository, addUserExamItemDto, cancellationToken);
+                await UpdateExamAsync(_userExamItemRepository, updateUserExamItemDto, cancellationToken);
             }
 
-            await CalcuteExamItemScore(_userExamItemRepository, addUserExamItemDto, cancellationToken);
+            await CalcuteExamItemScore(_userExamItemRepository, updateUserExamItemDto, cancellationToken);
 
             return startExamViewResponse;
         }
@@ -343,7 +352,7 @@ namespace Hotline.Application.Exam.Service.ExamManages
                 IsJoin = false
             };
 
-            if (userExam.StartTime == null)
+            if (userExam.StartTime == null || userExam.ExamStatus == EExamStatus.Complete)
                 userExam.StartTime = DateTime.Now;
 
             var startExamViewResponse = await CheckExamValid(userExam, cancellationToken);
@@ -364,6 +373,11 @@ namespace Hotline.Application.Exam.Service.ExamManages
                 await _repository.UpdateWithValidateAsync(userExam, cancellationToken);
 
             }
+            // TODO: 删除之前选项和答案
+            else if(userExam.ExamStatus == EExamStatus.Complete)
+            {
+                await ReExam(userExam, cancellationToken);
+            }
             var examManage = await _examManageRepository.GetAsync(x => x.Id == userExam.ExamId);
 
             return new StartExamViewResponse
@@ -374,6 +388,29 @@ namespace Hotline.Application.Exam.Service.ExamManages
             };
         }
 
+        /// <summary>
+        /// 重考
+        /// </summary>
+        /// <param name="userExam"></param>
+        /// <param name="cancellationToken"></param>
+        /// <returns></returns>
+        private async Task ReExam(ExamUserExam userExam, CancellationToken cancellationToken)
+        {
+            //新增重考记录
+            var reExamUserExam = _mapper.Map<ExamUserExam, ExamUserExam>(userExam);
+
+            reExamUserExam.IsCheck = false;
+            reExamUserExam.IsSubmit = false;
+            reExamUserExam.IsSuccess = false;
+            reExamUserExam.IsReExam = true;
+            reExamUserExam.Score = 0;
+            reExamUserExam.ExamStatus = EExamStatus.NoStart;
+
+            reExamUserExam.ToInsert(_sessionContext);
+
+            await _repository.AddWithValidateAsync(reExamUserExam, cancellationToken);
+        }
+
         private async Task<StartExamViewResponse> CheckExamValid(ExamUserExam examUserExam, CancellationToken cancellationToken)
         {
 
@@ -413,7 +450,7 @@ namespace Hotline.Application.Exam.Service.ExamManages
             }
 
             startExamViewResponse.TimeSpan = examManage?.TimeSpan ?? 0;
-            startExamViewResponse.StartTime = examManage?.StartTime;
+            startExamViewResponse.StartTime = examUserExam?.StartTime;
             return startExamViewResponse;
         }
 
@@ -591,7 +628,7 @@ namespace Hotline.Application.Exam.Service.ExamManages
                 .LeftJoin(userExamItemOptionTable, (e, i, q, o) => i.Id == o.UserExamItemId)
                 .LeftJoin(quesitonOptionTable, (e, i, q, o, qo) => o.QuestionOptionId == qo.Id)
                 .LeftJoin(examAnswerTable, (e, i, q, o, qo, a) => i.Id == a.UserExamItemId)
-                .LeftJoin(testPaperItemAnswerTable, (e, i, q, o, qo, a, ta) => ta.QuestionId == qo.QuestionId)
+                .LeftJoin(testPaperItemAnswerTable, (e, i, q, o, qo, a, ta) => ta.QuestionId == q.QuestionId)
                 .InnerJoin(questionScoreTable, (e, i, q, o, qo, a, ta, s) => q.QuestionType == s.QuestionType && e.ExamId == s.ExamManageId)
             .Where((e, i, q, o, qo, a, ta, s) => !(q.QuestionType == EQuestionType.Single || q.QuestionType == EQuestionType.Multi || q.QuestionType == EQuestionType.Judge))
             .Select(
@@ -718,7 +755,7 @@ namespace Hotline.Application.Exam.Service.ExamManages
         public async Task<GradingResultPageViewResponse> GetGradingResultPagedList(GradingPagedRequest gradingPagedRequest)
         {
             // 只要有阅卷记录就在已阅卷列表中,已阅卷和未阅卷会有重复数据,只有所有记录都已阅卷才会从未阅卷列表中排除
-            var userExamTable = _repository.Queryable().WhereIF(gradingPagedRequest.IsCheck != null, x => x.IsCheck == gradingPagedRequest.IsCheck && x.IsSubmit);
+            var userExamTable = _repository.Queryable().Where(x => x.IsSubmit);
 
             var examManageTable = new ExamRepository<ExamManage>(_uow, _dataPermissionFilterBuilder, _serviceProvider).Queryable();
 
@@ -737,10 +774,12 @@ namespace Hotline.Application.Exam.Service.ExamManages
                 TotalScore = e.TotalScore,
                 ExamName = e.Name,
                 ExamCode = e.Code,
-                IsCheck = SqlFunc.Subqueryable<ExamUserExam>().Where(x => x.ExamId == e.Id && x.IsCheck).Any(),
+                IsCheck = SqlFunc.Subqueryable<ExamUserExam>().Where(x => x.ExamId == e.Id && !x.IsCheck).Count() <= 0,
                 Id = e.Id,
                 Remark = e.Remark
-            });
+            }).MergeTable();
+
+            queryable = queryable.WhereIF(gradingPagedRequest.IsCheck != null, x => x.IsCheck == gradingPagedRequest.IsCheck);
 
             var list = await queryable.ToPageListAsync(gradingPagedRequest.PageIndex, gradingPagedRequest.PageSize);
             var total = await queryable.CountAsync();
@@ -998,6 +1037,8 @@ namespace Hotline.Application.Exam.Service.ExamManages
 
             await _examAnswerRepository.ValidateAddAsync(examAnswer, cancellationToken);
 
+            examAnswers.Add(examAnswer);
+
             return examAnswers;
         }
 
@@ -1259,12 +1300,13 @@ namespace Hotline.Application.Exam.Service.ExamManages
                 Status = u.Status,
                 SortIndex = u.SortIndex,
                 ExamStatus = u.ExamStatus,
-                IsSuccess = u.IsSuccess,
+                IsSuccess = u.IsCheck ? u.IsSuccess : null,
                 EndTime = e.EndTime,
                 StartTime = e.StartTime,
                 TimeSpan = e.TimeSpan,
                 ExamType = e.ExamType,
-                ExamId = e.Id
+                ExamId = e.Id,
+                IsCheck = u.IsCheck
             });
             return queryable;
         }

+ 8 - 17
src/Hotline.Application/Exam/Service/Practices/PracticeService.cs

@@ -40,6 +40,7 @@ using Hotline.Repository.SqlSugar.Exam.Service;
 using Hotline.Repository.SqlSugar.Exam.Extensions;
 using Exam.Infrastructure.Extensions;
 using Consul;
+using J2N.Collections.Generic.Extensions;
 
 namespace Hotline.Application.Exam.Service.Practices
 {
@@ -195,9 +196,9 @@ namespace Hotline.Application.Exam.Service.Practices
             var result = queryable.GroupBy(x => x.QuestionType).Select(m => new PracticeQuestionViewResponse
             {
                 QuestionType = m.Key,
-                Questions = m.Select(n => new SimpleViewResponse
+                Questions = m.GroupBy(g=>g.Id).Select(n => new SimpleViewResponse
                 {
-                    Id = n.Id
+                    Id = n.Key
                 }).ToList()
             }).ToList();
 
@@ -222,19 +223,7 @@ namespace Hotline.Application.Exam.Service.Practices
 
             if (viewPracticeQuestionDto != null)
             {
-                var practiceResultItemTable = _practiceResultItemRepository.Queryable();
-                var practiceResultTable = _practiceResultRepository.Queryable().Where(x => x.PracticeQuestionId == practiceQuestionRequest.PracticeQuestionId);
-
-                var practiceResultItems = await practiceResultTable.InnerJoin(practiceResultItemTable, (r, i) => r.Id == i.PracticeResultId).Select((r, i) => i).ToListAsync();
-
-                var practiceAnswers = await _practiceAnswerRepository.Queryable().Where(x => x.PracticeQuestionId == practiceQuestionRequest.PracticeQuestionId).ToListAsync();
-
-                viewPracticeQuestionDto.PracticeQuestionOptionsDtos.ForEach(item =>
-                {
-                    item.IsSelected = practiceResultItems.Any(x => x.QuestionOptionId == item.QuestionOptionId);
-
-                    item.IsAnswer = practiceAnswers.Any(x => x.PracticeOptionId == item.QuestionOptionId);
-                });
+               
 
                 viewPracticeQuestionDto.Answer = string.Join(",", viewPracticeQuestionDto.PracticeQuestionOptionsDtos.Where(x => x.IsAnswer).Select(m => m.Label));
             }
@@ -336,7 +325,7 @@ namespace Hotline.Application.Exam.Service.Practices
             if (practiceResultItems != null)
             {
                 practiceQuestionDto.PracticeQuestionOptionsDtos.ForEach(item =>
-                {
+                {        
                     item.IsSelected = practiceResultItems.Any(x => x.QuestionOptionId == item.QuestionOptionId);
                 });
             }
@@ -411,12 +400,14 @@ namespace Hotline.Application.Exam.Service.Practices
             var practiceQuestionOptionTable = _practiceQuestionOptionsRepository.Queryable();
 
             var queryResult = practiceQuestionTable.InnerJoin(practiceQuestionOptionTable, (p, o) => p.QuestionId == o.QuestionId && p.Id == o.PracticeQuestionId)
+                .OrderBy((p,o)=>o.Label)
                 .Select((p, o) => new PracticeQuestionOptionsDto
                 {
                     Content = o.Content,
                     Label = o.Label,
                     PracticeQuestionId = o.PracticeQuestionId,
-                    QuestionOptionId = o.QuestionOptionId
+                    QuestionOptionId = o.QuestionOptionId,
+                    IsAnswer = o.IsAnswer
                 });
 
             return await queryResult.Distinct().ToListAsync();

+ 99 - 84
src/Hotline.Application/Exam/Service/Questions/QuestionService.cs

@@ -38,10 +38,11 @@ using Hotline.Repository.SqlSugar.Exam.Repositories;
 using Hotline.Share.Dtos.TestPapers;
 using Hotline.Repository.SqlSugar.Exam.Service;
 using Hotline.Repository.SqlSugar.Exam.Extensions;
+using XF.Domain.Exceptions;
 
 namespace Hotline.Application.Exam.Service.Questions
 {
-    public class QuestionService : ApiService<ExamQuestion, AddQuestionDto,UpdateQuestionDto, HotlineDbContext>, IQuestionService, IScopeDependency
+    public class QuestionService : ApiService<ExamQuestion, AddQuestionDto, UpdateQuestionDto, HotlineDbContext>, IQuestionService, IScopeDependency
     {
         #region ctor
         private readonly IQuestionRepository _repository;
@@ -65,7 +66,7 @@ namespace Hotline.Application.Exam.Service.Questions
             IDataPermissionFilterBuilder dataPermissionFilterBuilder, IServiceProvider serviceProvider,
             IMapper mapper,
             ISessionContext sessionContext
-            ) : base(repository,mapper)
+            ) : base(repository, mapper)
         {
             _repository = repository;
             _questionTagRepository = questionTagRepository;
@@ -98,7 +99,7 @@ namespace Hotline.Application.Exam.Service.Questions
                 questionDto.QuestionOptionsDtos = await GetQuestionOptions(entityQueryRequest);
 
                 questionDto.QuestionSourcewareDtos = await GetQuestionSourcewares(entityQueryRequest);
-            }            
+            }
 
             return questionDto;
         }
@@ -116,7 +117,7 @@ namespace Hotline.Application.Exam.Service.Questions
                 DifficultyLevel = q.DifficultyLevel,
                 Title = q.Title,
                 QuestionType = q.QuestionType,
-                Id= q.Id
+                Id = q.Id
             });
 
             var result = await queryable.ToListAsync();
@@ -146,8 +147,8 @@ namespace Hotline.Application.Exam.Service.Questions
             var questionKnowladgeTable = new ExamRepository<ExamQuestionKnowladge>(_uow, _dataPermissionFilterBuilder, _serviceProvider).Queryable().Where(questionKnowladgeExpression);
 
 
-            var queryable = questionTable.InnerJoin(questionTagTable, (s, d) => s.Id == d.QuestionId).InnerJoin(examTagTable, (s,d, t) => d.TagId == t.Id)
-                //.InnerJoin(questionKnowladgeTable, (s, d, t,k) => s.Id == k.QuestionId)
+            var queryable = questionTable.InnerJoin(questionTagTable, (s, d) => s.Id == d.QuestionId).InnerJoin(examTagTable, (s, d, t) => d.TagId == t.Id)
+            //.InnerJoin(questionKnowladgeTable, (s, d, t,k) => s.Id == k.QuestionId)
             .Distinct()
             .Select((s, d, t) => new QuestionViewResponse
             {
@@ -155,7 +156,7 @@ namespace Hotline.Application.Exam.Service.Questions
                 FormalEnable = s.FormalEnable,
                 SimulateEnable = s.SimulateEnable,
                 SortIndex = s.SortIndex,
-                Status =s.Status,
+                Status = s.Status,
                 QuestionType = s.QuestionType,
                 Title = s.Title,
                 Id = s.Id
@@ -163,20 +164,20 @@ namespace Hotline.Application.Exam.Service.Questions
 
             if (queryRequest.KnowladgeIds != null && queryRequest.KnowladgeIds.Any())
             {
-                    queryable = questionTable.InnerJoin(questionTagTable, (s, d) => s.Id == d.QuestionId).InnerJoin(examTagTable, (s, d, t) => d.TagId == t.Id)
-                    .InnerJoin(questionKnowladgeTable, (s, d, t, k) => s.Id == k.QuestionId)
-                .Distinct()
-                .Select((s, d, t) => new QuestionViewResponse
-                {
-                    DifficultyLevel = s.DifficultyLevel,
-                    FormalEnable = s.FormalEnable,
-                    SimulateEnable = s.SimulateEnable,
-                    SortIndex = s.SortIndex,
-                    Status = s.Status,
-                    QuestionType = s.QuestionType,
-                    Title = s.Title,
-                    Id = s.Id
-                });
+                queryable = questionTable.InnerJoin(questionTagTable, (s, d) => s.Id == d.QuestionId).InnerJoin(examTagTable, (s, d, t) => d.TagId == t.Id)
+                .InnerJoin(questionKnowladgeTable, (s, d, t, k) => s.Id == k.QuestionId)
+            .Distinct()
+            .Select((s, d, t) => new QuestionViewResponse
+            {
+                DifficultyLevel = s.DifficultyLevel,
+                FormalEnable = s.FormalEnable,
+                SimulateEnable = s.SimulateEnable,
+                SortIndex = s.SortIndex,
+                Status = s.Status,
+                QuestionType = s.QuestionType,
+                Title = s.Title,
+                Id = s.Id
+            });
             }
 
             var list = await queryable.ToPageListAsync(queryRequest.PageIndex, queryRequest.PageSize);
@@ -212,7 +213,7 @@ namespace Hotline.Application.Exam.Service.Questions
         {
             base.StartTran();
 
-            base.Entity= await AddQuestion(actionRequest, cancellationToken);
+            base.Entity = await AddQuestion(actionRequest, cancellationToken);
 
             await base.Complete(base.Entity, OperationConstant.Create);
 
@@ -229,17 +230,17 @@ namespace Hotline.Application.Exam.Service.Questions
 
             _addQuestionDto = _mapper.Map<AddQuestionDto>(actionRequest);
 
-            base.Entity.QuestionTags =  await ModifyQuestionTags(actionRequest, cancellationToken);
+            base.Entity.QuestionTags = await ModifyQuestionTags(actionRequest, cancellationToken);
 
-            base.Entity.QuestionOptionses =  await ModifyQuestionOptions(actionRequest, cancellationToken);
+            base.Entity.QuestionOptionses = await ModifyQuestionOptions(actionRequest, cancellationToken);
 
             base.Entity.QuestionAnswers = await ModifyQuestionAnswer(actionRequest, cancellationToken);
 
-            base.Entity.QuestionKnowladges =  await ModifyKnowladges(actionRequest, cancellationToken);
+            base.Entity.QuestionKnowladges = await ModifyKnowladges(actionRequest, cancellationToken);
 
-            base.Entity.QuestionSourcewares =  await ModifySourcewares(actionRequest, cancellationToken);
+            base.Entity.QuestionSourcewares = await ModifySourcewares(actionRequest, cancellationToken);
 
-           await base.Complete(base.Entity,OperationConstant.Update);
+            await base.Complete(base.Entity, OperationConstant.Update);
         }
 
         public override async Task DeleteAsync(EntityQueryRequest entityQueryRequest, CancellationToken cancellationToken)
@@ -284,30 +285,39 @@ namespace Hotline.Application.Exam.Service.Questions
 
         public async Task ImportExcel(IFormFile files, CancellationToken cancellationToken)
         {
-            using (var stream = files.OpenReadStream())
+            var stream = files.OpenReadStream();
+            var contents = ExcelHelper.Read(stream, true);
+
+            var questions = new List<ExamQuestion>();
+            var examTags = await GetExamTags(contents);
+
+            base.StartTran();
+
+            var isEmptyValue = false;
+
+            contents.ForEach(async item =>
             {
-                var contents = ExcelHelper.Read(stream, true);
+                var value = (item as ExpandoObject).GetValueOrDefault(ExamSystemConstants.ColumnNames[16]);
 
-                var questions = new List<ExamQuestion>();
-                var examTags = await GetExamTags(contents);
+                if (value == null)
+                    return;
 
-                base.StartTran();
-                contents.ForEach(async item =>
-                {
-                    var questionDto = BuildQuestion(item as ExpandoObject, examTags);
+                var questionDto = BuildQuestion(item as ExpandoObject, examTags);
 
-                    var question = await AddQuestion(questionDto, cancellationToken);
-                    if (question != null)
-                        questions.Add(question);
-                });
+                var question = await AddQuestion(questionDto, cancellationToken);
+                if (question != null)
+                    questions.Add(question);
+            });
 
-                await _repository.AddNav(questions)
-                            .Include(x => x.QuestionTags)
-                            .Include(x => x.QuestionOptionses)
-                            .Include(x => x.QuestionSourcewares)
-                            .Include(x => x.QuestionAnswers)
-                            .Include(x => x.QuestionKnowladges).ExecuteCommandAsync();
-            }
+            if(isEmptyValue) throw new Exception("答案不能为空");
+
+            await _repository.AddNav(questions)
+                        .Include(x => x.QuestionTags)
+                        .Include(x => x.QuestionOptionses)
+                        .Include(x => x.QuestionSourcewares)
+                        .Include(x => x.QuestionAnswers)
+                        .Include(x => x.QuestionKnowladges).ExecuteCommandAsync();
+            stream.Close();            
         }
 
         private async Task<List<ExamTag>> GetExamTags(List<object> contents)
@@ -331,7 +341,7 @@ namespace Hotline.Application.Exam.Service.Questions
                             tagNames.Add(name);
                         }
                     });
-                    
+
                 }
             });
 
@@ -342,37 +352,42 @@ namespace Hotline.Application.Exam.Service.Questions
             return examTags;
         }
 
-        private AddQuestionDto BuildQuestion(ExpandoObject item,List<ExamTag> examTags)
+        private AddQuestionDto BuildQuestion(ExpandoObject item, List<ExamTag> examTags)
         {
-            if (item != null) {
+            if (item != null)
+            {
                 var options = new List<string>();
 
-                ResolveOptions(options,item,6,15);
+                ResolveOptions(options, item, 6, 15);
 
-                var answer = item.GetValueOrDefault(ExamSystemConstants.ColumnNames[16]).ToString();
+                var value = item.GetValueOrDefault(ExamSystemConstants.ColumnNames[16]);
 
-                var questionDto = new AddQuestionDto
-                {
-                    Title = item.GetValueOrDefault(ExamSystemConstants.ColumnNames[2]) != null ? item.GetValueOrDefault(ExamSystemConstants.ColumnNames[5]).ToString() : string.Empty,
-                    QuestionTagDtos = BuildQuestionTags(item.GetValueOrDefault(ExamSystemConstants.ColumnNames[1]).ToString(), examTags),
-                    DifficultyLevel = item.GetValueOrDefault(ExamSystemConstants.ColumnNames[2]).ToString().ToEnumByDesc<EDifficultyLevel>(),
-                    QuestionType = item.GetValueOrDefault(ExamSystemConstants.ColumnNames[0]).ToString().ToEnumByDesc<EQuestionType>(),
-                    FormalEnable = item.GetValueOrDefault(ExamSystemConstants.ColumnNames[4]).ToString().ToEnumByDesc<ECheck>() == ECheck.Yes,
-                    SimulateEnable = item.GetValueOrDefault(ExamSystemConstants.ColumnNames[5]).ToString().ToEnumByDesc<ECheck>() == ECheck.Yes,                   
-                };
-                questionDto.QuestionAnswerDto = BuildQuestionAnswer(answer, questionDto.QuestionType);
+                if (value != null) {
+                    var answer = value.ToString();
 
-                questionDto.QuestionOptionsDtos = BuildQuestionOptions(options, questionDto.QuestionType, answer);
+                    var questionDto = new AddQuestionDto
+                    {
+                        Title = item.GetValueOrDefault(ExamSystemConstants.ColumnNames[2]) != null ? item.GetValueOrDefault(ExamSystemConstants.ColumnNames[5]).ToString() : string.Empty,
+                        QuestionTagDtos = BuildQuestionTags(item.GetValueOrDefault(ExamSystemConstants.ColumnNames[1]).ToString(), examTags),
+                        DifficultyLevel = item.GetValueOrDefault(ExamSystemConstants.ColumnNames[2]).ToString().ToEnumByDesc<EDifficultyLevel>(),
+                        QuestionType = item.GetValueOrDefault(ExamSystemConstants.ColumnNames[0]).ToString().ToEnumByDesc<EQuestionType>(),
+                        FormalEnable = item.GetValueOrDefault(ExamSystemConstants.ColumnNames[4]).ToString().ToEnumByDesc<ECheck>() == ECheck.Yes,
+                        SimulateEnable = item.GetValueOrDefault(ExamSystemConstants.ColumnNames[5]).ToString().ToEnumByDesc<ECheck>() == ECheck.Yes,
+                    };
+                    questionDto.QuestionAnswerDto = BuildQuestionAnswer(answer, questionDto.QuestionType);
 
+                    questionDto.QuestionOptionsDtos = BuildQuestionOptions(options, questionDto.QuestionType, answer);
 
+                    return questionDto;
+                }
 
-                return questionDto;
+               
             }
 
             return null;
         }
 
-        private List<AddQuestionOptionsDto> BuildQuestionOptions(List<string> options,EQuestionType questionType,string answer)
+        private List<AddQuestionOptionsDto> BuildQuestionOptions(List<string> options, EQuestionType questionType, string answer)
         {
             if (questionType.CheckSelectType())
             {
@@ -400,7 +415,7 @@ namespace Hotline.Application.Exam.Service.Questions
 
         private void ResolveOptions(List<string> options, ExpandoObject item, int start, int end)
         {
-            for(int i = start; i <= end; i++)
+            for (int i = start; i <= end; i++)
             {
                 var option = item.GetValueOrDefault(ExamSystemConstants.ColumnNames[i])?.ToString();
                 if (option.IsNotNullOrEmpty())
@@ -424,7 +439,7 @@ namespace Hotline.Application.Exam.Service.Questions
             return null;
         }
 
-        private List<AddQuestionTagDto> BuildQuestionTags(string content,List<ExamTag> examTags)
+        private List<AddQuestionTagDto> BuildQuestionTags(string content, List<ExamTag> examTags)
         {
             if (content.IsNotNullOrEmpty())
             {
@@ -442,7 +457,7 @@ namespace Hotline.Application.Exam.Service.Questions
                         {
                             TagId = examTag.Id
                         });
-                    }                    
+                    }
                 });
 
                 return addQuestionTagDtos;
@@ -454,7 +469,7 @@ namespace Hotline.Application.Exam.Service.Questions
 
         #region private method
 
-        private void ResolveQuestionId(AddQuestionDto actionRequest,string id) 
+        private void ResolveQuestionId(AddQuestionDto actionRequest, string id)
         {
             actionRequest.QuestionKnowladgeDtos?.ForEach(x => x.QuestionId = id);
 
@@ -514,7 +529,7 @@ namespace Hotline.Application.Exam.Service.Questions
 
             var questionOptionseDtos = actionRequest.QuestionOptionsDtos.Where(x => x.OperationStatus == EEOperationStatus.Add).ToList();
 
-            
+
             var questionOptionses = _mapper.Map<List<ExamQuestionOptions>>(questionOptionseDtos);
 
             var sortIndex = 0;
@@ -563,7 +578,7 @@ namespace Hotline.Application.Exam.Service.Questions
             var questionSourcewareDtos = actionRequest.QuestionSourcewareDtos.Where(x => x.OperationStatus == EEOperationStatus.Add).ToList();
 
             var questionSourcewares = _mapper.Map<List<ExamQuestionSourceware>>(questionSourcewareDtos);
-                        
+
             questionSourcewares.ToInsert(_sessionContext);
 
             await _questionSourcewareRepository.ValidateAddAsync(questionSourcewares, cancellationToken);
@@ -642,7 +657,7 @@ namespace Hotline.Application.Exam.Service.Questions
                     entity.QuestionId = actionRequest.Id;
                     entitys.Add(_mapper.Map<UpdateQuestionKnowladgeDto, ExamQuestionKnowladge>(questionKnowladgeDto, entity));
                 }
-               
+
             }
 
 
@@ -665,9 +680,9 @@ namespace Hotline.Application.Exam.Service.Questions
 
             var questionAnswer = await _questionAnswerRepository.GetAsync(actionRequest.QuestionAnswerDto.Id);
 
-           questionAnswer = _mapper.Map<UpdateQuestionAnswerDto, ExamQuestionAnswer>(actionRequest.QuestionAnswerDto, questionAnswer);
+            questionAnswer = _mapper.Map<UpdateQuestionAnswerDto, ExamQuestionAnswer>(actionRequest.QuestionAnswerDto, questionAnswer);
 
-            questionAnswer.QuestionId = actionRequest.Id;            
+            questionAnswer.QuestionId = actionRequest.Id;
 
             questionAnswer.ToUpdate(_sessionContext);
 
@@ -675,7 +690,7 @@ namespace Hotline.Application.Exam.Service.Questions
 
             return questionAnswer;
         }
-        private async Task<List<ExamQuestionOptions>> UpdateQuestionOptions(UpdateQuestionDto actionRequest,List<ExamQuestionOptions> all, CancellationToken cancellationToken)
+        private async Task<List<ExamQuestionOptions>> UpdateQuestionOptions(UpdateQuestionDto actionRequest, List<ExamQuestionOptions> all, CancellationToken cancellationToken)
         {
             if (actionRequest.QuestionOptionsDtos == null) return null;
 
@@ -690,14 +705,14 @@ namespace Hotline.Application.Exam.Service.Questions
             var questionOptionses = all.Where(x => ids.Contains(x.Id)).ToList();
 
             var entitys = new List<ExamQuestionOptions>();
-            foreach(var questionOptionsDto in questionOptionsDtos)
+            foreach (var questionOptionsDto in questionOptionsDtos)
             {
                 var entity = questionOptionses.FirstOrDefault(x => x.Id == questionOptionsDto.Id);
                 if (entity != null)
                 {
                     entity.QuestionId = actionRequest.Id;
                     entitys.Add(_mapper.Map<UpdateQuestionOptionsDto, ExamQuestionOptions>(questionOptionsDto, entity));
-                }               
+                }
             }
 
             //questionOptionses =  _mapper.Map<List<QuestionOptionsDto>, List<QuestionOptions>>(questionOptionsDtos,questionOptionses);
@@ -711,7 +726,7 @@ namespace Hotline.Application.Exam.Service.Questions
             return entitys;
         }
 
-        private async Task<List<ExamQuestionTag>> UpdateQuestionTags(UpdateQuestionDto actionRequest,List<ExamQuestionTag> all, CancellationToken cancellationToken)
+        private async Task<List<ExamQuestionTag>> UpdateQuestionTags(UpdateQuestionDto actionRequest, List<ExamQuestionTag> all, CancellationToken cancellationToken)
         {
             if (actionRequest.QuestionTagDtos == null) return null;
 
@@ -730,7 +745,7 @@ namespace Hotline.Application.Exam.Service.Questions
                     entity.QuestionId = actionRequest.Id;
                     entitys.Add(_mapper.Map<UpdateQuestionTagDto, ExamQuestionTag>(questionOptionsDto, entity));
                 }
-                
+
             }
             //questionTags = _mapper.Map<List<UpdateQuestionTagDto>, List<QuestionTag>>(questionTagDtos,questionTags);
 
@@ -822,7 +837,7 @@ namespace Hotline.Application.Exam.Service.Questions
 
             if (actionRequest.QuestionAnswerDto == null)
             {
-                if (all == null) 
+                if (all == null)
                     return null;
                 else
                 {
@@ -859,7 +874,7 @@ namespace Hotline.Application.Exam.Service.Questions
                 {
                     questionAnswers.AddRange(add);
                 }
-            }    
+            }
 
             if (actionRequest.QuestionAnswerDto != null && actionRequest.QuestionAnswerDto.OperationStatus == EEOperationStatus.Delete)
             {
@@ -881,7 +896,7 @@ namespace Hotline.Application.Exam.Service.Questions
 
             var questionOptions = new List<ExamQuestionOptions>();
 
-            var all = await _questionOptionRepository.Queryable().Where(x=>x.QuestionId == actionRequest.Id).ToListAsync();
+            var all = await _questionOptionRepository.Queryable().Where(x => x.QuestionId == actionRequest.Id).ToListAsync();
 
             actionRequest.QuestionOptionsDtos.ResolveOperationStatus(all);
 
@@ -927,7 +942,7 @@ namespace Hotline.Application.Exam.Service.Questions
             return questionTags;
         }
 
-        private EntityQueryRequest ResovleDelete<T>(IEnumerable<string> ids) where T:class,IEntity<string>,new()
+        private EntityQueryRequest ResovleDelete<T>(IEnumerable<string> ids) where T : class, IEntity<string>, new()
         {
             Expressionable<T> expressionable = ExpressionableUtility.CreateExpression<T>();
             expressionable.AndIF(ids.Any(), x => ids.Contains(x.Id));
@@ -976,7 +991,7 @@ namespace Hotline.Application.Exam.Service.Questions
 
         private async Task<QuestionAnswerDto> GetQuestionAnswer(EntityQueryRequest entityQueryRequest)
         {
-            var questionAnswer = await _questionAnswerRepository.GetAsync(x=>x.QuestionId == entityQueryRequest.Id);
+            var questionAnswer = await _questionAnswerRepository.GetAsync(x => x.QuestionId == entityQueryRequest.Id);
 
             var questionAnswerDto = _mapper.Map<QuestionAnswerDto>(questionAnswer);
 
@@ -1015,7 +1030,7 @@ namespace Hotline.Application.Exam.Service.Questions
             base.Entity.QuestionKnowladges = await AddKnowladges(actionRequest, cancellationToken);
 
             base.Entity.QuestionSourcewares = await AddSourcewares(actionRequest, cancellationToken);
-            
+
             return base.Entity;
         }
         #endregion

+ 1 - 1
src/Hotline.Application/Exam/Service/Trains/TrainRecordService.cs

@@ -274,7 +274,7 @@ namespace Hotline.Application.Exam.Service.Trains
                     SortIndex = r.SortIndex,
                     Status = r.Status,
                     TrainTime = p.TrainEndTime
-                });
+                }).MergeTable();
 
             var calcuteRateResult = new TrainResultRateViewResponse
             {

+ 1 - 1
src/Hotline.Application/Exam/Strategy/CheckValidateCountStrategy.cs

@@ -35,7 +35,7 @@ namespace Hotline.Application.Exam.Strategy
 
         public bool Validate()
         {
-            if (_count>=_validateCount)
+            if (_count>_validateCount)
             {
                 ErroMessage = "已超过可考次数,考试结束";
                 _current = this;

+ 1 - 1
src/Hotline.Application/Exam/Strategy/ExamStrategyProxy.cs

@@ -50,7 +50,7 @@ namespace Hotline.Application.Exam.Strategy
             var current = examStrategys.FirstOrDefault();
             if (current != null)
             {
-                for (var i = 1; i < examStrategys.Count - 1; i++)
+                for (var i = 1; i <= examStrategys.Count - 1; i++)
                 {
                     current.SetNext(examStrategys[i]);
                     current = examStrategys[i];

+ 2 - 3
src/Hotline.Application/Snapshot/BiSnapshotApplication.cs

@@ -568,7 +568,7 @@ public class BiSnapshotApplication : IBiSnapshotApplication, IScopeDependency
             CommunityCode = it.Id,
             CommunityName = it.Name,
             CommunityFullName = it.FullName,
-            SumCount = SqlFunc.Subqueryable<OrderSnapshot>().Where(snapshot => snapshot.CommunityFullName!.Contains(it.FullName)).Count(),
+            SumCount = SqlFunc.Subqueryable<OrderSnapshot>().Where(s => s.CommunityFullName!.Contains(it.FullName) && s.CreationTime >= dto.StartTime && s.CreationTime <= dto.EndTime).Count(),
             HasChild = SqlFunc.Subqueryable<CommunityInfo>().Where(c => c.ParentCode == it.Id).Any()
         });
 #if DEBUG
@@ -579,10 +579,9 @@ public class BiSnapshotApplication : IBiSnapshotApplication, IScopeDependency
 
     public ISugarQueryable<CommunityStatisticsDetailsOutDto> GetCommunityStatisticsDetails(CommunityStatisticsDetailsInDto dto)
     {
-        var communityFullName = _communityInfoRepository.Queryable().Where(c => c.Id == dto.CommunityCode).Select(c => c.FullName).First();
         var query = _orderSnapshotRepository.Queryable(includeDeleted: true)
             .LeftJoin<Order>((snapshot, order) => snapshot.Id == order.Id)
-            .Where((snapshot, order) => snapshot.CreationTime >= dto.StartTime && snapshot.CreationTime <= dto.EndTime && snapshot.CommunityFullName!.Contains(communityFullName))
+            .Where((snapshot, order) => snapshot.CreationTime >= dto.StartTime && snapshot.CreationTime <= dto.EndTime && snapshot.CommunityFullName!.Contains(dto.CommunityFullName))
             .WhereIF(dto.IndustryId.NotNullOrEmpty(), (snapshot, order) => snapshot.IndustryId == dto.IndustryId)
             .Select((snapshot, order) => new CommunityStatisticsDetailsOutDto(), true);
 #if DEBUG

+ 36 - 0
src/Hotline.Share/Dtos/CallCenter/VoiceEvaluationButtonDto.cs

@@ -0,0 +1,36 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Hotline.Share.Dtos.CallCenter
+{
+    public class VoiceEvaluationButtonDto
+    {
+        /// <summary>
+        /// 应用ID
+        /// </summary>
+        public string? AppId { get; set; }
+
+        /// <summary>
+        /// 应用密钥
+        /// </summary>
+        public string? AppSecret { get; set; }
+
+        /// <summary>
+        /// 用户标识,我们传过去的
+        /// </summary>
+        public string? CustomerId { get; set; }
+
+        /// <summary>
+        /// 用户电话号码
+        /// </summary>
+        public string? Called { get; set; }
+
+        /// <summary>
+        /// 按键内容
+        /// </summary>
+        public string? Content { get; set; }
+    }
+}

+ 4 - 10
src/Hotline.Share/Dtos/Practices/PracticeQuestionOptionsDto.cs

@@ -35,27 +35,21 @@ namespace Hotline.Share.Dtos.Practices
         /// </summary>
         [Description("是否选中")]
         public bool IsSelected { get; set; }
-    }
 
-    public class GradingPracticeQuestionOptionsDto : PracticeQuestionOptionsDto, IActionRequest
-    {
-     
         /// <summary>
         /// 是否答案
         /// </summary>
         [Description("是否答案")]
         public bool IsAnswer { get; set; }
+    }
+
+    public class GradingPracticeQuestionOptionsDto : PracticeQuestionOptionsDto, IActionRequest
+    {
 
         /// <summary>
         /// 主键
         /// </summary>
         [Description("主键")]
         public string Id { get; set; }
-
-        /// <summary>
-        /// 是否选中
-        /// </summary>
-        [Description("是否选中")]
-        public bool IsSelected { get; set; }
     }
 }

+ 1 - 1
src/Hotline.Share/Dtos/Snapshot/StatisticsDto.cs

@@ -2092,7 +2092,7 @@ public record CommunityStatisticsDetailsInDto : PagedRequest
     /// 社区全称
     /// </summary>
     [Required]
-    public string CommunityCode { get; set; }
+    public string CommunityFullName { get; set; }
 
     /// <summary>
     /// 行业Id

+ 7 - 1
src/Hotline.Share/ViewResponses/Exam/UserExamResultViewResponse.cs

@@ -72,7 +72,7 @@ namespace Exam.Share.ViewResponses.Exam
         /// 是否考试合格
         /// </summary>
         [Description("是否考试合格")]
-        public bool IsSuccess { get; set; }
+        public bool? IsSuccess { get; set; }
 
         /// <summary>
         /// 开始时间
@@ -115,6 +115,12 @@ namespace Exam.Share.ViewResponses.Exam
         /// </summary>
         [Description("考试ID")]
         public string ExamId { get; set; }
+
+        /// <summary>
+        /// 是否阅卷
+        /// </summary>
+        [Description("是否阅卷")]
+        public bool IsCheck { get; set; }
     }
 
     public class GradingResultViewResponse : IViewResponse

+ 23 - 0
src/Hotline/CallCenter/Calls/VoiceEvaluationButton.cs

@@ -0,0 +1,23 @@
+using XF.Domain.Repository;
+
+namespace Hotline.CallCenter.Calls
+{
+    public class VoiceEvaluationButton : CreationEntity
+    {
+        /// <summary>
+        /// 用户标识,我们传过去的
+        /// </summary>
+        public string? CustomerId { get; set; }
+
+        /// <summary>
+        /// 用户电话号码
+        /// </summary>
+        public string? Called { get; set; }
+
+        /// <summary>
+        /// 按键内容
+        /// </summary>
+        public string? Content { get; set; }
+
+    }
+}

+ 9 - 1
src/Hotline/Realtimes/IRealtimeService.cs

@@ -24,7 +24,7 @@ namespace Hotline.Realtimes
 
         #region 系统消息通知
 
-        Task CircularRecoordAsync(string id,string title,string sendContent, CancellationToken cancellationToken);
+        Task CircularRecoordAsync(string id, string title, string sendContent, CancellationToken cancellationToken);
 
         #endregion
 
@@ -61,6 +61,14 @@ namespace Hotline.Realtimes
         Task OrderHandlingDetailAsync(object obj, CancellationToken cancellationToken);
 
         Task OrderSecondaryHandlingDetailAsync(object obj, CancellationToken cancellationToken);
+
+        /// <summary>
+        /// 推送110来源工单
+        /// </summary>
+        /// <param name="obj"></param>
+        /// <param name="cancellationToken"></param>
+        /// <returns></returns>
+        Task OrderSourePoliceDetailAsync(object obj, CancellationToken cancellationToken);
         #endregion
 
         #region 司法大屏

+ 14 - 2
src/Hotline/Snapshot/Services/NotificationDomainService.cs

@@ -1,7 +1,9 @@
-using Hotline.Share.Dtos.Snapshot;
+using Exam.Infrastructure.Extensions;
+using Hotline.Share.Dtos.Snapshot;
 using Hotline.Snapshot.Contracts;
 using Hotline.Snapshot.IRepository;
 using Mapster;
+using Microsoft.Extensions.Logging;
 using System;
 using System.Collections.Generic;
 using System.Linq;
@@ -15,16 +17,26 @@ public class NotificationDomainService : INotificationDomainService, IScopeDepen
 {
     private readonly INotificationRepository _notificationRepository;
     private readonly INotificationReceiverRepository _notificationReceiverRepository;
+    private readonly ILogger<NotificationDomainService> _logger;
 
-    public NotificationDomainService(INotificationRepository notificationRepository, INotificationReceiverRepository notificationReceiverRepository)
+    public NotificationDomainService(INotificationRepository notificationRepository, INotificationReceiverRepository notificationReceiverRepository, ILogger<NotificationDomainService> logger)
     {
         _notificationRepository = notificationRepository;
         _notificationReceiverRepository = notificationReceiverRepository;
+        _logger = logger;
     }
 
     public async Task AddNotifyAsync(AddNotifyInDto inDto, CancellationToken token)
     {
         var entity = inDto.Adapt<Notification>();
+        var hasOld = await _notificationRepository.Queryable()
+            .Where(m => m.ExternalId == inDto.ExternalId)
+            .AnyAsync();
+        if (hasOld) 
+        {
+            _logger.LogInformation("通知消息已存在, 不重复通知用户. " + inDto.ToJson());
+            return;
+        }
         var id = await _notificationRepository.AddAsync(entity, token);
         var items = new List<NotificationReceiver>();
         foreach (var userId in inDto.UserIds)

+ 1 - 1
test/Hotline.Tests/Application/BiSnapshotApplicationTest.cs

@@ -60,7 +60,7 @@ public class BiSnapshotApplicationTest : TestBase
             if (hasChild.SumCount != 0)
             {
                 var inDto2 = inDto.Adapt<CommunityStatisticsDetailsInDto>();
-                inDto2.CommunityCode = hasChild.CommunityCode;
+                inDto2.CommunityFullName = hasChild.CommunityFullName;
                 var details = await _biSnapshotApplication.GetCommunityStatisticsDetails(inDto2).ToListAsync();
                 details.Count.ShouldBe(hasChild.SumCount);
             }