Browse Source

Merge branch 'test' into dev

田爽 6 months ago
parent
commit
c5a38697b7
58 changed files with 1768 additions and 804 deletions
  1. 29 7
      src/Hotline.Api/Controllers/AiController.cs
  2. 4 0
      src/Hotline.Api/Controllers/CommonPController.cs
  3. 98 34
      src/Hotline.Api/Controllers/KnowledgeController.cs
  4. 330 142
      src/Hotline.Api/Controllers/OrderController.cs
  5. 72 2
      src/Hotline.Api/Controllers/SysController.cs
  6. 17 1
      src/Hotline.Api/Controllers/UserController.cs
  7. 59 24
      src/Hotline.Api/Controllers/WebPortalController.cs
  8. 0 1
      src/Hotline.Application.Contracts/Validators/Order/OrderBatchFileDtoValidator.cs
  9. 26 1
      src/Hotline.Application.Tests/Application/DefaultCallApplicationTest.cs
  10. 3 1
      src/Hotline.Application.Tests/Application/SystemSettingCacheManagerTest.cs
  11. 35 4
      src/Hotline.Application.Tests/Controller/OrderControllerTest.cs
  12. 44 0
      src/Hotline.Application.Tests/Controller/UserControllerTest.cs
  13. 1 0
      src/Hotline.Application.Tests/Infrastructure/TestSettingConstants.cs
  14. 1 1
      src/Hotline.Application.Tests/TestBase.cs
  15. 27 1
      src/Hotline.Application/CallCenter/DefaultCallApplication.cs
  16. 7 0
      src/Hotline.Application/CallCenter/ICallApplication.cs
  17. 3 2
      src/Hotline.Application/CallCenter/XingTangCallApplication.cs
  18. 2 2
      src/Hotline.Application/FlowEngine/IWorkflowApplication.cs
  19. 10 6
      src/Hotline.Application/FlowEngine/WorkflowApplication.cs
  20. 3 2
      src/Hotline.Application/Handlers/FlowEngine/WorkflowEndHandler.cs
  21. 2 1
      src/Hotline.Application/Handlers/FlowEngine/WorkflowNextHandler.cs
  22. 2 1
      src/Hotline.Application/Handlers/FlowEngine/WorkflowPreviousHandler.cs
  23. 2 1
      src/Hotline.Application/Handlers/FlowEngine/WorkflowRecallHandler.cs
  24. 2 1
      src/Hotline.Application/Handlers/FlowEngine/WorkflowStartHandler.cs
  25. 26 4
      src/Hotline.Application/Handlers/Order/OrderRelateCallHandler.cs
  26. 15 5
      src/Hotline.Application/Knowledge/KnowApplication.cs
  27. 2 1
      src/Hotline.Application/Mappers/WorkflowMapperConfigs.cs
  28. 1 0
      src/Hotline.Application/Orders/IOrderApplication.cs
  29. 436 398
      src/Hotline.Application/Orders/OrderApplication.cs
  30. 5 3
      src/Hotline.Application/StatisticalReport/OrderReportApplication.cs
  31. 22 10
      src/Hotline.Application/Subscribers/DatasharingSubscriber.cs
  32. 7 1
      src/Hotline.Application/Systems/BaseDataApplication.cs
  33. 1 1
      src/Hotline.Repository.SqlSugar/Orders/OrderRepository.cs
  34. 12 11
      src/Hotline.Share/Dtos/Article/BulletinDto.cs
  35. 42 6
      src/Hotline.Share/Dtos/Knowledge/KnowledgeDto.cs
  36. 10 5
      src/Hotline.Share/Dtos/Knowledge/KnowledgePagedDto.cs
  37. 2 0
      src/Hotline.Share/Dtos/Order/OrderDto.cs
  38. 5 0
      src/Hotline.Share/Dtos/Order/OrderVisitDto.cs
  39. 75 1
      src/Hotline.Share/Dtos/Order/QueryOrderDto.cs
  40. 15 0
      src/Hotline.Share/Dtos/Settings/CacheDto.cs
  41. 5 0
      src/Hotline.Share/Dtos/WebPortal/QueryKnowledgeList.cs
  42. 6 0
      src/Hotline.Share/Enums/KnowledgeBase/EKnowledgeApplyType.cs
  43. 16 4
      src/Hotline.Share/Enums/KnowledgeBase/EKnowledgeStatus.cs
  44. 5 1
      src/Hotline.Share/Mq/EventNames.cs
  45. 1 1
      src/Hotline/Article/Bulletin.cs
  46. 6 0
      src/Hotline/Caching/Interfaces/ISystemSettingCacheManager.cs
  47. 16 0
      src/Hotline/Caching/Services/SystemSettingCacheManager.cs
  48. 9 3
      src/Hotline/FlowEngine/WorkflowModules/WorkflowModuleConsts.cs
  49. 8 3
      src/Hotline/FlowEngine/Workflows/IWorkflowDomainService.cs
  50. 3 0
      src/Hotline/FlowEngine/Workflows/StepBasicEntity.cs
  51. 2 4
      src/Hotline/FlowEngine/Workflows/Workflow.cs
  52. 115 58
      src/Hotline/FlowEngine/Workflows/WorkflowDomainService.cs
  53. 2 8
      src/Hotline/FlowEngine/Workflows/WorkflowStep.cs
  54. 99 38
      src/Hotline/KnowledgeBase/KnowledgeDomainService.cs
  55. 1 1
      src/Hotline/OrderTranspond/TranspondCityRawData.cs
  56. 2 1
      src/Hotline/Orders/OrderDomainService.cs
  57. 1 1
      src/Hotline/SeedData/OrgSeedData.cs
  58. 16 0
      src/Hotline/Settings/SettingConstants.cs

+ 29 - 7
src/Hotline.Api/Controllers/AiController.cs

@@ -1,5 +1,6 @@
 
 
 using Consul;
 using Consul;
+using DocumentFormat.OpenXml.Wordprocessing;
 using DotNetCore.CAP;
 using DotNetCore.CAP;
 using Hotline.Ai.CallOut;
 using Hotline.Ai.CallOut;
 using Hotline.Ai.Jths;
 using Hotline.Ai.Jths;
@@ -661,7 +662,7 @@ namespace Hotline.Api.Controllers
                                     x.VisitContent = seatVisitContent;
                                     x.VisitContent = seatVisitContent;
                                 });
                                 });
 
 
-                                await _orderVisitDetailRepository.UpdateRangeAsync(seatDetail, HttpContext.RequestAborted);
+                               
 
 
                                 //处理结果
                                 //处理结果
                                 orgDetail.ForEach(x =>
                                 orgDetail.ForEach(x =>
@@ -678,8 +679,26 @@ namespace Hotline.Api.Controllers
                                     {
                                     {
                                         //x.OrgNoSatisfiedReason = new List<Kv>() { new Kv() { Key = "7", Value = "未回复" } };
                                         //x.OrgNoSatisfiedReason = new List<Kv>() { new Kv() { Key = "7", Value = "未回复" } };
                                         //TODO 记录不满意原因到内容中供人工回访甄别选择不满意原因
                                         //TODO 记录不满意原因到内容中供人工回访甄别选择不满意原因
-                                        aiOrderVisitDetail.OrderVisit.VisitState = Share.Enums.Order.EVisitState.WaitForVisit;
-                                        aiOrderVisitDetail.OrderVisit.IsEffectiveAiVisit = false;
+                                        if (dto.CallTimes.Value>=1)
+                                        {
+                                            aiOrderVisitDetail.OrderVisit.VisitState = EVisitState.Visited;
+                                            aiOrderVisitDetail.OrderVisit.NowEvaluate = new Kv() { Key = "6", Value = "未接通" };
+                                            x.VisitContent = "智能回访两次未接默认已回访";
+                                            x.Volved = true;
+                                            x.IsContact = true;
+                                            seatDetail.ForEach(x =>
+                                            {
+                                                x.VoiceEvaluate = Share.Enums.Order.EVoiceEvaluate.Satisfied;
+                                                x.SeatEvaluate =  ESeatEvaluate.DefaultSatisfied;
+                                                x.VisitContent = "智能回访两次未接默认已回访";
+                                            });
+                                        }
+                                        else
+                                        {
+                                            aiOrderVisitDetail.OrderVisit.VisitState = Share.Enums.Order.EVisitState.WaitForVisit;
+                                            aiOrderVisitDetail.OrderVisit.IsEffectiveAiVisit = false;
+                                        }
+                                        
                                     }
                                     }
                                     else
                                     else
                                     {
                                     {
@@ -687,6 +706,7 @@ namespace Hotline.Api.Controllers
                                         aiOrderVisitDetail.OrderVisit.IsEffectiveAiVisit = true;
                                         aiOrderVisitDetail.OrderVisit.IsEffectiveAiVisit = true;
                                     }
                                     }
                                 });
                                 });
+                                await _orderVisitDetailRepository.UpdateRangeAsync(seatDetail, HttpContext.RequestAborted);
                                 await _orderVisitDetailRepository.UpdateRangeAsync(orgDetail, HttpContext.RequestAborted);
                                 await _orderVisitDetailRepository.UpdateRangeAsync(orgDetail, HttpContext.RequestAborted);
 
 
                                 //var first = orgProcessingResults; //aiOrderVisitDetail.OrderVisit.OrderVisitDetails.FirstOrDefault(x => x.VisitTarget == EVisitTarget.Org);
                                 //var first = orgProcessingResults; //aiOrderVisitDetail.OrderVisit.OrderVisitDetails.FirstOrDefault(x => x.VisitTarget == EVisitTarget.Org);
@@ -1065,10 +1085,12 @@ namespace Hotline.Api.Controllers
         public async Task AddAiVisit([FromBody]AddAiVisitDto dto)
         public async Task AddAiVisit([FromBody]AddAiVisitDto dto)
         {
         {
             //验证是否有重复电话
             //验证是否有重复电话
-            if (dto.AiOrderVisitDetails.DistinctBy(x=>x.OuterNo.Trim()).Count() != dto.AiOrderVisitDetails.Count)
-            {
-                throw UserFriendlyException.SameMessage("任务中存在重复外呼号码,请检查后重新提交");
-            }
+            //有重复电话自动排除
+            dto.AiOrderVisitDetails = dto.AiOrderVisitDetails.DistinctBy(x => x.OuterNo.Trim()).ToList() ;
+            //if (dto.AiOrderVisitDetails.DistinctBy(x=>x.OuterNo.Trim()).Count() != dto.AiOrderVisitDetails.Count)
+            //{
+            //    throw UserFriendlyException.SameMessage("任务中存在重复外呼号码,请检查后重新提交");
+            //}
 
 
             var model = _mapper.Map<AiOrderVisit>(dto);
             var model = _mapper.Map<AiOrderVisit>(dto);
 
 

+ 4 - 0
src/Hotline.Api/Controllers/CommonPController.cs

@@ -230,6 +230,8 @@ namespace Hotline.Api.Controllers
 				var waitedDataList = await _orderRepository
 				var waitedDataList = await _orderRepository
 					.Queryable(hasHandledStep: false, isAdmin: isAdmin)
 					.Queryable(hasHandledStep: false, isAdmin: isAdmin)
 					.Includes(d => d.OrderSpecials)
 					.Includes(d => d.OrderSpecials)
+					.WhereIF(_appOptions.Value.IsZiGong, d => SqlFunc.Subqueryable<OrderDelay>().Where(od => od.OrderId == d.Id && od.DelayState == EDelayState.Examining).NotAny())
+					.WhereIF(_appOptions.Value.IsZiGong, d => SqlFunc.Subqueryable<OrderSendBackAudit>().Where(osba => osba.OrderId == d.Id && osba.State == ESendBackAuditState.Apply).NotAny())
 					.Where(d => d.Status != EOrderStatus.WaitForAccept && d.Status != EOrderStatus.BackToUnAccept && d.Status != EOrderStatus.SpecialToUnAccept && d.Status != EOrderStatus.HandOverToUnAccept)
 					.Where(d => d.Status != EOrderStatus.WaitForAccept && d.Status != EOrderStatus.BackToUnAccept && d.Status != EOrderStatus.SpecialToUnAccept && d.Status != EOrderStatus.HandOverToUnAccept)
 					.Where(d => d.Source < ESource.MLSQ || d.Source > ESource.WZSC)
 					.Where(d => d.Source < ESource.MLSQ || d.Source > ESource.WZSC)
 					.Where(d => d.Status != EOrderStatus.BackToProvince && d.Status < EOrderStatus.Filed)
 					.Where(d => d.Status != EOrderStatus.BackToProvince && d.Status < EOrderStatus.Filed)
@@ -406,6 +408,8 @@ namespace Hotline.Api.Controllers
 				var waitedDataList = await _orderRepository
 				var waitedDataList = await _orderRepository
 					.Queryable(hasHandledStep: false, isAdmin: isAdmin)
 					.Queryable(hasHandledStep: false, isAdmin: isAdmin)
 					.Includes(d => d.OrderSpecials)
 					.Includes(d => d.OrderSpecials)
+					.Where(d => SqlFunc.Subqueryable<OrderDelay>().Where(od => od.OrderId == d.Id && od.DelayState == EDelayState.Examining).NotAny())
+					.Where(d => SqlFunc.Subqueryable<OrderSendBackAudit>().Where(osba => osba.OrderId == d.Id && osba.State == ESendBackAuditState.Apply).NotAny())
 					.Where(d => d.Status != EOrderStatus.WaitForAccept && d.Status != EOrderStatus.BackToUnAccept && d.Status != EOrderStatus.SpecialToUnAccept && d.Status != EOrderStatus.HandOverToUnAccept)
 					.Where(d => d.Status != EOrderStatus.WaitForAccept && d.Status != EOrderStatus.BackToUnAccept && d.Status != EOrderStatus.SpecialToUnAccept && d.Status != EOrderStatus.HandOverToUnAccept)
 					.Where(d => d.Source < ESource.MLSQ || d.Source > ESource.WZSC)
 					.Where(d => d.Source < ESource.MLSQ || d.Source > ESource.WZSC)
 					.Where(d => d.Status != EOrderStatus.BackToProvince && d.Status < EOrderStatus.Filed)
 					.Where(d => d.Status != EOrderStatus.BackToProvince && d.Status < EOrderStatus.Filed)

+ 98 - 34
src/Hotline.Api/Controllers/KnowledgeController.cs

@@ -40,11 +40,14 @@ using Org.BouncyCastle.Utilities.IO;
 using SqlSugar;
 using SqlSugar;
 using System.Text;
 using System.Text;
 using System.Threading;
 using System.Threading;
+using Hotline.Configurations;
+using Microsoft.Extensions.Options;
 using XF.Domain.Authentications;
 using XF.Domain.Authentications;
 using XF.Domain.Exceptions;
 using XF.Domain.Exceptions;
 using XF.Domain.Repository;
 using XF.Domain.Repository;
 using XF.Utility.EnumExtensions;
 using XF.Utility.EnumExtensions;
 using static Microsoft.EntityFrameworkCore.DbLoggerCategory;
 using static Microsoft.EntityFrameworkCore.DbLoggerCategory;
+using Hotline.Share.Dtos.Schedulings;
 
 
 namespace Hotline.Api.Controllers
 namespace Hotline.Api.Controllers
 {
 {
@@ -82,9 +85,10 @@ namespace Hotline.Api.Controllers
         private readonly IBulletinApplication _bulletinApplication;
         private readonly IBulletinApplication _bulletinApplication;
         private readonly IRepository<KnowledgeCollectGroup> _knowledgeCollectGroupRepository;
         private readonly IRepository<KnowledgeCollectGroup> _knowledgeCollectGroupRepository;
         private readonly IRepository<KnowledgePv> _knowledgePvepository;
         private readonly IRepository<KnowledgePv> _knowledgePvepository;
+        private readonly IOptionsSnapshot<AppConfiguration> _appOptions;
 
 
 
 
-        public KnowledgeController(
+		public KnowledgeController(
            IKnowledgeRepository knowledgeRepository,
            IKnowledgeRepository knowledgeRepository,
            ISessionContext sessionContext,
            ISessionContext sessionContext,
            IKnowledgeDomainService knowledgeDomainService,
            IKnowledgeDomainService knowledgeDomainService,
@@ -115,7 +119,8 @@ namespace Hotline.Api.Controllers
             IRepository<KnowledgePv> knowledgePvepository,
             IRepository<KnowledgePv> knowledgePvepository,
             IRepository<KnowledgeWord> knowledgeWordRepository,
             IRepository<KnowledgeWord> knowledgeWordRepository,
             IRepository<KnowledgeHotWord> knowledgeWordHotRepository,
             IRepository<KnowledgeHotWord> knowledgeWordHotRepository,
-            IRepository<KnowledgeHotWord> knowledgeHotWordRepository)
+           IOptionsSnapshot<AppConfiguration> appOptions,
+			IRepository<KnowledgeHotWord> knowledgeHotWordRepository)
         {
         {
             _knowledgeRepository = knowledgeRepository;
             _knowledgeRepository = knowledgeRepository;
             _sessionContext = sessionContext;
             _sessionContext = sessionContext;
@@ -147,7 +152,8 @@ namespace Hotline.Api.Controllers
             _knowledgePvepository = knowledgePvepository;
             _knowledgePvepository = knowledgePvepository;
             _knowledgeWordRepository = knowledgeWordRepository;
             _knowledgeWordRepository = knowledgeWordRepository;
             _knowledgeHotWordRepository = knowledgeHotWordRepository;
             _knowledgeHotWordRepository = knowledgeHotWordRepository;
-        }
+            _appOptions = appOptions;
+		}
 
 
         #endregion
         #endregion
 
 
@@ -208,21 +214,34 @@ namespace Hotline.Api.Controllers
         [Permission(EPermission.KnowledgeOffShelf)]
         [Permission(EPermission.KnowledgeOffShelf)]
         [HttpPut("offshelf")]
         [HttpPut("offshelf")]
         [LogFilter("知识下架")]
         [LogFilter("知识下架")]
-        public async Task KnowledgeOffShelf(string Id)
+        public async Task KnowledgeOffShelf([FromBody] OffShelfStartFlowDto dto)
         {
         {
-            var know = await _knowledgeRepository.GetAsync(Id, HttpContext.RequestAborted);
+            var know = await _knowledgeRepository.GetAsync(dto.Data.Id, HttpContext.RequestAborted);
             if (know != null && know.Status == EKnowledgeStatus.OnShelf)
             if (know != null && know.Status == EKnowledgeStatus.OnShelf)
             {
             {
-                know.Status = EKnowledgeStatus.OffShelf;
-                know.OnShelfTime = null;
-                know.OffShelfTime = DateTime.Now;
-                await _knowledgeRepository.UpdateAsync(know, HttpContext.RequestAborted);
-
-                var pushKnowledge = _mapper.Map<KnowledgeSendDto>(know);
-                pushKnowledge.CategoryCode = "01";
-                pushKnowledge.CategoryName = "公共服务";
-                //推省上
-                await _capPublisher.PublishAsync(EventNames.HotlineKnowledgeRemove, pushKnowledge, cancellationToken: HttpContext.RequestAborted);
+                if (_sessionContext.OrgIsCenter)
+                {
+                    know.Status = EKnowledgeStatus.OffShelf;
+                    know.OnShelfTime = null;
+                    know.OffShelfTime = DateTime.Now;
+                    await _knowledgeRepository.UpdateAsync(know, HttpContext.RequestAborted);
+
+                    var pushKnowledge = _mapper.Map<KnowledgeSendDto>(know);
+                    pushKnowledge.CategoryCode = "01";
+                    pushKnowledge.CategoryName = "公共服务";
+                    //推省上
+                    await _capPublisher.PublishAsync(EventNames.HotlineKnowledgeRemove, pushKnowledge, cancellationToken: HttpContext.RequestAborted);
+                }
+                else {
+	                know.Status = EKnowledgeStatus.OffShelfAudit;
+	                await _knowledgeRepository.UpdateAsync(know, HttpContext.RequestAborted);
+
+					var startDto = _mapper.Map<StartWorkflowDto>(dto.Workflow);
+					startDto.DefinitionModuleCode = WorkflowModuleConsts.KnowledgeOffshelf;
+                    startDto.Opinion = dto.Data.Opinion;
+					startDto.Title = "知识库下架";
+					await StartFlow(know.Id, WorkflowModuleConsts.KnowledgeOffshelf, EKnowledgeApplyType.Offshelf, startDto);
+				}
             }
             }
             else
             else
                 throw UserFriendlyException.SameMessage("知识下架失败");
                 throw UserFriendlyException.SameMessage("知识下架失败");
@@ -294,6 +313,20 @@ namespace Hotline.Api.Controllers
             else
             else
                 knowledge.FileJson = new List<Share.Dtos.File.FileJson>();
                 knowledge.FileJson = new List<Share.Dtos.File.FileJson>();
             if (dto.Workflow != null) knowledge.Renewaln = knowledge.Status != EKnowledgeStatus.Drafts;
             if (dto.Workflow != null) knowledge.Renewaln = knowledge.Status != EKnowledgeStatus.Drafts;
+            if (_appOptions.Value.IsYiBin)
+            {
+				//临时处理 修改后提交修改基本信息
+				if (!_sessionContext.OrgIsCenter)
+				{
+					knowledge.Attribution = "部门知识库";
+				}
+				knowledge.CreatorId = _sessionContext.UserId;
+				knowledge.CreatorName = _sessionContext.UserName;
+				knowledge.CreatorOrgId = _sessionContext.OrgId;
+				knowledge.CreatorOrgName = _sessionContext.OrgName;
+				knowledge.CreatorOrgLevel = _sessionContext.OrgLevel;
+			}
+
             await _knowledgeRepository.UpdateNullAsync(knowledge, HttpContext.RequestAborted);
             await _knowledgeRepository.UpdateNullAsync(knowledge, HttpContext.RequestAborted);
             if (dto.Data.KnowledgeType.Any())
             if (dto.Data.KnowledgeType.Any())
             {
             {
@@ -407,13 +440,13 @@ namespace Hotline.Api.Controllers
         [LogFilter("删除知识")]
         [LogFilter("删除知识")]
         public async Task Remove([FromBody] DeleteStartFlowDto dto)
         public async Task Remove([FromBody] DeleteStartFlowDto dto)
         {
         {
-            var delete = _mapper.Map<KnowledgeDto>(dto.Data);
-            var knowledge = await _knowledgeRepository.GetAsync(delete.Id, HttpContext.RequestAborted);
+            //var delete = _mapper.Map<KnowledgeDto>(dto.Data);
+            var knowledge = await _knowledgeRepository.GetAsync(dto.Data.Id, HttpContext.RequestAborted);
             if (knowledge == null)
             if (knowledge == null)
                 throw UserFriendlyException.SameMessage("无效知识库数据");
                 throw UserFriendlyException.SameMessage("无效知识库数据");
             if (knowledge.Status == EKnowledgeStatus.OnShelf || knowledge.Status == EKnowledgeStatus.Auditing)
             if (knowledge.Status == EKnowledgeStatus.OnShelf || knowledge.Status == EKnowledgeStatus.Auditing)
                 throw UserFriendlyException.SameMessage("知识库数据不可删除");
                 throw UserFriendlyException.SameMessage("知识库数据不可删除");
-            if (knowledge.Status == EKnowledgeStatus.Drafts)
+            if (knowledge.Status == EKnowledgeStatus.Drafts || knowledge.Status == EKnowledgeStatus.Revert)
             {
             {
                 await _knowledgeRepository.RemoveAsync(knowledge, false, HttpContext.RequestAborted);
                 await _knowledgeRepository.RemoveAsync(knowledge, false, HttpContext.RequestAborted);
             }
             }
@@ -422,8 +455,8 @@ namespace Hotline.Api.Controllers
                 var startDto = _mapper.Map<StartWorkflowDto>(dto.Workflow);
                 var startDto = _mapper.Map<StartWorkflowDto>(dto.Workflow);
                 startDto.DefinitionModuleCode = WorkflowModuleConsts.KnowledgeDelete;
                 startDto.DefinitionModuleCode = WorkflowModuleConsts.KnowledgeDelete;
                 startDto.Title = "知识库删除";
                 startDto.Title = "知识库删除";
-                //await _workflowApplication.StartWorkflowAsync(startDto, _sessionContext, knowledge.Id, cancellationToken: HttpContext.RequestAborted);
-                await StartFlow(delete.Id, WorkflowModuleConsts.KnowledgeDelete, EKnowledgeApplyType.Delete, startDto);
+				//await _workflowApplication.StartWorkflowAsync(startDto, _sessionContext, knowledge.Id, cancellationToken: HttpContext.RequestAborted);
+                await StartFlow(dto.Data.Id, WorkflowModuleConsts.KnowledgeDelete, EKnowledgeApplyType.Delete, startDto);
             }
             }
         }
         }
 
 
@@ -641,18 +674,37 @@ namespace Hotline.Api.Controllers
         [HttpGet("knowledge-status-data")]
         [HttpGet("knowledge-status-data")]
         public Dictionary<string, dynamic> KnowledgeStatus()
         public Dictionary<string, dynamic> KnowledgeStatus()
         {
         {
-            var tabNames = new List<KeyValuePair<int, string>>
+            var tabNames = new List<KeyValuePair<string, string>>
             {
             {
-                new KeyValuePair<int, string>(3, "已上架"),
-                new KeyValuePair<int, string>(4, "已下架"),
-                new KeyValuePair<int, string>(1, "审核中"),
-                new KeyValuePair<int, string>(5, "审核不通过")
+                new KeyValuePair<string, string>("3", "已上架"),
+                new KeyValuePair<string, string>("4", "已下架"),
+                new KeyValuePair<string, string>("1", "审核中"),
+                new KeyValuePair<string, string>("8", "草稿"),
+                new KeyValuePair<string, string>("", "全部")
+			};
+
+            var tabNewDraftsNames = new List<KeyValuePair<string, string>>
+            {
+	            new KeyValuePair<string, string>("", "全部"),
+				new KeyValuePair<string, string>("0", "待提交"),
+	            new KeyValuePair<string, string>("5", "审核不通过"),
             };
             };
 
 
-            return _baseDataApplication
+            var tabAuditingNames = new List<KeyValuePair<string, string>>
+            {
+	            new KeyValuePair<string, string>("", "全部"),
+				new KeyValuePair<string, string>("知识新增", "新增审核"),
+	            new KeyValuePair<string, string>("知识更新", "修改审核"),
+	            new KeyValuePair<string, string>("知识删除", "删除审核"),
+	            new KeyValuePair<string, string>("知识下架", "下架审核"),
+			};
+
+			return _baseDataApplication
                 .FileType(EFileType.excel | EFileType.pdf)
                 .FileType(EFileType.excel | EFileType.pdf)
                 .Add("tabNames", tabNames)
                 .Add("tabNames", tabNames)
-                .Build();
+                .Add("tabNewDraftsNames", tabNewDraftsNames)
+                .Add("tabAuditingNames", tabAuditingNames)
+				.Build();
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -935,14 +987,26 @@ namespace Hotline.Api.Controllers
         }
         }
 
 
         /// <summary>
         /// <summary>
-        /// 开始流程
+        /// 查询知识库办理流程开启参数-下架
         /// </summary>
         /// </summary>
-        /// <param name="id">知识ID</param>
-        /// <param name="moduleCode">知识模板编号</param>
-        /// <param name="eKnowledgeApplyType">申请类型</param>
-        /// <param name="dto">流程开启参数</param>
         /// <returns></returns>
         /// <returns></returns>
-        private async Task<string> StartFlow(string id, string moduleCode, EKnowledgeApplyType eKnowledgeApplyType, StartWorkflowDto dto)
+        //[Permission(EPermission.KnowledgeDelete)]
+        [HttpGet("offshelf-flow-start")]
+        public async Task<NextStepsDto> GetOffshelfFlowStartOptionsAsync()
+        {
+	        return await _workflowApplication.GetStartStepsAsync(WorkflowModuleConsts.KnowledgeOffshelf,
+		        HttpContext.RequestAborted);
+        }
+
+		/// <summary>
+		/// 开始流程
+		/// </summary>
+		/// <param name="id">知识ID</param>
+		/// <param name="moduleCode">知识模板编号</param>
+		/// <param name="eKnowledgeApplyType">申请类型</param>
+		/// <param name="dto">流程开启参数</param>
+		/// <returns></returns>
+		private async Task<string> StartFlow(string id, string moduleCode, EKnowledgeApplyType eKnowledgeApplyType, StartWorkflowDto dto)
         {
         {
             var knowledge = await _knowledgeRepository.GetAsync(id, HttpContext.RequestAborted);
             var knowledge = await _knowledgeRepository.GetAsync(id, HttpContext.RequestAborted);
             if (knowledge == null)
             if (knowledge == null)
@@ -958,7 +1022,7 @@ namespace Hotline.Api.Controllers
             await _knowledgeDomainService.AddWorkFlowAsync(id, eKnowledgeApplyType, HttpContext.RequestAborted);
             await _knowledgeDomainService.AddWorkFlowAsync(id, eKnowledgeApplyType, HttpContext.RequestAborted);
 
 
             dto.DefinitionModuleCode = moduleCode;
             dto.DefinitionModuleCode = moduleCode;
-            dto.Title = knowledge.Title;
+			//dto.Title = knowledge.Title;
             return await _workflowApplication.StartWorkflowAsync(dto, id, cancellationToken: HttpContext.RequestAborted);
             return await _workflowApplication.StartWorkflowAsync(dto, id, cancellationToken: HttpContext.RequestAborted);
         }
         }
         #endregion
         #endregion

+ 330 - 142
src/Hotline.Api/Controllers/OrderController.cs

@@ -71,6 +71,9 @@ using static Lucene.Net.Util.Fst.Util;
 using DocumentFormat.OpenXml.Spreadsheet;
 using DocumentFormat.OpenXml.Spreadsheet;
 using System.Threading;
 using System.Threading;
 using Hotline.Caching.Services;
 using Hotline.Caching.Services;
+using Hotline.CallCenter.Calls;
+using Hotline.CallCenter.BlackLists;
+using Hotline.Share.Mq;
 
 
 namespace Hotline.Api.Controllers;
 namespace Hotline.Api.Controllers;
 
 
@@ -382,15 +385,24 @@ public class OrderController : BaseController
     public async Task PublishOrder([FromBody] PublishOrderDto dto)
     public async Task PublishOrder([FromBody] PublishOrderDto dto)
     {
     {
         //验证订单
         //验证订单
-        var order = await _orderRepository.GetAsync(dto.Id, HttpContext.RequestAborted);
-
-
-        if (order is null)
-            throw UserFriendlyException.SameMessage("未找到工单,无法发布");
+        var order = await _orderRepository.GetAsync(dto.Id, HttpContext.RequestAborted)
+                    ?? throw UserFriendlyException.SameMessage("未找到工单,无法发布");
 
 
         if (order.Status != EOrderStatus.Filed)
         if (order.Status != EOrderStatus.Filed)
             throw UserFriendlyException.SameMessage("当前状态无法发布");
             throw UserFriendlyException.SameMessage("当前状态无法发布");
 
 
+        var enabled = _systemSettingCacheManager.CancelPublishOrderEnabled;
+        if (enabled)
+        {
+            // 获取上一次被取消发布的发布信息
+            var publishedDeleted = await _orderPublishRepository.Queryable().Where(m => m.OrderId == dto.Id && m.IsDeleted == true)
+                .OrderByDescending(m => m.CreationTime).FirstAsync(HttpContext.RequestAborted);
+            if (publishedDeleted != null && _sessionContext.RequiredUserId != publishedDeleted.CreatorId)
+            {
+                throw UserFriendlyException.SameMessage($"改工单被取消发布过, 之前的发布人是 [{publishedDeleted.CreatorName}], 您不能发布;");
+            }
+        }
+
         //新增发布工单
         //新增发布工单
         var orderPublish = _mapper.Map<OrderPublish>(dto);
         var orderPublish = _mapper.Map<OrderPublish>(dto);
         orderPublish.OrderId = order.Id;
         orderPublish.OrderId = order.Id;
@@ -422,17 +434,30 @@ public class OrderController : BaseController
 
 
         // 取消发布功能开关
         // 取消发布功能开关
         var cancelPublishOrderEnabled = _systemSettingCacheManager.CancelPublishOrderEnabled;
         var cancelPublishOrderEnabled = _systemSettingCacheManager.CancelPublishOrderEnabled;
-        // 取消发布的工单数量
-        var orderPublishDeletedCount = await _orderPublishRepository.Queryable(includeDeleted: true)
+        // 取消发布的工单
+        var publishOrder = await _orderPublishRepository.Queryable(includeDeleted: true)
             .Where(m => m.OrderId == order.Id && m.IsDeleted == true)
             .Where(m => m.OrderId == order.Id && m.IsDeleted == true)
-            .CountAsync(HttpContext.RequestAborted);
-        var orderVisitVisitedCount = await _orderVisitRepository.Queryable()
-            .Where(m => m.OrderId == order.Id && m.VisitState == EVisitState.Visited)
-            .CountAsync(HttpContext.RequestAborted);
+            .FirstAsync(HttpContext.RequestAborted);
 
 
         // 若取消发布的工单,已经被回访过了,没有经过重新办理,再次发布后,自动跳过回访环节,展示取消发布前的回访结果
         // 若取消发布的工单,已经被回访过了,没有经过重新办理,再次发布后,自动跳过回访环节,展示取消发布前的回访结果
-        if (orderPublishDeletedCount != 0 && orderVisitVisitedCount != 0 && cancelPublishOrderEnabled == true)
+        if (publishOrder != null && cancelPublishOrderEnabled == true)
         {
         {
+            var history = new OrderPublishHistory
+            {
+                ArrangeTitleBefor = publishOrder.ArrangeTitle,
+                ArrangeTitleAfter = dto.ArrangeTitle,
+                ArrangeContentBefor = publishOrder.ArrangeContent,
+                ArrangeContentAfter = dto.ArrangeContent,
+                ArrangeOpinionBefor = publishOrder.ArrangeOpinion,
+                ArrangeOpinionAfter = dto.ArrangeOpinion,
+                PublishStateBefor = publishOrder.PublishState,
+                PublishStateAfter = dto.PublishState,
+                No = publishOrder.No,
+                OrderId = publishOrder.OrderId,
+                OrderPublishId = publishOrder.Id
+            };
+            await _orderPublishHistoryRepository.AddAsync(history, HttpContext.RequestAborted);
+            await _orderPublishRepository.RemoveAsync(publishOrder, false, HttpContext.RequestAborted);
             return;
             return;
         }
         }
 
 
@@ -767,16 +792,28 @@ public class OrderController : BaseController
     /// <param name="dto"></param>
     /// <param name="dto"></param>
     /// <returns></returns>
     /// <returns></returns>
     [HttpPut("publish/cancel")]
     [HttpPut("publish/cancel")]
-    public async Task<string> PublishCancelAsync([FromBody]PublishCancelInDto dto)
+    [LogFilter("取消发布")]
+    public async Task<string> PublishCancelAsync([FromBody] PublishCancelInDto dto)
     {
     {
         var enabled = _systemSettingCacheManager.CancelPublishOrderEnabled;
         var enabled = _systemSettingCacheManager.CancelPublishOrderEnabled;
         if (enabled == false) return "取消发布功能已被关闭";
         if (enabled == false) return "取消发布功能已被关闭";
+
         var publish = await _orderPublishRepository.GetAsync(dto.OrderPublishId)
         var publish = await _orderPublishRepository.GetAsync(dto.OrderPublishId)
-            ?? throw UserFriendlyException.SameMessage("发布单不存在");
+                      ?? throw UserFriendlyException.SameMessage("发布单不存在");
         publish.IsDeleted = true;
         publish.IsDeleted = true;
+        var order = await _orderRepository.GetAsync(publish.OrderId, HttpContext.RequestAborted) ??
+                    throw UserFriendlyException.SameMessage("工单不存在");
+        if (order.Status != EOrderStatus.Published && order.Status != EOrderStatus.Visited)
+            throw UserFriendlyException.SameMessage("工单状态非[已发布]和[已回访], 不可取消发布.");
+
         await _orderPublishRepository.UpdateAsync(publish);
         await _orderPublishRepository.UpdateAsync(publish);
+        order.Status = EOrderStatus.Filed;
+        // 被取消的工单继续由之前发布的用户继续发布;
+        order.WaitForPublisherId = publish.CreatorId;
+        await _orderRepository.UpdateAsync(order);
         return "取消成功";
         return "取消成功";
     }
     }
+
     #endregion
     #endregion
 
 
     #region 工单回访
     #region 工单回访
@@ -799,7 +836,7 @@ public class OrderController : BaseController
             .WhereIF(dto.VisitStateQuery == EVisitStateQuery.Visited, d => d.VisitState == EVisitState.Visited)
             .WhereIF(dto.VisitStateQuery == EVisitStateQuery.Visited, d => d.VisitState == EVisitState.Visited)
             .WhereIF(dto.VisitStateQuery == EVisitStateQuery.SMSUnsatisfied, d => d.VisitState == EVisitState.SMSUnsatisfied)
             .WhereIF(dto.VisitStateQuery == EVisitStateQuery.SMSUnsatisfied, d => d.VisitState == EVisitState.SMSUnsatisfied)
             .WhereIF(dto.VisitStateQuery == EVisitStateQuery.SMSVisiting, d => d.VisitState == EVisitState.SMSVisiting)
             .WhereIF(dto.VisitStateQuery == EVisitStateQuery.SMSVisiting, d => d.VisitState == EVisitState.SMSVisiting)
-            .WhereIF(dto.VisitStateQuery == EVisitStateQuery.NoPutThrough , d=>d.IsPutThrough == false)
+            .WhereIF(dto.VisitStateQuery == EVisitStateQuery.NoPutThrough, d => d.IsPutThrough == false && d.VisitState != EVisitState.Visited)
             .WhereIF(!string.IsNullOrEmpty(dto.Keyword), d => d.Order.Title.StartsWith(dto.Keyword!))
             .WhereIF(!string.IsNullOrEmpty(dto.Keyword), d => d.Order.Title.StartsWith(dto.Keyword!))
             .WhereIF(!string.IsNullOrEmpty(dto.No), d => d.No == dto.No)
             .WhereIF(!string.IsNullOrEmpty(dto.No), d => d.No == dto.No)
             .WhereIF(dto.VisitType != null, d => d.VisitType == dto.VisitType)
             .WhereIF(dto.VisitType != null, d => d.VisitType == dto.VisitType)
@@ -864,6 +901,7 @@ public class OrderController : BaseController
     /// </summary>
     /// </summary>
     /// <returns></returns>
     /// <returns></returns>
     [HttpGet("visit/{id}")]
     [HttpGet("visit/{id}")]
+    [AllowAnonymous]
     public async Task<object> VisitInfo(string id)
     public async Task<object> VisitInfo(string id)
     {
     {
         var orderVisit = await _orderVisitRepository.Queryable()
         var orderVisit = await _orderVisitRepository.Queryable()
@@ -922,20 +960,25 @@ public class OrderController : BaseController
         //获取系统配置智能回访语音URL头
         //获取系统配置智能回访语音URL头
         aiVisitVoiceBaseUrl = _systemSettingCacheManager.GetSetting(SettingConstants.AiVisitVoiceBaseUrl)?.SettingValue[0];
         aiVisitVoiceBaseUrl = _systemSettingCacheManager.GetSetting(SettingConstants.AiVisitVoiceBaseUrl)?.SettingValue[0];
 
 
-        var histories = await _orderVisitRepository.Queryable()
-            .Includes(m => m.OrderVisitDetails)
-            .Where(m => m.OrderId == orderVisit.OrderId)
-            .Where(m => m.VisitState == EVisitState.None && m.NowEvaluate != null)
-            .Select(m => new OrderVisitDetailHistoryDto
-            {
-                VoiceEvaluate = m.OrderVisitDetails.Where(n => n.VisitTarget == EVisitTarget.Seat).Select(s => s.VoiceEvaluate).First(),
-                SeatEvaluate = m.OrderVisitDetails.Where(n => n.VisitTarget == EVisitTarget.Seat).Select(s => s.SeatEvaluate).First(),
-                VisitOrgName = m.OrderVisitDetails.Where(n => n.VisitTarget == EVisitTarget.Org).Select(s => s.VisitOrgName).First(),
-                OrgProcessingResults = m.OrderVisitDetails.Where(n => n.VisitTarget == EVisitTarget.Org).Select(s => s.OrgProcessingResults).First(),
-                OrgHandledAttitude = m.OrderVisitDetails.Where(n => n.VisitTarget == EVisitTarget.Org).Select(s => s.OrgHandledAttitude).First(),
-                VisitContent = m.OrderVisitDetails.Where(n => n.VisitTarget == EVisitTarget.Org).Select(s => s.VisitContent).First(),
-                VisitTime = m.VisitTime
-            }).ToListAsync();
+        var histories = new List<OrderVisitDetailHistoryDto>();
+        if (_appOptions.Value.IsZiGong)
+        {
+            histories = await _orderVisitRepository.Queryable()
+             .Includes(m => m.OrderVisitDetails)
+             .Where(m => m.OrderId == orderVisit.OrderId)
+             .Where(m => m.VisitState == EVisitState.None && m.NowEvaluate != null)
+             .Select(m => new OrderVisitDetailHistoryDto
+             {
+                 VoiceEvaluate = m.OrderVisitDetails.Where(n => n.VisitTarget == EVisitTarget.Seat).Select(s => s.VoiceEvaluate).First(),
+                 SeatEvaluate = m.OrderVisitDetails.Where(n => n.VisitTarget == EVisitTarget.Seat).Select(s => s.SeatEvaluate).First(),
+                 VisitOrgName = m.OrderVisitDetails.Where(n => n.VisitTarget == EVisitTarget.Org).Select(s => s.VisitOrgName).First(),
+                 OrgProcessingResults = m.OrderVisitDetails.Where(n => n.VisitTarget == EVisitTarget.Org).Select(s => s.OrgProcessingResults).First(),
+                 OrgHandledAttitude = m.OrderVisitDetails.Where(n => n.VisitTarget == EVisitTarget.Org).Select(s => s.OrgHandledAttitude).First(),
+                 VisitContent = m.OrderVisitDetails.Where(n => n.VisitTarget == EVisitTarget.Org).Select(s => s.VisitContent).First(),
+                 VisitTime = m.VisitTime,
+                 CallId = m.CallId
+             }).ToListAsync();
+        }
 
 
         var seat = orderVisit.OrderVisitDetails.FirstOrDefault(m => m.VisitTarget == EVisitTarget.Seat);
         var seat = orderVisit.OrderVisitDetails.FirstOrDefault(m => m.VisitTarget == EVisitTarget.Seat);
         if (orderVisit.VisitState == EVisitState.WaitForVisit)
         if (orderVisit.VisitState == EVisitState.WaitForVisit)
@@ -993,7 +1036,17 @@ public class OrderController : BaseController
     [HttpPost("visit")]
     [HttpPost("visit")]
     [LogFilter("工单回访")]
     [LogFilter("工单回访")]
     public async Task Visit([FromBody] VisitDto dto)
     public async Task Visit([FromBody] VisitDto dto)
-        => await _orderApplication.SaveOrderVisit(dto, HttpContext.RequestAborted);
+    {
+        var seconds = _systemSettingCacheManager.VisitCallDelaySecond;
+        if (dto.CallId.NotNullOrEmpty())
+        {
+            await _capPublisher.PublishDelayAsync(TimeSpan.FromSeconds(seconds), EventNames.VisitCallDelay, dto, cancellationToken: HttpContext.RequestAborted);
+
+            dto.CallId = await _callApplication.GetOrSetCallIdAsync(dto.CallId, HttpContext.RequestAborted);
+        }
+
+        await _orderApplication.SaveOrderVisit(dto, HttpContext.RequestAborted);
+    }
 
 
     /// <summary>
     /// <summary>
     /// 批量保存回访
     /// 批量保存回访
@@ -1064,6 +1117,11 @@ public class OrderController : BaseController
                         });
                         });
                 }
                 }
 
 
+                if (visitDto.CallId != null)
+                {
+                    visitDto.CallId = await _callApplication.GetOrSetCallIdAsync(visitDto.CallId, HttpContext.RequestAborted);
+                }
+
                 await _orderApplication.SaveOrderVisit(visitDto, HttpContext.RequestAborted);
                 await _orderApplication.SaveOrderVisit(visitDto, HttpContext.RequestAborted);
                 await _orderVisitRepository.Updateable()
                 await _orderVisitRepository.Updateable()
                     .Where(m => m.Id == visit.VisitId)
                     .Where(m => m.Id == visit.VisitId)
@@ -1237,22 +1295,22 @@ public class OrderController : BaseController
     /// <param name="dto"></param>
     /// <param name="dto"></param>
     /// <returns></returns>
     /// <returns></returns>
     [HttpPut("visit/put_through")]
     [HttpPut("visit/put_through")]
-	public async Task VisitPutThrough([FromBody] VisitPutThroughDto dto ) {
-
-        await _orderVisitRepository.Updateable().SetColumns(x => new OrderVisit { IsPutThrough = false }).Where(x => x.Id == dto.id).ExecuteCommandAsync(HttpContext.RequestAborted);
+    public async Task VisitPutThrough([FromBody] VisitPutThroughDto dto)
+    {
+        await _orderVisitRepository.Updateable().SetColumns(x => new OrderVisit { IsPutThrough = false }).Where(x => x.Id == dto.id)
+            .ExecuteCommandAsync(HttpContext.RequestAborted);
     }
     }
 
 
+    #endregion
 
 
-	#endregion
-
-	#region 二次回访申请
+    #region 二次回访申请
 
 
-	/// <summary>
-	/// 可二次回访申请列表
-	/// </summary>
-	/// <param name="dto"></param>
-	/// <returns></returns>
-	[HttpGet("visitapply/visitagainlist")]
+    /// <summary>
+    /// 可二次回访申请列表
+    /// </summary>
+    /// <param name="dto"></param>
+    /// <returns></returns>
+    [HttpGet("visitapply/visitagainlist")]
     public async Task<PagedDto<OrderCanVisitAgainDto>> OrderVisitAgainList([FromQuery] OrderVisitAgainListDto dto)
     public async Task<PagedDto<OrderCanVisitAgainDto>> OrderVisitAgainList([FromQuery] OrderVisitAgainListDto dto)
     {
     {
         var (total, items) = await _orderVisitedDetailRepository.Queryable()
         var (total, items) = await _orderVisitedDetailRepository.Queryable()
@@ -1591,7 +1649,7 @@ public class OrderController : BaseController
 
 
         //验证延期次数
         //验证延期次数
         var setting = _systemSettingCacheManager.GetSetting(SettingConstants.DelayNum);
         var setting = _systemSettingCacheManager.GetSetting(SettingConstants.DelayNum);
-        if (int.Parse(setting?.SettingValue[0]) != 0)
+        if (int.Parse(setting?.SettingValue[0]) != 0 && !_sessionContext.OrgIsCenter)
         {
         {
             int count = await _orderDelayRepository.CountAsync(x =>
             int count = await _orderDelayRepository.CountAsync(x =>
                 x.OrderId == delaydto.OrderId && x.ApplyOrgCode == _sessionContext.RequiredOrgId && x.DelayState == EDelayState.Pass);
                 x.OrderId == delaydto.OrderId && x.ApplyOrgCode == _sessionContext.RequiredOrgId && x.DelayState == EDelayState.Pass);
@@ -1683,32 +1741,33 @@ public class OrderController : BaseController
     {
     {
         //var workflow = await _workflowRepository.GetAsync(workflowId, HttpContext.RequestAborted);
         //var workflow = await _workflowRepository.GetAsync(workflowId, HttpContext.RequestAborted);
         var workflow = await _workflowDomainService.GetWorkflowAsync(workflowId, withDefine: true, withSteps: true,
         var workflow = await _workflowDomainService.GetWorkflowAsync(workflowId, withDefine: true, withSteps: true,
-	        cancellationToken: HttpContext.RequestAborted);
-        var currentStep = workflow.Steps.FirstOrDefault(d => d.Status == EWorkflowStepStatus.WaitForAccept || d.Status == EWorkflowStepStatus.WaitForHandle);
+            cancellationToken: HttpContext.RequestAborted);
+        var currentStep =
+            workflow.Steps.FirstOrDefault(d => d.Status == EWorkflowStepStatus.WaitForAccept || d.Status == EWorkflowStepStatus.WaitForHandle);
         if (workflow != null)
         if (workflow != null)
         {
         {
-	    
-			var orderDelay = await _orderDelayRepository.Queryable().Includes(x => x.Order).Where(x => x.Id == workflow.ExternalId)
+            var orderDelay = await _orderDelayRepository.Queryable().Includes(x => x.Order).Where(x => x.Id == workflow.ExternalId)
                 .FirstAsync(HttpContext.RequestAborted);
                 .FirstAsync(HttpContext.RequestAborted);
             if (orderDelay != null)
             if (orderDelay != null)
             {
             {
-				var result = await _workflowApplication.GetNextStepsAsync(workflowId, HttpContext.RequestAborted);
-				if (!orderDelay.Order.IsProvince)
+                var result = await _workflowApplication.GetNextStepsAsync(workflowId, HttpContext.RequestAborted);
+                if (!orderDelay.Order.IsProvince)
                 {
                 {
                     if (result.Steps.Any(x => x.Value == "省审批"))
                     if (result.Steps.Any(x => x.Value == "省审批"))
                     {
                     {
                         result.Steps.Remove(result.Steps.First(x => x.Value == "省审批"));
                         result.Steps.Remove(result.Steps.First(x => x.Value == "省审批"));
                     }
                     }
                 }
                 }
+
                 if (!_sessionContext.OrgIsCenter && currentStep.Name != "中心初审")
                 if (!_sessionContext.OrgIsCenter && currentStep.Name != "中心初审")
                 {
                 {
-	                if (result.Steps.Any(x => x.Value == "中心终审"))
-	                {
-		                result.Steps.Remove(result.Steps.First(x => x.Value == "中心终审"));
-	                }
+                    if (result.Steps.Any(x => x.Value == "中心终审"))
+                    {
+                        result.Steps.Remove(result.Steps.First(x => x.Value == "中心终审"));
+                    }
                 }
                 }
 
 
-				return result;
+                return result;
             }
             }
         }
         }
 
 
@@ -1785,6 +1844,10 @@ public class OrderController : BaseController
             .WhereIF(dto.IsApply == true, d => d.DelayState != EDelayState.Examining)
             .WhereIF(dto.IsApply == true, d => d.DelayState != EDelayState.Examining)
             .WhereIF(dto.IsApply == false, d => d.DelayState == EDelayState.Examining)
             .WhereIF(dto.IsApply == false, d => d.DelayState == EDelayState.Examining)
             .WhereIF(dto.DelayState != null, d => d.DelayState == dto.DelayState)
             .WhereIF(dto.DelayState != null, d => d.DelayState == dto.DelayState)
+            .WhereIF(dto.DataScope is 1, x => x.CreatorId == _sessionContext.RequiredUserId)
+            .WhereIF(dto.QueryDelayState is EQueryDelayState.Examining, d => d.DelayState == EDelayState.Examining)
+            .WhereIF(dto.QueryDelayState is EQueryDelayState.Pass, d => d.DelayState == EDelayState.Pass)
+            .WhereIF(dto.QueryDelayState is EQueryDelayState.NoPass, d => d.DelayState == EDelayState.NoPass)
             .OrderByDescending(d => d.ApplyDelayTime)
             .OrderByDescending(d => d.ApplyDelayTime)
             .ToPagedListAsync(dto.PageIndex, dto.PageSize, HttpContext.RequestAborted);
             .ToPagedListAsync(dto.PageIndex, dto.PageSize, HttpContext.RequestAborted);
 
 
@@ -1883,14 +1946,16 @@ public class OrderController : BaseController
                 result.Steps.Remove(result.Steps.First(x => x.Value == "中心初审"));
                 result.Steps.Remove(result.Steps.First(x => x.Value == "中心初审"));
             }
             }
         }
         }
-        else {
-	        if (result.Steps.Any(x => x.Value == "中心终审"))
-	        {
-		        result.Steps.Remove(result.Steps.First(x => x.Value == "中心终审"));
-	        }
-		}
+        else
+        {
+            if (result.Steps.Any(x => x.Value == "中心终审"))
+            {
+                result.Steps.Remove(result.Steps.First(x => x.Value == "中心终审"));
+            }
+        }
+
         return result;
         return result;
-	}
+    }
 
 
     /// <summary>
     /// <summary>
     /// 延期页面基础信息
     /// 延期页面基础信息
@@ -1902,6 +1967,7 @@ public class OrderController : BaseController
         var rsp = new
         var rsp = new
         {
         {
             DelayState = EnumExts.GetDescriptions<EDelayState>(),
             DelayState = EnumExts.GetDescriptions<EDelayState>(),
+            QueryDelayState = EnumExts.GetDescriptions<EQueryDelayState>(),
             TimeType = EnumExts.GetDescriptions<ETimeType>()
             TimeType = EnumExts.GetDescriptions<ETimeType>()
         };
         };
         return rsp;
         return rsp;
@@ -1929,8 +1995,9 @@ public class OrderController : BaseController
             // dto.CreationTimeStart = _timeLimitDomainService.CalcWorkTimeReduce(DateTime.Now, 5);
             // dto.CreationTimeStart = _timeLimitDomainService.CalcWorkTimeReduce(DateTime.Now, 5);
             dto.CreationTimeStart = await _expireTime.CalcWorkTimeReduce(DateTime.Now, 5);
             dto.CreationTimeStart = await _expireTime.CalcWorkTimeReduce(DateTime.Now, 5);
         }
         }
-		var (total, items) = await _orderApplication.MayScreenList(dto)
-			.ToPagedListAsync(dto.PageIndex, dto.PageSize, HttpContext.RequestAborted);
+
+        var (total, items) = await _orderApplication.MayScreenList(dto)
+            .ToPagedListAsync(dto.PageIndex, dto.PageSize, HttpContext.RequestAborted);
         return new PagedDto<OrderVisitDetailDto>(total, _mapper.Map<IReadOnlyList<OrderVisitDetailDto>>(items));
         return new PagedDto<OrderVisitDetailDto>(total, _mapper.Map<IReadOnlyList<OrderVisitDetailDto>>(items));
     }
     }
 
 
@@ -1941,49 +2008,49 @@ public class OrderController : BaseController
     [HttpPost("mayscreen/_export")]
     [HttpPost("mayscreen/_export")]
     public async Task<FileStreamResult> ScreenListExport([FromBody] ExportExcelDto<MayScreenListDto> dto)
     public async Task<FileStreamResult> ScreenListExport([FromBody] ExportExcelDto<MayScreenListDto> dto)
     {
     {
-	    if (_appOptions.Value.IsYiBin) dto.QueryDto.ScreenType = EOrderScreenType.Org;
+        if (_appOptions.Value.IsYiBin) dto.QueryDto.ScreenType = EOrderScreenType.Org;
 
 
-	    dto.QueryDto.CreationTimeEnd = DateTime.Now;
-	    dto.QueryDto.CreationTimeStart = DateTime.Now;
-	    if (dto.QueryDto.IsHomePage != null && dto.QueryDto.IsHomePage == true)
-	    {
-		    // dto.CreationTimeStart = _timeLimitDomainService.CalcWorkTimeReduce(DateTime.Now, 5);
-		    dto.QueryDto.CreationTimeStart = await _expireTime.CalcWorkTimeReduce(DateTime.Now, 5);
-	    }
+        dto.QueryDto.CreationTimeEnd = DateTime.Now;
+        dto.QueryDto.CreationTimeStart = DateTime.Now;
+        if (dto.QueryDto.IsHomePage != null && dto.QueryDto.IsHomePage == true)
+        {
+            // dto.CreationTimeStart = _timeLimitDomainService.CalcWorkTimeReduce(DateTime.Now, 5);
+            dto.QueryDto.CreationTimeStart = await _expireTime.CalcWorkTimeReduce(DateTime.Now, 5);
+        }
 
 
-		var query = _orderApplication.MayScreenList(dto.QueryDto);
-	    List<OrderVisitDetail> data;
-	    if (dto.IsExportAll)
-	    {
-		    data = await query.ToListAsync(HttpContext.RequestAborted);
-	    }
-	    else
-	    {
-		    var (_, items) = await query.ToPagedListAsync(dto.QueryDto, HttpContext.RequestAborted);
-		    data = items;
-	    }
+        var query = _orderApplication.MayScreenList(dto.QueryDto);
+        List<OrderVisitDetail> data;
+        if (dto.IsExportAll)
+        {
+            data = await query.ToListAsync(HttpContext.RequestAborted);
+        }
+        else
+        {
+            var (_, items) = await query.ToPagedListAsync(dto.QueryDto, HttpContext.RequestAborted);
+            data = items;
+        }
 
 
-	    var dataDtos = _mapper.Map<ICollection<OrderVisitDetailDto>>(data);
+        var dataDtos = _mapper.Map<ICollection<OrderVisitDetailDto>>(data);
 
 
-	    dynamic? dynamicClass = DynamicClassHelper.CreateDynamicClass(dto.ColumnInfos);
+        dynamic? dynamicClass = DynamicClassHelper.CreateDynamicClass(dto.ColumnInfos);
 
 
-	    var dtos = dataDtos
-		    .Select(stu => _mapper.Map(stu, typeof(OrderVisitDetailDto), dynamicClass))
-		    .Cast<object>()
-		    .ToList();
+        var dtos = dataDtos
+            .Select(stu => _mapper.Map(stu, typeof(OrderVisitDetailDto), dynamicClass))
+            .Cast<object>()
+            .ToList();
 
 
-	    var stream = ExcelHelper.CreateStream(dtos);
+        var stream = ExcelHelper.CreateStream(dtos);
 
 
-	    return ExcelStreamResult(stream, "工单甄别待申请列表数据");
+        return ExcelStreamResult(stream, "工单甄别待申请列表数据");
     }
     }
 
 
 
 
-	/// <summary>
-	/// 工单甄别列表
-	/// </summary>
-	/// <param name="dto"></param>
-	/// <returns></returns>
-	[HttpGet("screen")]
+    /// <summary>
+    /// 工单甄别列表
+    /// </summary>
+    /// <param name="dto"></param>
+    /// <returns></returns>
+    [HttpGet("screen")]
     public async Task<PagedDto<OrderScreenListDto>> ScreenList([FromQuery] ScreenListDto dto)
     public async Task<PagedDto<OrderScreenListDto>> ScreenList([FromQuery] ScreenListDto dto)
     {
     {
         var (total, items) = await _orderApplication.OrderScreenList(dto)
         var (total, items) = await _orderApplication.OrderScreenList(dto)
@@ -2022,7 +2089,7 @@ public class OrderController : BaseController
 
 
         var stream = ExcelHelper.CreateStream(dtos);
         var stream = ExcelHelper.CreateStream(dtos);
 
 
-        return ExcelStreamResult(stream, "工单甄别列表数据");
+        return ExcelStreamResult(stream, "工单甄别数据");
     }
     }
 
 
 
 
@@ -2181,13 +2248,15 @@ public class OrderController : BaseController
                 result.Steps.Remove(result.Steps.First(x => x.Value == "中心初审"));
                 result.Steps.Remove(result.Steps.First(x => x.Value == "中心初审"));
             }
             }
         }
         }
-        else {
-			if (result.Steps.Any(x => x.Value == "中心班长"))
-			{
-				result.Steps.Remove(result.Steps.First(x => x.Value == "中心班长"));
-			}
-		}
-		return result;
+        else
+        {
+            if (result.Steps.Any(x => x.Value == "中心班长"))
+            {
+                result.Steps.Remove(result.Steps.First(x => x.Value == "中心班长"));
+            }
+        }
+
+        return result;
     }
     }
 
 
     /// <summary>
     /// <summary>
@@ -2227,11 +2296,12 @@ public class OrderController : BaseController
 
 
                 if (!_sessionContext.OrgIsCenter)
                 if (!_sessionContext.OrgIsCenter)
                 {
                 {
-					if (result.Steps.Any(x => x.Value == "中心班长"))
-					{
-						result.Steps.Remove(result.Steps.First(x => x.Value == "中心班长"));
-					}
-				}
+                    if (result.Steps.Any(x => x.Value == "中心班长"))
+                    {
+                        result.Steps.Remove(result.Steps.First(x => x.Value == "中心班长"));
+                    }
+                }
+
                 return result;
                 return result;
             }
             }
         }
         }
@@ -2250,6 +2320,7 @@ public class OrderController : BaseController
         {
         {
             ScreenStatus = EnumExts.GetDescriptions<EScreenStatus>(),
             ScreenStatus = EnumExts.GetDescriptions<EScreenStatus>(),
             OrderScreenType = EnumExts.GetDescriptions<EOrderScreenType>(),
             OrderScreenType = EnumExts.GetDescriptions<EOrderScreenType>(),
+            QueryScreenType = EnumExts.GetDescriptions<EQueryOrderScreenType>(),
             ScreenType = _sysDicDataCacheManager.GetSysDicDataCache(SysDicTypeConsts.ScreenType),
             ScreenType = _sysDicDataCacheManager.GetSysDicDataCache(SysDicTypeConsts.ScreenType),
             CounterSignType = EnumExts.GetDescriptions<ECounterSignType>(),
             CounterSignType = EnumExts.GetDescriptions<ECounterSignType>(),
             AcceptType = _sysDicDataCacheManager.GetSysDicDataCache(SysDicTypeConsts.AcceptType),
             AcceptType = _sysDicDataCacheManager.GetSysDicDataCache(SysDicTypeConsts.AcceptType),
@@ -3274,7 +3345,7 @@ public class OrderController : BaseController
     [HttpPost("add-anonymous")]
     [HttpPost("add-anonymous")]
     [AllowAnonymous]
     [AllowAnonymous]
     [LogFilter("省平台调用记录")]
     [LogFilter("省平台调用记录")]
-	public async Task<AddOrderResponse> AddAnonymous([FromBody] AddOrderDto dto)
+    public async Task<AddOrderResponse> AddAnonymous([FromBody] AddOrderDto dto)
     {
     {
         return await _orderApplication.ReceiveOrderFromExternalAsync(dto, HttpContext.RequestAborted);
         return await _orderApplication.ReceiveOrderFromExternalAsync(dto, HttpContext.RequestAborted);
     }
     }
@@ -3468,6 +3539,11 @@ public class OrderController : BaseController
         CityBaseConfiguration cityBase = JsonSerializer.Deserialize<CityBaseConfiguration>(setting);
         CityBaseConfiguration cityBase = JsonSerializer.Deserialize<CityBaseConfiguration>(setting);
         if (dto.Workflow.NextHandlers.Any(d => d.Key == cityBase.CityProvince.OrgId || d.Key == cityBase.CityProvinceAssign.OrgId))
         if (dto.Workflow.NextHandlers.Any(d => d.Key == cityBase.CityProvince.OrgId || d.Key == cityBase.CityProvinceAssign.OrgId))
         {
         {
+            if (dto.Workflow.NextHandlers.Any(d => d.Key == cityBase.CityProvince.OrgId) && order.OrderExtension is null)
+            {
+                throw UserFriendlyException.SameMessage("该工单不存在拓展信息,不能推送至全国12315平台!");
+            }
+
             var timeResult = await _expireTime.CalcEndTime(DateTime.Now, ETimeType.WorkDay, 45, 80, 50);
             var timeResult = await _expireTime.CalcEndTime(DateTime.Now, ETimeType.WorkDay, 45, 80, 50);
             expiredTimeConfig = new ExpiredTimeWithConfig
             expiredTimeConfig = new ExpiredTimeWithConfig
             {
             {
@@ -3588,18 +3664,18 @@ public class OrderController : BaseController
         BasicWorkflowDto workflowDto, CancellationToken cancellationToken)
         BasicWorkflowDto workflowDto, CancellationToken cancellationToken)
     {
     {
         var isAutoFillSummaryOpinion = bool.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.IsAutoFillSummaryOpinion).SettingValue[0]);
         var isAutoFillSummaryOpinion = bool.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.IsAutoFillSummaryOpinion).SettingValue[0]);
-        
+
         switch (orderHandleFlowDto.OrderAssignMode)
         switch (orderHandleFlowDto.OrderAssignMode)
         {
         {
             case EOrderAssignMode.AdjoinLevel:
             case EOrderAssignMode.AdjoinLevel:
                 var nextDto = _mapper.Map<NextWorkflowDto>(workflowDto);
                 var nextDto = _mapper.Map<NextWorkflowDto>(workflowDto);
                 nextDto.WorkflowId = startStep.WorkflowId;
                 nextDto.WorkflowId = startStep.WorkflowId;
                 nextDto.StepId = startStep.Id;
                 nextDto.StepId = startStep.Id;
-                
+
                 if (workflowDto.BusinessType == EBusinessType.Send)
                 if (workflowDto.BusinessType == EBusinessType.Send)
                 {
                 {
-                    // 宜宾需求: 1.是否是派单节点  2.是否存在历史派单节点  3.存在获取上个派单节点  4.不存在走平均派单
-                    if (_appOptions.Value.IsYiBin)
+                    // 宜宾需求: 1.是否是派单节点  2.是否存在历史派单节点  3.存在获取上个派单节点  4.不存在走平均派单 
+                    if (_appOptions.Value.IsYiBin || _appOptions.Value.IsZiGong)
                     {
                     {
                         var sendOrderTraces = workflow.Traces.Where(x => x.BusinessType == EBusinessType.Send);
                         var sendOrderTraces = workflow.Traces.Where(x => x.BusinessType == EBusinessType.Send);
                         if (sendOrderTraces.Any())
                         if (sendOrderTraces.Any())
@@ -3628,7 +3704,7 @@ public class OrderController : BaseController
                     }
                     }
                 }
                 }
 
 
-                await _workflowDomainService.NextAsync(_sessionContext, nextDto, order.ExpiredTime,isAutoFillSummaryOpinion, cancellationToken);
+                await _workflowDomainService.NextAsync(_sessionContext, nextDto, order.ExpiredTime, isAutoFillSummaryOpinion, cancellationToken);
                 break;
                 break;
             case EOrderAssignMode.CrossLevel:
             case EOrderAssignMode.CrossLevel:
                 if (!orderHandleFlowDto.CrossSteps.Any())
                 if (!orderHandleFlowDto.CrossSteps.Any())
@@ -3637,7 +3713,7 @@ public class OrderController : BaseController
                 orderHandleFlowDto.CrossSteps = orderHandleFlowDto.CrossSteps.OrderBy(d => d.Sort).ToList();
                 orderHandleFlowDto.CrossSteps = orderHandleFlowDto.CrossSteps.OrderBy(d => d.Sort).ToList();
                 var stepCount = orderHandleFlowDto.CrossSteps.Count;
                 var stepCount = orderHandleFlowDto.CrossSteps.Count;
                 var unhandleSteps = new List<WorkflowStep> { startStep };
                 var unhandleSteps = new List<WorkflowStep> { startStep };
-                for (int i = 0; i < stepCount; i++)
+                for (int i = 0;i < stepCount;i++)
                 {
                 {
                     var crossStep = orderHandleFlowDto.CrossSteps[i];
                     var crossStep = orderHandleFlowDto.CrossSteps[i];
                     var tempSteps = new List<WorkflowStep>();
                     var tempSteps = new List<WorkflowStep>();
@@ -3833,7 +3909,7 @@ public class OrderController : BaseController
             IdentityTypeOptions = EnumExts.GetDescriptions<EIdentityType>(),
             IdentityTypeOptions = EnumExts.GetDescriptions<EIdentityType>(),
             OrderTags = _sysDicDataCacheManager.GetSysDicDataCache(SysDicTypeConsts.OrderTag),
             OrderTags = _sysDicDataCacheManager.GetSysDicDataCache(SysDicTypeConsts.OrderTag),
             FocusOnEvents = _sysDicDataCacheManager.GetSysDicDataCache(SysDicTypeConsts.FocusOnEvent)
             FocusOnEvents = _sysDicDataCacheManager.GetSysDicDataCache(SysDicTypeConsts.FocusOnEvent)
-    };
+        };
         return rsp;
         return rsp;
     }
     }
 
 
@@ -3948,19 +4024,43 @@ public class OrderController : BaseController
     }
     }
 
 
     /// <summary>
     /// <summary>
-    /// 批量归档
+    /// 批量归档(暂时只支持派单组节点操作)
     /// </summary>
     /// </summary>
+    /// <returns>成功总量</returns>
     [HttpPost("batch-file")]
     [HttpPost("batch-file")]
-    public async Task BatchFile([FromBody]OrderBatchFileDto dto)
+    public async Task<string> BatchFile([FromBody] OrderBatchFileDto dto)
     {
     {
         var orders = await _orderRepository.Queryable()
         var orders = await _orderRepository.Queryable()
+            .Includes(w => w.Workflow, d => d.WorkflowDefinition)
+            .Includes(w => w.Workflow, d => d.Steps)
+            .Includes(w => w.Workflow, d => d.Traces)
+            .Includes(w => w.Workflow, d => d.Countersigns)
             .Where(d => dto.OrderIds.Contains(d.Id))
             .Where(d => dto.OrderIds.Contains(d.Id))
             .ToListAsync(HttpContext.RequestAborted);
             .ToListAsync(HttpContext.RequestAborted);
+        var success = 0;
         foreach (var order in orders)
         foreach (var order in orders)
         {
         {
-            await _workflowDomainService.JumpToEndAsync(_sessionContext, order.WorkflowId,dto.Opinion,dto.Files,
+            //非派单组节点办理时不允许操作
+            var unhandleSteps = order.Workflow.Steps
+                .Where(d => d.Status != EWorkflowStepStatus.Handled)
+                .ToList();
+            if (unhandleSteps.Count == 0
+                || unhandleSteps.Count > 2
+                || unhandleSteps.First().BusinessType != EBusinessType.Send)
+                continue;
+
+            var startStep = order.Workflow.Steps.Where(d => d.StepType == EStepType.Start && d.IsOrigin)
+                .MaxBy(d => d.CreationTime);
+            if (startStep?.Status is not EWorkflowStepStatus.Handled || string.IsNullOrEmpty(startStep.Opinion))
+                continue;
+            var opinion = startStep.Opinion;
+
+            await _workflowDomainService.JumpToEndAsync(_sessionContext, order.Workflow, opinion, dto.Files,
                 order.ExpiredTime, cancellationToken: HttpContext.RequestAborted);
                 order.ExpiredTime, cancellationToken: HttpContext.RequestAborted);
+            success++;
         }
         }
+
+        return $"批量办理{dto.OrderIds.Count}件,成功{success}件";
     }
     }
 
 
     #endregion
     #endregion
@@ -3985,7 +4085,7 @@ public class OrderController : BaseController
         {
         {
             query.WhereIF(dto.QueryType is 1, d => d.IsForwarded == false)
             query.WhereIF(dto.QueryType is 1, d => d.IsForwarded == false)
                 .WhereIF(dto.QueryType is 2, d => d.IsForwarded == true)
                 .WhereIF(dto.QueryType is 2, d => d.IsForwarded == true)
-                .Where(d => SqlFunc.Subqueryable<OrderSpecial>().Where(os => os.OrderId == d.Id && os.SpecialType == ESpecialType.ReTransact)
+                .Where(d => SqlFunc.Subqueryable<OrderSpecial>().Where(os => os.OrderId == d.Id && os.IsDeleted == false && os.SpecialType == ESpecialType.ReTransact)
                     .NotAny());
                     .NotAny());
         }
         }
 
 
@@ -3995,7 +4095,10 @@ public class OrderController : BaseController
                         d.Status != EOrderStatus.SpecialToUnAccept &&
                         d.Status != EOrderStatus.SpecialToUnAccept &&
                         d.Status != EOrderStatus.HandOverToUnAccept)
                         d.Status != EOrderStatus.HandOverToUnAccept)
             .WhereIF(dto.QueryType is 3,
             .WhereIF(dto.QueryType is 3,
-                d => SqlFunc.Subqueryable<OrderSpecial>().Where(os => os.OrderId == d.Id && os.SpecialType == ESpecialType.ReTransact).Any())
+                d => SqlFunc.Subqueryable<OrderSpecial>().Where(os => os.OrderId == d.Id  && os.IsDeleted == false && os.SpecialType == ESpecialType.ReTransact).Any())
+            .Where(d => SqlFunc.Subqueryable<OrderDelay>().Where(od => od.OrderId == d.Id && od.IsDeleted == false && od.DelayState == EDelayState.Examining).NotAny())
+            .Where(d => SqlFunc.Subqueryable<OrderSendBackAudit>().Where(osba => osba.OrderId == d.Id && osba.IsDeleted == false  && osba.State == ESendBackAuditState.Apply)
+                .NotAny())
             .WhereIF(dto.IsProvince.HasValue, d => d.IsProvince == dto.IsProvince)
             .WhereIF(dto.IsProvince.HasValue, d => d.IsProvince == dto.IsProvince)
             .WhereIF(!string.IsNullOrEmpty(dto.Keyword), d => d.Title.StartsWith(dto.Keyword))
             .WhereIF(!string.IsNullOrEmpty(dto.Keyword), d => d.Title.StartsWith(dto.Keyword))
             .WhereIF(!string.IsNullOrEmpty(dto.No), d => d.No == dto.No)
             .WhereIF(!string.IsNullOrEmpty(dto.No), d => d.No == dto.No)
@@ -4015,9 +4118,9 @@ public class OrderController : BaseController
             .WhereIF(dto.EndTime.HasValue, d => d.StartTime <= dto.EndTime)
             .WhereIF(dto.EndTime.HasValue, d => d.StartTime <= dto.EndTime)
             .WhereIF(dto.IsUrgent.HasValue, d => d.IsUrgent == dto.IsUrgent!.Value)
             .WhereIF(dto.IsUrgent.HasValue, d => d.IsUrgent == dto.IsUrgent!.Value)
             .WhereIF(dto.Status.HasValue, d => d.Status == dto.Status)
             .WhereIF(dto.Status.HasValue, d => d.Status == dto.Status)
-            .OrderByDescending(d=> d.IsUrgent)
-            .OrderByIF(string.IsNullOrEmpty(dto.SortField),d => d.StartTime ,OrderByType.Desc)
-            .OrderByIF(dto is { SortField: "creationTime", SortRule:0 },d=> d.CreationTime,OrderByType.Asc) //创建时间升序
+            .OrderByDescending(d => d.IsUrgent)
+            .OrderByIF(string.IsNullOrEmpty(dto.SortField), d => d.StartTime, OrderByType.Desc)
+            .OrderByIF(dto is { SortField: "creationTime", SortRule: 0 }, d => d.CreationTime, OrderByType.Asc) //创建时间升序
             .OrderByIF(dto is { SortField: "creationTime", SortRule: 1 }, d => d.CreationTime, OrderByType.Desc) //创建时间降序
             .OrderByIF(dto is { SortField: "creationTime", SortRule: 1 }, d => d.CreationTime, OrderByType.Desc) //创建时间降序
             .OrderByIF(dto is { SortField: "startTime", SortRule: 0 }, d => d.StartTime, OrderByType.Asc) //受理时间升序
             .OrderByIF(dto is { SortField: "startTime", SortRule: 0 }, d => d.StartTime, OrderByType.Asc) //受理时间升序
             .OrderByIF(dto is { SortField: "startTime", SortRule: 1 }, d => d.StartTime, OrderByType.Desc) //受理时间降序
             .OrderByIF(dto is { SortField: "startTime", SortRule: 1 }, d => d.StartTime, OrderByType.Desc) //受理时间降序
@@ -4112,7 +4215,7 @@ public class OrderController : BaseController
             .WhereIF(!string.IsNullOrEmpty(dto.CenterToOrgHandlerName), d => d.CenterToOrgHandlerName == dto.CenterToOrgHandlerName)
             .WhereIF(!string.IsNullOrEmpty(dto.CenterToOrgHandlerName), d => d.CenterToOrgHandlerName == dto.CenterToOrgHandlerName)
             .WhereIF(dto.IsUrgent.HasValue, d => d.IsUrgent == dto.IsUrgent!.Value)
             .WhereIF(dto.IsUrgent.HasValue, d => d.IsUrgent == dto.IsUrgent!.Value)
             .OrderBy(d => d.Status)
             .OrderBy(d => d.Status)
-            .OrderByIF(string.IsNullOrEmpty(dto.SortField),d => d.CreationTime, OrderByType.Desc)
+            .OrderByIF(string.IsNullOrEmpty(dto.SortField), d => d.CreationTime, OrderByType.Desc)
             .OrderByIF(dto is { SortField: "startTime", SortRule: 0 }, d => d.StartTime, OrderByType.Asc) //受理时间升序
             .OrderByIF(dto is { SortField: "startTime", SortRule: 0 }, d => d.StartTime, OrderByType.Asc) //受理时间升序
             .OrderByIF(dto is { SortField: "startTime", SortRule: 1 }, d => d.StartTime, OrderByType.Desc) //受理时间降序
             .OrderByIF(dto is { SortField: "startTime", SortRule: 1 }, d => d.StartTime, OrderByType.Desc) //受理时间降序
             .OrderByIF(dto is { SortField: "expiredTime", SortRule: 0 }, d => d.ExpiredTime, OrderByType.Asc) //期满时间升序
             .OrderByIF(dto is { SortField: "expiredTime", SortRule: 0 }, d => d.ExpiredTime, OrderByType.Asc) //期满时间升序
@@ -4239,6 +4342,7 @@ public class OrderController : BaseController
             .Queryable()
             .Queryable()
             .Includes(d => d.Workflow)
             .Includes(d => d.Workflow)
             .FirstAsync(d => d.Id == workflow.ExternalId);
             .FirstAsync(d => d.Id == workflow.ExternalId);
+        dto.ExpiredTime = order.ExpiredTime;
         var (currentStep, prevStep, isOrgToCenter, isSecondToFirstOrgLevel) = await _workflowApplication.GetPreviousInformationAsync(
         var (currentStep, prevStep, isOrgToCenter, isSecondToFirstOrgLevel) = await _workflowApplication.GetPreviousInformationAsync(
             dto.WorkflowId, _sessionContext.RequiredUserId, _sessionContext.RequiredOrgId, _sessionContext.Roles, HttpContext.RequestAborted);
             dto.WorkflowId, _sessionContext.RequiredUserId, _sessionContext.RequiredOrgId, _sessionContext.Roles, HttpContext.RequestAborted);
         var audit = new OrderSendBackAudit
         var audit = new OrderSendBackAudit
@@ -4305,7 +4409,6 @@ public class OrderController : BaseController
                 audit.State = ESendBackAuditState.End;
                 audit.State = ESendBackAuditState.End;
                 audit.AuditUser = "默认通过";
                 audit.AuditUser = "默认通过";
                 audit.AuditTime = DateTime.Now;
                 audit.AuditTime = DateTime.Now;
-                dto.ExpiredTime = order.ExpiredTime;
                 var flowDirection = await _workflowApplication.PreviousAsync(dto, HttpContext.RequestAborted);
                 var flowDirection = await _workflowApplication.PreviousAsync(dto, HttpContext.RequestAborted);
                 var processType = flowDirection == EFlowDirection.OrgToCenter || flowDirection == EFlowDirection.CenterToCenter
                 var processType = flowDirection == EFlowDirection.OrgToCenter || flowDirection == EFlowDirection.CenterToCenter
                     ? EProcessType.Zhiban
                     ? EProcessType.Zhiban
@@ -4363,6 +4466,23 @@ public class OrderController : BaseController
         if (sendBack.State == ESendBackAuditState.End)
         if (sendBack.State == ESendBackAuditState.End)
         {
         {
             var order = await _orderRepository.Queryable().Includes(d => d.Workflow).FirstAsync(d => d.Id == sendBack.OrderId);
             var order = await _orderRepository.Queryable().Includes(d => d.Workflow).FirstAsync(d => d.Id == sendBack.OrderId);
+            if (_appOptions.Value.IsZiGong)
+            {
+                var (currentStep, prevStep, isOrgToCenter, isSecondToFirstOrgLevel) = await _workflowApplication.GetPreviousInformationAsync(
+                    order.WorkflowId, sendBack.WorkflowUserId, sendBack.WorkflowOrgId, sendBack.WorkflowRoleIds.ToArray(),
+                    HttpContext.RequestAborted);
+                if (prevStep.BusinessType == EBusinessType.Send)
+                {
+                    // 平均派单
+                    var averageSendOrder = bool.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.AverageSendOrder).SettingValue[0]);
+                    if (averageSendOrder)
+                    {
+                        var handler = await _orderDomainService.AverageOrder(HttpContext.RequestAborted);
+                        sendBack.SendBackData.Handler = handler;
+                    }
+                }
+            }
+
             //string applicantId, string applicantOrgId, string[] applicantRoleIds,
             //string applicantId, string applicantOrgId, string[] applicantRoleIds,
             //    ISessionContext current, CancellationToken cancellationToken);
             //    ISessionContext current, CancellationToken cancellationToken);
             sendBack.SendBackData.ExpiredTime = order.ExpiredTime;
             sendBack.SendBackData.ExpiredTime = order.ExpiredTime;
@@ -4416,6 +4536,23 @@ public class OrderController : BaseController
             if (sendBack.State == ESendBackAuditState.End)
             if (sendBack.State == ESendBackAuditState.End)
             {
             {
                 var order = await _orderRepository.Queryable().Includes(d => d.Workflow).FirstAsync(d => d.Id == sendBack.OrderId);
                 var order = await _orderRepository.Queryable().Includes(d => d.Workflow).FirstAsync(d => d.Id == sendBack.OrderId);
+                if (_appOptions.Value.IsZiGong)
+                {
+                    var (currentStep, prevStep, isOrgToCenter, isSecondToFirstOrgLevel) = await _workflowApplication.GetPreviousInformationAsync(
+                        order.WorkflowId, sendBack.WorkflowUserId, sendBack.WorkflowOrgId, sendBack.WorkflowRoleIds.ToArray(),
+                        HttpContext.RequestAborted);
+                    if (prevStep.BusinessType == EBusinessType.Send)
+                    {
+                        // 平均派单
+                        var averageSendOrder = bool.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.AverageSendOrder).SettingValue[0]);
+                        if (averageSendOrder)
+                        {
+                            var handler = await _orderDomainService.AverageOrder(HttpContext.RequestAborted);
+                            sendBack.SendBackData.Handler = handler;
+                        }
+                    }
+                }
+
                 sendBack.SendBackData.ExpiredTime = order.ExpiredTime;
                 sendBack.SendBackData.ExpiredTime = order.ExpiredTime;
                 var flowDirection = await _workflowApplication.PreviousAsync(sendBack.SendBackData,
                 var flowDirection = await _workflowApplication.PreviousAsync(sendBack.SendBackData,
                     sendBack.WorkflowUserId, sendBack.WorkflowOrgId, sendBack.WorkflowRoleIds.ToArray(),
                     sendBack.WorkflowUserId, sendBack.WorkflowOrgId, sendBack.WorkflowRoleIds.ToArray(),
@@ -4746,17 +4883,27 @@ public class OrderController : BaseController
             .AnyAsync();
             .AnyAsync();
         if (specialAny) throw UserFriendlyException.SameMessage("工单已存在待审批特提信息!");
         if (specialAny) throw UserFriendlyException.SameMessage("工单已存在待审批特提信息!");
         var order = await _orderRepository.Queryable().Includes(d => d.Workflow).FirstAsync(d => d.Id == dto.OrderId);
         var order = await _orderRepository.Queryable().Includes(d => d.Workflow).FirstAsync(d => d.Id == dto.OrderId);
-		await _orderApplication.SpecialVerify(dto, order, HttpContext.RequestAborted);
+        await _orderApplication.SpecialVerify(dto, order, HttpContext.RequestAborted);
+        if (string.IsNullOrEmpty(dto.Cause))
+        {
+            dto.Cause = dto.Reason;
+        }
 
 
-		var  workflow = await _workflowDomainService.GetWorkflowAsync(order.WorkflowId, withSteps: true, cancellationToken: HttpContext.RequestAborted);
+        var workflow = await _workflowDomainService.GetWorkflowAsync(order.WorkflowId, withSteps: true,
+            cancellationToken: HttpContext.RequestAborted);
         var currentStep = workflow.Steps.FirstOrDefault(x => x.Status != EWorkflowStepStatus.Handled);
         var currentStep = workflow.Steps.FirstOrDefault(x => x.Status != EWorkflowStepStatus.Handled);
         if (currentStep is null)
         if (currentStep is null)
-	        currentStep = workflow.Steps.OrderByDescending(x => x.CreationTime).FirstOrDefault(x => x.StepType == EStepType.End);
+            currentStep = workflow.Steps.OrderByDescending(x => x.CreationTime).FirstOrDefault(x => x.StepType == EStepType.End);
+
+        if (string.IsNullOrEmpty(dto.Cause))
+        {
+            dto.Cause = dto.Reason;
+        }
 
 
-		var model = _mapper.Map<OrderSpecial>(dto);
-        model.OrgId = currentStep is null ? _sessionContext.RequiredOrgId: currentStep.HandlerOrgId;
+        var model = _mapper.Map<OrderSpecial>(dto);
+        model.OrgId = currentStep is null ? _sessionContext.RequiredOrgId : currentStep.HandlerOrgId;
         model.OrgName = currentStep is null ? _sessionContext.OrgName : currentStep.HandlerOrgName;
         model.OrgName = currentStep is null ? _sessionContext.OrgName : currentStep.HandlerOrgName;
-		var step = await _workflowDomainService.FindLastStepAsync(model.WorkflowId, HttpContext.RequestAborted);
+        var step = await _workflowDomainService.FindLastStepAsync(model.WorkflowId, HttpContext.RequestAborted);
         model.StepName = step.Name;
         model.StepName = step.Name;
         model.StepCode = step.Code;
         model.StepCode = step.Code;
         model.Status = order.Status;
         model.Status = order.Status;
@@ -4796,6 +4943,7 @@ public class OrderController : BaseController
                 {
                 {
                     var handler = await _orderDomainService.AverageOrder(HttpContext.RequestAborted);
                     var handler = await _orderDomainService.AverageOrder(HttpContext.RequestAborted);
                     dto.NextHandlers = new List<FlowStepHandler> { handler };
                     dto.NextHandlers = new List<FlowStepHandler> { handler };
+                    await _orderSpecialRepository.Updateable().SetColumns(x => new OrderSpecial { NextHandlers = dto.NextHandlers }).Where(x => x.Id == model.Id).ExecuteCommandAsync();
                 }
                 }
             }
             }
 
 
@@ -4927,12 +5075,12 @@ public class OrderController : BaseController
     }
     }
 
 
 
 
-	/// <summary>
-	/// 工单重办信息
-	/// </summary>
-	/// <param name="dto"></param>
-	/// <returns></returns>
-	[HttpPost("re_transact")]
+    /// <summary>
+    /// 工单重办信息
+    /// </summary>
+    /// <param name="dto"></param>
+    /// <returns></returns>
+    [HttpPost("re_transact")]
     [LogFilter("工单重办")]
     [LogFilter("工单重办")]
     public async Task Add([FromBody] OrderReTransactDto dto)
     public async Task Add([FromBody] OrderReTransactDto dto)
     {
     {
@@ -4957,14 +5105,15 @@ public class OrderController : BaseController
 
 
         var model = _mapper.Map<OrderSpecial>(dto);
         var model = _mapper.Map<OrderSpecial>(dto);
 
 
-        var workflow = await _workflowDomainService.GetWorkflowAsync(order.WorkflowId, withSteps: true, cancellationToken: HttpContext.RequestAborted);
+        var workflow = await _workflowDomainService.GetWorkflowAsync(order.WorkflowId, withSteps: true,
+            cancellationToken: HttpContext.RequestAborted);
         var currentStep = workflow.Steps.FirstOrDefault(x => x.Status != EWorkflowStepStatus.Handled);
         var currentStep = workflow.Steps.FirstOrDefault(x => x.Status != EWorkflowStepStatus.Handled);
         if (currentStep is null)
         if (currentStep is null)
-	        currentStep = workflow.Steps.OrderByDescending(x => x.CreationTime).FirstOrDefault(x => x.StepType == EStepType.End);
+            currentStep = workflow.Steps.OrderByDescending(x => x.CreationTime).FirstOrDefault(x => x.StepType == EStepType.End);
 
 
         model.OrgId = currentStep is null ? _sessionContext.RequiredOrgId : currentStep.HandlerOrgId;
         model.OrgId = currentStep is null ? _sessionContext.RequiredOrgId : currentStep.HandlerOrgId;
         model.OrgName = currentStep is null ? _sessionContext.OrgName : currentStep.HandlerOrgName;
         model.OrgName = currentStep is null ? _sessionContext.OrgName : currentStep.HandlerOrgName;
-		var step = await _workflowDomainService.FindLastStepAsync(model.WorkflowId, HttpContext.RequestAborted);
+        var step = await _workflowDomainService.FindLastStepAsync(model.WorkflowId, HttpContext.RequestAborted);
         model.StepName = step.Name;
         model.StepName = step.Name;
         model.StepCode = step.Code;
         model.StepCode = step.Code;
         model.State = 1;
         model.State = 1;
@@ -4994,6 +5143,18 @@ public class OrderController : BaseController
 
 
         if (model.State == 1)
         if (model.State == 1)
         {
         {
+            if (_appOptions.Value.IsZiGong && dto.BusinessType == EBusinessType.Send)
+            {
+                // 平均派单
+                var averageSendOrder = bool.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.AverageSendOrder).SettingValue[0]);
+                if (averageSendOrder)
+                {
+                    var handler = await _orderDomainService.AverageOrder(HttpContext.RequestAborted);
+                    dto.NextHandlers = new List<FlowStepHandler> { handler };
+                    await _orderSpecialRepository.Updateable().SetColumns(x => new OrderSpecial { NextHandlers = dto.NextHandlers }).Where(x => x.Id == model.Id).ExecuteCommandAsync();
+                }
+            }
+
             var recall = new RecallDto
             var recall = new RecallDto
             {
             {
                 WorkflowId = dto.WorkflowId!,
                 WorkflowId = dto.WorkflowId!,
@@ -5111,6 +5272,18 @@ public class OrderController : BaseController
         var order = await _orderRepository.GetAsync(x => x.Id == special.OrderId);
         var order = await _orderRepository.GetAsync(x => x.Id == special.OrderId);
         if (special.State == 1)
         if (special.State == 1)
         {
         {
+            if (_appOptions.Value.IsZiGong && special.BusinessType == EBusinessType.Send)
+            {
+                // 平均派单
+                var averageSendOrder = bool.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.AverageSendOrder).SettingValue[0]);
+                if (averageSendOrder)
+                {
+                    var handler = await _orderDomainService.AverageOrder(HttpContext.RequestAborted);
+                    special.NextHandlers = new List<FlowStepHandler> { handler };
+                    await _orderSpecialRepository.Updateable().SetColumns(x => new OrderSpecial { NextHandlers = special.NextHandlers }).Where(x => x.Id == special.Id).ExecuteCommandAsync();
+                }
+            }
+
             var recall = new RecallDto
             var recall = new RecallDto
             {
             {
                 WorkflowId = special.WorkflowId!,
                 WorkflowId = special.WorkflowId!,
@@ -5148,6 +5321,7 @@ public class OrderController : BaseController
                     expiredTime = await _expireTime.CalcEndTime(DateTime.Now, order.AcceptTypeCode);
                     expiredTime = await _expireTime.CalcEndTime(DateTime.Now, order.AcceptTypeCode);
                 }
                 }
             }
             }
+
             if (expiredTime.ExpiredTime < order.ExpiredTime)
             if (expiredTime.ExpiredTime < order.ExpiredTime)
             {
             {
                 expiredTime.ExpiredTime = order.ExpiredTime.Value;
                 expiredTime.ExpiredTime = order.ExpiredTime.Value;
@@ -5246,6 +5420,18 @@ public class OrderController : BaseController
             var order = await _orderRepository.GetAsync(x => x.Id == special.OrderId);
             var order = await _orderRepository.GetAsync(x => x.Id == special.OrderId);
             if (special.State == 1)
             if (special.State == 1)
             {
             {
+                if (_appOptions.Value.IsZiGong && special.BusinessType == EBusinessType.Send)
+                {
+                    // 平均派单
+                    var averageSendOrder = bool.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.AverageSendOrder).SettingValue[0]);
+                    if (averageSendOrder)
+                    {
+                        var handler = await _orderDomainService.AverageOrder(HttpContext.RequestAborted);
+                        special.NextHandlers = new List<FlowStepHandler> { handler };
+                        await _orderSpecialRepository.Updateable().SetColumns(x => new OrderSpecial { NextHandlers = special.NextHandlers }).Where(x => x.Id == special.Id).ExecuteCommandAsync();
+                    }
+                }
+
                 var recall = new RecallDto
                 var recall = new RecallDto
                 {
                 {
                     WorkflowId = special.WorkflowId!,
                     WorkflowId = special.WorkflowId!,
@@ -5283,12 +5469,14 @@ public class OrderController : BaseController
                         expiredTime = await _expireTime.CalcEndTime(DateTime.Now, order.AcceptTypeCode);
                         expiredTime = await _expireTime.CalcEndTime(DateTime.Now, order.AcceptTypeCode);
                     }
                     }
                 }
                 }
+
                 if (expiredTime.ExpiredTime < order.ExpiredTime)
                 if (expiredTime.ExpiredTime < order.ExpiredTime)
                 {
                 {
                     expiredTime.ExpiredTime = order.ExpiredTime.Value;
                     expiredTime.ExpiredTime = order.ExpiredTime.Value;
                     expiredTime.NearlyExpiredTime = order.NearlyExpiredTime.Value;
                     expiredTime.NearlyExpiredTime = order.NearlyExpiredTime.Value;
                     expiredTime.NearlyExpiredTimeOne = order.NearlyExpiredTimeOne.Value;
                     expiredTime.NearlyExpiredTimeOne = order.NearlyExpiredTimeOne.Value;
                 }
                 }
+
                 var processType = special.FlowDirection is EFlowDirection.OrgToCenter or EFlowDirection.CenterToCenter or EFlowDirection.FiledToCenter
                 var processType = special.FlowDirection is EFlowDirection.OrgToCenter or EFlowDirection.CenterToCenter or EFlowDirection.FiledToCenter
                     ? EProcessType.Zhiban
                     ? EProcessType.Zhiban
                     : EProcessType.Jiaoban;
                     : EProcessType.Jiaoban;

+ 72 - 2
src/Hotline.Api/Controllers/SysController.cs

@@ -1,6 +1,12 @@
-using Hotline.Application.Systems;
+using EasyCaching.Core;
+using Hotline.Application.Systems;
 using Hotline.Caching.Interfaces;
 using Hotline.Caching.Interfaces;
+using Hotline.CallCenter.Ivrs;
+using Hotline.CallCenter.Tels;
+using Hotline.FlowEngine.WorkflowModules;
+using Hotline.Orders;
 using Hotline.Permissions;
 using Hotline.Permissions;
+using Hotline.Realtimes;
 using Hotline.Repository.SqlSugar.Extensions;
 using Hotline.Repository.SqlSugar.Extensions;
 using Hotline.Settings;
 using Hotline.Settings;
 using Hotline.Settings.CommonOpinions;
 using Hotline.Settings.CommonOpinions;
@@ -11,10 +17,16 @@ using Hotline.Share.Dtos.Settings;
 using Hotline.Share.Enums.Settings;
 using Hotline.Share.Enums.Settings;
 using Hotline.Share.Requests;
 using Hotline.Share.Requests;
 using Hotline.Share.Tools;
 using Hotline.Share.Tools;
+using Hotline.Users;
+using Hotline.YbEnterprise.Sdk;
 using MapsterMapper;
 using MapsterMapper;
 using Microsoft.AspNetCore.Authorization;
 using Microsoft.AspNetCore.Authorization;
 using Microsoft.AspNetCore.Mvc;
 using Microsoft.AspNetCore.Mvc;
+using NPOI.SS.Formula.Functions;
+using StackExchange.Redis;
+using Wex.Sdk;
 using XF.Domain.Authentications;
 using XF.Domain.Authentications;
+using XF.Domain.Cache;
 using XF.Domain.Exceptions;
 using XF.Domain.Exceptions;
 using XF.Domain.Repository;
 using XF.Domain.Repository;
 using XF.Utility.EnumExtensions;
 using XF.Utility.EnumExtensions;
@@ -39,6 +51,9 @@ namespace Hotline.Api.Controllers
         private readonly ISystemDicDataCacheManager _systemDicDataCacheManager;
         private readonly ISystemDicDataCacheManager _systemDicDataCacheManager;
         private readonly IRepository<SystemLog> _systemLogRepository;
         private readonly IRepository<SystemLog> _systemLogRepository;
         private readonly ISystemMobilAreaApplication _systemMobilAreaApplication;
         private readonly ISystemMobilAreaApplication _systemMobilAreaApplication;
+        private readonly IServiceScopeFactory _serviceScopeFactory;
+        private readonly IRedisCachingProvider _redisCaching;
+        private readonly IEasyCachingProvider _easyCaching;
 
 
         /// <summary>
         /// <summary>
         /// 系统管理相关接口
         /// 系统管理相关接口
@@ -69,7 +84,10 @@ namespace Hotline.Api.Controllers
             ISessionContext sessionContext,
             ISessionContext sessionContext,
             ISystemDicDataCacheManager systemDicDataCacheManager,
             ISystemDicDataCacheManager systemDicDataCacheManager,
             IRepository<SystemLog> systemLogRepository,
             IRepository<SystemLog> systemLogRepository,
-            ISystemMobilAreaApplication systemMobilAreaApplication
+            ISystemMobilAreaApplication systemMobilAreaApplication,
+            IServiceScopeFactory serviceScopeFactory,
+            IRedisCachingProvider redisCaching,
+            IEasyCachingProvider easyCaching
             )
             )
         {
         {
             _mapper = mapper;
             _mapper = mapper;
@@ -85,6 +103,9 @@ namespace Hotline.Api.Controllers
             _systemDicDataCacheManager = systemDicDataCacheManager;
             _systemDicDataCacheManager = systemDicDataCacheManager;
             _systemLogRepository = systemLogRepository;
             _systemLogRepository = systemLogRepository;
             _systemMobilAreaApplication = systemMobilAreaApplication;
             _systemMobilAreaApplication = systemMobilAreaApplication;
+            _serviceScopeFactory = serviceScopeFactory;
+            _redisCaching = redisCaching;
+            _easyCaching = easyCaching;
         }
         }
 
 
         #region 菜单管理
         #region 菜单管理
@@ -595,5 +616,54 @@ namespace Hotline.Api.Controllers
         {
         {
             return await _systemMobilAreaApplication.VaildMobile(mobile);
             return await _systemMobilAreaApplication.VaildMobile(mobile);
         }
         }
+
+        /// <summary>
+        /// 删除缓存
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [HttpPut("cache/remove")]
+        public async Task<IActionResult> ClearCache([FromBody] CleanCacheInDto dto)
+        {
+            foreach (var key in dto.Keys)
+            {
+                await _easyCaching.RemoveAsync(key, HttpContext.RequestAborted);
+                await _redisCaching.KeyDelAsync(key);
+            }
+            return Ok();
+        }
+
+        /// <summary>
+        /// 获取所有缓存的Key
+        /// </summary>
+        /// <param name="cacheKey">不传默认: Hotline:*</param>
+        /// <returns></returns>
+        [HttpGet("cache")]
+        public async Task<List<string>> GetCache(string cacheKey = "Hotline:*")
+        {
+            return await _redisCaching.SearchKeysAsync(cacheKey);
+        }
+
+        /// <summary>
+        /// 获取缓存
+        /// </summary>
+        /// <param name="key"></param>
+        /// <returns></returns>
+        [HttpGet("cache/{key}")]
+        public async Task<List<string>> GetCacheByKey(string key)
+        {
+            var cacheResult = new List<string>();
+            var result = await _easyCaching.GetAsync<object>(key,cancellationToken: HttpContext.RequestAborted);
+            if (result != null)
+            {
+                cacheResult.Add(result.ToJson());
+            }
+            var stringResult = await _redisCaching.StringGetAsync(key);
+            if (stringResult != null)
+            {
+                cacheResult.Add(stringResult);
+            }
+            return cacheResult;
+        }
     }
     }
 }
 }

+ 17 - 1
src/Hotline.Api/Controllers/UserController.cs

@@ -1,4 +1,5 @@
-using Hotline.Application.Users;
+using Hotline.Api.Filter;
+using Hotline.Application.Users;
 using Hotline.Caching.Interfaces;
 using Hotline.Caching.Interfaces;
 using Hotline.CallCenter.Tels;
 using Hotline.CallCenter.Tels;
 using Hotline.Identity.Accounts;
 using Hotline.Identity.Accounts;
@@ -254,6 +255,21 @@ public class UserController : BaseController
         await _accountRepository.SetAccountRolesAsync(user.Id, dto.RoleIds, HttpContext.RequestAborted);
         await _accountRepository.SetAccountRolesAsync(user.Id, dto.RoleIds, HttpContext.RequestAborted);
     }
     }
 
 
+    /// <summary>
+    /// 解锁用户
+    /// </summary>
+    /// <returns></returns>
+    [HttpPut("unlock")]
+    [LogFilter("解锁用户")]
+    public async Task<string> UnlockUserAsync([FromQuery]string id)
+    {
+        var user = await _accountRepository.GetAsync(id) ??
+            throw UserFriendlyException.SameMessage("用户不存在");
+        user.LockoutEnd = null;
+        await _accountRepository.UpdateNullAsync(user, HttpContext.RequestAborted);
+        return "解锁成功";
+    }
+
     /// <summary>
     /// <summary>
     /// 新增用户
     /// 新增用户
     /// </summary>
     /// </summary>

+ 59 - 24
src/Hotline.Api/Controllers/WebPortalController.cs

@@ -116,16 +116,16 @@ namespace Hotline.Api.Controllers
                 .Where(p => SqlFunc.JsonListObjectAny(p.PushRanges, "Key", "2"))
                 .Where(p => SqlFunc.JsonListObjectAny(p.PushRanges, "Key", "2"))
                  .Where(p => p.BulletinTypeId == dto.NoticeType)
                  .Where(p => p.BulletinTypeId == dto.NoticeType)
                  .WhereIF(!string.IsNullOrEmpty(dto.Condition), p => p.Title.Contains(dto.Condition))
                  .WhereIF(!string.IsNullOrEmpty(dto.Condition), p => p.Title.Contains(dto.Condition))
-                 .OrderByDescending(p => p.BulletinTime)
+                 .OrderByDescending(p => p.CreationTime)
                  .Select(it => new
                  .Select(it => new
                  {
                  {
-                     Page = SqlFunc.RowNumber($"{it.BulletinTime} desc "),// SqlFunc.MappingColumn(default(int), "  row_number()  over( order by 'NoticeCreateDate'  ) "),
+                     Page = SqlFunc.RowNumber($"{it.CreationTime} desc "),// SqlFunc.MappingColumn(default(int), "  row_number()  over( order by 'NoticeCreateDate'  ) "),
                      NoticeID = it.Id,
                      NoticeID = it.Id,
                      NoticeContent = it.Content,
                      NoticeContent = it.Content,
                      NoticeTypeName = it.BulletinTypeName,
                      NoticeTypeName = it.BulletinTypeName,
                      NoticeTitle = it.Title,
                      NoticeTitle = it.Title,
                      NoticeBMName = it.SourceOrgName,
                      NoticeBMName = it.SourceOrgName,
-                     NoticeCreateDate = it.BulletinTime,
+                     NoticeCreateDate = it.CreationTime,
                      VideoUrl = "",
                      VideoUrl = "",
                      WNLT_FullCode = ""
                      WNLT_FullCode = ""
                  })
                  })
@@ -159,12 +159,12 @@ namespace Hotline.Api.Controllers
                 .WhereIF(!string.IsNullOrEmpty(dto.BulletinTypeId), p => p.BulletinTypeId == dto.BulletinTypeId)
                 .WhereIF(!string.IsNullOrEmpty(dto.BulletinTypeId), p => p.BulletinTypeId == dto.BulletinTypeId)
                 .WhereIF(!string.IsNullOrEmpty(dto.CheckChar), p => p.Content.Contains(dto.CheckChar))
                 .WhereIF(!string.IsNullOrEmpty(dto.CheckChar), p => p.Content.Contains(dto.CheckChar))
                 .WhereIF(!string.IsNullOrEmpty(dto.BulletinDisplayLocation), p => SqlFunc.JsonListObjectAny(p.DisplayLocation, "Key", dto.BulletinDisplayLocation))
                 .WhereIF(!string.IsNullOrEmpty(dto.BulletinDisplayLocation), p => SqlFunc.JsonListObjectAny(p.DisplayLocation, "Key", dto.BulletinDisplayLocation))
-                .OrderByDescending(p => p.BulletinTime)
+                .OrderByDescending(p => p.CreationTime)
                 .Select(it => new
                 .Select(it => new
                 {
                 {
                     DataID = it.Id,
                     DataID = it.Id,
                     it.Title,
                     it.Title,
-                    CreateDate = it.BulletinTime,
+                    CreateDate = it.CreationTime,
                     it.Content,
                     it.Content,
                     it.BulletinTypeName
                     it.BulletinTypeName
                 })
                 })
@@ -197,7 +197,7 @@ namespace Hotline.Api.Controllers
                     NoticeTypeName = data.BulletinTypeName,
                     NoticeTypeName = data.BulletinTypeName,
                     NoticeTitle = data.Title,
                     NoticeTitle = data.Title,
                     NoticeBMName = data.SourceOrgName,
                     NoticeBMName = data.SourceOrgName,
-                    NoticeCreateDate = data.BulletinTime,
+                    NoticeCreateDate = data.CreationTime,
                     NoticeRCount = data.ReadedNum,
                     NoticeRCount = data.ReadedNum,
                     WNED_VideoUrl = "",
                     WNED_VideoUrl = "",
                     NoticeContent = data.Content
                     NoticeContent = data.Content
@@ -237,13 +237,13 @@ namespace Hotline.Api.Controllers
                     .Where(p => p.BulletinTypeId == dto.NoticeTypeId);
                     .Where(p => p.BulletinTypeId == dto.NoticeTypeId);
             }
             }
 
 
-            var list = await sugar.OrderByDescending(p => p.BulletinTime)
+            var list = await sugar.OrderByDescending(p => p.CreationTime)
                 .Select(it => new
                 .Select(it => new
                 {
                 {
-                    Page = SqlFunc.RowNumber($"{it.BulletinTime} desc "),// SqlFunc.MappingColumn(default(int), "  row_number()  over( order by 'NoticeCreateDate'  ) "),
+                    Page = SqlFunc.RowNumber($"{it.CreationTime} desc "),// SqlFunc.MappingColumn(default(int), "  row_number()  over( order by 'NoticeCreateDate'  ) "),
                     NoticeID = it.Id,
                     NoticeID = it.Id,
                     NoticeTitle = it.Title,
                     NoticeTitle = it.Title,
-                    NoticeCreateDate = it.BulletinTime
+                    NoticeCreateDate = it.CreationTime
                 })
                 })
                 .ToListAsync();
                 .ToListAsync();
             //数据为空返回空数据
             //数据为空返回空数据
@@ -301,17 +301,17 @@ namespace Hotline.Api.Controllers
                 .Where(p => SqlFunc.JsonListObjectAny(p.PushRanges, "Key", "2"))
                 .Where(p => SqlFunc.JsonListObjectAny(p.PushRanges, "Key", "2"))
                  .Where(p => p.BulletinTypeId == "1" || p.BulletinTypeId == "5" || p.BulletinTypeId == "6" || p.BulletinTypeId == "7" || p.BulletinTypeId == "3" || p.BulletinTypeId == "4")
                  .Where(p => p.BulletinTypeId == "1" || p.BulletinTypeId == "5" || p.BulletinTypeId == "6" || p.BulletinTypeId == "7" || p.BulletinTypeId == "3" || p.BulletinTypeId == "4")
                  .WhereIF(!string.IsNullOrEmpty(dto.Condition), p => p.Title.Contains(dto.Condition))
                  .WhereIF(!string.IsNullOrEmpty(dto.Condition), p => p.Title.Contains(dto.Condition))
-                 .OrderByDescending(p => p.BulletinTime)
+                 .OrderByDescending(p => p.CreationTime)
                  .Select(it => new
                  .Select(it => new
                  {
                  {
-                     Page = SqlFunc.RowNumber($"{it.BulletinTime} desc "),
+                     Page = SqlFunc.RowNumber($"{it.CreationTime} desc "),
                      NoticeID = it.Id,
                      NoticeID = it.Id,
                      Content = it.Content,
                      Content = it.Content,
                      NoticeTypeID = it.BulletinTypeId,
                      NoticeTypeID = it.BulletinTypeId,
                      NoticeTypeName = it.BulletinTypeName,
                      NoticeTypeName = it.BulletinTypeName,
                      NoticeTitle = it.Title,
                      NoticeTitle = it.Title,
                      NoticeBMName = it.SourceOrgName,
                      NoticeBMName = it.SourceOrgName,
-                     NoticeCreateDate = it.BulletinTime,
+                     NoticeCreateDate = it.CreationTime,
                      TypeCode = "",
                      TypeCode = "",
                  })
                  })
                 .ToPageListAsync(dto.PageIndex, dto.PageSize, total);
                 .ToPageListAsync(dto.PageIndex, dto.PageSize, total);
@@ -565,13 +565,14 @@ namespace Hotline.Api.Controllers
         public async Task<OpenResponse> GetOrderListByNum([FromBody] QueryOrderListByNumDto dto)
         public async Task<OpenResponse> GetOrderListByNum([FromBody] QueryOrderListByNumDto dto)
         {
         {
             var queryNew = _orderRepository.Queryable()
             var queryNew = _orderRepository.Queryable()
+                .LeftJoin<OrderPublish>((p, op) => p.Id == op.OrderId)
                 .Where(p => p.IsPublicity == true)
                 .Where(p => p.IsPublicity == true)
-                .OrderByDescending(p => p.CreationTime)
-               .Select(p => new DataListTopDto
+                .OrderByDescending((p, op) => p.CreationTime)
+               .Select((p, op) => new DataListTopDto
                {
                {
                    DataID = p.Id,
                    DataID = p.Id,
                    Title = p.Title,
                    Title = p.Title,
-                   CreateDate = p.CreationTime
+                   CreateDate = op.CreationTime
                });
                });
 
 
             var queryold = _oldPublicDataRepository.Queryable()
             var queryold = _oldPublicDataRepository.Queryable()
@@ -580,7 +581,7 @@ namespace Hotline.Api.Controllers
                        {
                        {
                            DataID = p.OrderId,
                            DataID = p.OrderId,
                            Title = p.Title,
                            Title = p.Title,
-                           CreateDate = p.PubDate
+                           CreateDate = p.CreationTime
                        });
                        });
             var items = await _orderRepository.UnionAll(queryNew, queryold)
             var items = await _orderRepository.UnionAll(queryNew, queryold)
               .OrderByDescending(p => p.CreateDate)
               .OrderByDescending(p => p.CreateDate)
@@ -761,6 +762,7 @@ namespace Hotline.Api.Controllers
                 var orderPublish = await _orderPublishRepository.GetAsync(p => p.OrderId == data.Id, HttpContext.RequestAborted);
                 var orderPublish = await _orderPublishRepository.GetAsync(p => p.OrderId == data.Id, HttpContext.RequestAborted);
                 if (orderPublish != null)
                 if (orderPublish != null)
                 {
                 {
+                    orderDetail.PubFlag = "1";
                     orderDetail.FlowTitle = orderPublish.ArrangeTitle;
                     orderDetail.FlowTitle = orderPublish.ArrangeTitle;
                     orderDetail.FlowContent = orderPublish.ArrangeContent;
                     orderDetail.FlowContent = orderPublish.ArrangeContent;
                     orderDetail.FlowResult = orderPublish.ArrangeOpinion;
                     orderDetail.FlowResult = orderPublish.ArrangeOpinion;
@@ -826,6 +828,7 @@ namespace Hotline.Api.Controllers
                     var orderPublish = await _orderPublishRepository.GetAsync(p => p.OrderId == data.Id, HttpContext.RequestAborted);
                     var orderPublish = await _orderPublishRepository.GetAsync(p => p.OrderId == data.Id, HttpContext.RequestAborted);
                     if (orderPublish != null)
                     if (orderPublish != null)
                     {
                     {
+                        orderDetail.PubFlag = "1";
                         orderDetail.FlowTitle = orderPublish.ArrangeTitle;
                         orderDetail.FlowTitle = orderPublish.ArrangeTitle;
                         orderDetail.FlowContent = orderPublish.ArrangeContent;
                         orderDetail.FlowContent = orderPublish.ArrangeContent;
                         orderDetail.FlowResult = orderPublish.ArrangeOpinion;
                         orderDetail.FlowResult = orderPublish.ArrangeOpinion;
@@ -922,7 +925,7 @@ namespace Hotline.Api.Controllers
 
 
             //数据查询
             //数据查询
             var listType = await _orderRepository.Queryable()
             var listType = await _orderRepository.Queryable()
-                .Where(p => p.StartTime >= startDate && p.StartTime <= endDate && p.Status > EOrderStatus.WaitForAccept)
+                .Where(p => p.CreationTime >= startDate && p.CreationTime <= endDate && p.Status > EOrderStatus.WaitForAccept)
                 .Select(it => new
                 .Select(it => new
                 {
                 {
                     it.AcceptType,
                     it.AcceptType,
@@ -938,12 +941,13 @@ namespace Hotline.Api.Controllers
                  .ToListAsync();
                  .ToListAsync();
 
 
             var listHot = await _orderRepository.Queryable()
             var listHot = await _orderRepository.Queryable()
-                .Where(it => it.StartTime >= startDate && it.StartTime <= endDate && it.Status > EOrderStatus.WaitForAccept)
+                .Where(it => it.CreationTime >= startDate && it.CreationTime <= endDate && it.Status > EOrderStatus.WaitForAccept)
                  .Select(it => new
                  .Select(it => new
                  {
                  {
                      HotspotId = it.HotspotId.Substring(SqlFunc.MappingColumn<int>("0"), SqlFunc.MappingColumn<int>("2")),
                      HotspotId = it.HotspotId.Substring(SqlFunc.MappingColumn<int>("0"), SqlFunc.MappingColumn<int>("2")),
                  })
                  })
                  .MergeTable()
                  .MergeTable()
+                 .Where(it => it.HotspotId != "18")
                  .GroupBy(it => it.HotspotId)//对新表进行分组
                  .GroupBy(it => it.HotspotId)//对新表进行分组
                  .Select(it => new
                  .Select(it => new
                  {
                  {
@@ -960,8 +964,6 @@ namespace Hotline.Api.Controllers
                      num = it.num
                      num = it.num
                  }).ToListAsync();
                  }).ToListAsync();
 
 
-
-
             ////数据查询-查询总数前5的数据
             ////数据查询-查询总数前5的数据
             //var listHot = await _orderRepository.Queryable()
             //var listHot = await _orderRepository.Queryable()
             //    .Where(p => p.StartTime >= startDate && p.StartTime <= endDate && p.Status > EOrderStatus.WaitForAccept)
             //    .Where(p => p.StartTime >= startDate && p.StartTime <= endDate && p.Status > EOrderStatus.WaitForAccept)
@@ -1012,8 +1014,8 @@ namespace Hotline.Api.Controllers
           {
           {
               AllCount = SqlFunc.AggregateSum(SqlFunc.IIF(p.Status > EOrderStatus.WaitForAccept, 1, 0)) + 7079457,
               AllCount = SqlFunc.AggregateSum(SqlFunc.IIF(p.Status > EOrderStatus.WaitForAccept, 1, 0)) + 7079457,
               AllTrandCount = SqlFunc.AggregateSum(SqlFunc.IIF(p.Status >= EOrderStatus.Filed, 1, 0)) + 7079214,
               AllTrandCount = SqlFunc.AggregateSum(SqlFunc.IIF(p.Status >= EOrderStatus.Filed, 1, 0)) + 7079214,
-              DayCount = SqlFunc.AggregateSum(SqlFunc.IIF(p.StartTime >= startDate && p.StartTime <= endDate && p.Status > EOrderStatus.WaitForAccept, 1, 0)),
-              DayTrandCount = SqlFunc.AggregateSum(SqlFunc.IIF(p.FiledTime >= startDate && p.FiledTime <= endDate && p.Status >= EOrderStatus.Filed, 1, 0)),
+              DayCount = SqlFunc.AggregateSum(SqlFunc.IIF(p.CreationTime >= startDate && p.CreationTime <= endDate && p.Status > EOrderStatus.WaitForAccept, 1, 0)),
+              DayTrandCount = SqlFunc.AggregateSum(SqlFunc.IIF(p.CreationTime >= startDate && p.CreationTime <= endDate && p.FiledTime >= startDate && p.FiledTime <= endDate && p.Status >= EOrderStatus.Filed, 1, 0)),
           })
           })
           .FirstAsync();
           .FirstAsync();
             return OpenResponse.Ok(WebPortalDeResponse<GetStatistDto>.Success(getStatistDto));
             return OpenResponse.Ok(WebPortalDeResponse<GetStatistDto>.Success(getStatistDto));
@@ -1127,7 +1129,7 @@ namespace Hotline.Api.Controllers
             var list = _systemDicDataCacheManager.GetSysDicDataCache(SysDicTypeConsts.KnowledgeBaseTags).Select(p => new
             var list = _systemDicDataCacheManager.GetSysDicDataCache(SysDicTypeConsts.KnowledgeBaseTags).Select(p => new
             {
             {
                 SDICT_Name = p.DicDataName,
                 SDICT_Name = p.DicDataName,
-                SDICT_ID = p.DicDataValue
+                SDICT_ID = p.DicDataName
             }).ToList();
             }).ToList();
 
 
             var rsp = new
             var rsp = new
@@ -1154,9 +1156,16 @@ namespace Hotline.Api.Controllers
                 typeSpliceName = type?.SpliceName;
                 typeSpliceName = type?.SpliceName;
             }
             }
 
 
+            var typeSpliceNameTags = string.Empty;
+            if (!string.IsNullOrEmpty(dto.KnowledgeBaseTags))
+            {
+                var type = await _knowledgeTypeRepository.GetAsync(x => x.Name == dto.KnowledgeBaseTags);
+                typeSpliceNameTags = type?.SpliceName;
+            }
+
             var (total, items) = await _knowledgeRepository.Queryable()
             var (total, items) = await _knowledgeRepository.Queryable()
                 .WhereIF(!string.IsNullOrEmpty(dto.Title), p => p.Title.Contains(dto.Title))
                 .WhereIF(!string.IsNullOrEmpty(dto.Title), p => p.Title.Contains(dto.Title))
-                .WhereIF(!string.IsNullOrEmpty(dto.KnowledgeBaseTags), p => p.KnowledgeType.Any(t => t.KnowledgeTypeSpliceName.EndsWith(dto.KnowledgeBaseTags)))
+                .WhereIF(!string.IsNullOrEmpty(typeSpliceNameTags), p => p.KnowledgeType.Any(t => t.KnowledgeTypeSpliceName.EndsWith(typeSpliceNameTags)))
                 .WhereIF(!string.IsNullOrEmpty(typeSpliceName), x => x.KnowledgeType.Any(t => t.KnowledgeTypeSpliceName.EndsWith(typeSpliceName)))
                 .WhereIF(!string.IsNullOrEmpty(typeSpliceName), x => x.KnowledgeType.Any(t => t.KnowledgeTypeSpliceName.EndsWith(typeSpliceName)))
                 .OrderByDescending(p => p.CreationTime)
                 .OrderByDescending(p => p.CreationTime)
                 .ToPagedListAsync(dto.PageIndex, dto.PageSize, HttpContext.RequestAborted);
                 .ToPagedListAsync(dto.PageIndex, dto.PageSize, HttpContext.RequestAborted);
@@ -1164,5 +1173,31 @@ namespace Hotline.Api.Controllers
             var data = new PagedDto<KnowledgeInfoDto>(total, _mapper.Map<IReadOnlyList<KnowledgeInfoDto>>(items));
             var data = new PagedDto<KnowledgeInfoDto>(total, _mapper.Map<IReadOnlyList<KnowledgeInfoDto>>(items));
             return OpenResponse.Ok(WebPortalDeResponse<PagedDto<KnowledgeInfoDto>>.Success(data));
             return OpenResponse.Ok(WebPortalDeResponse<PagedDto<KnowledgeInfoDto>>.Success(data));
         }
         }
+
+        /// <summary>
+        /// 获取知识库详情
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [HttpPost("getknowledgeinfo")]
+        [AllowAnonymous]
+        public async Task<OpenResponse> GetKnowledgeInfo([FromBody] QueryKnowledgeInfo dto)
+        {
+            var data = await _knowledgeRepository.GetAsync(p => p.Id == dto.Id, HttpContext.RequestAborted);
+            KnowledgeInfoDto detailsDto = null;
+            if (data != null)
+            {
+                detailsDto = _mapper.Map<KnowledgeInfoDto>(data);
+
+                if (detailsDto != null && !string.IsNullOrEmpty(detailsDto.Content))
+                    data.Content = _bulletinApplication.GetSiteUrls(data.Content);
+            }
+            else
+            {
+                detailsDto = new();
+            }
+            List<KnowledgeInfoDto> dataDto = [detailsDto];
+            return OpenResponse.Ok(WebPortalDeResponse<IReadOnlyList<KnowledgeInfoDto>>.Success(dataDto));
+        }
     }
     }
 }
 }

+ 0 - 1
src/Hotline.Application.Contracts/Validators/Order/OrderBatchFileDtoValidator.cs

@@ -8,6 +8,5 @@ public class OrderBatchFileDtoValidator:AbstractValidator<OrderBatchFileDto>
     public OrderBatchFileDtoValidator()
     public OrderBatchFileDtoValidator()
     {
     {
         RuleFor(d => d.OrderIds).NotEmpty();
         RuleFor(d => d.OrderIds).NotEmpty();
-        RuleFor(d=>d.Opinion).NotEmpty();
     }
     }
 }
 }

+ 26 - 1
src/Hotline.Application.Tests/Application/DefaultCallApplicationTest.cs

@@ -2,7 +2,9 @@
 using Hotline.Application.CallCenter;
 using Hotline.Application.CallCenter;
 using Hotline.Identity.Accounts;
 using Hotline.Identity.Accounts;
 using Hotline.Identity.Roles;
 using Hotline.Identity.Roles;
+using Hotline.Orders;
 using Hotline.Share.Dtos.CallCenter;
 using Hotline.Share.Dtos.CallCenter;
+using Hotline.Share.Dtos.Order;
 using Hotline.Share.Enums.CallCenter;
 using Hotline.Share.Enums.CallCenter;
 using Hotline.Users;
 using Hotline.Users;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.DependencyInjection;
@@ -14,10 +16,12 @@ namespace Hotline.Application.Tests.Application;
 public class DefaultCallApplicationTest
 public class DefaultCallApplicationTest
 {
 {
     private readonly XingTangCallApplication _defaultCallApplication;
     private readonly XingTangCallApplication _defaultCallApplication;
+    private readonly IOrderVisitRepository _orderVisitRepository;
 
 
-    public DefaultCallApplicationTest(XingTangCallApplication defaultCallApplication)
+    public DefaultCallApplicationTest(XingTangCallApplication defaultCallApplication, IOrderVisitRepository orderVisitRepository)
     {
     {
         _defaultCallApplication = defaultCallApplication;
         _defaultCallApplication = defaultCallApplication;
+        _orderVisitRepository = orderVisitRepository;
     }
     }
 
 
     [Fact]
     [Fact]
@@ -61,4 +65,25 @@ public class DefaultCallApplicationTest
             item.OrderNo.ShouldNotBeNull();
             item.OrderNo.ShouldNotBeNull();
         }
         }
     }
     }
+
+    [Fact]
+    public async Task GetCall_Test()
+    {
+        var result = await _defaultCallApplication.GetCallAsync("01J2ZP7J6X3K5FXN4B5KD54FBA", CancellationToken.None);
+        result.ShouldNotBeNull();
+        result = await _defaultCallApplication.GetCallAsync("2024062419085100943CTI", CancellationToken.None);
+        result.ShouldNotBeNull();
+    }
+
+    [Fact]
+    public async Task OrderVisitRelevanceCallId_Test()
+    {
+        var inDto = new VisitDto();
+        inDto.CallId = "202411141932330603100751FLOW";
+        inDto.Id = "08dd046b-b3b3-4ccf-8a15-09133d27dce0";
+        await _defaultCallApplication.OrderVisitRelevanceCallIdAsync(inDto, CancellationToken.None);
+
+        var visit = await _orderVisitRepository.GetAsync(inDto.Id);
+        visit.CallId.ShouldBe("01JCN757M8F3Z9JJPZ0VBSNQPJ");
+    }
 }
 }

+ 3 - 1
src/Hotline.Application.Tests/Application/SystemSettingCacheManagerTest.cs

@@ -21,6 +21,8 @@ public class SystemSettingCacheManagerTest
     public void CancelPublishOrderEnabled_Test()
     public void CancelPublishOrderEnabled_Test()
     {
     {
         var result = _systemSettingCacheManager.CancelPublishOrderEnabled;
         var result = _systemSettingCacheManager.CancelPublishOrderEnabled;
-        result.ShouldBeFalse();
+        result.ShouldBeTrue();
+        var seconds = _systemSettingCacheManager.VisitCallDelaySecond;
+        seconds.ShouldBe(60);
     }
     }
 }
 }

+ 35 - 4
src/Hotline.Application.Tests/Controller/OrderControllerTest.cs

@@ -15,6 +15,7 @@ using Hotline.Settings.Hotspots;
 using Hotline.Share.Dtos.File;
 using Hotline.Share.Dtos.File;
 using Hotline.Share.Dtos.FlowEngine;
 using Hotline.Share.Dtos.FlowEngine;
 using Hotline.Share.Dtos.Order;
 using Hotline.Share.Dtos.Order;
+using Hotline.Share.Dtos.Order.Publish;
 using Hotline.Share.Dtos.Users;
 using Hotline.Share.Dtos.Users;
 using Hotline.Share.Enums.FlowEngine;
 using Hotline.Share.Enums.FlowEngine;
 using Hotline.Share.Enums.Order;
 using Hotline.Share.Enums.Order;
@@ -34,6 +35,7 @@ using System.Linq;
 using System.Text;
 using System.Text;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using XF.Domain.Authentications;
 using XF.Domain.Authentications;
+using XF.Domain.Exceptions;
 using XF.Domain.Repository;
 using XF.Domain.Repository;
 
 
 namespace Hotline.Application.Tests.Controller;
 namespace Hotline.Application.Tests.Controller;
@@ -204,9 +206,38 @@ public class OrderControllerTest : TestBase
 
 
         await _orderController.PublishCancelAsync(new PublishCancelInDto { OrderPublishId = publish.Id });
         await _orderController.PublishCancelAsync(new PublishCancelInDto { OrderPublishId = publish.Id });
         var visit = await _orderVisitRepository.GetAsync(m => m.OrderId == order.Id);
         var visit = await _orderVisitRepository.GetAsync(m => m.OrderId == order.Id);
-        var publishCount = await _orderPublishRepository.Queryable().Where(m => m.OrderId == order.Id).CountAsync();
-        publishCount.ShouldBe(0);
-        visit.ShouldNotBeNull();
-        visit.VisitState.ShouldBe(EVisitState.Visited);
+        var publishCount = await _orderPublishRepository.Queryable().Where(m => m.OrderId == order.Id && m.IsDeleted == false).CountAsync();
+        publishCount.ShouldBe(0, "发布工单数据已经被删除, 应该是0条");
+        visit.ShouldNotBeNull("回访信息不存在");
+        visit.VisitState.ShouldBe(EVisitState.Visited, "回访状态应该是已回访");
+
+        orderEntity = await _orderRepository.GetAsync(order.Id);
+        orderEntity.Status.ShouldBe(EOrderStatus.Filed);
+        var publishList = await _orderController.PublishOrderList(new QueryOrderPublishDto 
+        {
+            PageSize = 1000,
+            QuerySelf = true
+        });
+
+        publishList.Items.Any(m => m.Id == order.Id).ShouldBeTrue("工单发布列表中没有取消的工单");
+        SetZuoXi();
+        // 验证是否能非上次发布人员发布
+        try
+        {
+            await _orderController.PublishOrder(new PublishOrderDto { Id = order.Id });
+        }
+        catch (UserFriendlyException e)
+        {
+            e.Message.Contains("您不能发布").ShouldBeTrue();
+        }
+
+        // 验证被取消前的发布人能否继续发布
+        SetPaiDanYuan();
+        var inDto = _fixture.Create<PublishOrderDto>();
+        inDto.Id = order.Id;
+        await _orderController.PublishOrder(inDto);
+        var published = await _orderPublishRepository.Queryable().Where(m => m.OrderId == order.Id && m.IsDeleted == false).FirstAsync();
+        published.ShouldNotBeNull();
+        published.IsDeleted.ShouldBeFalse();
     }
     }
 }
 }

+ 44 - 0
src/Hotline.Application.Tests/Controller/UserControllerTest.cs

@@ -0,0 +1,44 @@
+using Hotline.Api.Controllers;
+using Hotline.Identity.Accounts;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using Shouldly;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using XF.Domain.Repository;
+
+namespace Hotline.Application.Tests.Controller;
+public class UserControllerTest
+{
+    private readonly UserController _userController;
+    private readonly IRepository<Account> _accountRepository;
+
+    public UserControllerTest(UserController userController, IRepository<Account> accountRepository)
+    {
+        _userController = userController;
+        _userController.ControllerContext = new ControllerContext
+        {
+            HttpContext = new DefaultHttpContext()
+        };
+        _accountRepository = accountRepository;
+    }
+
+    [Fact]
+    public async Task UnlockUser_Test()
+    {
+        var user = await _accountRepository.Queryable()
+            .Where(m => m.LockoutEnd == null).FirstAsync();
+        user.ShouldNotBeNull();
+        user.LockoutEnd = DateTime.Now.AddDays(1);
+        await _accountRepository.UpdateAsync(user);
+        user = await _accountRepository.GetAsync(user.Id);
+        user.LockoutEnd.ShouldNotBeNull();
+
+        await _userController.UnlockUserAsync(user.Id);
+        user = await _accountRepository.GetAsync(user.Id);
+        user.LockoutEnd.ShouldBeNull();
+    }
+}

+ 1 - 0
src/Hotline.Application.Tests/Infrastructure/TestSettingConstants.cs

@@ -7,6 +7,7 @@ using System.Threading.Tasks;
 namespace Hotline.Application.Tests.Infrastructure;
 namespace Hotline.Application.Tests.Infrastructure;
 public static class TestSettingConstants
 public static class TestSettingConstants
 {
 {
+    public const string ZuoXiAccountName = "UnitTestZuoXi";
     public const string PaiDanYuanAccountName = "UnitTestPDY";
     public const string PaiDanYuanAccountName = "UnitTestPDY";
     public const string FirstOrgAccountName = "cs";
     public const string FirstOrgAccountName = "cs";
     public const string SecondOrgAccountName = "cs21";
     public const string SecondOrgAccountName = "cs21";

+ 1 - 1
src/Hotline.Application.Tests/TestBase.cs

@@ -58,7 +58,7 @@ public class TestBase
 
 
     public void SetZuoXi()
     public void SetZuoXi()
     {
     {
-        SetOperator("坐席", "市民热线服务中心", "单元测试派单员", "001", "13408389849", EUserType.Seat, TestSettingConstants.PaiDanYuanAccountName);
+        SetOperator("坐席", "市民热线服务中心", "单元测试坐席", "001", "13408389849", EUserType.Seat, TestSettingConstants.ZuoXiAccountName);
     }
     }
 
 
     private void SetOperator(string displayName, string fullOrgName, string name, string orgId, string phoneNo, EUserType userType, string userName)
     private void SetOperator(string displayName, string fullOrgName, string name, string orgId, string phoneNo, EUserType userType, string userName)

+ 27 - 1
src/Hotline.Application/CallCenter/DefaultCallApplication.cs

@@ -6,6 +6,7 @@ using Hotline.Orders;
 using Hotline.Repository.SqlSugar.Extensions;
 using Hotline.Repository.SqlSugar.Extensions;
 using Hotline.Share.Dtos;
 using Hotline.Share.Dtos;
 using Hotline.Share.Dtos.CallCenter;
 using Hotline.Share.Dtos.CallCenter;
+using Hotline.Share.Dtos.Order;
 using Hotline.Share.Dtos.TrCallCenter;
 using Hotline.Share.Dtos.TrCallCenter;
 using Hotline.Share.Enums.CallCenter;
 using Hotline.Share.Enums.CallCenter;
 using Hotline.Share.Tools;
 using Hotline.Share.Tools;
@@ -34,6 +35,7 @@ public abstract class DefaultCallApplication : ICallApplication
     private readonly ISessionContext _sessionContext;
     private readonly ISessionContext _sessionContext;
     private readonly IMapper _mapper;
     private readonly IMapper _mapper;
     private readonly ILogger<DefaultCallApplication> _logger;
     private readonly ILogger<DefaultCallApplication> _logger;
+    private readonly IOrderVisitRepository _orderVisitRepository;
 
 
     public DefaultCallApplication(
     public DefaultCallApplication(
         IRepository<Tel> telRepository,
         IRepository<Tel> telRepository,
@@ -47,7 +49,8 @@ public abstract class DefaultCallApplication : ICallApplication
         IUserCacheManager userCacheManager,
         IUserCacheManager userCacheManager,
         ISessionContext sessionContext,
         ISessionContext sessionContext,
         IMapper mapper,
         IMapper mapper,
-        ILogger<DefaultCallApplication> logger)
+        ILogger<DefaultCallApplication> logger,
+        IOrderVisitRepository orderVisitRepository)
     {
     {
         _telRepository = telRepository;
         _telRepository = telRepository;
         _telGroupRepository = telGroupRepository;
         _telGroupRepository = telGroupRepository;
@@ -61,6 +64,7 @@ public abstract class DefaultCallApplication : ICallApplication
         _sessionContext = sessionContext;
         _sessionContext = sessionContext;
         _mapper = mapper;
         _mapper = mapper;
         _logger = logger;
         _logger = logger;
+        _orderVisitRepository = orderVisitRepository;
     }
     }
 
 
     public DefaultCallApplication()
     public DefaultCallApplication()
@@ -323,6 +327,8 @@ public abstract class DefaultCallApplication : ICallApplication
     public virtual async Task<CallNative> GetCallAsync(string callId, CancellationToken cancellationToken)
     public virtual async Task<CallNative> GetCallAsync(string callId, CancellationToken cancellationToken)
     {
     {
         if (string.IsNullOrEmpty(callId)) return null;
         if (string.IsNullOrEmpty(callId)) return null;
+        if (callId.StartsWith("2024"))
+            return await _callNativeRepository.GetAsync(m => m.CallNo == callId, cancellationToken);
         return await _callNativeRepository.GetAsync(callId, cancellationToken);
         return await _callNativeRepository.GetAsync(callId, cancellationToken);
     }
     }
 
 
@@ -359,6 +365,25 @@ public abstract class DefaultCallApplication : ICallApplication
             .ToListAsync(cancellationToken);
             .ToListAsync(cancellationToken);
     }
     }
 
 
+    /// <summary>
+    /// 保存回访详情时发送延迟消息同步通话记录
+    /// 如果回访通话记录有多条, 需要关联通话时长最长的那条
+    /// </summary>
+    public async Task OrderVisitRelevanceCallIdAsync(VisitDto dto, CancellationToken cancellationToken)
+    {
+        var visit = await _orderVisitRepository.GetAsync(dto.Id);
+        if (visit == null) return;
+
+        var callId = await _callNativeRepository.Queryable()
+            .Where(m => m.CallNo == dto.CallId && m.AudioFile != null)
+            .OrderByDescending(m => m.Duration)
+            .Select(m => m.Id)
+            .FirstAsync();
+        if (callId == null) return;
+        visit.CallId = callId;
+        await _orderVisitRepository.UpdateAsync(visit);
+    }
+
     #region tianrun 临时方案
     #region tianrun 临时方案
 
 
     public virtual Task<TrCallRecord?> GetTianrunCallAsync(string callId, CancellationToken cancellationToken)
     public virtual Task<TrCallRecord?> GetTianrunCallAsync(string callId, CancellationToken cancellationToken)
@@ -408,5 +433,6 @@ public abstract class DefaultCallApplication : ICallApplication
     /// <returns></returns>
     /// <returns></returns>
     public abstract List<Kv> GetTelOperationOptions();
     public abstract List<Kv> GetTelOperationOptions();
 
 
+
     #endregion
     #endregion
 }
 }

+ 7 - 0
src/Hotline.Application/CallCenter/ICallApplication.cs

@@ -3,6 +3,7 @@ using Hotline.CallCenter.Calls;
 using Hotline.CallCenter.Tels;
 using Hotline.CallCenter.Tels;
 using Hotline.Share.Dtos;
 using Hotline.Share.Dtos;
 using Hotline.Share.Dtos.CallCenter;
 using Hotline.Share.Dtos.CallCenter;
+using Hotline.Share.Dtos.Order;
 using Hotline.Share.Dtos.TrCallCenter;
 using Hotline.Share.Dtos.TrCallCenter;
 using Hotline.Share.Enums.CallCenter;
 using Hotline.Share.Enums.CallCenter;
 
 
@@ -146,5 +147,11 @@ namespace Hotline.Application.CallCenter
         /// </summary>
         /// </summary>
         /// <returns></returns>
         /// <returns></returns>
         List<Kv> GetTelOperationOptions();
         List<Kv> GetTelOperationOptions();
+
+        /// <summary>
+        /// 保存回访详情时发送延迟消息同步通话记录
+        /// 如果回访通话记录有多条, 需要关联通话时长最长的那条
+        /// </summary>
+        Task OrderVisitRelevanceCallIdAsync(VisitDto dto, CancellationToken cancellationToken);
     }
     }
 }
 }

+ 3 - 2
src/Hotline.Application/CallCenter/XingTangCallApplication.cs

@@ -59,10 +59,11 @@ namespace Hotline.Application.CallCenter
             IUserCacheManager userCacheManager,
             IUserCacheManager userCacheManager,
             ISessionContext sessionContext,
             ISessionContext sessionContext,
             IMapper mapper,
             IMapper mapper,
-            ILogger<XingTangCallApplication> logger
+            ILogger<XingTangCallApplication> logger,
+            IOrderVisitRepository orderVisitRepository
         ) : base(telRepository, telGroupRepository, workRepository, telRestRepository, callNativeRepository,
         ) : base(telRepository, telGroupRepository, workRepository, telRestRepository, callNativeRepository,
             teloperationRepository, callidRelationRepository, cacheWork, userCacheManager, sessionContext, mapper,
             teloperationRepository, callidRelationRepository, cacheWork, userCacheManager, sessionContext, mapper,
-            logger)
+            logger, orderVisitRepository)
         {
         {
             // _telRepository = telRepository;
             // _telRepository = telRepository;
             // _telGroupRepository = telGroupRepository;
             // _telGroupRepository = telGroupRepository;

+ 2 - 2
src/Hotline.Application/FlowEngine/IWorkflowApplication.cs

@@ -29,11 +29,11 @@ namespace Hotline.Application.FlowEngine
         /// </summary>
         /// </summary>
         Task<string> StartWorkflowToStartStepAsync(StartWorkflowDto dto, string externalId, DateTime? expiredTime = null,
         Task<string> StartWorkflowToStartStepAsync(StartWorkflowDto dto, string externalId, DateTime? expiredTime = null,
             CancellationToken cancellationToken = default);
             CancellationToken cancellationToken = default);
-        
+
         /// <summary>
         /// <summary>
         /// 查询下一节点办理对象类型(user or org)及实际办理对象
         /// 查询下一节点办理对象类型(user or org)及实际办理对象
         /// </summary>
         /// </summary>
-		Task<FlowAssignInfo> GetNextStepFlowAssignInfoAsync(Workflow workflow, WorkflowStep currentStep,
+        Task<FlowAssignInfo> GetNextStepFlowAssignInfoAsync(Workflow workflow, WorkflowStep currentStep,
             BasicWorkflowDto dto, StepDefine nextStepDefine, bool isNextDynamic, CancellationToken cancellationToken);
             BasicWorkflowDto dto, StepDefine nextStepDefine, bool isNextDynamic, CancellationToken cancellationToken);
 
 
         /// <summary>
         /// <summary>

+ 10 - 6
src/Hotline.Application/FlowEngine/WorkflowApplication.cs

@@ -343,7 +343,8 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
             _sessionContextProvider.SessionContext.RequiredUserId, _sessionContextProvider.SessionContext.UserName,
             _sessionContextProvider.SessionContext.RequiredUserId, _sessionContextProvider.SessionContext.UserName,
             _sessionContextProvider.SessionContext.RequiredOrgId, _sessionContextProvider.SessionContext.OrgName,
             _sessionContextProvider.SessionContext.RequiredOrgId, _sessionContextProvider.SessionContext.OrgName,
             _sessionContextProvider.SessionContext.OrgAreaCode, _sessionContextProvider.SessionContext.OrgAreaName,
             _sessionContextProvider.SessionContext.OrgAreaCode, _sessionContextProvider.SessionContext.OrgAreaName,
-            _sessionContextProvider.SessionContext.OrgIsCenter, _sessionContextProvider.SessionContext.Roles, cancellationToken);
+            _sessionContextProvider.SessionContext.OrgIsCenter, _sessionContextProvider.SessionContext.Roles,
+            cancellationToken);
     }
     }
 
 
     /// <summary>
     /// <summary>
@@ -365,7 +366,7 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
     }
     }
 
 
     /// <summary>
     /// <summary>
-    /// 撤回至任意节点
+    /// 撤回至之前任意节点
     /// </summary>
     /// </summary>
     public async Task RecallAsync(RecallDto dto, DateTime? expiredTime, bool isOrderFiled, EWorkflowTraceType traceType,
     public async Task RecallAsync(RecallDto dto, DateTime? expiredTime, bool isOrderFiled, EWorkflowTraceType traceType,
         CancellationToken cancellationToken)
         CancellationToken cancellationToken)
@@ -957,7 +958,7 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
                         UserId = d.Id,
                         UserId = d.Id,
                         Username = d.Name,
                         Username = d.Name,
                         OrgId = d.OrgId,
                         OrgId = d.OrgId,
-                        OrgName = d.Organization.Name,
+                        OrgName = d.Organization?.Name,
                         RoleId = defineTypeItem.Key,
                         RoleId = defineTypeItem.Key,
                         RoleName = defineTypeItem.Value
                         RoleName = defineTypeItem.Value
                     })
                     })
@@ -1097,10 +1098,11 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
                 throw new ArgumentOutOfRangeException(nameof(policy), policy, null);
                 throw new ArgumentOutOfRangeException(nameof(policy), policy, null);
         }
         }
 
 
+        var centerOrgName = _systemSettingCacheManager.GetSetting(SettingConstants.CenterOrgName)?.SettingValue[0];
         return new NextStepOption
         return new NextStepOption
         {
         {
             Key = orgLevel.ToString(),
             Key = orgLevel.ToString(),
-            Value = orgLevel == 0 ? "市民热线服务中心" : $"{orgLevel.ToChinese()}级部门",
+            Value = orgLevel == 0 ? centerOrgName : $"{orgLevel.ToChinese()}级部门",
         };
         };
     }
     }
 
 
@@ -1454,10 +1456,11 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
                 throw new ArgumentOutOfRangeException(nameof(policy), policy, null);
                 throw new ArgumentOutOfRangeException(nameof(policy), policy, null);
         }
         }
 
 
+        var centerOrgName = _systemSettingCacheManager.GetSetting(SettingConstants.CenterOrgName)?.SettingValue[0];
         return new NextStepOption
         return new NextStepOption
         {
         {
             Key = orgLevel.ToString(),
             Key = orgLevel.ToString(),
-            Value = orgLevel == 0 ? "市民热线服务中心" : $"{orgLevel.ToChinese()}级部门 {roleName}",
+            Value = orgLevel == 0 ? centerOrgName : $"{orgLevel.ToChinese()}级部门 {roleName}",
             FlowDirection = flowDirection,
             FlowDirection = flowDirection,
             StepType = stepType,
             StepType = stepType,
             BusinessType = businessType,
             BusinessType = businessType,
@@ -1593,10 +1596,11 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
                 throw new ArgumentOutOfRangeException(nameof(policy), policy, null);
                 throw new ArgumentOutOfRangeException(nameof(policy), policy, null);
         }
         }
 
 
+        var centerOrgName = _systemSettingCacheManager.GetSetting(SettingConstants.CenterOrgName)?.SettingValue[0];
         return new NextStepOption
         return new NextStepOption
         {
         {
             Key = orgLevel.ToString(),
             Key = orgLevel.ToString(),
-            Value = orgLevel == 0 ? "市民热线服务中心" : $"{orgLevel.ToChinese()}级部门",
+            Value = orgLevel == 0 ? centerOrgName : $"{orgLevel.ToChinese()}级部门",
             FlowDirection = flowDirection,
             FlowDirection = flowDirection,
             StepType = stepType,
             StepType = stepType,
             BusinessType = businessType,
             BusinessType = businessType,

+ 3 - 2
src/Hotline.Application/Handlers/FlowEngine/WorkflowEndHandler.cs

@@ -102,8 +102,9 @@ public class WorkflowEndHandler : INotificationHandler<EndWorkflowNotify>
                 case WorkflowModuleConsts.KnowledgeAdd://新增知识库
                 case WorkflowModuleConsts.KnowledgeAdd://新增知识库
                 case WorkflowModuleConsts.KnowledgeUpdate://修改知识库
                 case WorkflowModuleConsts.KnowledgeUpdate://修改知识库
                 case WorkflowModuleConsts.KnowledgeDelete://删除知识库
                 case WorkflowModuleConsts.KnowledgeDelete://删除知识库
-                    //var knowledgeWork = await _knowledgeWorkFlowRepository.Queryable().Where(x => x.Id == workflow.ExternalId).FirstAsync(cancellationToken);
-                    var knowledge = await _knowledgeRepository.Queryable().Where(x => x.Id == workflow.ExternalId).FirstAsync(cancellationToken);
+                case WorkflowModuleConsts.KnowledgeOffshelf: //下架知识库
+					//var knowledgeWork = await _knowledgeWorkFlowRepository.Queryable().Where(x => x.Id == workflow.ExternalId).FirstAsync(cancellationToken);
+					var knowledge = await _knowledgeRepository.Queryable().Where(x => x.Id == workflow.ExternalId).FirstAsync(cancellationToken);
                     knowledge.Flowed(workflow.FlowedUserIds, workflow.FlowedOrgIds, workflow.HandlerUsers, workflow.HandlerOrgs);
                     knowledge.Flowed(workflow.FlowedUserIds, workflow.FlowedOrgIds, workflow.HandlerUsers, workflow.HandlerOrgs);
                     await _knowledgeRepository.UpdateAsync(knowledge, cancellationToken);
                     await _knowledgeRepository.UpdateAsync(knowledge, cancellationToken);
                     if (isReviewPass)
                     if (isReviewPass)

+ 2 - 1
src/Hotline.Application/Handlers/FlowEngine/WorkflowNextHandler.cs

@@ -233,7 +233,8 @@ public class WorkflowNextHandler : INotificationHandler<NextStepNotify>
                     break;
                     break;
                 case WorkflowModuleConsts.KnowledgeAdd:
                 case WorkflowModuleConsts.KnowledgeAdd:
                 case WorkflowModuleConsts.KnowledgeUpdate:
                 case WorkflowModuleConsts.KnowledgeUpdate:
-                case WorkflowModuleConsts.KnowledgeDelete:
+                case WorkflowModuleConsts.KnowledgeOffshelf:
+				case WorkflowModuleConsts.KnowledgeDelete:
                     //var knowledgeWork = await _knowledgeWorkFlowRepository.Queryable().Where(x => x.Id == workflow.ExternalId).FirstAsync(cancellationToken);
                     //var knowledgeWork = await _knowledgeWorkFlowRepository.Queryable().Where(x => x.Id == workflow.ExternalId).FirstAsync(cancellationToken);
                     var knowledge = await _knowledgeRepository.Queryable().Where(x => x.Id == workflow.ExternalId).FirstAsync(cancellationToken);
                     var knowledge = await _knowledgeRepository.Queryable().Where(x => x.Id == workflow.ExternalId).FirstAsync(cancellationToken);
                     knowledge.Flowed(workflow.FlowedUserIds, workflow.FlowedOrgIds, workflow.HandlerUsers, workflow.HandlerOrgs);
                     knowledge.Flowed(workflow.FlowedUserIds, workflow.FlowedOrgIds, workflow.HandlerUsers, workflow.HandlerOrgs);

+ 2 - 1
src/Hotline.Application/Handlers/FlowEngine/WorkflowPreviousHandler.cs

@@ -191,7 +191,8 @@ namespace Hotline.Application.Handlers.FlowEngine
                     case WorkflowModuleConsts.KnowledgeAdd:
                     case WorkflowModuleConsts.KnowledgeAdd:
                     case WorkflowModuleConsts.KnowledgeUpdate:
                     case WorkflowModuleConsts.KnowledgeUpdate:
                     case WorkflowModuleConsts.KnowledgeDelete:
                     case WorkflowModuleConsts.KnowledgeDelete:
-                    case WorkflowModuleConsts.TelRestApply:
+                    case WorkflowModuleConsts.KnowledgeOffshelf:
+					case WorkflowModuleConsts.TelRestApply:
                         break;
                         break;
                     case WorkflowModuleConsts.OrderTerminate:
                     case WorkflowModuleConsts.OrderTerminate:
 	                    var orderTerminate = await _orderTerminateRepository.Queryable()
 	                    var orderTerminate = await _orderTerminateRepository.Queryable()

+ 2 - 1
src/Hotline.Application/Handlers/FlowEngine/WorkflowRecallHandler.cs

@@ -134,7 +134,8 @@ public class WorkflowRecallHandler : INotificationHandler<RecallNotify>
                 case WorkflowModuleConsts.KnowledgeAdd:
                 case WorkflowModuleConsts.KnowledgeAdd:
                 case WorkflowModuleConsts.KnowledgeUpdate:
                 case WorkflowModuleConsts.KnowledgeUpdate:
                 case WorkflowModuleConsts.KnowledgeDelete:
                 case WorkflowModuleConsts.KnowledgeDelete:
-                case WorkflowModuleConsts.TelRestApply:
+                case WorkflowModuleConsts.KnowledgeOffshelf:
+				case WorkflowModuleConsts.TelRestApply:
                     break;
                     break;
             }
             }
         }
         }

+ 2 - 1
src/Hotline.Application/Handlers/FlowEngine/WorkflowStartHandler.cs

@@ -215,7 +215,8 @@ namespace Hotline.Application.Handlers.FlowEngine
                     case WorkflowModuleConsts.KnowledgeAdd:
                     case WorkflowModuleConsts.KnowledgeAdd:
                     case WorkflowModuleConsts.KnowledgeUpdate:
                     case WorkflowModuleConsts.KnowledgeUpdate:
                     case WorkflowModuleConsts.KnowledgeDelete:
                     case WorkflowModuleConsts.KnowledgeDelete:
-                        await _knowledgeDomainService.UpdateWorkFlowId(workflow.ExternalId, workflow.Id,
+                    case WorkflowModuleConsts.KnowledgeOffshelf:
+						await _knowledgeDomainService.UpdateWorkFlowId(workflow.ExternalId, workflow.Id,
                             workflow.HandlerUsers, workflow.HandlerOrgs, workflow.FlowedUserIds, workflow.FlowedOrgIds,
                             workflow.HandlerUsers, workflow.HandlerOrgs, workflow.FlowedUserIds, workflow.FlowedOrgIds,
                             cancellationToken);
                             cancellationToken);
                         break;
                         break;

+ 26 - 4
src/Hotline.Application/Handlers/Order/OrderRelateCallHandler.cs

@@ -1,6 +1,8 @@
 using DotNetCore.CAP;
 using DotNetCore.CAP;
+using Hotline.Application.CallCenter;
 using Hotline.CallCenter.Calls;
 using Hotline.CallCenter.Calls;
 using Hotline.Orders;
 using Hotline.Orders;
+using Hotline.Share.Dtos.Order;
 using Hotline.Share.Mq;
 using Hotline.Share.Mq;
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
@@ -12,11 +14,11 @@ using XF.Domain.Repository;
 
 
 namespace Hotline.Application.Handlers.Order
 namespace Hotline.Application.Handlers.Order
 {
 {
-    public class OrderRelateCallHandler: ICapSubscribe,ITransientDependency
+    public class OrderRelateCallHandler : ICapSubscribe, ITransientDependency
     {
     {
         private readonly IOrderRepository _orderRepository;
         private readonly IOrderRepository _orderRepository;
         private readonly IRepository<TrCallRecord> _trCallRecordRepository;
         private readonly IRepository<TrCallRecord> _trCallRecordRepository;
-        
+
 
 
         public OrderRelateCallHandler(IOrderRepository orderRepository, IRepository<TrCallRecord> trCallRecordRepository)
         public OrderRelateCallHandler(IOrderRepository orderRepository, IRepository<TrCallRecord> trCallRecordRepository)
         {
         {
@@ -31,10 +33,10 @@ namespace Hotline.Application.Handlers.Order
         /// <param name="cancellationToken"></param>
         /// <param name="cancellationToken"></param>
         /// <returns></returns>
         /// <returns></returns>
         [CapSubscribe(EventNames.OrderRelateCall)]
         [CapSubscribe(EventNames.OrderRelateCall)]
-        public async Task OrderRelateCallAsync(string orderId,CancellationToken cancellationToken)
+        public async Task OrderRelateCallAsync(string orderId, CancellationToken cancellationToken)
         {
         {
             var order = await _orderRepository.GetAsync(orderId, cancellationToken);
             var order = await _orderRepository.GetAsync(orderId, cancellationToken);
-            if (order!=null)
+            if (order != null)
             {
             {
                 var callRecords = await _trCallRecordRepository.Queryable().Where(p => p.OtherAccept == order.CallId && string.IsNullOrEmpty(p.OtherAccept) == false).ToListAsync(cancellationToken);
                 var callRecords = await _trCallRecordRepository.Queryable().Where(p => p.OtherAccept == order.CallId && string.IsNullOrEmpty(p.OtherAccept) == false).ToListAsync(cancellationToken);
                 if (callRecords.Any())
                 if (callRecords.Any())
@@ -52,4 +54,24 @@ namespace Hotline.Application.Handlers.Order
             }
             }
         }
         }
     }
     }
+
+    /// <summary>
+    /// 保存回访详情时发送延迟消息同步通话记录
+    /// 如果回访通话记录有多条, 需要关联通话时长最长的那条
+    /// </summary>
+    public class OrderVisitRelateCallHandler : ICapSubscribe, ITransientDependency
+    {
+        private readonly ICallApplication _callApplication;
+
+        public OrderVisitRelateCallHandler(ICallApplication callApplication)
+        {
+            _callApplication = callApplication;
+        }
+
+        [CapSubscribe(EventNames.VisitCallDelay)]
+        public async Task OrderRelateCallAsync(VisitDto dto, CancellationToken cancellationToken)
+        {
+            await _callApplication.OrderVisitRelevanceCallIdAsync(dto, cancellationToken);
+        }
+    }
 }
 }

+ 15 - 5
src/Hotline.Application/Knowledge/KnowApplication.cs

@@ -91,11 +91,14 @@ namespace Hotline.Application.Knowledge
                 .WhereIF(pagedDto.Status.HasValue && pagedDto.Status == EKnowledgeStatus.OffShelf, x => x.Status == pagedDto.Status || (x.ExpiredTime != null && x.ExpiredTime < DateTime.Now && x.Status != EKnowledgeStatus.Drafts))
                 .WhereIF(pagedDto.Status.HasValue && pagedDto.Status == EKnowledgeStatus.OffShelf, x => x.Status == pagedDto.Status || (x.ExpiredTime != null && x.ExpiredTime < DateTime.Now && x.Status != EKnowledgeStatus.Drafts))
                 .WhereIF(pagedDto.IsPublic.HasValue, x => x.IsPublic == pagedDto.IsPublic)
                 .WhereIF(pagedDto.IsPublic.HasValue, x => x.IsPublic == pagedDto.IsPublic)
                 .WhereIF(!string.IsNullOrEmpty(pagedDto.Summary), x => x.Summary != null && x.Summary.Contains(pagedDto.Summary!))
                 .WhereIF(!string.IsNullOrEmpty(pagedDto.Summary), x => x.Summary != null && x.Summary.Contains(pagedDto.Summary!))
-                .WhereIF(!string.IsNullOrEmpty(typeSpliceName), x => x.KnowledgeType.Any(t => t.KnowledgeTypeSpliceName.EndsWith(typeSpliceName)))
+                .WhereIF(!string.IsNullOrEmpty(typeSpliceName), x => x.KnowledgeType.Any(t => t.KnowledgeTypeSpliceName.StartsWith(typeSpliceName)))
                 .WhereIF(!string.IsNullOrEmpty(hotspotHotSpotFullName), x => x.HotspotType.HotSpotFullName.EndsWith(hotspotHotSpotFullName!))
                 .WhereIF(!string.IsNullOrEmpty(hotspotHotSpotFullName), x => x.HotspotType.HotSpotFullName.EndsWith(hotspotHotSpotFullName!))
                 .WhereIF(!string.IsNullOrEmpty(pagedDto.CreateOrgId), x => x.SourceOrganizeId != null && x.SourceOrganizeId.EndsWith(pagedDto.CreateOrgId!))
                 .WhereIF(!string.IsNullOrEmpty(pagedDto.CreateOrgId), x => x.SourceOrganizeId != null && x.SourceOrganizeId.EndsWith(pagedDto.CreateOrgId!))
                 .WhereIF(!string.IsNullOrEmpty(pagedDto.ModuleCode), x => x.Workflow.ModuleCode == pagedDto.ModuleCode)
                 .WhereIF(!string.IsNullOrEmpty(pagedDto.ModuleCode), x => x.Workflow.ModuleCode == pagedDto.ModuleCode)
-                .OrderByDescending(d => d.CreationTime)
+                .WhereIF(pagedDto.Status ==  EKnowledgeStatus.NewDrafts ,x=>x.Status == EKnowledgeStatus.Drafts || x.Status == EKnowledgeStatus.Revert)
+                .WhereIF(pagedDto.NewDraftsStatus is EKnowledgeStatus.Drafts , x=>x.Status == EKnowledgeStatus.Drafts)
+                .WhereIF(pagedDto.NewDraftsStatus is EKnowledgeStatus.Revert, x => x.Status == EKnowledgeStatus.Revert)
+				.OrderByDescending(d => d.CreationTime)
                 .ToPagedListAsync(pagedDto.PageIndex, pagedDto.PageSize, cancellationToken);
                 .ToPagedListAsync(pagedDto.PageIndex, pagedDto.PageSize, cancellationToken);
             //返回数据
             //返回数据
             return (total, _mapper.Map<IList<KnowledgeDataDto>>(temp));
             return (total, _mapper.Map<IList<KnowledgeDataDto>>(temp));
@@ -206,12 +209,19 @@ namespace Hotline.Application.Knowledge
 
 
         public async Task<(int, IList<PageViewOutDto>)> GetPageViewListAsync(PageViewInDto dto, CancellationToken requestAborted = default)
         public async Task<(int, IList<PageViewOutDto>)> GetPageViewListAsync(PageViewInDto dto, CancellationToken requestAborted = default)
         {
         {
-            var query = _knowledgePvepository.Queryable(includeDeleted: true)
+	        var typeSpliceName = string.Empty;
+	        if (!string.IsNullOrEmpty(dto.KnowledgeTypeId))
+	        {
+		        var type = await _knowledgeTypeRepository.GetAsync(x => x.Id == dto.KnowledgeTypeId);
+		        typeSpliceName = type?.SpliceName;
+			}
+
+			var query = _knowledgePvepository.Queryable(includeDeleted: true)
                 .LeftJoin<KnowledgeBase.Knowledge>((p, k) => p.KnowledgeCode == k.Code)
                 .LeftJoin<KnowledgeBase.Knowledge>((p, k) => p.KnowledgeCode == k.Code)
                 .LeftJoin<KnowledgeRelationType>((p, k, r) => r.KnowledgeId == k.Id)
                 .LeftJoin<KnowledgeRelationType>((p, k, r) => r.KnowledgeId == k.Id)
                 .WhereIF(dto.Title.NotNullOrEmpty(), (p, k, r) => k.Title.Contains(dto.Title))
                 .WhereIF(dto.Title.NotNullOrEmpty(), (p, k, r) => k.Title.Contains(dto.Title))
                 .WhereIF(dto.CreatorName.NotNullOrEmpty(), (p, k, r) => p.CreatorName.Contains(dto.CreatorName))
                 .WhereIF(dto.CreatorName.NotNullOrEmpty(), (p, k, r) => p.CreatorName.Contains(dto.CreatorName))
-                .WhereIF(dto.KnowledgeTypeId.NotNullOrEmpty(), (p, k, r) => r.KnowledgeTypeId == dto.KnowledgeTypeId)
+                .WhereIF(dto.KnowledgeTypeId.NotNullOrEmpty(), (p, k, r) => r.KnowledgeTypeSpliceName.StartsWith(typeSpliceName))
                 .WhereIF(dto.StartTime.HasValue && dto.EndTime.HasValue, (p, k, r) => p.CreationTime >= dto.StartTime && p.CreationTime <= dto.EndTime)
                 .WhereIF(dto.StartTime.HasValue && dto.EndTime.HasValue, (p, k, r) => p.CreationTime >= dto.StartTime && p.CreationTime <= dto.EndTime)
                 .OrderByDescending((p, k, r) => p.CreationTime);
                 .OrderByDescending((p, k, r) => p.CreationTime);
 
 
@@ -281,7 +291,7 @@ namespace Hotline.Application.Knowledge
                 .Where(x => x.IsDeleted == false)
                 .Where(x => x.IsDeleted == false)
                 .Where(x => x.Status == EKnowledgeStatus.OnShelf)
                 .Where(x => x.Status == EKnowledgeStatus.OnShelf)
                 .Where(x => x.KnowledgeType.Any(t => t.KnowledgeType.KnowledgeTypeOrgs.Any(to => to.OrgId == _sessionContext.RequiredOrgId) || t.KnowledgeType.KnowledgeTypeOrgs.Any() == false))
                 .Where(x => x.KnowledgeType.Any(t => t.KnowledgeType.KnowledgeTypeOrgs.Any(to => to.OrgId == _sessionContext.RequiredOrgId) || t.KnowledgeType.KnowledgeTypeOrgs.Any() == false))
-                .WhereIF(!string.IsNullOrEmpty(typeSpliceName), x => x.KnowledgeType.Any(t => t.KnowledgeTypeSpliceName.EndsWith(typeSpliceName)))
+                .WhereIF(!string.IsNullOrEmpty(typeSpliceName), x => x.KnowledgeType.Any(t => t.KnowledgeTypeSpliceName.StartsWith(typeSpliceName)))
                 .WhereIF(!string.IsNullOrEmpty(hotspotHotSpotFullName), x => x.HotspotType.HotSpotFullName.EndsWith(hotspotHotSpotFullName!))
                 .WhereIF(!string.IsNullOrEmpty(hotspotHotSpotFullName), x => x.HotspotType.HotSpotFullName.EndsWith(hotspotHotSpotFullName!))
                 .WhereIF(!string.IsNullOrEmpty(dto.HotspotName), x => x.HotspotType.HotSpotFullName.EndsWith(dto.HotspotName!))
                 .WhereIF(!string.IsNullOrEmpty(dto.HotspotName), x => x.HotspotType.HotSpotFullName.EndsWith(dto.HotspotName!))
                 .WhereIF(!string.IsNullOrEmpty(dto.CreateOrgId), x => x.CreatorOrgId != null && x.CreatorOrgId.EndsWith(dto.CreateOrgId!))
                 .WhereIF(!string.IsNullOrEmpty(dto.CreateOrgId), x => x.CreatorOrgId != null && x.CreatorOrgId.EndsWith(dto.CreateOrgId!))

+ 2 - 1
src/Hotline.Application/Mappers/WorkflowMapperConfigs.cs

@@ -37,13 +37,14 @@ public class WorkflowMapperConfigs : IRegister
             .Ignore(d => d.IsSms)
             .Ignore(d => d.IsSms)
             .Ignore(d => d.Opinion)
             .Ignore(d => d.Opinion)
             .Ignore(d => d.FileJson)
             .Ignore(d => d.FileJson)
-            //.Ignore(d => d.StepExpiredTime)
+            .Ignore(d => d.StepExpiredTime)
             .Ignore(d => d.Workflow)
             .Ignore(d => d.Workflow)
             .Ignore(d => d.WorkflowTrace)
             .Ignore(d => d.WorkflowTrace)
             //.IgnoreIf((d, s) => s.StepHandlers == null || !s.StepHandlers.Any(), d => d.StepHandlers)
             //.IgnoreIf((d, s) => s.StepHandlers == null || !s.StepHandlers.Any(), d => d.StepHandlers)
             ;
             ;
 
 
         config.ForType<WorkflowStep, WorkflowTrace>()
         config.ForType<WorkflowStep, WorkflowTrace>()
+            .Ignore(d=>d.Workflow)
             .Ignore(d => d.ParentId)
             .Ignore(d => d.ParentId)
             .Ignore(d => d.TraceType)
             .Ignore(d => d.TraceType)
             .Map(d => d.StepId, s => s.Id)
             .Map(d => d.StepId, s => s.Id)

+ 1 - 0
src/Hotline.Application/Orders/IOrderApplication.cs

@@ -78,6 +78,7 @@ namespace Hotline.Application.Orders
 
 
         /// <summary>
         /// <summary>
         /// 回访保存
         /// 回访保存
+        /// 调用本方法前, 先调用 _callApplication.GetOrSetCallIdAsync 转换 CallId
         /// </summary>
         /// </summary>
         /// <param name="dto"></param>
         /// <param name="dto"></param>
         /// <param name="cancellationToken"></param>
         /// <param name="cancellationToken"></param>

+ 436 - 398
src/Hotline.Application/Orders/OrderApplication.cs

@@ -71,6 +71,7 @@ using static NPOI.SS.Format.CellNumberFormatter;
 using System.Linq;
 using System.Linq;
 using System.Linq.Dynamic.Core;
 using System.Linq.Dynamic.Core;
 using System.Threading;
 using System.Threading;
+using Hotline.Application.CallCenter;
 
 
 namespace Hotline.Application.Orders;
 namespace Hotline.Application.Orders;
 
 
@@ -113,8 +114,10 @@ public class OrderApplication : IOrderApplication, IScopeDependency
     private readonly IRepository<OrderObserve> _orderObserveRepository;
     private readonly IRepository<OrderObserve> _orderObserveRepository;
     private readonly IOrderTerminateRepository _orderTerminateRepository;
     private readonly IOrderTerminateRepository _orderTerminateRepository;
     private readonly IRepository<OrderPublishHistory> _orderPublishHistoryRepository;
     private readonly IRepository<OrderPublishHistory> _orderPublishHistoryRepository;
+    private readonly IOrderDelayRepository _orderDelayRepository;
+    private readonly IRepository<OrderSecondaryHandling> _orderSecondaryHandlingRepository;
 
 
-    public OrderApplication(
+	public OrderApplication(
         IOrderDomainService orderDomainService,
         IOrderDomainService orderDomainService,
         IOrderRepository orderRepository,
         IOrderRepository orderRepository,
         IWorkflowDomainService workflowDomainService,
         IWorkflowDomainService workflowDomainService,
@@ -150,7 +153,9 @@ public class OrderApplication : IOrderApplication, IScopeDependency
         IRepository<TranspondCityRawData> transpondCityRawDataRepository,
         IRepository<TranspondCityRawData> transpondCityRawDataRepository,
         IRepository<OrderObserve> orderObserveRepository,
         IRepository<OrderObserve> orderObserveRepository,
         IOrderTerminateRepository orderTerminateRepository,
         IOrderTerminateRepository orderTerminateRepository,
-        IRepository<OrderPublishHistory> orderPublishHistoryRepository)
+        IRepository<OrderPublishHistory> orderPublishHistoryRepository,
+        IRepository<OrderSecondaryHandling> orderSecondaryHandlingRepository,
+		IOrderDelayRepository orderDelayRepository)
     {
     {
         _orderDomainService = orderDomainService;
         _orderDomainService = orderDomainService;
         _workflowDomainService = workflowDomainService;
         _workflowDomainService = workflowDomainService;
@@ -188,7 +193,9 @@ public class OrderApplication : IOrderApplication, IScopeDependency
         _orderTerminateRepository = orderTerminateRepository;
         _orderTerminateRepository = orderTerminateRepository;
         _orderPublishHistoryRepository = orderPublishHistoryRepository;
         _orderPublishHistoryRepository = orderPublishHistoryRepository;
         _sessionContext = sessionContext;
         _sessionContext = sessionContext;
-    }
+        _orderDelayRepository = orderDelayRepository;
+        _orderSecondaryHandlingRepository = orderSecondaryHandlingRepository;
+	}
 
 
     /// <summary>
     /// <summary>
     /// 更新工单办理期满时间(延期调用,其他不调用)
     /// 更新工单办理期满时间(延期调用,其他不调用)
@@ -430,8 +437,8 @@ public class OrderApplication : IOrderApplication, IScopeDependency
                                  _sessionContextProvider.SessionContext.Roles.Contains(step.RoleId))))
                                  _sessionContextProvider.SessionContext.Roles.Contains(step.RoleId))))
                 .Any())
                 .Any())
             // .WhereIF(orgLevel == 2 || orgLevel == 1, d =>  d.ActualHandleOrgCode.StartsWith(orgCode))
             // .WhereIF(orgLevel == 2 || orgLevel == 1, d =>  d.ActualHandleOrgCode.StartsWith(orgCode))
-            .WhereIF(orgLevel == 2 || orgLevel == 1,  d => SqlFunc.Subqueryable<WorkflowStep>()
-                .Where(step => step.ExternalId == d.Id && 
+            .WhereIF(orgLevel == 2 || orgLevel == 1, d => SqlFunc.Subqueryable<WorkflowStep>()
+                .Where(step => step.ExternalId == d.Id &&
                                step.Status != EWorkflowStepStatus.Handled &&
                                step.Status != EWorkflowStepStatus.Handled &&
                                (!string.IsNullOrEmpty(step.HandlerOrgId) && step.HandlerOrgId.StartsWith(orgCode)))
                                (!string.IsNullOrEmpty(step.HandlerOrgId) && step.HandlerOrgId.StartsWith(orgCode)))
                 .Any())
                 .Any())
@@ -486,7 +493,7 @@ public class OrderApplication : IOrderApplication, IScopeDependency
         var seg = new Segment();
         var seg = new Segment();
         ICollection<WordInfo> splitWords = seg.DoSegment(inputStr);
         ICollection<WordInfo> splitWords = seg.DoSegment(inputStr);
         var words = new List<string>();
         var words = new List<string>();
-        for (int i = 0;i < splitWords.Count;i++)
+        for (int i = 0; i < splitWords.Count; i++)
         {
         {
             var word = splitWords.ElementAt(i);
             var word = splitWords.ElementAt(i);
             if (word is { WordType: WordType.SimplifiedChinese, Word.Length: > 1 })
             if (word is { WordType: WordType.SimplifiedChinese, Word.Length: > 1 })
@@ -713,7 +720,7 @@ public class OrderApplication : IOrderApplication, IScopeDependency
             visit.VisitTime = dto.VisitTime;
             visit.VisitTime = dto.VisitTime;
             visit.VisitType = dto.VisitType;
             visit.VisitType = dto.VisitType;
 
 
-            for (int i = 0;i < visit.OrderVisitDetails.Count;i++)
+            for (int i = 0; i < visit.OrderVisitDetails.Count; i++)
             {
             {
                 var detail = visit.OrderVisitDetails[i];
                 var detail = visit.OrderVisitDetails[i];
                 var detaildto = dto.OrderVisitDetailDto.FirstOrDefault(x => x.Id == detail.Id);
                 var detaildto = dto.OrderVisitDetailDto.FirstOrDefault(x => x.Id == detail.Id);
@@ -759,7 +766,7 @@ public class OrderApplication : IOrderApplication, IScopeDependency
     /// <returns></returns>
     /// <returns></returns>
     public async Task<Order> SaveOrderWorkflowInfo(NextWorkflowDto<OrderHandleFlowDto> dto, CancellationToken cancellationToken)
     public async Task<Order> SaveOrderWorkflowInfo(NextWorkflowDto<OrderHandleFlowDto> dto, CancellationToken cancellationToken)
     {
     {
-        var order = await _orderRepository.Queryable().Includes(d=>d.OrderExtension)
+        var order = await _orderRepository.Queryable().Includes(d => d.OrderExtension)
                         .FirstAsync(d => d.Id == dto.Data.OrderId, cancellationToken)
                         .FirstAsync(d => d.Id == dto.Data.OrderId, cancellationToken)
                     ?? throw UserFriendlyException.SameMessage("无效工单编号");
                     ?? throw UserFriendlyException.SameMessage("无效工单编号");
         if (await _orderSendBackAuditRepository.AnyAsync(x => x.OrderId == order.Id && x.State == ESendBackAuditState.Apply,
         if (await _orderSendBackAuditRepository.AnyAsync(x => x.OrderId == order.Id && x.State == ESendBackAuditState.Apply,
@@ -771,12 +778,12 @@ public class OrderApplication : IOrderApplication, IScopeDependency
         ExpiredTimeWithConfig? expiredTimeConfig = null;
         ExpiredTimeWithConfig? expiredTimeConfig = null;
         var settingBase = _systemSettingCacheManager.GetSetting(SettingConstants.CityBaseConfiguration)?.SettingValue[0];
         var settingBase = _systemSettingCacheManager.GetSetting(SettingConstants.CityBaseConfiguration)?.SettingValue[0];
         CityBaseConfiguration cityBase = System.Text.Json.JsonSerializer.Deserialize<CityBaseConfiguration>(settingBase);
         CityBaseConfiguration cityBase = System.Text.Json.JsonSerializer.Deserialize<CityBaseConfiguration>(settingBase);
-		if (dto.Workflow.NextHandlers.Any(d => d.Key == cityBase.CityProvince.OrgId || d.Key == cityBase.CityProvinceAssign.OrgId))
+        if (dto.Workflow.NextHandlers.Any(d => d.Key == cityBase.CityProvince.OrgId || d.Key == cityBase.CityProvinceAssign.OrgId))
         {
         {
-	        if (dto.Workflow.NextHandlers.Any(d => d.Key == cityBase.CityProvince.OrgId) && order.OrderExtension is null)
-	        {
-				throw UserFriendlyException.SameMessage("该工单不存在拓展信息,不能推送至全国12315平台!");
-			}
+            if (dto.Workflow.NextHandlers.Any(d => d.Key == cityBase.CityProvince.OrgId) && order.OrderExtension is null)
+            {
+                throw UserFriendlyException.SameMessage("该工单不存在拓展信息,不能推送至全国12315平台!");
+            }
             var timeResult = await _expireTime.CalcEndTime(DateTime.Now, ETimeType.WorkDay, 45, 80, 50);
             var timeResult = await _expireTime.CalcEndTime(DateTime.Now, ETimeType.WorkDay, 45, 80, 50);
             expiredTimeConfig = new ExpiredTimeWithConfig
             expiredTimeConfig = new ExpiredTimeWithConfig
             {
             {
@@ -837,8 +844,7 @@ public class OrderApplication : IOrderApplication, IScopeDependency
             await _orderRepository.UpdateAsync(order, cancellationToken);
             await _orderRepository.UpdateAsync(order, cancellationToken);
         }
         }
 
 
-        if (expiredTimeConfig is not null)
-            _mapper.Map(expiredTimeConfig, order);
+        
 
 
         if (dto.Data.LeaderSMSKey != null)
         if (dto.Data.LeaderSMSKey != null)
         {
         {
@@ -847,6 +853,10 @@ public class OrderApplication : IOrderApplication, IScopeDependency
         }
         }
 
 
         _mapper.Map(dto.Data, order);
         _mapper.Map(dto.Data, order);
+
+        if (expiredTimeConfig is not null)
+            _mapper.Map(expiredTimeConfig, order);
+
         await _orderRepository.UpdateAsync(order, cancellationToken);
         await _orderRepository.UpdateAsync(order, cancellationToken);
 
 
         return order;
         return order;
@@ -854,6 +864,7 @@ public class OrderApplication : IOrderApplication, IScopeDependency
 
 
     /// <summary>
     /// <summary>
     /// 回访保存
     /// 回访保存
+    /// 调用本方法前, 先调用 _callApplication.GetOrSetCallIdAsync 转换 CallId
     /// </summary>
     /// </summary>
     /// <param name="dto"></param>
     /// <param name="dto"></param>
     /// <param name="cancellationToken"></param>
     /// <param name="cancellationToken"></param>
@@ -906,7 +917,7 @@ public class OrderApplication : IOrderApplication, IScopeDependency
             visit.JudgeState = EJudgeState.Judging;
             visit.JudgeState = EJudgeState.Judging;
         }
         }
 
 
-        for (int i = 0;i < visit.OrderVisitDetails.Count;i++)
+        for (int i = 0; i < visit.OrderVisitDetails.Count; i++)
         {
         {
             var detail = visit.OrderVisitDetails[i];
             var detail = visit.OrderVisitDetails[i];
             var detaildto = dto.VisitDetails.FirstOrDefault(x => x.Id == detail.Id);
             var detaildto = dto.VisitDetails.FirstOrDefault(x => x.Id == detail.Id);
@@ -1057,121 +1068,122 @@ public class OrderApplication : IOrderApplication, IScopeDependency
                                step.HandlerOrgId.StartsWith(_sessionContextProvider.SessionContext.RequiredOrgId)).Any());
                                step.HandlerOrgId.StartsWith(_sessionContextProvider.SessionContext.RequiredOrgId)).Any());
         }
         }
 
 
-            query = query.Includes(x => x.OrderScreens);
-            if (!_appOptions.Value.IsYiBin)
-            {
-                query = query.Includes(x => x.OrderVisits.Where(m => m.VisitState == EVisitState.Visited).ToList())
-             .Includes(x => x.OrderVisits.Where(m => m.VisitState == EVisitState.Visited).ToList(), ovd => ovd.OrderVisitDetails);
-            }
-            query = query
-             .WhereIF(!string.IsNullOrEmpty(dto.Keyword), d => d.Title.Contains(dto.Keyword!)) //标题
-             .WhereIF(!string.IsNullOrEmpty(dto.ProvinceNo), d => d.ProvinceNo == dto.ProvinceNo) //省本地编号
-             .WhereIF(!string.IsNullOrEmpty(dto.ReceiveProvinceNo), d => d.ReceiveProvinceNo == dto.ReceiveProvinceNo) //省编号
-             .WhereIF(!string.IsNullOrEmpty(dto.No), d => d.No == dto.No) //工单编码
-             .WhereIF(!string.IsNullOrEmpty(dto.AcceptType), d => d.AcceptTypeCode == dto.AcceptType) //受理类型
-                                                                                                      //.WhereIF(dto.AcceptTypes.Any(), d => dto.AcceptTypes.Contains(d.AcceptTypeCode)) //受理类型
-             .WhereIF(!string.IsNullOrEmpty(dto.Channel), d => d.SourceChannelCode == dto.Channel)
-             //.WhereIF(dto.Channels.Any(), d => dto.Channels.Contains(d.SourceChannelCode)) //来源渠道
-             //.WhereIF(dto.HotspotIds.Any(), d => dto.HotspotIds.Contains(d.HotspotId)) //热点类型
-             .WhereIF(!string.IsNullOrEmpty(dto.Hotspot), d => d.HotspotSpliceName != null && d.HotspotSpliceName.Contains(dto.Hotspot))
-             .WhereIF(!string.IsNullOrEmpty(dto.TransferPhone), d => d.TransferPhone == dto.TransferPhone!) //转接号码
-                                                                                                            //.WhereIF(dto.OrgCodes.Any(), d => d.Workflow.Assigns.Any(s => dto.OrgCodes.Contains(s.OrgCode)))
-                                                                                                            //.WhereIF(dto.OrgCodes.Any(), d => dto.OrgCodes.Contains(d.ActualHandleOrgCode)) //接办部门
-                                                                                                            //.WhereIF(!string.IsNullOrEmpty(dto.OrgId), d => d.CurrentHandleOrgId == dto.OrgId)//接办部门
-             .WhereIF(!string.IsNullOrEmpty(dto.OrgLevelOneName), d => d.OrgLevelOneName.Contains(dto.OrgLevelOneName)) //一级部门
-             .WhereIF(!string.IsNullOrEmpty(dto.ActualHandleOrgName), d => d.ActualHandleOrgName.Contains(dto.ActualHandleOrgName)) //接办部门(综合查询模糊)
-             .WhereIF(!string.IsNullOrEmpty(dto.NameOrNo), d => d.AcceptorName == dto.NameOrNo! || d.AcceptorStaffNo == dto.NameOrNo!) //受理人/坐席
-             .WhereIF(dto.CreationTimeStart.HasValue, d => d.CreationTime >= dto.CreationTimeStart) //受理时间开始
-             .WhereIF(dto.CreationTimeEnd.HasValue, d => d.CreationTime <= dto.CreationTimeEnd) //受理时间结束
-                                                                                                //.WhereIF(dto.EmergencyLevels.Any(), d => dto.EmergencyLevels.Contains(d.EmergencyLevel))  //紧急程度
-             .WhereIF(!string.IsNullOrEmpty(dto.FromPhone), d => d.FromPhone == dto.FromPhone) //来电号码
-             .WhereIF(!string.IsNullOrEmpty(dto.PhoneNo), d => d.Contact == dto.PhoneNo!) //联系电话
-                                                                                          //.WhereIF(!string.IsNullOrEmpty(dto.PushTypeCode), d => d.PushTypeCode == dto.PushTypeCode) //推送分类
-             .WhereIF(!string.IsNullOrEmpty(dto.PushTypeCode), x => x.OrderPushTypes.Any(opt => opt.PushTypeCode == dto.PushTypeCode)) //推送分类
-             .WhereIF(dto.ExpiredTimeStart.HasValue, d => d.ExpiredTime >= dto.ExpiredTimeStart) //超期时间开始
-             .WhereIF(dto.ExpiredTimeEnd.HasValue, d => d.ExpiredTime <= dto.ExpiredTimeEnd) //超期时间结束
-                                                                                             //.WhereIF(dto.Statuses.Any(), d => dto.Statuses.Contains(d.Status))  //工单状态
-             .WhereIF(dto.Status.HasValue, d => d.Status == dto.Status) //工单状态
-                                                                        //.WhereIF(dto.Statuses.Any(d => d == EOrderStatus.SpecialToUnAccept), d => d.Status <= EOrderStatus.SpecialToUnAccept)
-             .WhereIF(!string.IsNullOrEmpty(dto.ActualHandlerName), d => d.ActualHandlerName == dto.ActualHandlerName) //接办人
-             .WhereIF(dto.IsScreen == true, d => d.OrderScreens.Any(x => x.Status != EScreenStatus.Refuse)) //有甄别
-             .WhereIF(dto.IsScreen == false, d => !d.OrderScreens.Any(x => x.Status != EScreenStatus.Refuse)) //无甄别
-             .WhereIF(!string.IsNullOrEmpty(dto.CurrentStepCode), d => d.CurrentStepCode == dto.CurrentStepCode) //当前办理节点
-             .WhereIF(dto.ActualHandleTimeStart.HasValue, d => d.ActualHandleTime >= dto.ActualHandleTimeStart) //办结时间开始
-             .WhereIF(dto.ActualHandleTimeEnd.HasValue, d => d.ActualHandleTime <= dto.ActualHandleTimeEnd) //办结时间结束
-             .WhereIF(dto.IsOverTime == true,
-                 d => (d.ExpiredTime < DateTime.Now && d.Status < EOrderStatus.Filed) ||
-                      (d.ExpiredTime < d.ActualHandleTime && d.Status >= EOrderStatus.Filed)) //是 超期
-             .WhereIF(dto.IsOverTime == false,
-                 d => (d.ExpiredTime > DateTime.Now && d.Status < EOrderStatus.Filed) ||
-                      (d.ExpiredTime > d.ActualHandleTime && d.Status >= EOrderStatus.Filed)) //否 超期
-             .WhereIF(dto.IdentityType != null, d => d.IdentityType == dto.IdentityType) //来电主体
-             .WhereIF(!string.IsNullOrEmpty(dto.FromName), d => d.FromName == dto.FromName) //来电人姓名
-                                                                                            //.WhereIF(dto.AreaCodes.Any(), d => dto.AreaCodes.Contains(d.AreaCode)) //区域
-                                                                                            //.WhereIF(!string.IsNullOrEmpty(dto.AreaCode), d => d.AreaCode == dto.AreaCode)//区域
-             .WhereIF(!string.IsNullOrEmpty(dto.AreaCode) && dto.AreaCode.LastIndexOf("00") > 0,
-                 d => d.AreaCode.StartsWith(SqlFunc.Substring(dto.AreaCode, 0, 4)))
-             .WhereIF(!string.IsNullOrEmpty(dto.AreaCode) && dto.AreaCode.LastIndexOf("00") <= 0, d => d.AreaCode.StartsWith(dto.AreaCode))
-             .WhereIF(dto.IsProvinceOrder.HasValue && dto.IsProvinceOrder == true, d => d.Source == ESource.ProvinceStraight)
-             .WhereIF(dto.IsProvinceOrder.HasValue && dto.IsProvinceOrder == false, d => d.Source != ESource.ProvinceStraight)
-             .WhereIF(!string.IsNullOrEmpty(dto.SensitiveWord), d => SqlFunc.JsonArrayAny(d.Sensitive, dto.SensitiveWord))
-             .WhereIF(dto.IsSensitiveWord.HasValue && dto.IsSensitiveWord == true,
-                 d => d.Sensitive != null && SqlFunc.JsonArrayLength(d.Sensitive) > 0)
-             .WhereIF(dto.IsUrgent.HasValue, d => d.IsUrgent == dto.IsUrgent.Value)
-             .WhereIF(!string.IsNullOrEmpty(dto.ProvinceChannel) && dto.ProvinceChannel == "1", d => d.Source == ESource.ProvinceStraight &&
-                 d.SourceChannelCode == "SZMHD" && d.IsProvince == false) //政民互动直派
-             .WhereIF(!string.IsNullOrEmpty(dto.ProvinceChannel) && dto.ProvinceChannel == "2", d => d.Source == ESource.ProvinceStraight &&
-                 d.SourceChannelCode == "SZMHD" && d.IsProvince == true) //政民互动
-             .WhereIF(!string.IsNullOrEmpty(dto.ProvinceChannel) && dto.ProvinceChannel == "3", d => d.Source == ESource.ProvinceStraight &&
-                 d.SourceChannelCode == "S12345" && d.IsProvince == true) //省12345
-             .WhereIF(!string.IsNullOrEmpty(dto.ContentRetrieval),
-                 d => d.Title.Contains(dto.ContentRetrieval) || d.Content.Contains(dto.ContentRetrieval) || d.FileOpinion.Contains(dto.ContentRetrieval) || d.ActualOpinion.Contains(dto.ContentRetrieval))
-             .WhereIF(dto.IsSgin.HasValue && dto.IsSgin == true, d => d.CurrentStepAcceptTime != null)
-             .WhereIF(dto.IsSgin.HasValue && dto.IsSgin == false, d => d.CurrentStepAcceptTime == null)
-             .WhereIF(dto.FiledType is FiledType.CenterFiled, d => d.ProcessType == EProcessType.Zhiban)
-             .WhereIF(dto.FiledType is FiledType.OrgFiled, d => d.ProcessType == EProcessType.Jiaoban)
-             .WhereIF(!string.IsNullOrEmpty(dto.OrderTagCode), d => d.OrderTagCode == dto.OrderTagCode)
-             .WhereIF(!string.IsNullOrEmpty(dto.FocusOnEvents),d=>SqlFunc.SplitIn(d.FocusOnEvents, dto.FocusOnEvents))
-             .OrderByIF(string.IsNullOrEmpty(dto.SortField), d => d.CreationTime, OrderByType.Desc)//默认排序时间为创建时间
-             .OrderByIF(dto is { SortField: "no", SortRule: 0 }, d => d.No, OrderByType.Asc) //工单编号升序
-             .OrderByIF(dto is { SortField: "no", SortRule: 1 }, d => d.No, OrderByType.Desc) //工单编号降序
-             .OrderByIF(dto is { SortField: "isProvinceText", SortRule: 0 }, d => d.IsProvince, OrderByType.Asc)//是否省工单升序
-             .OrderByIF(dto is { SortField: "isProvinceText", SortRule: 1 }, d => d.IsProvince, OrderByType.Desc)//是否省工单降序
-             .OrderByIF(dto is { SortField: "reTransactNum", SortRule: 0 }, d => d.ReTransactNum, OrderByType.Asc) //重办次数升序
-             .OrderByIF(dto is { SortField: "reTransactNum", SortRule: 1 }, d => d.ReTransactNum, OrderByType.Desc) //重办次数降序
-             .OrderByIF(dto is { SortField: "isUrgentText",SortRule:0 }, d=>d.IsUrgent,OrderByType.Asc) //是否紧急升序
-             .OrderByIF(dto is { SortField: "isUrgentText",SortRule:1 },d=>d.IsUrgent,OrderByType.Desc) //是否紧急降序
-             .OrderByIF(dto is { SortField: "isSecret",SortRule:0 },d=>d.IsSecret,OrderByType.Asc) //是否紧急升序
-             .OrderByIF(dto is { SortField: "isSecret", SortRule: 1 }, d => d.IsSecret, OrderByType.Desc) //是否紧急降序
-             .OrderByIF(dto is { SortField: "currentStepName", SortRule: 0 }, d => d.CurrentStepName, OrderByType.Asc)//当前节点升序
-             .OrderByIF(dto is { SortField: "currentStepName", SortRule: 1 }, d => d.CurrentStepName, OrderByType.Desc)//当前节点降序
-             .OrderByIF(dto is { SortField: "actualStepAcceptText",SortRule:0 },d=>d.ActualHandleStepAcceptTime.HasValue,OrderByType.Asc) //受理情况升序
-             .OrderByIF(dto is { SortField: "actualStepAcceptText", SortRule: 1 }, d => d.ActualHandleStepAcceptTime.HasValue, OrderByType.Desc) //受理情况降序
-             .OrderByIF(dto is { SortField: "statusText",SortRule:0 },d=>d.Status,OrderByType.Asc) //工单状态升序
-             .OrderByIF(dto is { SortField: "statusText",SortRule:1 },d=>d.Status,OrderByType.Desc) //工单状态降序
-             .OrderByIF(dto is { SortField: "startTime",SortRule:0 },d=>d.StartTime,OrderByType.Asc) //受理时间升序
-             .OrderByIF(dto is { SortField: "startTime",SortRule:1 },d=>d.StartTime,OrderByType.Desc) //受理时间降序
-             .OrderByIF(dto is { SortField: "expiredTime",SortRule:0 },d=>d.ExpiredTime,OrderByType.Asc) //超期时间升序
-             .OrderByIF(dto is { SortField: "expiredTime",SortRule:1 },d=>d.ExpiredTime,OrderByType.Desc) //超期时间降序
-             .OrderByIF(dto is { SortField: "filedTime", SortRule:0 },d=>d.FiledTime,OrderByType.Asc) //办结时间升序
-             .OrderByIF(dto is { SortField: "filedTime", SortRule:1 },d=>d.FiledTime,OrderByType.Desc) //办结时间降序
-             .OrderByIF(dto is { SortField: "orgLevelOneName",SortRule:0 },d=>d.OrgLevelOneName,OrderByType.Asc ) //一级部门升序
-             .OrderByIF(dto is { SortField: "orgLevelOneName",SortRule:1 },d=>d.OrgLevelOneName,OrderByType.Desc ) //一级部门降序
-             .OrderByIF(dto is { SortField: "orgLevelTwoName",SortRule:0 },d=>d.OrgLevelTwoName,OrderByType.Asc) //二级部门升序
-             .OrderByIF(dto is { SortField: "orgLevelTwoName",SortRule:1 },d=>d.OrgLevelTwoName,OrderByType.Desc) //二级部门降序
-             .OrderByIF(dto is { SortField: "actualHandleOrgName",SortRule:0 },d=>d.ActualHandleOrgName,OrderByType.Asc) //接办部门升序
-             .OrderByIF(dto is { SortField: "actualHandleOrgName",SortRule:1 },d=>d.ActualHandleOrgName,OrderByType.Desc) //接办部门降序
-             .OrderByIF(dto is { SortField: "acceptType",SortRule:0 },d=>d.AcceptTypeCode,OrderByType.Asc) //受理类型升序
-             .OrderByIF(dto is { SortField: "acceptType",SortRule:1 },d=>d.AcceptTypeCode,OrderByType.Desc) //受理类型降序
-             .OrderByIF(dto is { SortField: "counterSignTypeText",SortRule:0 },d=>d.CounterSignType,OrderByType.Asc) //是否会签升序
-             .OrderByIF(dto is { SortField: "counterSignTypeText",SortRule:1 },d=>d.CounterSignType,OrderByType.Desc) //是否会签降序
-             .OrderByIF(dto is { SortField: "hotspotSpliceName",SortRule:0 },d=>d.HotspotSpliceName,OrderByType.Asc)//热点分类全称升序
-             .OrderByIF(dto is { SortField: "hotspotSpliceName",SortRule:1 },d=>d.HotspotSpliceName,OrderByType.Desc) //热点分类全称降序
-             .OrderByIF(dto is { SortField: "hotspotName",SortRule:0 },d=>d.HotspotName,OrderByType.Asc) //热点分类升序
-             .OrderByIF(dto is { SortField: "hotspotName",SortRule:1 },d=>d.HotspotName,OrderByType.Desc) //热点分类降序
-             .OrderByIF(dto is { SortField: "acceptorName",SortRule:0 },d=>d.AcceptorName,OrderByType.Asc) //受理人升序
-             .OrderByIF(dto is { SortField: "acceptorName",SortRule:1 },d=>d.AcceptorName,OrderByType.Desc) //受理人降序
-             ;
+        query = query.Includes(x => x.OrderScreens);
+        if (!_appOptions.Value.IsYiBin)
+        {
+            query = query.Includes(x => x.OrderVisits.Where(m => m.VisitState == EVisitState.Visited).ToList())
+         .Includes(x => x.OrderVisits.Where(m => m.VisitState == EVisitState.Visited).ToList(), ovd => ovd.OrderVisitDetails);
+        }
+        query = query
+         .WhereIF(!string.IsNullOrEmpty(dto.Keyword), d => d.Title.Contains(dto.Keyword!)) //标题
+         .WhereIF(!string.IsNullOrEmpty(dto.ProvinceNo), d => d.ProvinceNo.Contains(dto.ProvinceNo)) //省本地编号
+         .WhereIF(!string.IsNullOrEmpty(dto.ReceiveProvinceNo), d => d.ReceiveProvinceNo.Contains(dto.ReceiveProvinceNo)) //省编号
+         .WhereIF(dto.IsSecret.HasValue, d => d.IsSecret == dto.IsSecret.Value)
+         .WhereIF(!string.IsNullOrEmpty(dto.No), d => d.No.Contains(dto.No))//工单编码
+         .WhereIF(!string.IsNullOrEmpty(dto.AcceptType), d => d.AcceptTypeCode == dto.AcceptType) //受理类型
+                                                                                                  //.WhereIF(dto.AcceptTypes.Any(), d => dto.AcceptTypes.Contains(d.AcceptTypeCode)) //受理类型
+         .WhereIF(!string.IsNullOrEmpty(dto.Channel), d => d.SourceChannelCode == dto.Channel)
+         //.WhereIF(dto.Channels.Any(), d => dto.Channels.Contains(d.SourceChannelCode)) //来源渠道
+         //.WhereIF(dto.HotspotIds.Any(), d => dto.HotspotIds.Contains(d.HotspotId)) //热点类型
+         .WhereIF(!string.IsNullOrEmpty(dto.Hotspot), d => d.HotspotSpliceName != null && d.HotspotSpliceName.Contains(dto.Hotspot))
+         .WhereIF(!string.IsNullOrEmpty(dto.TransferPhone), d => d.TransferPhone == dto.TransferPhone!) //转接号码
+                                                                                                        //.WhereIF(dto.OrgCodes.Any(), d => d.Workflow.Assigns.Any(s => dto.OrgCodes.Contains(s.OrgCode)))
+                                                                                                        //.WhereIF(dto.OrgCodes.Any(), d => dto.OrgCodes.Contains(d.ActualHandleOrgCode)) //接办部门
+                                                                                                        //.WhereIF(!string.IsNullOrEmpty(dto.OrgId), d => d.CurrentHandleOrgId == dto.OrgId)//接办部门
+         .WhereIF(!string.IsNullOrEmpty(dto.OrgLevelOneName), d => d.OrgLevelOneName.Contains(dto.OrgLevelOneName)) //一级部门
+         .WhereIF(!string.IsNullOrEmpty(dto.ActualHandleOrgName), d => d.ActualHandleOrgName.Contains(dto.ActualHandleOrgName)) //接办部门(综合查询模糊)
+         .WhereIF(!string.IsNullOrEmpty(dto.NameOrNo), d => d.AcceptorName == dto.NameOrNo! || d.AcceptorStaffNo == dto.NameOrNo!) //受理人/坐席
+		 .WhereIF(dto.CreationTimeStart.HasValue, d => d.CreationTime >= dto.CreationTimeStart) //受理时间开始
+         .WhereIF(dto.CreationTimeEnd.HasValue, d => d.CreationTime <= dto.CreationTimeEnd) //受理时间结束
+                                                                                            //.WhereIF(dto.EmergencyLevels.Any(), d => dto.EmergencyLevels.Contains(d.EmergencyLevel))  //紧急程度
+         .WhereIF(!string.IsNullOrEmpty(dto.FromPhone), d => d.FromPhone.Contains(dto.FromPhone)) //来电号码
+         .WhereIF(!string.IsNullOrEmpty(dto.PhoneNo), d => d.Contact.Contains(dto.PhoneNo)) //联系电话
+                                                                                            //.WhereIF(!string.IsNullOrEmpty(dto.PushTypeCode), d => d.PushTypeCode == dto.PushTypeCode) //推送分类
+         .WhereIF(!string.IsNullOrEmpty(dto.PushTypeCode), x => x.OrderPushTypes.Any(opt => opt.PushTypeCode == dto.PushTypeCode)) //推送分类
+         .WhereIF(dto.ExpiredTimeStart.HasValue, d => d.ExpiredTime >= dto.ExpiredTimeStart) //超期时间开始
+         .WhereIF(dto.ExpiredTimeEnd.HasValue, d => d.ExpiredTime <= dto.ExpiredTimeEnd) //超期时间结束
+                                                                                         //.WhereIF(dto.Statuses.Any(), d => dto.Statuses.Contains(d.Status))  //工单状态
+         .WhereIF(dto.Status.HasValue, d => d.Status == dto.Status) //工单状态
+                                                                    //.WhereIF(dto.Statuses.Any(d => d == EOrderStatus.SpecialToUnAccept), d => d.Status <= EOrderStatus.SpecialToUnAccept)
+         .WhereIF(!string.IsNullOrEmpty(dto.ActualHandlerName), d => d.ActualHandlerName == dto.ActualHandlerName) //接办人
+         .WhereIF(dto.IsScreen == true, d => d.OrderScreens.Any(x => x.Status != EScreenStatus.Refuse)) //有甄别
+         .WhereIF(dto.IsScreen == false, d => !d.OrderScreens.Any(x => x.Status != EScreenStatus.Refuse)) //无甄别
+         .WhereIF(!string.IsNullOrEmpty(dto.CurrentStepCode), d => d.CurrentStepCode == dto.CurrentStepCode) //当前办理节点
+         .WhereIF(dto.ActualHandleTimeStart.HasValue, d => d.FiledTime >= dto.ActualHandleTimeStart) //办结时间开始
+         .WhereIF(dto.ActualHandleTimeEnd.HasValue, d => d.FiledTime <= dto.ActualHandleTimeEnd) //办结时间结束
+         .WhereIF(dto.IsOverTime == true,
+             d => (d.ExpiredTime < DateTime.Now && d.Status < EOrderStatus.Filed) ||
+                  (d.ExpiredTime < d.FiledTime && d.Status >= EOrderStatus.Filed)) //是 超期
+         .WhereIF(dto.IsOverTime == false,
+             d => (d.ExpiredTime > DateTime.Now && d.Status < EOrderStatus.Filed) ||
+                  (d.ExpiredTime > d.FiledTime && d.Status >= EOrderStatus.Filed)) //否 超期
+         .WhereIF(dto.IdentityType != null, d => d.IdentityType == dto.IdentityType) //来电主体
+         .WhereIF(!string.IsNullOrEmpty(dto.FromName), d => d.FromName == dto.FromName) //来电人姓名
+                                                                                        //.WhereIF(dto.AreaCodes.Any(), d => dto.AreaCodes.Contains(d.AreaCode)) //区域
+                                                                                        //.WhereIF(!string.IsNullOrEmpty(dto.AreaCode), d => d.AreaCode == dto.AreaCode)//区域
+         .WhereIF(!string.IsNullOrEmpty(dto.AreaCode) && dto.AreaCode.LastIndexOf("00") > 0,
+             d => d.AreaCode.StartsWith(SqlFunc.Substring(dto.AreaCode, 0, 4)))
+         .WhereIF(!string.IsNullOrEmpty(dto.AreaCode) && dto.AreaCode.LastIndexOf("00") <= 0, d => d.AreaCode.StartsWith(dto.AreaCode))
+         .WhereIF(dto.IsProvinceOrder.HasValue && dto.IsProvinceOrder == true, d => d.Source == ESource.ProvinceStraight)
+         .WhereIF(dto.IsProvinceOrder.HasValue && dto.IsProvinceOrder == false, d => d.Source != ESource.ProvinceStraight)
+         .WhereIF(!string.IsNullOrEmpty(dto.SensitiveWord), d => SqlFunc.JsonArrayAny(d.Sensitive, dto.SensitiveWord))
+         .WhereIF(dto.IsSensitiveWord.HasValue && dto.IsSensitiveWord == true,
+             d => d.Sensitive != null && SqlFunc.JsonArrayLength(d.Sensitive) > 0)
+         .WhereIF(dto.IsUrgent.HasValue, d => d.IsUrgent == dto.IsUrgent.Value)
+         .WhereIF(!string.IsNullOrEmpty(dto.ProvinceChannel) && dto.ProvinceChannel == "1", d => d.Source == ESource.ProvinceStraight &&
+             d.SourceChannelCode == "SZMHD" && d.IsProvince == false) //政民互动直派
+         .WhereIF(!string.IsNullOrEmpty(dto.ProvinceChannel) && dto.ProvinceChannel == "2", d => d.Source == ESource.ProvinceStraight &&
+             d.SourceChannelCode == "SZMHD" && d.IsProvince == true) //政民互动
+         .WhereIF(!string.IsNullOrEmpty(dto.ProvinceChannel) && dto.ProvinceChannel == "3", d => d.Source == ESource.ProvinceStraight &&
+             d.SourceChannelCode == "S12345" && d.IsProvince == true) //省12345
+         .WhereIF(!string.IsNullOrEmpty(dto.ContentRetrieval),
+             d => d.Title.Contains(dto.ContentRetrieval) || d.Content.Contains(dto.ContentRetrieval) || d.FileOpinion.Contains(dto.ContentRetrieval) || d.ActualOpinion.Contains(dto.ContentRetrieval))
+         .WhereIF(dto.IsSgin.HasValue && dto.IsSgin == true, d => d.CurrentStepAcceptTime != null)
+         .WhereIF(dto.IsSgin.HasValue && dto.IsSgin == false, d => d.CurrentStepAcceptTime == null)
+         .WhereIF(dto.FiledType is FiledType.CenterFiled, d => d.ProcessType == EProcessType.Zhiban)
+         .WhereIF(dto.FiledType is FiledType.OrgFiled, d => d.ProcessType == EProcessType.Jiaoban)
+         .WhereIF(!string.IsNullOrEmpty(dto.OrderTagCode), d => d.OrderTagCode == dto.OrderTagCode)
+         .WhereIF(!string.IsNullOrEmpty(dto.FocusOnEvents), d => SqlFunc.SplitIn(d.FocusOnEvents, dto.FocusOnEvents))
+         .OrderByIF(string.IsNullOrEmpty(dto.SortField), d => d.CreationTime, OrderByType.Desc)//默认排序时间为创建时间
+         .OrderByIF(dto is { SortField: "no", SortRule: 0 }, d => d.No, OrderByType.Asc) //工单编号升序
+         .OrderByIF(dto is { SortField: "no", SortRule: 1 }, d => d.No, OrderByType.Desc) //工单编号降序
+         .OrderByIF(dto is { SortField: "isProvinceText", SortRule: 0 }, d => d.IsProvince, OrderByType.Asc)//是否省工单升序
+         .OrderByIF(dto is { SortField: "isProvinceText", SortRule: 1 }, d => d.IsProvince, OrderByType.Desc)//是否省工单降序
+         .OrderByIF(dto is { SortField: "reTransactNum", SortRule: 0 }, d => d.ReTransactNum, OrderByType.Asc) //重办次数升序
+         .OrderByIF(dto is { SortField: "reTransactNum", SortRule: 1 }, d => d.ReTransactNum, OrderByType.Desc) //重办次数降序
+         .OrderByIF(dto is { SortField: "isUrgentText", SortRule: 0 }, d => d.IsUrgent, OrderByType.Asc) //是否紧急升序
+         .OrderByIF(dto is { SortField: "isUrgentText", SortRule: 1 }, d => d.IsUrgent, OrderByType.Desc) //是否紧急降序
+         .OrderByIF(dto is { SortField: "isSecret", SortRule: 0 }, d => d.IsSecret, OrderByType.Asc) //是否紧急升序
+         .OrderByIF(dto is { SortField: "isSecret", SortRule: 1 }, d => d.IsSecret, OrderByType.Desc) //是否紧急降序
+         .OrderByIF(dto is { SortField: "currentStepName", SortRule: 0 }, d => d.CurrentStepName, OrderByType.Asc)//当前节点升序
+         .OrderByIF(dto is { SortField: "currentStepName", SortRule: 1 }, d => d.CurrentStepName, OrderByType.Desc)//当前节点降序
+         .OrderByIF(dto is { SortField: "actualStepAcceptText", SortRule: 0 }, d => d.ActualHandleStepAcceptTime.HasValue, OrderByType.Asc) //受理情况升序
+         .OrderByIF(dto is { SortField: "actualStepAcceptText", SortRule: 1 }, d => d.ActualHandleStepAcceptTime.HasValue, OrderByType.Desc) //受理情况降序
+         .OrderByIF(dto is { SortField: "statusText", SortRule: 0 }, d => d.Status, OrderByType.Asc) //工单状态升序
+         .OrderByIF(dto is { SortField: "statusText", SortRule: 1 }, d => d.Status, OrderByType.Desc) //工单状态降序
+         .OrderByIF(dto is { SortField: "startTime", SortRule: 0 }, d => d.StartTime, OrderByType.Asc) //受理时间升序
+         .OrderByIF(dto is { SortField: "startTime", SortRule: 1 }, d => d.StartTime, OrderByType.Desc) //受理时间降序
+         .OrderByIF(dto is { SortField: "expiredTime", SortRule: 0 }, d => d.ExpiredTime, OrderByType.Asc) //超期时间升序
+         .OrderByIF(dto is { SortField: "expiredTime", SortRule: 1 }, d => d.ExpiredTime, OrderByType.Desc) //超期时间降序
+         .OrderByIF(dto is { SortField: "filedTime", SortRule: 0 }, d => d.FiledTime, OrderByType.Asc) //办结时间升序
+         .OrderByIF(dto is { SortField: "filedTime", SortRule: 1 }, d => d.FiledTime, OrderByType.Desc) //办结时间降序
+         .OrderByIF(dto is { SortField: "orgLevelOneName", SortRule: 0 }, d => d.OrgLevelOneName, OrderByType.Asc) //一级部门升序
+         .OrderByIF(dto is { SortField: "orgLevelOneName", SortRule: 1 }, d => d.OrgLevelOneName, OrderByType.Desc) //一级部门降序
+         .OrderByIF(dto is { SortField: "orgLevelTwoName", SortRule: 0 }, d => d.OrgLevelTwoName, OrderByType.Asc) //二级部门升序
+         .OrderByIF(dto is { SortField: "orgLevelTwoName", SortRule: 1 }, d => d.OrgLevelTwoName, OrderByType.Desc) //二级部门降序
+         .OrderByIF(dto is { SortField: "actualHandleOrgName", SortRule: 0 }, d => d.ActualHandleOrgName, OrderByType.Asc) //接办部门升序
+         .OrderByIF(dto is { SortField: "actualHandleOrgName", SortRule: 1 }, d => d.ActualHandleOrgName, OrderByType.Desc) //接办部门降序
+         .OrderByIF(dto is { SortField: "acceptType", SortRule: 0 }, d => d.AcceptTypeCode, OrderByType.Asc) //受理类型升序
+         .OrderByIF(dto is { SortField: "acceptType", SortRule: 1 }, d => d.AcceptTypeCode, OrderByType.Desc) //受理类型降序
+         .OrderByIF(dto is { SortField: "counterSignTypeText", SortRule: 0 }, d => d.CounterSignType, OrderByType.Asc) //是否会签升序
+         .OrderByIF(dto is { SortField: "counterSignTypeText", SortRule: 1 }, d => d.CounterSignType, OrderByType.Desc) //是否会签降序
+         .OrderByIF(dto is { SortField: "hotspotSpliceName", SortRule: 0 }, d => d.HotspotSpliceName, OrderByType.Asc)//热点分类全称升序
+         .OrderByIF(dto is { SortField: "hotspotSpliceName", SortRule: 1 }, d => d.HotspotSpliceName, OrderByType.Desc) //热点分类全称降序
+         .OrderByIF(dto is { SortField: "hotspotName", SortRule: 0 }, d => d.HotspotName, OrderByType.Asc) //热点分类升序
+         .OrderByIF(dto is { SortField: "hotspotName", SortRule: 1 }, d => d.HotspotName, OrderByType.Desc) //热点分类降序
+         .OrderByIF(dto is { SortField: "acceptorName", SortRule: 0 }, d => d.AcceptorName, OrderByType.Asc) //受理人升序
+         .OrderByIF(dto is { SortField: "acceptorName", SortRule: 1 }, d => d.AcceptorName, OrderByType.Desc) //受理人降序
+         ;
 
 
         return query;
         return query;
     }
     }
@@ -1326,8 +1338,8 @@ public class OrderApplication : IOrderApplication, IScopeDependency
         var quer = _orderRepository.Queryable()
         var quer = _orderRepository.Queryable()
             .InnerJoin<SystemOrganize>((x, so) => x.ActualHandleOrgCode == so.Id && so.Level == 1)
             .InnerJoin<SystemOrganize>((x, so) => x.ActualHandleOrgCode == so.Id && so.Level == 1)
             .Where((x, so) => x.CreationTime >= dto.StartTime && x.CreationTime <= dto.EndTime)
             .Where((x, so) => x.CreationTime >= dto.StartTime && x.CreationTime <= dto.EndTime)
-			//.WhereIF(dto.Statuses.Any(), x => dto.Statuses.Contains(x.Status))  //工单状态
-			.WhereIF(dto.QueryType == 1, (x, so) => x.Status >= EOrderStatus.Filed && x.ExpiredTime < x.FiledTime) //业务已办超期
+            //.WhereIF(dto.Statuses.Any(), x => dto.Statuses.Contains(x.Status))  //工单状态
+            .WhereIF(dto.QueryType == 1, (x, so) => x.Status >= EOrderStatus.Filed && x.ExpiredTime < x.FiledTime) //业务已办超期
             .WhereIF(dto.QueryType == 3, (x, so) => x.Status < EOrderStatus.Filed && x.ExpiredTime < SqlFunc.GetDate()) //业务待办超期
             .WhereIF(dto.QueryType == 3, (x, so) => x.Status < EOrderStatus.Filed && x.ExpiredTime < SqlFunc.GetDate()) //业务待办超期
             .WhereIF(dto.TypeId != null && dto.TypeId == 1, (x, so) => x.IdentityType == EIdentityType.Citizen)
             .WhereIF(dto.TypeId != null && dto.TypeId == 1, (x, so) => x.IdentityType == EIdentityType.Citizen)
             .WhereIF(dto.TypeId != null && dto.TypeId == 2, (x, so) => x.IdentityType == EIdentityType.Enterprise)
             .WhereIF(dto.TypeId != null && dto.TypeId == 2, (x, so) => x.IdentityType == EIdentityType.Enterprise)
@@ -1353,7 +1365,7 @@ public class OrderApplication : IOrderApplication, IScopeDependency
 
 
             quer = _orderRepository.Queryable().InnerJoin(queryCountersign, (o, w) => o.Id == w.Id)
             quer = _orderRepository.Queryable().InnerJoin(queryCountersign, (o, w) => o.Id == w.Id)
                 .Select((o, w) => new Order { DaysOverdueOrgName = w.HandlerOrgName, Id = o.Id.SelectAll() })
                 .Select((o, w) => new Order { DaysOverdueOrgName = w.HandlerOrgName, Id = o.Id.SelectAll() })
-				.WhereIF(!string.IsNullOrEmpty(dto.No), o => o.No == dto.No);
+                .WhereIF(!string.IsNullOrEmpty(dto.No), o => o.No == dto.No);
             //quer = _orderRepository.UnionAll(quer, queryCountersignOrder);
             //quer = _orderRepository.UnionAll(quer, queryCountersignOrder);
         }
         }
 
 
@@ -1372,7 +1384,7 @@ public class OrderApplication : IOrderApplication, IScopeDependency
         var quer = _orderRepository.Queryable()
         var quer = _orderRepository.Queryable()
             .InnerJoin<SystemOrganize>((x, so) => x.ActualHandleOrgCode == so.Id && so.Level == 1)
             .InnerJoin<SystemOrganize>((x, so) => x.ActualHandleOrgCode == so.Id && so.Level == 1)
             .Where((x, so) => x.CreationTime >= dto.StartTime && x.CreationTime <= dto.EndTime)
             .Where((x, so) => x.CreationTime >= dto.StartTime && x.CreationTime <= dto.EndTime)
-			.WhereIF(dto.Statuses.Any(), (x, so) => dto.Statuses.Contains(x.Status)) //工单状态
+            .WhereIF(dto.Statuses.Any(), (x, so) => dto.Statuses.Contains(x.Status)) //工单状态
             .WhereIF(dto.ExpiredType is 2, (x, so) => x.OrderDelays.Any(x => x.DelayState == EDelayState.Pass))
             .WhereIF(dto.ExpiredType is 2, (x, so) => x.OrderDelays.Any(x => x.DelayState == EDelayState.Pass))
             .Where((x, so) =>
             .Where((x, so) =>
                 (x.Status >= EOrderStatus.Filed && x.ExpiredTime < x.FiledTime) ||
                 (x.Status >= EOrderStatus.Filed && x.ExpiredTime < x.FiledTime) ||
@@ -1398,7 +1410,7 @@ public class OrderApplication : IOrderApplication, IScopeDependency
             .Select((o, w) => new Order { DaysOverdueOrgName = w.HandlerOrgName, Id = o.Id.SelectAll() });
             .Select((o, w) => new Order { DaysOverdueOrgName = w.HandlerOrgName, Id = o.Id.SelectAll() });
         quer = _orderRepository.UnionAll(quer, queryCountersignOrder).MergeTable()
         quer = _orderRepository.UnionAll(quer, queryCountersignOrder).MergeTable()
             .InnerJoin<SystemOrganize>((x, so) => x.ActualHandleOrgCode == so.Id)
             .InnerJoin<SystemOrganize>((x, so) => x.ActualHandleOrgCode == so.Id)
-			.WhereIF(!string.IsNullOrEmpty(dto.No), (x, so) => x.No == dto.No);
+            .WhereIF(!string.IsNullOrEmpty(dto.No), (x, so) => x.No == dto.No);
         return quer;
         return quer;
     }
     }
 
 
@@ -1412,7 +1424,7 @@ public class OrderApplication : IOrderApplication, IScopeDependency
         return _orderSpecialDetailRepository.Queryable()
         return _orderSpecialDetailRepository.Queryable()
             .Includes(x => x.OrderSpecial)
             .Includes(x => x.OrderSpecial)
             .WhereIF(!string.IsNullOrEmpty(dto.OrgName), x => x.OrgName.Contains(dto.OrgName!))
             .WhereIF(!string.IsNullOrEmpty(dto.OrgName), x => x.OrgName.Contains(dto.OrgName!))
-            .Where(x => x.OrderSpecial.SpecialType == ESpecialType.ReTransact || x.OrderSpecial.SpecialType == ESpecialType.SendBack )
+            .Where(x => x.OrderSpecial.SpecialType == ESpecialType.ReTransact || x.OrderSpecial.SpecialType == ESpecialType.SendBack)
             .Where(x => x.OrderSpecial.CreationTime >= dto.StartTime)
             .Where(x => x.OrderSpecial.CreationTime >= dto.StartTime)
             .Where(x => x.OrderSpecial.CreationTime <= dto.EndTime)
             .Where(x => x.OrderSpecial.CreationTime <= dto.EndTime)
             .GroupBy(x => new { Time = x.OrderSpecial.CreationTime.ToString("yyyy-MM-dd"), x.OrgId, x.OrgName })
             .GroupBy(x => new { Time = x.OrderSpecial.CreationTime.ToString("yyyy-MM-dd"), x.OrgId, x.OrgName })
@@ -1786,10 +1798,10 @@ public class OrderApplication : IOrderApplication, IScopeDependency
             dt.Columns.Remove("Column1");
             dt.Columns.Remove("Column1");
 
 
         //计算小计
         //计算小计
-        for (int i = 0;i < dt.Rows.Count;i++)
+        for (int i = 0; i < dt.Rows.Count; i++)
         {
         {
             int sumcount = 0;
             int sumcount = 0;
-            for (int j = 1;j < dt.Columns.Count - 1;j++)
+            for (int j = 1; j < dt.Columns.Count - 1; j++)
             {
             {
                 sumcount += Convert.ToInt32(dt.Rows[i][j].ToString());
                 sumcount += Convert.ToInt32(dt.Rows[i][j].ToString());
             }
             }
@@ -1801,10 +1813,10 @@ public class OrderApplication : IOrderApplication, IScopeDependency
         DataRow totalRow = dt.NewRow();
         DataRow totalRow = dt.NewRow();
         if (dt.Columns[0].ColumnName == "HotspotName") totalRow["HotspotName"] = "合计";
         if (dt.Columns[0].ColumnName == "HotspotName") totalRow["HotspotName"] = "合计";
         else totalRow["一级热点"] = "合计";
         else totalRow["一级热点"] = "合计";
-        for (int i = 1;i < dt.Columns.Count;i++)
+        for (int i = 1; i < dt.Columns.Count; i++)
         {
         {
             int sumcount = 0;
             int sumcount = 0;
-            for (int j = 0;j < dt.Rows.Count;j++)
+            for (int j = 0; j < dt.Rows.Count; j++)
             {
             {
                 sumcount += Convert.ToInt32(dt.Rows[j][i].ToString());
                 sumcount += Convert.ToInt32(dt.Rows[j][i].ToString());
             }
             }
@@ -2318,7 +2330,7 @@ public class OrderApplication : IOrderApplication, IScopeDependency
     public ISugarQueryable<OrderScreenAuditVo> OrderScreenAudit(OrderScreenAuditPagedRequest dto)
     public ISugarQueryable<OrderScreenAuditVo> OrderScreenAudit(OrderScreenAuditPagedRequest dto)
     {
     {
         var query = _workflowTraceRepository.Queryable()
         var query = _workflowTraceRepository.Queryable()
-            .Where(x=>x.ModuleCode == "OrderScreen")
+            .Where(x => x.ModuleCode == "OrderScreen")
             .WhereIF(dto.StartTime.HasValue && dto.EndTime.HasValue, x => x.HandleTime >= dto.StartTime && x.HandleTime <= dto.EndTime)
             .WhereIF(dto.StartTime.HasValue && dto.EndTime.HasValue, x => x.HandleTime >= dto.StartTime && x.HandleTime <= dto.EndTime)
             .WhereIF(!string.IsNullOrEmpty(dto.AuditUserName), x => x.HandlerName == dto.AuditUserName)
             .WhereIF(!string.IsNullOrEmpty(dto.AuditUserName), x => x.HandlerName == dto.AuditUserName)
             .Where(x => x.HandlerName != null && x.HandlerName != "")
             .Where(x => x.HandlerName != null && x.HandlerName != "")
@@ -2410,10 +2422,10 @@ public class OrderApplication : IOrderApplication, IScopeDependency
         else
         else
         {
         {
             query = _orderScreenRepository.Queryable(isAdmin: isAdmin)
             query = _orderScreenRepository.Queryable(isAdmin: isAdmin)
-                .WhereIF(!isAdmin, x => x.CreatorOrgId.StartsWith(_sessionContext.RequiredOrgId));
+                .WhereIF(!isAdmin, d => d.CreatorOrgId.StartsWith(_sessionContext.RequiredOrgId));
         }
         }
 
 
-        query = query
+        return query
             .Includes(d => d.Order)
             .Includes(d => d.Order)
             .Includes(d => d.VisitDetail)
             .Includes(d => d.VisitDetail)
             .Includes(d => d.Visit, v => v.Order)
             .Includes(d => d.Visit, v => v.Order)
@@ -2421,51 +2433,44 @@ public class OrderApplication : IOrderApplication, IScopeDependency
             .Includes(d => d.ScreenDetails.Where(sd => sd.AuditUserId == _sessionContext.UserId).OrderByDescending(sd => sd.AuditTime).Take(1)
             .Includes(d => d.ScreenDetails.Where(sd => sd.AuditUserId == _sessionContext.UserId).OrderByDescending(sd => sd.AuditTime).Take(1)
                 .ToList())
                 .ToList())
             .WhereIF(!string.IsNullOrEmpty(dto.Title), d => d.Visit.Order.Title.Contains(dto.Title!))
             .WhereIF(!string.IsNullOrEmpty(dto.Title), d => d.Visit.Order.Title.Contains(dto.Title!))
-            .WhereIF(!string.IsNullOrEmpty(dto.No), d => d.Visit.Order.No.Contains(dto.No!));
-        if (dto.TabStatus is EScreenStatus.Apply)
-        {
-            query.Where(d =>
-                (d.Status == EScreenStatus.Apply || d.Status == EScreenStatus.Approval ||
-                 (d.Status == EScreenStatus.SendBack && d.SendBackApply == false)));
-        }
-
-        if (dto.TabStatus.HasValue && dto.Status == EScreenStatus.MyHandle)
-        {
-            query.Where(d => (d.Status != EScreenStatus.Apply));
-        }
-        return query
-            .WhereIF(dto.DataScope is 1, x => x.CreatorId == _sessionContext.RequiredUserId)
-            .WhereIF(dto.Status.HasValue, x => x.Status == dto.Status)
-            .WhereIF(!string.IsNullOrEmpty(dto.AcceptType), x => x.Order!.AcceptTypeCode! == dto.AcceptType!)
-            .WhereIF(!string.IsNullOrEmpty(dto.HotspotSpliceName), x => x.Order!.Hotspot.HotSpotFullName!.StartsWith(dto.HotspotSpliceName!))
-            .WhereIF(!string.IsNullOrEmpty(dto.SourceChannel), x => x.Order!.SourceChannelCode! == dto.SourceChannel!)
-            .WhereIF(!string.IsNullOrEmpty(dto.VisitOrgName), x => x.VisitDetail.VisitOrgName!.Contains(dto.VisitOrgName!))
+            .WhereIF(!string.IsNullOrEmpty(dto.No), d => d.Visit.Order.No.Contains(dto.No!))
+            .WhereIF(dto.TabStatus is EScreenStatus.Apply, d => (d.Status == EScreenStatus.Apply || d.Status == EScreenStatus.Approval || (d.Status == EScreenStatus.SendBack && d.SendBackApply == false)))
+            .WhereIF(dto.TabStatus.HasValue && dto.Status == EScreenStatus.MyHandle, d => (d.Status != EScreenStatus.Apply))
+            .WhereIF(dto.DataScope is 1, d => d.CreatorId == _sessionContext.RequiredUserId)
+            .WhereIF(dto.Status.HasValue, d => d.Status == dto.Status)
+            .WhereIF(_appOptions.Value.IsZiGong && dto.QueryScreenType is EQueryOrderScreenType.Org, d => d.ScreenType == EOrderScreenType.Org)
+            .WhereIF(_appOptions.Value.IsZiGong && dto.QueryScreenType is EQueryOrderScreenType.Seat, d => d.ScreenType == EOrderScreenType.Seat)
+            .WhereIF(!string.IsNullOrEmpty(dto.AcceptType), d => d.Order!.AcceptTypeCode! == dto.AcceptType!)
+            .WhereIF(!string.IsNullOrEmpty(dto.HotspotSpliceName), d => d.Order!.Hotspot.HotSpotFullName!.StartsWith(dto.HotspotSpliceName!))
+            .WhereIF(!string.IsNullOrEmpty(dto.SourceChannel), d => d.Order!.SourceChannelCode! == dto.SourceChannel!)
+            .WhereIF(!string.IsNullOrEmpty(dto.VisitOrgName), d => d.VisitDetail.VisitOrgName!.Contains(dto.VisitOrgName!))
             .WhereIF(!string.IsNullOrEmpty(dto.CreatorOrgName), d => d.CreatorOrgName == dto.CreatorOrgName)
             .WhereIF(!string.IsNullOrEmpty(dto.CreatorOrgName), d => d.CreatorOrgName == dto.CreatorOrgName)
             .WhereIF(!string.IsNullOrEmpty(dto.CreatorName), d => d.CreatorName == dto.CreatorName)
             .WhereIF(!string.IsNullOrEmpty(dto.CreatorName), d => d.CreatorName == dto.CreatorName)
-            .WhereIF(dto.IsProvince.HasValue, x => x.Order!.IsProvince == dto.IsProvince)
-            .WhereIF(dto.IsSendBackApplyNum is true, x => x.SendBackApplyNum > 0)
-            .WhereIF(dto.IsSendBackApplyNum is false, x => x.SendBackApplyNum == 0)
+            .WhereIF(dto.IsProvince.HasValue, d => d.Order!.IsProvince == dto.IsProvince)
+            .WhereIF(dto.IsSendBackApplyNum is true, d => d.SendBackApplyNum > 0)
+            .WhereIF(dto.IsSendBackApplyNum is false, d => d.SendBackApplyNum == 0)
             .WhereIF(dto.CreationTimeStart.HasValue, d => d.CreationTime >= dto.CreationTimeStart)
             .WhereIF(dto.CreationTimeStart.HasValue, d => d.CreationTime >= dto.CreationTimeStart)
             .WhereIF(dto.CreationTimeEnd.HasValue, d => d.CreationTime <= dto.CreationTimeEnd)
             .WhereIF(dto.CreationTimeEnd.HasValue, d => d.CreationTime <= dto.CreationTimeEnd)
             .WhereIF(!string.IsNullOrEmpty(dto.OrderId), d => d.OrderId == dto.OrderId)
             .WhereIF(!string.IsNullOrEmpty(dto.OrderId), d => d.OrderId == dto.OrderId)
             //甄别列表
             //甄别列表
-            .WhereIF(!string.IsNullOrEmpty(dto.OrgLevelOneName), x => x.Order!.OrgLevelOneName!.Contains(dto.OrgLevelOneName!))
-            .WhereIF(!string.IsNullOrEmpty(dto.CurrentHandleOrgName), x => x.Order!.CurrentHandleOrgName!.Contains(dto.CurrentHandleOrgName!))
+            .WhereIF(!string.IsNullOrEmpty(dto.OrgLevelOneName), d => d.Order!.OrgLevelOneName!.Contains(dto.OrgLevelOneName!))
+            .WhereIF(!string.IsNullOrEmpty(dto.CurrentHandleOrgName), d => d.Order!.CurrentHandleOrgName!.Contains(dto.CurrentHandleOrgName!))
             .WhereIF(dto.CreationTime.HasValue && dto.EndCreationTime.HasValue,
             .WhereIF(dto.CreationTime.HasValue && dto.EndCreationTime.HasValue,
-                x => x.Order!.CreationTime >= dto.CreationTime && x.Order!.CreationTime <= dto.EndCreationTime)
+                d => d.Order!.CreationTime >= dto.CreationTime && d.Order!.CreationTime <= dto.EndCreationTime)
             .WhereIF(dto.CurrentHandleTime.HasValue && dto.EndCurrentHandleTime.HasValue,
             .WhereIF(dto.CurrentHandleTime.HasValue && dto.EndCurrentHandleTime.HasValue,
-                x => x.Order!.CurrentHandleTime >= dto.CurrentHandleTime && x.Order!.ActualHandleTime <= dto.EndCurrentHandleTime)
+                d => d.Order!.CurrentHandleTime >= dto.CurrentHandleTime && d.Order!.ActualHandleTime <= dto.EndCurrentHandleTime)
             .WhereIF(dto.FiledTime.HasValue && dto.EndFiledTime.HasValue,
             .WhereIF(dto.FiledTime.HasValue && dto.EndFiledTime.HasValue,
-                x => x.Order!.FiledTime >= dto.FiledTime && x.Order!.FiledTime <= dto.EndFiledTime)
+                d => d.Order!.FiledTime >= dto.FiledTime && d.Order!.FiledTime <= dto.EndFiledTime)
             .WhereIF(dto.VisitTime.HasValue && dto.EndVisitTime.HasValue,
             .WhereIF(dto.VisitTime.HasValue && dto.EndVisitTime.HasValue,
-                x => x.Visit.VisitTime >= dto.VisitTime && x.Visit.VisitTime <= dto.EndVisitTime)
-            .WhereIF(!string.IsNullOrEmpty(dto.Contact), x => x.Order!.Contact! == dto.Contact!)
-            .WhereIF(!string.IsNullOrEmpty(dto.FromPhone), x => x.Order!.FromPhone! == dto.FromPhone!)
-            .OrderByIF(dto is { SortRule: 0, SortField: "creationTime" }, x => x.CreationTime, OrderByType.Asc)
-            .OrderByIF(dto is { SortRule: 1, SortField: "creationTime" } || dto.SortRule is null, x => x.CreationTime, OrderByType.Desc);
+                d => d.Visit.VisitTime >= dto.VisitTime && d.Visit.VisitTime <= dto.EndVisitTime)
+            .WhereIF(!string.IsNullOrEmpty(dto.Contact), d => d.Order!.Contact! == dto.Contact!)
+            .WhereIF(!string.IsNullOrEmpty(dto.FromPhone), d => d.Order!.FromPhone! == dto.FromPhone!)
+            .OrderByIF(dto is { SortRule: 0, SortField: "creationTime" }, d => d.CreationTime, OrderByType.Asc)
+            .OrderByIF(dto is { SortRule: 1, SortField: "creationTime" } || dto.SortRule is null, d => d.CreationTime, OrderByType.Desc);
     }
     }
 
 
-    public ISugarQueryable<OrderVisitDetail> MayScreenList(MayScreenListDto dto) {
+    public ISugarQueryable<OrderVisitDetail> MayScreenList(MayScreenListDto dto)
+    {
         var query = _orderVisitedDetailRepository.Queryable(false, true)
         var query = _orderVisitedDetailRepository.Queryable(false, true)
             .Includes(x => x.OrderVisit)
             .Includes(x => x.OrderVisit)
             .Includes(x => x.OrderVisit, y => y.Order)
             .Includes(x => x.OrderVisit, y => y.Order)
@@ -2483,6 +2488,7 @@ public class OrderApplication : IOrderApplication, IScopeDependency
                 x => x.OrderScreens.Any(s => (s.Status != EScreenStatus.SendBack && s.ScreenType == dto.ScreenType && s.SendBackApply != true)) ==
                 x => x.OrderScreens.Any(s => (s.Status != EScreenStatus.SendBack && s.ScreenType == dto.ScreenType && s.SendBackApply != true)) ==
                      false)
                      false)
             .WhereIF(!string.IsNullOrEmpty(dto.No), x => x.OrderVisit.Order!.No!.Contains(dto.No!))
             .WhereIF(!string.IsNullOrEmpty(dto.No), x => x.OrderVisit.Order!.No!.Contains(dto.No!))
+            .WhereIF(!string.IsNullOrEmpty(dto.NameOrNo), x => x.OrderVisit.Order!.AcceptorName == dto.NameOrNo! || x.OrderVisit.Order!.AcceptorStaffNo == dto.NameOrNo!) //受理人/坐席
             .WhereIF(!string.IsNullOrEmpty(dto.Title), x => x.OrderVisit.Order!.Title!.Contains(dto.Title!))
             .WhereIF(!string.IsNullOrEmpty(dto.Title), x => x.OrderVisit.Order!.Title!.Contains(dto.Title!))
             .WhereIF(dto.IsProvince.HasValue, x => x.OrderVisit.Order!.IsProvince == dto.IsProvince)
             .WhereIF(dto.IsProvince.HasValue, x => x.OrderVisit.Order!.IsProvince == dto.IsProvince)
             .WhereIF(!string.IsNullOrEmpty(dto.AcceptType), x => x.OrderVisit.Order!.AcceptTypeCode! == dto.AcceptType!)
             .WhereIF(!string.IsNullOrEmpty(dto.AcceptType), x => x.OrderVisit.Order!.AcceptTypeCode! == dto.AcceptType!)
@@ -2512,111 +2518,130 @@ public class OrderApplication : IOrderApplication, IScopeDependency
             //.WhereIF(!string.IsNullOrEmpty(dto.OrgNoSatisfiedReason),
             //.WhereIF(!string.IsNullOrEmpty(dto.OrgNoSatisfiedReason),
             //    x => SqlFunc.JsonField(x.OrgNoSatisfiedReason, "Key") == dto.OrgNoSatisfiedReason)
             //    x => SqlFunc.JsonField(x.OrgNoSatisfiedReason, "Key") == dto.OrgNoSatisfiedReason)
             .Where(x => x.OrderVisit.VisitState == EVisitState.Visited && x.OrderVisit.IsCanHandle);
             .Where(x => x.OrderVisit.VisitState == EVisitState.Visited && x.OrderVisit.IsCanHandle);
-		if (_sessionContext.OrgId != null && !_sessionContext.OrgIsCenter)
-		{
-			query.WhereIF(!string.IsNullOrEmpty(dto.Keyword),
-					x => x.OrderVisit.Order.Title.Contains(dto.Keyword!) ||
-						 x.OrderVisit.Order.No.Contains(dto.Keyword!))
-				.Where(x => x.VisitTarget == EVisitTarget.Org && x.VisitOrgCode == _sessionContext.OrgId && (
-					SqlFunc.JsonField(x.OrgProcessingResults, "Key") == "1" ||
-					SqlFunc.JsonField(x.OrgProcessingResults, "Key") == "2" ||
-					SqlFunc.JsonField(x.OrgHandledAttitude, "Key") == "1" ||
-					SqlFunc.JsonField(x.OrgHandledAttitude, "Key") == "2"
-				));
-		}
-		else
-		{
-			query.WhereIF(!string.IsNullOrEmpty(dto.Keyword),
-					x => x.OrderVisit.Order.Title.Contains(dto.Keyword!) ||
-						 x.OrderVisit.Order.No.Contains(dto.Keyword!))
-				.WhereIF(dto.ScreenType == EOrderScreenType.Org, x => x.VisitTarget == EVisitTarget.Org && (
-					SqlFunc.JsonField(x.OrgProcessingResults, "Key") == "1" ||
-					SqlFunc.JsonField(x.OrgProcessingResults, "Key") == "2" ||
-					SqlFunc.JsonField(x.OrgHandledAttitude, "Key") == "1" ||
-					SqlFunc.JsonField(x.OrgHandledAttitude, "Key") == "2"
-				))
-				.WhereIF(dto.ScreenType == EOrderScreenType.Seat,
-					x => x.VisitTarget == EVisitTarget.Seat &&
-						 (x.SeatEvaluate == ESeatEvaluate.VeryNoSatisfied || x.SeatEvaluate == ESeatEvaluate.NoSatisfied))
-				;
-		}
-
-		 return query.OrderByIF(dto is { SortRule: 0, SortField: "order.startTime" }, x => x.OrderVisit.Order.StartTime, OrderByType.Asc)
-			.OrderByIF(dto is { SortRule: 1, SortField: "order.startTime" }, x => x.OrderVisit.Order.StartTime, OrderByType.Desc)
-			.OrderByIF(dto is { SortRule: 0, SortField: "order.actualHandleTime" }, x => x.OrderVisit.Order.ActualHandleTime, OrderByType.Asc)
-			.OrderByIF(dto is { SortRule: 1, SortField: "order.actualHandleTime" }, x => x.OrderVisit.Order.ActualHandleTime, OrderByType.Desc)
-			.OrderByIF(dto is { SortRule: 0, SortField: "order.filedTime" }, x => x.OrderVisit.Order.FiledTime, OrderByType.Asc)
-			.OrderByIF(dto is { SortRule: 1, SortField: "order.filedTime" }, x => x.OrderVisit.Order.FiledTime, OrderByType.Desc)
-			.OrderByIF(dto is { SortRule: 0, SortField: "orderVisit.visitTime" }, x => x.OrderVisit.VisitTime, OrderByType.Asc)
-			.OrderByIF(dto is { SortRule: 1, SortField: "orderVisit.visitTime" }, x => x.OrderVisit.VisitTime, OrderByType.Desc)
-			.OrderByIF(dto.SortRule is null, x => x.CreationTime, OrderByType.Desc);
-	}
-	#endregion
-
-	#region 特提
-
-	/// <summary>
-	/// 特提验证
-	/// </summary>
-	/// <param name="dto"></param>
-	public async Task SpecialVerify(OrderSpecialAddDto dto, Order order, CancellationToken cancellationToken)
-	{
-		var screen = await _orderScreenRepository.Queryable().Where(x => x.OrderId == dto.OrderId && (int)x.Status < 2).ToListAsync(cancellationToken);
-		//var order = await _orderRepository.Queryable().Includes(d => d.Workflow).FirstAsync(d => d.Id == dto.OrderId);
-		var sendBackAudit = await _orderSendBackAuditRepository.Queryable().Where(x => x.OrderId == dto.OrderId && x.State == ESendBackAuditState.Apply).ToListAsync(cancellationToken);
-		//if (_appOptions.Value.IsYiBin)
-		//{
-		//	if (screen.Any())
-		//	{
-		//		_orderScreenRepository.RemoveRangeAsync(screen, true, cancellationToken);
-		//	}
-
-		//	if (sendBackAudit.Any())
-		//	{
-		//		_orderSendBackAuditRepository.RemoveRangeAsync(sendBackAudit, true, cancellationToken);
-		//	}
-
-		//	if (order.Workflow.IsInCountersign)
-		//	{
-		//		var workflowStep = await _workflowStepRepository.Queryable().Where(x => x.Id == order.Workflow.TopCountersignStepId).FirstAsync(cancellationToken);
-		//		if (workflowStep != null)
-		//		{
-		//			var dtoEnd = new EndCountersignDto() { CountersignId = workflowStep.StartCountersignId };
-		//			await EndCountersign(dtoEnd,cancellationToken);
-		//		}
-		//	}
-		//}
-		//else
-		//{
-			if (screen.Any()) throw UserFriendlyException.SameMessage("工单存在甄别中的信息!");
-
-			if (sendBackAudit.Any())
-				throw UserFriendlyException.SameMessage("该工单存在正在审核中的退回,不能办理");
-
-			if (order.Workflow.IsInCountersign) throw UserFriendlyException.SameMessage("工单会签中,无法进行特提!");
-		//}
-	}
+        if (_sessionContext.OrgId != null && !_sessionContext.OrgIsCenter)
+        {
+            query.WhereIF(!string.IsNullOrEmpty(dto.Keyword),
+                    x => x.OrderVisit.Order.Title.Contains(dto.Keyword!) ||
+                         x.OrderVisit.Order.No.Contains(dto.Keyword!))
+                .Where(x => x.VisitTarget == EVisitTarget.Org && x.VisitOrgCode == _sessionContext.OrgId && (
+                    SqlFunc.JsonField(x.OrgProcessingResults, "Key") == "1" ||
+                    SqlFunc.JsonField(x.OrgProcessingResults, "Key") == "2" ||
+                    SqlFunc.JsonField(x.OrgHandledAttitude, "Key") == "1" ||
+                    SqlFunc.JsonField(x.OrgHandledAttitude, "Key") == "2"
+                ));
+        }
+        else
+        {
+            query.WhereIF(!string.IsNullOrEmpty(dto.Keyword),
+                    x => x.OrderVisit.Order.Title.Contains(dto.Keyword!) ||
+                         x.OrderVisit.Order.No.Contains(dto.Keyword!))
+                .WhereIF(dto.ScreenType == EOrderScreenType.Org, x => x.VisitTarget == EVisitTarget.Org && (
+                    SqlFunc.JsonField(x.OrgProcessingResults, "Key") == "1" ||
+                    SqlFunc.JsonField(x.OrgProcessingResults, "Key") == "2" ||
+                    SqlFunc.JsonField(x.OrgHandledAttitude, "Key") == "1" ||
+                    SqlFunc.JsonField(x.OrgHandledAttitude, "Key") == "2"
+                ))
+                .WhereIF(dto.ScreenType == EOrderScreenType.Seat,
+                    x => x.VisitTarget == EVisitTarget.Seat &&
+                         (x.SeatEvaluate == ESeatEvaluate.VeryNoSatisfied || x.SeatEvaluate == ESeatEvaluate.NoSatisfied))
+                ;
+        }
 
 
-	public async Task EndCountersign( EndCountersignDto dto, CancellationToken cancellationToken)
-	{
-		var workflow = await _workflowDomainService.TerminalCountersignAsync(dto.CountersignId, cancellationToken);
-		var order = await _orderRepository.GetAsync(d => d.WorkflowId == workflow.Id, cancellationToken);
-		if (order is null)
-			throw new UserFriendlyException($"工单未开启流程, workflowId: {workflow.Id}");
-		order.UpdateHandlingStatus(workflow.IsInCountersign);
-		_mapper.Map(workflow, order);
-		await _orderRepository.UpdateAsync(order, cancellationToken);
-	}
-	#endregion
-
-	#region private
-	/// <summary>
-	/// 接受外部工单(除省平台)
-	/// </summary>
-	/// <param name="dto"></param>
-	/// <param name="cancellationToken"></param>
-	/// <returns></returns>
-	private async Task<AddOrderResponse> ReceiveOrderFromOtherPlatformAsync(AddOrderDto dto, List<FileDto> files, CancellationToken cancellationToken)
+        return query.OrderByIF(dto is { SortRule: 0, SortField: "order.startTime" }, x => x.OrderVisit.Order.StartTime, OrderByType.Asc)
+           .OrderByIF(dto is { SortRule: 1, SortField: "order.startTime" }, x => x.OrderVisit.Order.StartTime, OrderByType.Desc)
+           .OrderByIF(dto is { SortRule: 0, SortField: "order.actualHandleTime" }, x => x.OrderVisit.Order.ActualHandleTime, OrderByType.Asc)
+           .OrderByIF(dto is { SortRule: 1, SortField: "order.actualHandleTime" }, x => x.OrderVisit.Order.ActualHandleTime, OrderByType.Desc)
+           .OrderByIF(dto is { SortRule: 0, SortField: "order.filedTime" }, x => x.OrderVisit.Order.FiledTime, OrderByType.Asc)
+           .OrderByIF(dto is { SortRule: 1, SortField: "order.filedTime" }, x => x.OrderVisit.Order.FiledTime, OrderByType.Desc)
+           .OrderByIF(dto is { SortRule: 0, SortField: "orderVisit.visitTime" }, x => x.OrderVisit.VisitTime, OrderByType.Asc)
+           .OrderByIF(dto is { SortRule: 1, SortField: "orderVisit.visitTime" }, x => x.OrderVisit.VisitTime, OrderByType.Desc)
+           .OrderByIF(dto.SortRule is null, x => x.CreationTime, OrderByType.Desc);
+    }
+    #endregion
+
+    #region 特提
+
+    /// <summary>
+    /// 特提验证
+    /// </summary>
+    /// <param name="dto"></param>
+    public async Task SpecialVerify(OrderSpecialAddDto dto, Order order, CancellationToken cancellationToken)
+    {
+        var screen = await _orderScreenRepository.Queryable().Where(x => x.OrderId == dto.OrderId && (int)x.Status < 2).ToListAsync(cancellationToken);
+        //var order = await _orderRepository.Queryable().Includes(d => d.Workflow).FirstAsync(d => d.Id == dto.OrderId);
+        var sendBackAudit = await _orderSendBackAuditRepository.Queryable().Where(x => x.OrderId == dto.OrderId && x.State == ESendBackAuditState.Apply).ToListAsync(cancellationToken);
+        var orderDelay = await _orderDelayRepository.Queryable().Where(x => x.OrderId == dto.OrderId && x.DelayState == EDelayState.Examining).ToListAsync(cancellationToken);
+        var orderSecondaries = await _orderSecondaryHandlingRepository.Queryable().Where(x=>x.OrderId == dto.OrderId  && x.State == ESecondaryHandlingState.Apply).ToListAsync(cancellationToken);
+
+		if (_appOptions.Value.IsYiBin)
+        {
+            if (screen.Any())
+            {
+               await  _orderScreenRepository.RemoveRangeAsync(screen, false, cancellationToken);
+            }
+
+            if (sendBackAudit.Any())
+            {
+               await _orderSendBackAuditRepository.RemoveRangeAsync(sendBackAudit, false, cancellationToken);
+            }
+
+            if (orderDelay.Any())
+            {
+                await _orderDelayRepository.RemoveRangeAsync(orderDelay, false, cancellationToken);
+            }
+
+            if (orderSecondaries.Any())
+            {
+	            await _orderSecondaryHandlingRepository.RemoveRangeAsync(orderSecondaries, false, cancellationToken);
+            }
+
+			if (order.Workflow.IsInCountersign)
+            {
+                var workflowStep = await _workflowStepRepository.Queryable().Where(x => x.Id == order.Workflow.TopCountersignStepId).FirstAsync(cancellationToken);
+                if (workflowStep != null)
+                {
+                    var dtoEnd = new EndCountersignDto() { CountersignId = workflowStep.StartCountersignId };
+                    await EndCountersign(dtoEnd, cancellationToken);
+                }
+            }
+        }
+        else
+        {
+            if (screen.Any()) throw UserFriendlyException.SameMessage("工单存在甄别中的信息!");
+
+            if (sendBackAudit.Any())
+                throw UserFriendlyException.SameMessage("该工单存在正在审核中的退回,不能办理");
+
+            if (order.Workflow.IsInCountersign) throw UserFriendlyException.SameMessage("工单会签中,无法进行特提!");
+        }
+    }
+
+    public async Task EndCountersign(EndCountersignDto dto, CancellationToken cancellationToken)
+    {
+        var countersign = await _workflowCountersignRepository.GetAsync(dto.CountersignId, cancellationToken);
+        if (countersign is null)
+            throw new UserFriendlyException("无效会签编号");
+        
+        var order = await _orderRepository.GetAsync(d => d.WorkflowId == countersign.WorkflowId, cancellationToken);
+        if (order is null)
+            throw new UserFriendlyException($"工单未开启流程, workflowId: {countersign.WorkflowId}");
+        
+        var workflow = await _workflowDomainService.TerminalCountersignAsync(countersign, order.ExpiredTime.Value, cancellationToken);
+        
+        order.UpdateHandlingStatus(workflow.IsInCountersign);
+        _mapper.Map(workflow, order);
+        await _orderRepository.UpdateAsync(order, cancellationToken);
+    }
+    #endregion
+
+    #region private
+    /// <summary>
+    /// 接受外部工单(除省平台)
+    /// </summary>
+    /// <param name="dto"></param>
+    /// <param name="cancellationToken"></param>
+    /// <returns></returns>
+    private async Task<AddOrderResponse> ReceiveOrderFromOtherPlatformAsync(AddOrderDto dto, List<FileDto> files, CancellationToken cancellationToken)
     {
     {
         if (string.IsNullOrEmpty(dto.ExternalId))
         if (string.IsNullOrEmpty(dto.ExternalId))
             throw new UserFriendlyException("工单外部编号不能为空");
             throw new UserFriendlyException("工单外部编号不能为空");
@@ -2703,25 +2728,37 @@ public class OrderApplication : IOrderApplication, IScopeDependency
             }
             }
 
 
             _mapper.Map(expiredTimeConfig, order);
             _mapper.Map(expiredTimeConfig, order);
-            await _orderRepository.UpdateAsync(order, cancellationToken);
-			//特提(撤回至发起)
-			if (!string.IsNullOrEmpty(order.WorkflowId))
+            //await _orderRepository.UpdateAsync(order, cancellationToken);
+            //特提(撤回至发起)
+            if (!string.IsNullOrEmpty(order.WorkflowId))
             {
             {
+                var nextHandler = new List<FlowStepHandler>();
+                if (_appOptions.Value.IsZiGong)
+                {
+                    // 平均派单
+                    var averageSendOrder = bool.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.AverageSendOrder).SettingValue[0]);
+                    if (averageSendOrder)
+                    {
+                        var handler = await _orderDomainService.AverageOrder(cancellationToken);
+                        nextHandler = new List<FlowStepHandler> { handler };
+                    }
+                }
+
                 //await _workflowDomainService.RecallToStartStepAsync(order.WorkflowId, "省工单重派", current, order.Status >= EOrderStatus.Filed, cancellationToken);
                 //await _workflowDomainService.RecallToStartStepAsync(order.WorkflowId, "省工单重派", current, order.Status >= EOrderStatus.Filed, cancellationToken);
                 var isPaiDan = await _workflowDomainService.RecallToCenterFirstToSendAsync(order.WorkflowId, "省工单重派", order.Status >= EOrderStatus.Filed,
                 var isPaiDan = await _workflowDomainService.RecallToCenterFirstToSendAsync(order.WorkflowId, "省工单重派", order.Status >= EOrderStatus.Filed,
-                    order.ExpiredTime, cancellationToken);
+                    order.ExpiredTime, nextHandler, cancellationToken);
                 order.FileEmpty();
                 order.FileEmpty();
 
 
-                var status = EOrderStatus.WaitForAccept;
-				if (isPaiDan)
+                order.Status = EOrderStatus.WaitForAccept;
+                if (isPaiDan)
                 {
                 {
-                    order.Status = EOrderStatus.Handling;
+	                order.Status = EOrderStatus.Handling;
                 }
                 }
-                await _orderRepository.Updateable().SetColumns(o => new Order{ Status = status }).Where(o => o.Id == order.Id).ExecuteCommandAsync(cancellationToken);
-				//await _orderRepository.UpdateAsync(order, cancellationToken);
-				//处理回访和发布信息
+                //await _orderRepository.Updateable().SetColumns(o => new Order { Status = status }).Where(o => o.Id == order.Id).ExecuteCommandAsync(cancellationToken);
+                
+                //处理回访和发布信息
 
 
-				var publish = await _orderPublishRepository.GetAsync(x => x.OrderId == order.Id);
+                var publish = await _orderPublishRepository.GetAsync(x => x.OrderId == order.Id);
                 if (publish != null)
                 if (publish != null)
                 {
                 {
                     var publishHistory = _mapper.Map<OrderPublishHistory>(publish);
                     var publishHistory = _mapper.Map<OrderPublishHistory>(publish);
@@ -2744,9 +2781,10 @@ public class OrderApplication : IOrderApplication, IScopeDependency
                 }
                 }
 
 
             }
             }
-			//await _workflowDomainService.RecallToStartStepAsync(order.WorkflowId, "省工单重派", current, cancellationToken);
-		}
-        return _mapper.Map<AddOrderResponse>(order);
+            await _orderRepository.UpdateAsync(order, cancellationToken);
+            //await _workflowDomainService.RecallToStartStepAsync(order.WorkflowId, "省工单重派", current, cancellationToken);
+        }
+		return _mapper.Map<AddOrderResponse>(order);
     }
     }
 
 
     /// <summary>
     /// <summary>
@@ -3007,113 +3045,113 @@ public class OrderApplication : IOrderApplication, IScopeDependency
     /// <param name="isExport"></param>
     /// <param name="isExport"></param>
     /// <returns></returns>
     /// <returns></returns>
 	public async Task<(List<SystemDicData> acceptTypes, object items, DataTable data)> AcceptTypeTop10List(ReportPagedRequest dto, bool isExport)
 	public async Task<(List<SystemDicData> acceptTypes, object items, DataTable data)> AcceptTypeTop10List(ReportPagedRequest dto, bool isExport)
-	{
-
-		var dicList = _systemDicDataRepository.Queryable().Where(x => x.DicTypeCode == "AcceptType").OrderBy(x => x.Sort).MergeTable();
-		var IsCenter = _sessionContext.OrgIsCenter;
-
-		var hotspotList = _hotspotRepository.Queryable().Where(x => SqlFunc.Length(x.Id) == 2)
-			.Select(x => new
-			{
-				HotspotId = x.Id,
-				HotspotName = x.HotSpotFullName,
-			}).MergeTable();
-
-		var orderList = _orderRepository.Queryable()
-			.WhereIF(dto.StartTime.HasValue, x => x.CreationTime >= dto.StartTime)
-			.WhereIF(dto.EndTime.HasValue, x => x.CreationTime <= dto.EndTime)
-			.WhereIF(IsCenter == false, x => x.ActualHandleOrgCode.StartsWith(_sessionContext.RequiredOrgId))
-			.Select(x => new
-			{
-				HotspotId = x.HotspotId.Substring(0, 2),
-				AcceptTypeCode = x.AcceptTypeCode,
-			}).MergeTable();
-
-		var hotListAndOrder = hotspotList.LeftJoin(orderList, (it, o) => it.HotspotId == o.HotspotId)
-			.GroupBy((it, o) => new
-			{
-				it.HotspotId,
-				it.HotspotName,
-				AcceptTypeCode = o.AcceptTypeCode,
-			})
-			.OrderBy((it, o) => it.HotspotId)
-			.Select((it, o) => new
-			{
-				HotspotId = it.HotspotId,
-				HotspotName = it.HotspotName,
-				AcceptTypeCode = o.AcceptTypeCode,
-				Count = SqlFunc.AggregateCount(it.HotspotId)
-			}).MergeTable();
-
-		var returnList = await dicList.LeftJoin(hotListAndOrder, (pp, dd) => pp.DicDataValue == dd.AcceptTypeCode)
-			.GroupBy((pp, dd) => new
-			{
-				HotspotId = dd.HotspotId,
-				HotspotName = dd.HotspotName,
-				AcceptTypeCode = pp.DicDataValue,
-				AcceptType = pp.DicDataName,
-			})
-			.OrderBy((pp, dd) => dd.HotspotId)
-			.Select((pp, dd) => new
-			{
-				HotspotId = dd.HotspotId,
-				HotspotName = dd.HotspotName,
-				AcceptTypeCode = pp.DicDataValue,
-				AcceptType = pp.DicDataName,
-				Count = SqlFunc.AggregateSum(dd.Count)
-			}).ToPivotTableAsync(q => q.AcceptType, q => new { q.HotspotName }, q => q.Sum(x => x.Count));
-		DataColumn totalColumn = new DataColumn("有效受理量", typeof(int));
-		returnList.Columns.Add(totalColumn);
-
-		returnList.Columns["HotspotName"].ColumnName = "省一级热点名称";
+    {
+
+        var dicList = _systemDicDataRepository.Queryable().Where(x => x.DicTypeCode == "AcceptType").OrderBy(x => x.Sort).MergeTable();
+        var IsCenter = _sessionContext.OrgIsCenter;
+
+        var hotspotList = _hotspotRepository.Queryable().Where(x => SqlFunc.Length(x.Id) == 2)
+            .Select(x => new
+            {
+                HotspotId = x.Id,
+                HotspotName = x.HotSpotFullName,
+            }).MergeTable();
+
+        var orderList = _orderRepository.Queryable()
+            .WhereIF(dto.StartTime.HasValue, x => x.CreationTime >= dto.StartTime)
+            .WhereIF(dto.EndTime.HasValue, x => x.CreationTime <= dto.EndTime)
+            .WhereIF(IsCenter == false, x => x.ActualHandleOrgCode.StartsWith(_sessionContext.RequiredOrgId))
+            .Select(x => new
+            {
+                HotspotId = x.HotspotId.Substring(0, 2),
+                AcceptTypeCode = x.AcceptTypeCode,
+            }).MergeTable();
+
+        var hotListAndOrder = hotspotList.LeftJoin(orderList, (it, o) => it.HotspotId == o.HotspotId)
+            .GroupBy((it, o) => new
+            {
+                it.HotspotId,
+                it.HotspotName,
+                AcceptTypeCode = o.AcceptTypeCode,
+            })
+            .OrderBy((it, o) => it.HotspotId)
+            .Select((it, o) => new
+            {
+                HotspotId = it.HotspotId,
+                HotspotName = it.HotspotName,
+                AcceptTypeCode = o.AcceptTypeCode,
+                Count = SqlFunc.AggregateCount(it.HotspotId)
+            }).MergeTable();
+
+        var returnList = await dicList.LeftJoin(hotListAndOrder, (pp, dd) => pp.DicDataValue == dd.AcceptTypeCode)
+            .GroupBy((pp, dd) => new
+            {
+                HotspotId = dd.HotspotId,
+                HotspotName = dd.HotspotName,
+                AcceptTypeCode = pp.DicDataValue,
+                AcceptType = pp.DicDataName,
+            })
+            .OrderBy((pp, dd) => dd.HotspotId)
+            .Select((pp, dd) => new
+            {
+                HotspotId = dd.HotspotId,
+                HotspotName = dd.HotspotName,
+                AcceptTypeCode = pp.DicDataValue,
+                AcceptType = pp.DicDataName,
+                Count = SqlFunc.AggregateSum(dd.Count)
+            }).ToPivotTableAsync(q => q.AcceptType, q => new { q.HotspotName }, q => q.Sum(x => x.Count));
+        DataColumn totalColumn = new DataColumn("有效受理量", typeof(int));
+        returnList.Columns.Add(totalColumn);
+
+        returnList.Columns["HotspotName"].ColumnName = "省一级热点名称";
         var titleList = await _systemDicDataRepository.Queryable().Where(x => x.DicTypeCode == "AcceptType").OrderBy(x => x.Sort).ToListAsync();
         var titleList = await _systemDicDataRepository.Queryable().Where(x => x.DicTypeCode == "AcceptType").OrderBy(x => x.Sort).ToListAsync();
-		for (int z = 0; z < returnList.Rows.Count; z++)
-		{
-			if (string.IsNullOrEmpty(returnList.Rows[z]["省一级热点名称"].ToString()))
-			{
-				returnList.Rows.Remove(returnList.Rows[z]);
-			}
-			else
-			{
-				int num = 0;
-				var colTotal = returnList.Columns.Count - 1;
-				for (int i = 1; i < colTotal; i++)
-				{
-					num += int.Parse(returnList.Rows[z][i].ToString());
-				}
-				returnList.Rows[z]["有效受理量"] = num;
-			}
-		}
-		returnList.Columns["有效受理量"].SetOrdinal(1);
-		DataTable list = new DataTable();
-		if (returnList.Rows.Count > 0)
-			list = returnList.Select("", "有效受理量 DESC").Take(10).CopyToDataTable();
-		if (isExport)
-		{
-			return (new List<SystemDicData>(), null, list);
-		}
-		var listDy = ToDynamicList(list);
-		return (titleList, listDy, list);
-	}
+        for (int z = 0; z < returnList.Rows.Count; z++)
+        {
+            if (string.IsNullOrEmpty(returnList.Rows[z]["省一级热点名称"].ToString()))
+            {
+                returnList.Rows.Remove(returnList.Rows[z]);
+            }
+            else
+            {
+                int num = 0;
+                var colTotal = returnList.Columns.Count - 1;
+                for (int i = 1; i < colTotal; i++)
+                {
+                    num += int.Parse(returnList.Rows[z][i].ToString());
+                }
+                returnList.Rows[z]["有效受理量"] = num;
+            }
+        }
+        returnList.Columns["有效受理量"].SetOrdinal(1);
+        DataTable list = new DataTable();
+        if (returnList.Rows.Count > 0)
+            list = returnList.Select("", "有效受理量 DESC").Take(10).CopyToDataTable();
+        if (isExport)
+        {
+            return (new List<SystemDicData>(), null, list);
+        }
+        var listDy = ToDynamicList(list);
+        return (titleList, listDy, list);
+    }
 
 
-	public static List<dynamic> ToDynamicList(DataTable dataTable)
-	{
-		var list = new List<dynamic>();
+    public static List<dynamic> ToDynamicList(DataTable dataTable)
+    {
+        var list = new List<dynamic>();
 
 
-		foreach (DataRow row in dataTable.Rows)
-		{
-			dynamic expando = new ExpandoObject();
-			var expandoDic = (IDictionary<string, object>)expando;
+        foreach (DataRow row in dataTable.Rows)
+        {
+            dynamic expando = new ExpandoObject();
+            var expandoDic = (IDictionary<string, object>)expando;
 
 
-			foreach (DataColumn column in dataTable.Columns)
-			{
-				expandoDic.Add(column.ColumnName, row[column]);
-			}
+            foreach (DataColumn column in dataTable.Columns)
+            {
+                expandoDic.Add(column.ColumnName, row[column]);
+            }
 
 
-			list.Add(expando);
-		}
+            list.Add(expando);
+        }
 
 
-		return list;
-	}
-	#endregion
+        return list;
+    }
+    #endregion
 }
 }

+ 5 - 3
src/Hotline.Application/StatisticalReport/OrderReportApplication.cs

@@ -1497,6 +1497,7 @@ namespace Hotline.Application.StatisticalReport
         {
         {
             var IsCenter = _sessionContext.OrgIsCenter;
             var IsCenter = _sessionContext.OrgIsCenter;
 
 
+            var centerOrgName = _systemSettingCacheManager.GetSetting(SettingConstants.CenterOrgName)?.SettingValue[0];
             var orderData = _orderRepository.Queryable()
             var orderData = _orderRepository.Queryable()
               .Where(it => it.CreationTime >= dto.StartTime && it.CreationTime <= dto.EndTime && it.Status >= EOrderStatus.Filed)
               .Where(it => it.CreationTime >= dto.StartTime && it.CreationTime <= dto.EndTime && it.Status >= EOrderStatus.Filed)
             .WhereIF(dto.TypeId != null && dto.TypeId == 1, it => it.IdentityType == EIdentityType.Citizen)
             .WhereIF(dto.TypeId != null && dto.TypeId == 1, it => it.IdentityType == EIdentityType.Citizen)
@@ -1524,7 +1525,7 @@ namespace Hotline.Application.StatisticalReport
                })
                })
                 .Select((it, o) => new DepartmentAcceptanceTypeStatisticsDto
                 .Select((it, o) => new DepartmentAcceptanceTypeStatisticsDto
                 {
                 {
-                    OrgName = it.OrgCode == "001" ? "市民热线服务中心" : o.Name,
+                    OrgName = it.OrgCode == "001" ? centerOrgName : o.Name,
                     OrgCode = it.OrgCode,
                     OrgCode = it.OrgCode,
                     OrgType = o.OrgType == EOrgType.County ? "区县部门" : "市直部门",
                     OrgType = o.OrgType == EOrgType.County ? "区县部门" : "市直部门",
                     ZxAllCount = SqlFunc.AggregateSum(SqlFunc.IIF(it.AcceptTypeCode == "10", 1, 0)),
                     ZxAllCount = SqlFunc.AggregateSum(SqlFunc.IIF(it.AcceptTypeCode == "10", 1, 0)),
@@ -1764,6 +1765,7 @@ namespace Hotline.Application.StatisticalReport
             dto.EndTime = dto.EndTime.AddDays(1).AddSeconds(-1);
             dto.EndTime = dto.EndTime.AddDays(1).AddSeconds(-1);
 
 
             var isCenter = _sessionContext.OrgIsCenter;
             var isCenter = _sessionContext.OrgIsCenter;
+            var centerOrgName = _systemSettingCacheManager.GetSetting(SettingConstants.CenterOrgName)?.SettingValue[0];
             //工单未超期
             //工单未超期
             var queryOrderNoSigen = _orderRepository.Queryable()
             var queryOrderNoSigen = _orderRepository.Queryable()
                .LeftJoin<WorkflowStep>((o, w) => o.Id == w.ExternalId)
                .LeftJoin<WorkflowStep>((o, w) => o.Id == w.ExternalId)
@@ -1786,7 +1788,7 @@ namespace Hotline.Application.StatisticalReport
                 })
                 })
                  .Select((it, sy) => new OrderNoSigenDto
                  .Select((it, sy) => new OrderNoSigenDto
                  {
                  {
-                     OrgName = it.OrgCode == "001" ? "市民热线服务中心" : sy.Name,
+                     OrgName = it.OrgCode == "001" ? centerOrgName : sy.Name,
                      OrgCode = it.OrgCode,
                      OrgCode = it.OrgCode,
                      OrderNoSigen = SqlFunc.AggregateSum(1),
                      OrderNoSigen = SqlFunc.AggregateSum(1),
                      CounterNoSign = 0
                      CounterNoSign = 0
@@ -1814,7 +1816,7 @@ namespace Hotline.Application.StatisticalReport
                  })
                  })
                   .Select((it, sy) => new OrderNoSigenDto
                   .Select((it, sy) => new OrderNoSigenDto
                   {
                   {
-                      OrgName = it.OrgCode == "001" ? "市民热线服务中心" : sy.Name,
+                      OrgName = it.OrgCode == "001" ? centerOrgName : sy.Name,
                       OrgCode = it.OrgCode,
                       OrgCode = it.OrgCode,
                       OrderNoSigen = 0,
                       OrderNoSigen = 0,
                       CounterNoSign = SqlFunc.AggregateSum(1)
                       CounterNoSign = SqlFunc.AggregateSum(1)

+ 22 - 10
src/Hotline.Application/Subscribers/DatasharingSubscriber.cs

@@ -303,8 +303,19 @@ namespace Hotline.Application.Subscribers
             }
             }
             else
             else
             {
             {
-                var isPaiDan = await _workflowDomainService.RecallToCenterFirstToSendAsync(order.WorkflowId, dto.Opinion,
-                    order.Status >= EOrderStatus.Filed, order.ExpiredTime, cancellationToken);
+	            var nextHandler = new List<FlowStepHandler>();
+				if (_appOptions.Value.IsZiGong)
+	            {
+		            // 平均派单
+		            var averageSendOrder = bool.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.AverageSendOrder).SettingValue[0]);
+		            if (averageSendOrder)
+		            {
+			            var handler = await _orderDomainService.AverageOrder(cancellationToken);
+			            nextHandler = new List<FlowStepHandler> { handler };
+		            }
+	            }
+				var isPaiDan = await _workflowDomainService.RecallToCenterFirstToSendAsync(order.WorkflowId, dto.Opinion,
+                    order.Status >= EOrderStatus.Filed, order.ExpiredTime, nextHandler, cancellationToken);
                 order.FiledTime = null;
                 order.FiledTime = null;
                 if (isPaiDan)
                 if (isPaiDan)
                 {
                 {
@@ -330,6 +341,7 @@ namespace Hotline.Application.Subscribers
             if (exists)
             if (exists)
                 throw new UserFriendlyException($"收到重复催办编号: {dto.DsBisId}");
                 throw new UserFriendlyException($"收到重复催办编号: {dto.DsBisId}");
 
 
+            var centerOrgName = _systemSettingCacheManager.GetSetting(SettingConstants.CenterOrgName)?.SettingValue[0];
             var order = await _orderRepository.Queryable().Where(x => x.ReceiveProvinceNo == dto.CaseSerial).FirstAsync(cancellationToken);
             var order = await _orderRepository.Queryable().Where(x => x.ReceiveProvinceNo == dto.CaseSerial).FirstAsync(cancellationToken);
             var model = new OrderUrge();
             var model = new OrderUrge();
             model.InitId();
             model.InitId();
@@ -339,7 +351,7 @@ namespace Hotline.Application.Subscribers
             model.ApplyContent = dto.RemindReasion;
             model.ApplyContent = dto.RemindReasion;
             model.OrderId = order.Id;
             model.OrderId = order.Id;
             model.OrgId = "001";
             model.OrgId = "001";
-            model.OrgName = "市民热线服务中心";
+            model.OrgName = centerOrgName;
             model.RemindTime = dto.RemindTime;
             model.RemindTime = dto.RemindTime;
             model.DsBisId = dto.DsBisId;
             model.DsBisId = dto.DsBisId;
 
 
@@ -377,6 +389,7 @@ namespace Hotline.Application.Subscribers
             var order = await _orderRepository.Queryable().Where(x => x.ReceiveProvinceNo == dto.CaseSerial).FirstAsync(cancellationToken);
             var order = await _orderRepository.Queryable().Where(x => x.ReceiveProvinceNo == dto.CaseSerial).FirstAsync(cancellationToken);
             if (order != null)
             if (order != null)
             {
             {
+                var centerOrgName = _systemSettingCacheManager.GetSetting(SettingConstants.CenterOrgName)?.SettingValue[0];
                 var model = new OrderUrge();
                 var model = new OrderUrge();
                 model.InitId();
                 model.InitId();
                 model.State = 0;
                 model.State = 0;
@@ -386,7 +399,7 @@ namespace Hotline.Application.Subscribers
                 model.OrderId = order.Id;
                 model.OrderId = order.Id;
                 model.ReplyLimitTime = dto.WarnTimebf;
                 model.ReplyLimitTime = dto.WarnTimebf;
                 model.OrgId = "001";
                 model.OrgId = "001";
-                model.OrgName = "市民热线服务中心";
+                model.OrgName = centerOrgName;
 
 
                 //if (!string.IsNullOrEmpty(order.WorkflowId))
                 //if (!string.IsNullOrEmpty(order.WorkflowId))
                 //{
                 //{
@@ -643,7 +656,7 @@ namespace Hotline.Application.Subscribers
             {
             {
                 //处理省下行回访
                 //处理省下行回访
                 var order = await _orderRepository.Queryable().Where(x => x.ReceiveProvinceNo == dto.ProvinceNo).FirstAsync();
                 var order = await _orderRepository.Queryable().Where(x => x.ReceiveProvinceNo == dto.ProvinceNo).FirstAsync();
-                if (order != null)
+                if (order != null && order.Status<= EOrderStatus.Filed)
                 {
                 {
                     //判断是否有发布数据
                     //判断是否有发布数据
                     var orderPublish = await _orderPublishRepository.Queryable()
                     var orderPublish = await _orderPublishRepository.Queryable()
@@ -667,11 +680,10 @@ namespace Hotline.Application.Subscribers
                     orderVisit = new OrderVisit();
                     orderVisit = new OrderVisit();
                     orderVisit.No = order.No;
                     orderVisit.No = order.No;
                     orderVisit.OrderId = order.Id;
                     orderVisit.OrderId = order.Id;
-                    orderVisit.VisitState = EVisitState.WaitForVisit;
                     orderVisit.PublishTime = DateTime.Now;
                     orderVisit.PublishTime = DateTime.Now;
                     orderVisit.IsCanHandle = true;
                     orderVisit.IsCanHandle = true;
                     orderVisit.EmployeeId = order.CenterToOrgHandlerId;
                     orderVisit.EmployeeId = order.CenterToOrgHandlerId;
-                    orderVisit.VisitState = EVisitState.Visited;
+                    orderVisit.VisitState = EVisitState.None;
                     orderVisit.VisitTime = dto.VisitTime;
                     orderVisit.VisitTime = dto.VisitTime;
                     orderVisit.VisitType = dto.VisitType;
                     orderVisit.VisitType = dto.VisitType;
                     orderVisit.IsCanAiVisit = false;
                     orderVisit.IsCanAiVisit = false;
@@ -708,9 +720,9 @@ namespace Hotline.Application.Subscribers
 
 
 
 
                     await _orderVisitedDetailRepository.AddRangeAsync(visitedDetail, cancellationToken);
                     await _orderVisitedDetailRepository.AddRangeAsync(visitedDetail, cancellationToken);
-                    order.Visited(satisfy.Key, satisfy.Value);
-                    order.Status = EOrderStatus.Visited;
-                    await _orderRepository.UpdateAsync(order, cancellationToken);
+                    //order.Visited(satisfy.Key, satisfy.Value);
+                    //order.Status = EOrderStatus.Visited;
+                    //await _orderRepository.UpdateAsync(order, cancellationToken);
                 }
                 }
             }
             }
         }
         }

+ 7 - 1
src/Hotline.Application/Systems/BaseDataApplication.cs

@@ -170,7 +170,13 @@ public class BaseDataApplication : IScopeDependency
         return this;
         return this;
     }
     }
 
 
-    public BaseDataApplication SourceChannel()
+    public BaseDataApplication Add(string name, List<KeyValuePair<string, string>> items)
+    {
+	    _baseData.TryAdd(name, items);
+	    return this;
+    }
+
+	public BaseDataApplication SourceChannel()
     {
     {
         Add(SysDicTypeConsts.SourceChannel);
         Add(SysDicTypeConsts.SourceChannel);
         return this;
         return this;

+ 1 - 1
src/Hotline.Repository.SqlSugar/Orders/OrderRepository.cs

@@ -1283,7 +1283,7 @@ namespace Hotline.Repository.SqlSugar.Orders
                     HotspotSpliceName = x.OrderVisit.Order.HotspotSpliceName,
                     HotspotSpliceName = x.OrderVisit.Order.HotspotSpliceName,
                     OrgLevelOneName = x.OrderVisit.Order.OrgLevelOneName,
                     OrgLevelOneName = x.OrderVisit.Order.OrgLevelOneName,
                     CurrentHandleOrgName = x.OrderVisit.Order.CurrentHandleOrgName,
                     CurrentHandleOrgName = x.OrderVisit.Order.CurrentHandleOrgName,
-                    CreationTime = x.CreationTime,
+                    CreationTime = x.OrderVisit.Order.CreationTime,
                     Title = x.OrderVisit.Order.Title,
                     Title = x.OrderVisit.Order.Title,
                     VisitUser = x.OrderVisit.Employee.Name,
                     VisitUser = x.OrderVisit.Employee.Name,
                     VisitType = x.OrderVisit.VisitType,
                     VisitType = x.OrderVisit.VisitType,

+ 12 - 11
src/Hotline.Share/Dtos/Article/BulletinDto.cs

@@ -117,9 +117,10 @@ namespace Hotline.Share.Dtos.Article
 
 
         public string BulletinStateText => BulletinState.GetDescription();
         public string BulletinStateText => BulletinState.GetDescription();
 
 
-        public string PushRangesText => PushRanges.Any() ? string.Join("、", PushRanges.Select(x => x.Value)) : "-";
+        public string PushRangesText => PushRanges != null && PushRanges.Any() ? string.Join("、", PushRanges.Select(x => x.Value)) : "-";
 
 
-	}
+        public string DisplayLocationText => DisplayLocation != null && DisplayLocation.Any() ? string.Join("、", DisplayLocation.Select(x => x.Value)) : "-";
+    }
 
 
     public record CircularDto
     public record CircularDto
     {
     {
@@ -229,7 +230,7 @@ namespace Hotline.Share.Dtos.Article
         /// 审核时间
         /// 审核时间
         /// </summary>
         /// </summary>
         public DateTime? ExaminTime { get; set; }
         public DateTime? ExaminTime { get; set; }
-	}
+    }
 
 
     public record CircularReadGroupDto
     public record CircularReadGroupDto
     {
     {
@@ -295,7 +296,7 @@ namespace Hotline.Share.Dtos.Article
         public List<CircularReadGroupDto> CircularReadGroups { get; set; }
         public List<CircularReadGroupDto> CircularReadGroups { get; set; }
     }
     }
 
 
-    public record UpdateCircularDto:AddCircularDto
+    public record UpdateCircularDto : AddCircularDto
     {
     {
         public string Id { get; set; }
         public string Id { get; set; }
     }
     }
@@ -376,10 +377,10 @@ namespace Hotline.Share.Dtos.Article
         /// </summary>
         /// </summary>
         public DateTime LoseEfficacyTime { get; set; }
         public DateTime LoseEfficacyTime { get; set; }
 
 
-		/// <summary>
-		/// 发布范围  EPushRange
-		/// </summary>
-		public List<Kv> PushRanges { get; set; }
+        /// <summary>
+        /// 发布范围  EPushRange
+        /// </summary>
+        public List<Kv> PushRanges { get; set; }
 
 
         /// <summary>
         /// <summary>
         /// 显示位置(多选) 位枚举  BulletinDisplayLocation
         /// 显示位置(多选) 位枚举  BulletinDisplayLocation
@@ -407,13 +408,13 @@ namespace Hotline.Share.Dtos.Article
         public bool IsArrive { get; set; }
         public bool IsArrive { get; set; }
 
 
     }
     }
-    public record UpdateBulletinDto:AddBulletinDto
+    public record UpdateBulletinDto : AddBulletinDto
     {
     {
         public string Id { get; set; }
         public string Id { get; set; }
     }
     }
 
 
 
 
-    public record QueryCircularListRequestDto:PagedRequest
+    public record QueryCircularListRequestDto : PagedRequest
     {
     {
         /// <summary>
         /// <summary>
         /// 标题
         /// 标题
@@ -455,7 +456,7 @@ namespace Hotline.Share.Dtos.Article
         public string Reason { get; set; }
         public string Reason { get; set; }
     }
     }
 
 
-    public record QueryBulletinListRequestDto:PagedRequest
+    public record QueryBulletinListRequestDto : PagedRequest
     {
     {
         /// <summary>
         /// <summary>
         /// 标题
         /// 标题

+ 42 - 6
src/Hotline.Share/Dtos/Knowledge/KnowledgeDto.cs

@@ -55,10 +55,36 @@ namespace Hotline.Share.Dtos.Knowledge
 
 
 	}
 	}
 
 
-    /// <summary>
-    /// 查询详情
-    /// </summary>
-    public class KnowledgeInfoDto : KnowledgeDto
+    public class OffShelfKnowledgeDto
+	{
+		/// <summary>
+		/// ID
+		/// </summary>
+		public string Id { get; set; }
+
+		/// <summary>
+		/// 下架原因
+		/// </summary>
+		public string Opinion { get; set; }
+	}
+
+    public class DeleteKnowledgeDto
+    {
+	    /// <summary>
+	    /// ID
+	    /// </summary>
+	    public string Id { get; set; }
+
+	    /// <summary>
+	    /// 删除原因
+	    /// </summary>
+	    public string Opinion { get; set; }
+    }
+
+	/// <summary>
+	/// 查询详情
+	/// </summary>
+	public class KnowledgeInfoDto : KnowledgeDto
     {
     {
         /// <summary>
         /// <summary>
         /// ID
         /// ID
@@ -336,12 +362,22 @@ namespace Hotline.Share.Dtos.Knowledge
 
 
     }
     }
 
 
-    public class DeleteStartFlowDto : StartWorkflowDto<KnowledgeDto>
+    //public class DeleteStartFlowDto : StartWorkflowDto<KnowledgeDto>
+    //{
+
+    //}
+
+    public class DeleteStartFlowDto : StartWorkflowDto<DeleteKnowledgeDto>
+    {
+
+    }
+
+    public class OffShelfStartFlowDto : StartWorkflowDto<OffShelfKnowledgeDto>
     {
     {
 
 
     }
     }
 
 
-    public class KnowledgeCollectGroupOutDto
+	public class KnowledgeCollectGroupOutDto
     {
     {
         /// <summary>
         /// <summary>
         /// Id
         /// Id

+ 10 - 5
src/Hotline.Share/Dtos/Knowledge/KnowledgePagedDto.cs

@@ -74,12 +74,17 @@ namespace Hotline.Share.Dtos.Knowledge
         /// <summary>
         /// <summary>
         /// 状态
         /// 状态
         /// </summary>
         /// </summary>
-        public EKnowledgeStatus? Status { get; set; }    
+        public EKnowledgeStatus? Status { get; set; }
 
 
-        /// <summary>
-        /// 是否公开
-        /// </summary>
-        public bool? IsPublic { get; set; }
+		/// <summary>
+		/// 草稿状态下的查询条件
+		/// </summary>
+		public EKnowledgeStatus? NewDraftsStatus { get; set; }
+
+		/// <summary>
+		/// 是否公开
+		/// </summary>
+		public bool? IsPublic { get; set; }
 
 
 		/// <summary>
 		/// <summary>
 		/// 摘要
 		/// 摘要

+ 2 - 0
src/Hotline.Share/Dtos/Order/OrderDto.cs

@@ -1024,6 +1024,8 @@ namespace Hotline.Share.Dtos.Order
         /// </summary>
         /// </summary>
         public bool IsSecret { get; set; }
         public bool IsSecret { get; set; }
 
 
+        public string IsSecretText => IsSecret ? "保密" : "";
+
         /// <summary>
         /// <summary>
         /// 工作单位(当“来电/信人身份”为“企业”时必填,其他情况非必填)
         /// 工作单位(当“来电/信人身份”为“企业”时必填,其他情况非必填)
         /// </summary>
         /// </summary>

+ 5 - 0
src/Hotline.Share/Dtos/Order/OrderVisitDto.cs

@@ -937,6 +937,11 @@ namespace Hotline.Share.Dtos.Order
         public string? OrgHandledAttitudeValue => this.OrgHandledAttitude?.Value;
         public string? OrgHandledAttitudeValue => this.OrgHandledAttitude?.Value;
         public string? VisitContent { get; set; }
         public string? VisitContent { get; set; }
         public DateTime? VisitTime { get; set; }
         public DateTime? VisitTime { get; set; }
+
+        /// <summary>
+        /// 通话Id
+        /// </summary>
+        public string? CallId { get; set; }
     }
     }
 
 
     public class DistributionVisitRspDto
     public class DistributionVisitRspDto

+ 75 - 1
src/Hotline.Share/Dtos/Order/QueryOrderDto.cs

@@ -210,6 +210,11 @@ namespace Hotline.Share.Dtos.Order
         /// </summary>
         /// </summary>
         public string? FocusOnEvents { get; set; }
         public string? FocusOnEvents { get; set; }
 
 
+        /// <summary>
+        /// 是否保密
+        /// </summary>
+        public bool? IsSecret { get; set; }
+
     }
     }
 
 
 
 
@@ -484,7 +489,18 @@ namespace Hotline.Share.Dtos.Order
         public bool? IsApply { get; set; }
         public bool? IsApply { get; set; }
 
 
         public EDelayState? DelayState { get; set; }
         public EDelayState? DelayState { get; set; }
-    }
+
+        /// <summary>
+        /// 延期状态快捷查询
+        /// </summary>
+
+        public EQueryDelayState? QueryDelayState { get; set; }
+
+		/// <summary>
+		/// 0 全部  1 我的
+		/// </summary>
+		public int? DataScope { get; set; }
+	}
 
 
     public record ApplySuperviseDto
     public record ApplySuperviseDto
     {
     {
@@ -510,6 +526,11 @@ namespace Hotline.Share.Dtos.Order
 		/// </summary>
 		/// </summary>
 		public int? DataScope { get; set; }
 		public int? DataScope { get; set; }
 
 
+        /// <summary>
+        /// 甄别类型查询条件
+        /// </summary>
+        public EQueryOrderScreenType? QueryScreenType { get; set; }
+
 		/// <summary>
 		/// <summary>
 		/// 甄别申请状态选项卡
 		/// 甄别申请状态选项卡
 		/// </summary>
 		/// </summary>
@@ -770,6 +791,11 @@ namespace Hotline.Share.Dtos.Order
 		/// 甄别类型
 		/// 甄别类型
 		/// </summary>
 		/// </summary>
 		public EOrderScreenType? ScreenType { get; set; }
 		public EOrderScreenType? ScreenType { get; set; }
+
+		/// <summary>
+		/// 受理坐席名字或工号(×)
+		/// </summary>
+		public string? NameOrNo { get; set; }
 	}
 	}
 
 
     public record UrgeListDto : PagedKeywordRequest
     public record UrgeListDto : PagedKeywordRequest
@@ -829,4 +855,52 @@ namespace Hotline.Share.Dtos.Order
         NoPub = 2
         NoPub = 2
     }
     }
 
 
+    public enum EQueryDelayState
+    {
+
+	    /// <summary>
+	    /// 全部
+	    /// </summary>
+	    [Description("全部")]
+	    All = -1,
+
+		/// <summary>
+		/// 待审批
+		/// </summary>
+		[Description("待审批")]
+	    Examining = 0,
+
+	    /// <summary>
+	    /// 审批通过
+	    /// </summary>
+	    [Description("审批通过")]
+	    Pass = 1,
+
+		/// <summary>
+		/// 审批拒绝
+		/// </summary>
+		[Description("审批拒绝")]
+	    NoPass = 2,
+    }
+	public enum EQueryOrderScreenType
+	{
+		/// <summary>
+		/// 全部
+		/// </summary>
+		[Description("全部")]
+		All = -1,
+
+		/// <summary>
+		/// 部门办件
+		/// </summary>
+		[Description("部门办件")]
+		Org = 0,
+
+		/// <summary>
+		/// 中心话务
+		/// </summary>
+		[Description("中心话务")]
+		Seat = 1,
+	}
+
 }
 }

+ 15 - 0
src/Hotline.Share/Dtos/Settings/CacheDto.cs

@@ -0,0 +1,15 @@
+using Hotline.Share.Enums.Settings;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Hotline.Share.Dtos.Settings;
+public class CleanCacheInDto
+{
+    /// <summary>
+    /// Keys
+    /// </summary>
+    public List<string> Keys { get; set; }
+}

+ 5 - 0
src/Hotline.Share/Dtos/WebPortal/QueryKnowledgeList.cs

@@ -21,6 +21,11 @@ namespace Hotline.Share.Dtos.WebPortal
         public string? KnowledgeBaseTags { get; set; }
         public string? KnowledgeBaseTags { get; set; }
     }
     }
 
 
+    public class QueryKnowledgeInfo
+    {
+        public string Id { get; set; }
+    }
+
     /// <summary>
     /// <summary>
     /// 知识实体
     /// 知识实体
     /// </summary>
     /// </summary>

+ 6 - 0
src/Hotline.Share/Enums/KnowledgeBase/EKnowledgeApplyType.cs

@@ -24,6 +24,12 @@ public enum EKnowledgeApplyType
     /// </summary>
     /// </summary>
     [Description("删除")]
     [Description("删除")]
     Delete = 2,
     Delete = 2,
+
+	/// <summary>
+	/// 下架
+	/// </summary>
+	[Description("下架")]
+    Offshelf = 3,
 }
 }
 
 
 /// <summary>
 /// <summary>

+ 16 - 4
src/Hotline.Share/Enums/KnowledgeBase/EKnowledgeStatus.cs

@@ -7,10 +7,10 @@ namespace Hotline.Share.Enums.KnowledgeBase;
 /// </summary>
 /// </summary>
 public enum EKnowledgeStatus
 public enum EKnowledgeStatus
 {
 {
-    /// <summary>
-    /// 草稿
-    /// </summary>
-    [Description("草稿")]
+	/// <summary>
+	/// 待提交
+/// </summary>
+	[Description("待提交")]
     Drafts = 0,
     Drafts = 0,
 
 
     /// <summary>
     /// <summary>
@@ -48,4 +48,16 @@ public enum EKnowledgeStatus
 	/// </summary>
 	/// </summary>
 	[Description("已过期")]
 	[Description("已过期")]
 	Overdue = 6,
 	Overdue = 6,
+
+	/// <summary>
+	/// 下架审批中
+	/// </summary>
+	[Description("下架审批中")]
+	OffShelfAudit = 7,
+
+	/// <summary>
+	/// 草稿
+	/// </summary>
+	[Description("草稿")]
+	NewDrafts = 8,
 }
 }

+ 5 - 1
src/Hotline.Share/Mq/EventNames.cs

@@ -18,6 +18,10 @@
         /// </summary>
         /// </summary>
         public const string OrderRelateCall = "order.relate.call";
         public const string OrderRelateCall = "order.relate.call";
 
 
-
+        /// <summary>
+        /// 保存回访详情时发送延迟消息同步通话记录
+        /// 如果回访通话记录有多条, 需要关联通话时长最长的那条
+        /// </summary>
+        public const string VisitCallDelay = "order.visitrelate.call";
     }
     }
 }
 }

+ 1 - 1
src/Hotline/Article/Bulletin.cs

@@ -13,7 +13,7 @@ namespace Hotline.Article
     {
     {
         public string Title { get; set; }
         public string Title { get; set; }
 
 
-        [SugarColumn(ColumnDataType = "varchar(8000)")]
+        [SugarColumn(ColumnDataType = "text")]
         public string Content { get; set; }
         public string Content { get; set; }
 
 
         public string BulletinTypeId { get; set; }
         public string BulletinTypeId { get; set; }

+ 6 - 0
src/Hotline/Caching/Interfaces/ISystemSettingCacheManager.cs

@@ -26,5 +26,11 @@ namespace Hotline.Caching.Interfaces
         /// </summary>
         /// </summary>
         bool AutomaticPublishOrder { get; }
         bool AutomaticPublishOrder { get; }
         bool CancelPublishOrderEnabled { get; }
         bool CancelPublishOrderEnabled { get; }
+
+        /// <summary>
+        /// 保存回访详情时发送延迟消息同步通话记录
+        /// 如果回访通话记录有多条, 需要关联通话时长最长的那条
+        /// </summary>
+        int VisitCallDelaySecond { get; }
     }
     }
 }
 }

+ 16 - 0
src/Hotline/Caching/Services/SystemSettingCacheManager.cs

@@ -80,6 +80,22 @@ namespace Hotline.Caching.Services
 
 
         public int FixedQueryCount => int.Parse(GetSetting(SettingConstants.FixedQueryCount)?.SettingValue[0]);
         public int FixedQueryCount => int.Parse(GetSetting(SettingConstants.FixedQueryCount)?.SettingValue[0]);
 
 
+        /// <summary>
+        /// 保存回访详情时发送延迟消息同步通话记录
+        /// 如果回访通话记录有多条, 需要关联通话时长最长的那条
+        /// </summary>
+        public int VisitCallDelaySecond
+        {
+            get
+            {
+                var value = GetOrDefault(SettingConstants.VisitCallDelaySecond, "回访通话记录同步延时时间(秒)", "60", "保存回访详情时发送延迟消息同步通话记录,如果回访通话记录有多条, 需要关联通话时长最长的那条");
+                if (int.TryParse(value, out var seconds))
+                    return seconds;
+
+                return 60 * 2;
+            }
+        }
+
         public bool AutomaticPublishOrder
         public bool AutomaticPublishOrder
         {
         {
             get
             get

+ 9 - 3
src/Hotline/FlowEngine/WorkflowModules/WorkflowModuleConsts.cs

@@ -35,9 +35,14 @@ public class WorkflowModuleConsts
     public const string KnowledgeUpdate = "KnowledgeUpdate";
     public const string KnowledgeUpdate = "KnowledgeUpdate";
 
 
     /// <summary>
     /// <summary>
-    /// 知识删除
+    /// 知识下架
     /// </summary>
     /// </summary>
-    public const string KnowledgeDelete = "KnowledgeDelete";
+    public const string KnowledgeOffshelf = "KnowledgeOffshelf";
+
+	/// <summary>
+	/// 知识删除
+	/// </summary>
+	public const string KnowledgeDelete = "KnowledgeDelete";
 
 
     /// <summary>
     /// <summary>
     /// 分机小休申请
     /// 分机小休申请
@@ -59,8 +64,9 @@ public class WorkflowModuleConsts
         new()
         new()
         {
         {
             new(OrderHandle, "工单办理"),
             new(OrderHandle, "工单办理"),
-            new(KnowledgeAdd, "新增知识审批"),
+            new(KnowledgeAdd, "新增知识"),
             new(KnowledgeUpdate, "知识更新"),
             new(KnowledgeUpdate, "知识更新"),
+            new(KnowledgeOffshelf,"知识下架"),
             new(KnowledgeDelete, "知识删除"),
             new(KnowledgeDelete, "知识删除"),
             new(TelRestApply, "分机小休申请"),
             new(TelRestApply, "分机小休申请"),
             new(OrderDelay,"工单延期"),
             new(OrderDelay,"工单延期"),

+ 8 - 3
src/Hotline/FlowEngine/Workflows/IWorkflowDomainService.cs

@@ -96,8 +96,8 @@ namespace Hotline.FlowEngine.Workflows
         /// 特提至中心(优先派单组其次坐席)
         /// 特提至中心(优先派单组其次坐席)
         /// </summary>
         /// </summary>
         /// <returns></returns>
         /// <returns></returns>
-        Task<bool> RecallToCenterFirstToSendAsync(string workflowId, string opinion, bool isOrderFiled, DateTime? expiredTime,
-            CancellationToken cancellationToken);
+        Task<bool> RecallToCenterFirstToSendAsync(string workflowId, string opinion, bool isOrderFiled, DateTime? expiredTime, List<FlowStepHandler> handlers,
+			CancellationToken cancellationToken);
 
 
         ///// <summary>
         ///// <summary>
         ///// 跳转(直接将流程跳转至任意节点)
         ///// 跳转(直接将流程跳转至任意节点)
@@ -193,7 +193,9 @@ namespace Hotline.FlowEngine.Workflows
         /// <param name="countersignId"></param>
         /// <param name="countersignId"></param>
         /// <param name="cancellationToken"></param>
         /// <param name="cancellationToken"></param>
         /// <returns></returns>
         /// <returns></returns>
-        Task<Workflow> TerminalCountersignAsync(string countersignId, CancellationToken cancellationToken);
+        Task<Workflow> TerminalCountersignAsync(string countersignId, DateTime expireTime, CancellationToken cancellationToken);
+
+        Task<Workflow> TerminalCountersignAsync(WorkflowCountersign countersign, DateTime expireTime, CancellationToken cancellationToken);
 
 
         /// <summary>
         /// <summary>
         /// 办理节点
         /// 办理节点
@@ -292,5 +294,8 @@ namespace Hotline.FlowEngine.Workflows
         /// </summary>
         /// </summary>
         Task JumpToEndAsync(ISessionContext current,string workflowId, string opinion, List<FileDto> files, DateTime? expiredTime,
         Task JumpToEndAsync(ISessionContext current,string workflowId, string opinion, List<FileDto> files, DateTime? expiredTime,
             EReviewResult reviewResult = EReviewResult.Unknown, CancellationToken cancellationToken = default);
             EReviewResult reviewResult = EReviewResult.Unknown, CancellationToken cancellationToken = default);
+
+        Task JumpToEndAsync(ISessionContext current, Workflow workflow, string opinion, List<FileDto> files, DateTime? expiredTime,
+            EReviewResult reviewResult = EReviewResult.Unknown, CancellationToken cancellationToken = default);
     }
     }
 }
 }

+ 3 - 0
src/Hotline/FlowEngine/Workflows/StepBasicEntity.cs

@@ -369,6 +369,9 @@ public abstract class StepBasicEntity : CreationEntity
     #endregion
     #endregion
     #endregion
     #endregion
 
 
+    [Navigate(NavigateType.ManyToOne, nameof(WorkflowId))]
+    public Workflow Workflow { get; set; }
+    
     #region method
     #region method
 
 
     /// <summary>
     /// <summary>

+ 2 - 4
src/Hotline/FlowEngine/Workflows/Workflow.cs

@@ -387,18 +387,16 @@ public partial class Workflow
     public List<WorkflowCountersign> Countersigns { get; set; }
     public List<WorkflowCountersign> Countersigns { get; set; }
 
 
     /// <summary>
     /// <summary>
-    /// 节点,依据流转进度动态生成或删除
+    /// 节点,依据流转进度动态生成或删除
     /// </summary>
     /// </summary>
-    //[SugarColumn(IsIgnore = true)]
     [Navigate(NavigateType.OneToMany, nameof(WorkflowStep.WorkflowId))]
     [Navigate(NavigateType.OneToMany, nameof(WorkflowStep.WorkflowId))]
     public List<WorkflowStep> Steps { get; set; }
     public List<WorkflowStep> Steps { get; set; }
 
 
     /// <summary>
     /// <summary>
     /// 流转记录
     /// 流转记录
     /// </summary>
     /// </summary>
-    //[SugarColumn(IsIgnore = true)]
     [Navigate(NavigateType.OneToMany, nameof(WorkflowTrace.WorkflowId))]
     [Navigate(NavigateType.OneToMany, nameof(WorkflowTrace.WorkflowId))]
-    public List<WorkflowTrace> Traces { get; set; } = new();
+    public List<WorkflowTrace> Traces { get; set; }
 
 
     #region Method
     #region Method
 
 

+ 115 - 58
src/Hotline/FlowEngine/Workflows/WorkflowDomainService.cs

@@ -21,7 +21,11 @@ using XF.Domain.Exceptions;
 using XF.Domain.Repository;
 using XF.Domain.Repository;
 using System.Diagnostics;
 using System.Diagnostics;
 using System.Text;
 using System.Text;
+using Hotline.Configurations;
+using Hotline.Orders;
 using Hotline.Share.Dtos.File;
 using Hotline.Share.Dtos.File;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Options;
 
 
 namespace Hotline.FlowEngine.Workflows
 namespace Hotline.FlowEngine.Workflows
 {
 {
@@ -39,6 +43,7 @@ namespace Hotline.FlowEngine.Workflows
         private readonly ISystemSettingCacheManager _systemSettingCacheManager;
         private readonly ISystemSettingCacheManager _systemSettingCacheManager;
         private readonly IWfModuleCacheManager _wfModuleCacheManager;
         private readonly IWfModuleCacheManager _wfModuleCacheManager;
         private readonly ISessionContextProvider _sessionContextProvider;
         private readonly ISessionContextProvider _sessionContextProvider;
+        private readonly IOptionsSnapshot<AppConfiguration> _appOptions;
 
 
         public WorkflowDomainService(
         public WorkflowDomainService(
             IWorkflowRepository workflowRepository,
             IWorkflowRepository workflowRepository,
@@ -51,6 +56,7 @@ namespace Hotline.FlowEngine.Workflows
             IMapper mapper,
             IMapper mapper,
             Publisher publisher,
             Publisher publisher,
             ILogger<WorkflowDomainService> logger,
             ILogger<WorkflowDomainService> logger,
+            IOptionsSnapshot<AppConfiguration> appOptions,
             IFileRepository fileRepository)
             IFileRepository fileRepository)
         {
         {
             _workflowRepository = workflowRepository;
             _workflowRepository = workflowRepository;
@@ -64,6 +70,7 @@ namespace Hotline.FlowEngine.Workflows
             _systemSettingCacheManager = systemSettingCacheManager;
             _systemSettingCacheManager = systemSettingCacheManager;
             _wfModuleCacheManager = wfModuleCacheManager;
             _wfModuleCacheManager = wfModuleCacheManager;
             _sessionContextProvider = sessionContextProvider;
             _sessionContextProvider = sessionContextProvider;
+            _appOptions = appOptions;
         }
         }
 
 
         public async Task<Workflow> CreateWorkflowAsync(WorkflowModule wfModule, string title, string userId,
         public async Task<Workflow> CreateWorkflowAsync(WorkflowModule wfModule, string title, string userId,
@@ -418,6 +425,19 @@ namespace Hotline.FlowEngine.Workflows
 
 
             await _workflowTraceRepository.UpdateRangeAsync(updateTraces, cancellationToken);
             await _workflowTraceRepository.UpdateRangeAsync(updateTraces, cancellationToken);
 
 
+            if (workflow.ActualHandleStepId == currentStep.Id)
+            {
+                //更新实际办理节点信息
+                workflow.UpdateActualStepWhenHandle(currentStep, _sessionContextProvider.SessionContext.OrgAreaCode,
+                    _sessionContextProvider.SessionContext.OrgAreaName, _sessionContextProvider.SessionContext.OrgLevel);
+            }
+
+            if (workflow.CurrentStepId == currentStep.Id)
+            {
+                workflow.UpdateCurrentStepWhenHandle(currentStep, _sessionContextProvider.SessionContext.OrgAreaCode,
+                    _sessionContextProvider.SessionContext.OrgAreaName, _sessionContextProvider.SessionContext.OrgLevel);
+            }
+            
             //var trace = await NextTraceAsync(workflow, dto, currentStep, cancellationToken);
             //var trace = await NextTraceAsync(workflow, dto, currentStep, cancellationToken);
 
 
             #endregion
             #endregion
@@ -436,10 +456,15 @@ namespace Hotline.FlowEngine.Workflows
             //检查是否流转到流程终点
             //检查是否流转到流程终点
             if (nextStepDefine.StepType is EStepType.End)
             if (nextStepDefine.StepType is EStepType.End)
             {
             {
-                //更新实际办理节点信息
-                workflow.UpdateActualStepWhenHandle(currentStep, _sessionContextProvider.SessionContext.OrgAreaCode,
-                    _sessionContextProvider.SessionContext.OrgAreaName, _sessionContextProvider.SessionContext.OrgLevel);
-                workflow.ActualHandleStepAcceptTime = currentStep.AcceptTime;
+                if (string.IsNullOrEmpty(workflow.ActualHandlerId)
+                    || string.IsNullOrEmpty(workflow.ActualHandleOrgCode)) //开始流程直接归档
+                {
+                    //更新实际办理节点信息
+                    workflow.UpdateActualStepWhenHandle(currentStep, _sessionContextProvider.SessionContext.OrgAreaCode,
+                        _sessionContextProvider.SessionContext.OrgAreaName, _sessionContextProvider.SessionContext.OrgLevel);
+                    workflow.ActualHandleStepAcceptTime = currentStep.AcceptTime;
+                    workflow.ActualHandleTime = currentStep.HandleTime;
+                }
 
 
                 var endTrace = await EndAsync(current, workflow, dto, nextStepDefine, currentStep, expiredTime, cancellationToken);
                 var endTrace = await EndAsync(current, workflow, dto, nextStepDefine, currentStep, expiredTime, cancellationToken);
                 return new List<WorkflowStep>();
                 return new List<WorkflowStep>();
@@ -532,22 +557,13 @@ namespace Hotline.FlowEngine.Workflows
                 query = query.Includes(d => d.Countersigns, x => x.Members);
                 query = query.Includes(d => d.Countersigns, x => x.Members);
             if (withSteps)
             if (withSteps)
                 query = query.Includes(d => d.Steps);
                 query = query.Includes(d => d.Steps);
-            //if (withTraces)
-            //    query = query.Includes(d => d.Traces);
+            if (withTraces)
+                query = query.Includes(d => d.Traces);
 
 
             var workflow = await query.FirstAsync(cancellationToken);
             var workflow = await query.FirstAsync(cancellationToken);
             if (workflow is null)
             if (workflow is null)
                 throw new UserFriendlyException("无效workflowId");
                 throw new UserFriendlyException("无效workflowId");
 
 
-            //if (withSteps)
-            //{
-            //    var steps = await _workflowStepRepository.Queryable()
-            //        .Where(d => d.WorkflowId == workflow.Id)
-            //        .OrderBy(d => d.CreationTime)
-            //        .ToTreeAsync(d => d.Steps, d => d.ParentId, null);
-            //    workflow.Steps = steps;
-            //}
-
             if (withTracesTree)
             if (withTracesTree)
             {
             {
                 workflow.Traces = await _workflowTraceRepository.Queryable()
                 workflow.Traces = await _workflowTraceRepository.Queryable()
@@ -556,14 +572,6 @@ namespace Hotline.FlowEngine.Workflows
                     .ToTreeAsync(d => d.Traces, d => d.ParentId, null);
                     .ToTreeAsync(d => d.Traces, d => d.ParentId, null);
             }
             }
 
 
-            if (withTraces)
-            {
-                workflow.Traces = await _workflowTraceRepository.Queryable()
-                    .Where(d => d.WorkflowId == workflow.Id)
-                    .OrderBy(d => d.CreationTime)
-                    .ToListAsync(cancellationToken);
-            }
-
             return workflow;
             return workflow;
         }
         }
 
 
@@ -595,16 +603,20 @@ namespace Hotline.FlowEngine.Workflows
                 d.Status is not EWorkflowStepStatus.Handled
                 d.Status is not EWorkflowStepStatus.Handled
             );
             );
 
 
-            var unCompletedCountersign = workflow.Countersigns
-                .FirstOrDefault(d => !d.IsCompleted() && d.StarterOrgId == orgId);
-            if (unCompletedCountersign is null)
-                return (workflow, null, canHandle, canPrevious, unhandlePreviousTrace);
+            WorkflowCountersign? unCompletedCountersign = null;
+            if (!canHandle)
+            {
+                unCompletedCountersign = workflow.Countersigns
+                    .FirstOrDefault(d => !d.IsCompleted() && d.StarterOrgId == orgId);
+                if (unCompletedCountersign is null)
+                    return (workflow, null, canHandle, canPrevious, unhandlePreviousTrace);
+            }
 
 
             //var existCountersignEndStep = workflow.Steps.Exists(d =>
             //var existCountersignEndStep = workflow.Steps.Exists(d =>
             //    d.IsCountersignEndStep && d.CountersignStartStepId == unCompletedCountersign.StartStepId);
             //    d.IsCountersignEndStep && d.CountersignStartStepId == unCompletedCountersign.StartStepId);
             //return (workflow, existCountersignEndStep ? null : unCompletedCountersign.Id, canPrevious);
             //return (workflow, existCountersignEndStep ? null : unCompletedCountersign.Id, canPrevious);
 
 
-            return (workflow, unCompletedCountersign.Id, canHandle, canPrevious, unhandlePreviousTrace);
+            return (workflow, unCompletedCountersign?.Id ?? null, canHandle, canPrevious, unhandlePreviousTrace);
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -888,8 +900,8 @@ namespace Hotline.FlowEngine.Workflows
             string applicantId, string applicantName,
             string applicantId, string applicantName,
             string applicantOrgId, string applicantOrgName,
             string applicantOrgId, string applicantOrgName,
             string applicantOrgAreaCode, string applicantOrgAreaName,
             string applicantOrgAreaCode, string applicantOrgAreaName,
-            bool applicantIsCenter,
-            string[] applicantRoleIds, CancellationToken cancellationToken)
+            bool applicantIsCenter, string[] applicantRoleIds,
+            CancellationToken cancellationToken)
         {
         {
             //ValidatePermission(workflow, operater.OrgId, operater.Id);
             //ValidatePermission(workflow, operater.OrgId, operater.Id);
 
 
@@ -946,8 +958,10 @@ namespace Hotline.FlowEngine.Workflows
             //    applicantOrgAreaCode, applicantOrgAreaName,
             //    applicantOrgAreaCode, applicantOrgAreaName,
             //    applicantIsCenter, cancellationToken);
             //    applicantIsCenter, cancellationToken);
             var trace = workflow.Traces.First(t => t.StepId == currentStep.Id);
             var trace = workflow.Traces.First(t => t.StepId == currentStep.Id);
-            _mapper.Map(dto, trace);
+            // _mapper.Map(dto, trace);
             trace.FileJson = currentStep.FileJson;
             trace.FileJson = currentStep.FileJson;
+            trace.IsSms = dto.AcceptSms;
+            trace.Opinion = dto.Opinion;
 
 
             //HandleTrace(trace, dto.Opinion, current);
             //HandleTrace(trace, dto.Opinion, current);
 
 
@@ -963,9 +977,9 @@ namespace Hotline.FlowEngine.Workflows
                 prevStep.StepExpiredTime = dto.ExpiredTime;
                 prevStep.StepExpiredTime = dto.ExpiredTime;
 
 
             //复制上一个节点为待接办
             //复制上一个节点为待接办
-            var newPrevStep =
-                await DuplicateStepWithTraceAsync(workflow, prevStep, EWorkflowTraceType.Previous, cancellationToken);
-
+            // var newPrevStep =
+            //     await DuplicateStepWithTraceAsync(workflow, prevStep, EWorkflowTraceType.Previous, cancellationToken);
+            var newPrevStep = DuplicateStep(prevStep, EWorkflowTraceType.Previous, dto.ExpiredTime);
             //退给派单组节点,需按照平均分配原则派给一个派单员 禅道299 TODO
             //退给派单组节点,需按照平均分配原则派给一个派单员 禅道299 TODO
             if (dto.Handler != null) //todo 改为按策略判断
             if (dto.Handler != null) //todo 改为按策略判断
             {
             {
@@ -973,6 +987,9 @@ namespace Hotline.FlowEngine.Workflows
                 newPrevStep.Assign(handle.UserId, handle.Username, handle.OrgId, handle.OrgName, handle.RoleId, handle.RoleName);
                 newPrevStep.Assign(handle.UserId, handle.Username, handle.OrgId, handle.OrgName, handle.RoleId, handle.RoleName);
             }
             }
 
 
+            await _workflowStepRepository.AddAsync(newPrevStep, cancellationToken);
+            await CreateTraceAsync(workflow, newPrevStep, EWorkflowTraceType.Previous, cancellationToken);
+
             //remove workflow.steps
             //remove workflow.steps
             await _workflowStepRepository.RemoveRangeAsync(removeSteps, cancellationToken);
             await _workflowStepRepository.RemoveRangeAsync(removeSteps, cancellationToken);
 
 
@@ -986,18 +1003,18 @@ namespace Hotline.FlowEngine.Workflows
             //更新实际办理节点信息
             //更新实际办理节点信息
             workflow.UpdateActualStepWhenAssign(newPrevStep, new FlowStepHandler
             workflow.UpdateActualStepWhenAssign(newPrevStep, new FlowStepHandler
             {
             {
-                UserId = prevStep.HandlerId,
-                Username = prevStep.HandlerName,
-                OrgId = prevStep.HandlerOrgId,
-                OrgName = prevStep.HandlerOrgName,
+                UserId = newPrevStep.HandlerId,
+                Username = newPrevStep.HandlerName,
+                OrgId = newPrevStep.HandlerOrgId,
+                OrgName = newPrevStep.HandlerOrgName,
             });
             });
 
 
             workflow.UpdateCurrentStepWhenAssign(newPrevStep, new FlowStepHandler
             workflow.UpdateCurrentStepWhenAssign(newPrevStep, new FlowStepHandler
             {
             {
-                UserId = prevStep.HandlerId,
-                Username = prevStep.HandlerName,
-                OrgId = prevStep.HandlerOrgId,
-                OrgName = prevStep.HandlerOrgName,
+                UserId = newPrevStep.HandlerId,
+                Username = newPrevStep.HandlerName,
+                OrgId = newPrevStep.HandlerOrgId,
+                OrgName = newPrevStep.HandlerOrgName,
             });
             });
 
 
             // //更新流程可办理对象
             // //更新流程可办理对象
@@ -1444,6 +1461,15 @@ namespace Hotline.FlowEngine.Workflows
         {
         {
             var workflow = await GetWorkflowAsync(workflowId, withDefine: true, withSteps: true, withTraces: true,
             var workflow = await GetWorkflowAsync(workflowId, withDefine: true, withSteps: true, withTraces: true,
                 withCountersigns: true, cancellationToken: cancellationToken);
                 withCountersigns: true, cancellationToken: cancellationToken);
+            await JumpToEndAsync(current, workflow, opinion, files, expiredTime, reviewResult, cancellationToken);
+        }
+
+        /// <summary>
+        /// 跳转至结束节点(无视流程模板配置以及当前办理对象,直接跳至结束节点)
+        /// </summary>
+        public async Task JumpToEndAsync(ISessionContext current, Workflow workflow, string opinion, List<FileDto> files, DateTime? expiredTime,
+            EReviewResult reviewResult = EReviewResult.Unknown, CancellationToken cancellationToken = default)
+        {
             var endStepDefine = workflow.WorkflowDefinition.FindEndStepDefine();
             var endStepDefine = workflow.WorkflowDefinition.FindEndStepDefine();
             if (endStepDefine is null)
             if (endStepDefine is null)
                 throw new UserFriendlyException("未正确配置结束节点");
                 throw new UserFriendlyException("未正确配置结束节点");
@@ -1653,7 +1679,7 @@ namespace Hotline.FlowEngine.Workflows
         /// </summary>
         /// </summary>
         /// <returns>true 派单组  false 话务部</returns>
         /// <returns>true 派单组  false 话务部</returns>
         public async Task<bool> RecallToCenterFirstToSendAsync(string workflowId, string opinion, bool isOrderFiled,
         public async Task<bool> RecallToCenterFirstToSendAsync(string workflowId, string opinion, bool isOrderFiled,
-            DateTime? expiredTime, CancellationToken cancellationToken)
+            DateTime? expiredTime, List<FlowStepHandler>? handlers, CancellationToken cancellationToken)
         {
         {
             bool isPaiDan = true;
             bool isPaiDan = true;
             var workflow = await GetWorkflowAsync(workflowId, withDefine: true, withSteps: true, withTraces: true, withCountersigns: true,
             var workflow = await GetWorkflowAsync(workflowId, withDefine: true, withSteps: true, withTraces: true, withCountersigns: true,
@@ -1685,6 +1711,11 @@ namespace Hotline.FlowEngine.Workflows
                         }
                         }
                     }
                     }
                 };
                 };
+                if (_appOptions.Value.IsZiGong && handlers.Any())
+                {
+                    dto.NextHandlers = handlers;
+                }
+
                 var flowAssignInfo = await GetNextStepFlowAssignInfoByDefineAsync(targetStepDefine, dto.HandlerType, dto.IsStartCountersign,
                 var flowAssignInfo = await GetNextStepFlowAssignInfoByDefineAsync(targetStepDefine, dto.HandlerType, dto.IsStartCountersign,
                     dto.NextHandlers.Select(d => new Kv(d.Key, d.Value)).ToList(), cancellationToken);
                     dto.NextHandlers.Select(d => new Kv(d.Key, d.Value)).ToList(), cancellationToken);
                 await RecallAsync(workflow, dto, targetStepDefine, flowAssignInfo, EWorkflowTraceType.Recall, expiredTime, isOrderFiled,
                 await RecallAsync(workflow, dto, targetStepDefine, flowAssignInfo, EWorkflowTraceType.Recall, expiredTime, isOrderFiled,
@@ -2081,8 +2112,7 @@ namespace Hotline.FlowEngine.Workflows
                 var nextHandler = dto.NextHandlers.First();
                 var nextHandler = dto.NextHandlers.First();
                 workflow.UpdateActualStepWhenAssign(nextStep, nextHandler);
                 workflow.UpdateActualStepWhenAssign(nextStep, nextHandler);
             }
             }
-
-
+            
             //与实际办理节点的接办状态保持一致
             //与实际办理节点的接办状态保持一致
             workflow.ActualHandleStepAcceptTime = nextStep.AcceptTime;
             workflow.ActualHandleStepAcceptTime = nextStep.AcceptTime;
         }
         }
@@ -2512,7 +2542,22 @@ namespace Hotline.FlowEngine.Workflows
         /// 复制一个节点为待接办
         /// 复制一个节点为待接办
         /// </summary>
         /// </summary>
         private async Task<WorkflowStep> DuplicateStepWithTraceAsync(Workflow workflow, WorkflowStep step,
         private async Task<WorkflowStep> DuplicateStepWithTraceAsync(Workflow workflow, WorkflowStep step,
-            EWorkflowTraceType traceType, CancellationToken cancellationToken)
+            EWorkflowTraceType traceType, DateTime expiredTime, CancellationToken cancellationToken)
+        {
+            var newStep = DuplicateStep(step, traceType, expiredTime);
+
+            await _workflowStepRepository.AddAsync(newStep, cancellationToken);
+            //await _workflowStepRepository.AddNav(newStep)
+            //    .Include(d => d.StepHandlers)
+            //    .ExecuteCommandAsync();
+
+
+            await CreateTraceAsync(workflow, newStep, traceType, cancellationToken);
+
+            return newStep;
+        }
+
+        private WorkflowStep DuplicateStep(WorkflowStep step, EWorkflowTraceType traceType, DateTime? expiredTime)
         {
         {
             var newStep = _mapper.Map<WorkflowStep>(step);
             var newStep = _mapper.Map<WorkflowStep>(step);
             newStep.Reset();
             newStep.Reset();
@@ -2525,6 +2570,8 @@ namespace Hotline.FlowEngine.Workflows
             newStep.StartCountersignId = step.StartCountersignId;
             newStep.StartCountersignId = step.StartCountersignId;
             newStep.CountersignId = step.CountersignId;
             newStep.CountersignId = step.CountersignId;
             newStep.IsStartedCountersignEnd = step.IsStartedCountersignEnd;
             newStep.IsStartedCountersignEnd = step.IsStartedCountersignEnd;
+            newStep.StepExpiredTime = expiredTime;
+            newStep.InitId();
 
 
             //退回场景:指派给原办理人,其余场景:按照原节点原始指派方式复制 //todo 重构为参数传入办理对象
             //退回场景:指派给原办理人,其余场景:按照原节点原始指派方式复制 //todo 重构为参数传入办理对象
             if (traceType is EWorkflowTraceType.Previous)
             if (traceType is EWorkflowTraceType.Previous)
@@ -2540,14 +2587,6 @@ namespace Hotline.FlowEngine.Workflows
                 newStep.Assign(step.HandlerId, step.HandlerName, step.HandlerOrgId, step.HandlerOrgName, step.RoleId, step.RoleName);
                 newStep.Assign(step.HandlerId, step.HandlerName, step.HandlerOrgId, step.HandlerOrgName, step.RoleId, step.RoleName);
             }
             }
 
 
-            await _workflowStepRepository.AddAsync(newStep, cancellationToken);
-            //await _workflowStepRepository.AddNav(newStep)
-            //    .Include(d => d.StepHandlers)
-            //    .ExecuteCommandAsync();
-
-
-            await CreateTraceAsync(workflow, newStep, traceType, cancellationToken);
-
             return newStep;
             return newStep;
         }
         }
 
 
@@ -3091,12 +3130,29 @@ namespace Hotline.FlowEngine.Workflows
         /// <param name="countersignId"></param>
         /// <param name="countersignId"></param>
         /// <param name="cancellationToken"></param>
         /// <param name="cancellationToken"></param>
         /// <returns></returns>
         /// <returns></returns>
-        public async Task<Workflow> TerminalCountersignAsync(string countersignId, CancellationToken cancellationToken)
+        public async Task<Workflow> TerminalCountersignAsync(string countersignId, DateTime expireTime, CancellationToken cancellationToken)
         {
         {
             var countersign = await _workflowCountersignRepository.GetAsync(countersignId, cancellationToken);
             var countersign = await _workflowCountersignRepository.GetAsync(countersignId, cancellationToken);
             if (countersign is null)
             if (countersign is null)
                 throw new UserFriendlyException("无效会签编号");
                 throw new UserFriendlyException("无效会签编号");
 
 
+            //1. 检查会签是否已结束 t: return 2.检查是否有嵌套会签 t: 一起结束 3.结束会签 4.trace 5.检查workflow会签状态,如果会签全结束需更新状态 6.cp会签发起节点变为待办节点
+            if (countersign.IsCompleted())
+                throw new UserFriendlyException("该会签已结束");
+
+            return await TerminalCountersignAsync(countersign, expireTime, cancellationToken);
+        }
+
+        /// <summary>
+        /// 终止会签
+        /// </summary>
+        public async Task<Workflow> TerminalCountersignAsync(WorkflowCountersign countersign, DateTime expireTime,
+            CancellationToken cancellationToken)
+        {
+            // var countersign = await _workflowCountersignRepository.GetAsync(countersignId, cancellationToken);
+            // if (countersign is null)
+            //     throw new UserFriendlyException("无效会签编号");
+
             //1. 检查会签是否已结束 t: return 2.检查是否有嵌套会签 t: 一起结束 3.结束会签 4.trace 5.检查workflow会签状态,如果会签全结束需更新状态 6.cp会签发起节点变为待办节点
             //1. 检查会签是否已结束 t: return 2.检查是否有嵌套会签 t: 一起结束 3.结束会签 4.trace 5.检查workflow会签状态,如果会签全结束需更新状态 6.cp会签发起节点变为待办节点
             if (countersign.IsCompleted())
             if (countersign.IsCompleted())
                 throw new UserFriendlyException("该会签已结束");
                 throw new UserFriendlyException("该会签已结束");
@@ -3105,9 +3161,9 @@ namespace Hotline.FlowEngine.Workflows
                 withCountersigns: true, cancellationToken: cancellationToken);
                 withCountersigns: true, cancellationToken: cancellationToken);
             if (!workflow.IsInCountersign)
             if (!workflow.IsInCountersign)
                 throw new UserFriendlyException("该流程未处于会签中");
                 throw new UserFriendlyException("该流程未处于会签中");
-            countersign = workflow.Countersigns.First(d => d.Id == countersignId);
+            countersign = workflow.Countersigns.First(d => d.Id == countersign.Id);
 
 
-            var startCountersignStep = workflow.Steps.Find(d => d.StartCountersignId == countersignId);
+            var startCountersignStep = workflow.Steps.Find(d => d.StartCountersignId == countersign.Id);
             if (startCountersignStep is null)
             if (startCountersignStep is null)
                 throw new UserFriendlyException("未查询到发起会签节点");
                 throw new UserFriendlyException("未查询到发起会签节点");
             if (startCountersignStep.IsStartedCountersignEnd)
             if (startCountersignStep.IsStartedCountersignEnd)
@@ -3136,7 +3192,8 @@ namespace Hotline.FlowEngine.Workflows
 
 
                 //cp会签发起节点变为待办节点
                 //cp会签发起节点变为待办节点
                 //1. create terminal trace 2. 撤回至startStep
                 //1. create terminal trace 2. 撤回至startStep
-                var newStep = await DuplicateStepWithTraceAsync(workflow, startCountersignStep, EWorkflowTraceType.Normal, cancellationToken);
+                var newStep = await DuplicateStepWithTraceAsync(workflow, startCountersignStep, EWorkflowTraceType.Normal, expireTime,
+                    cancellationToken);
 
 
                 //当topcsStep结束cs时,实际办理节点应该更新为newStep
                 //当topcsStep结束cs时,实际办理节点应该更新为newStep
                 if (startCountersignStep.Id == workflow.TopCountersignStepId)
                 if (startCountersignStep.Id == workflow.TopCountersignStepId)

+ 2 - 8
src/Hotline/FlowEngine/Workflows/WorkflowStep.cs

@@ -12,16 +12,10 @@ namespace Hotline.FlowEngine.Workflows;
 
 
 public class WorkflowStep : StepBasicEntity
 public class WorkflowStep : StepBasicEntity
 {
 {
-    [Navigate(NavigateType.ManyToOne, nameof(WorkflowId))]
-    public Workflow Workflow { get; set; }
-
-    [Navigate(NavigateType.OneToOne, nameof(Id),
+   [Navigate(NavigateType.OneToOne, nameof(Id),
         nameof(Workflows.WorkflowTrace.StepId))]
         nameof(Workflows.WorkflowTrace.StepId))]
     public WorkflowTrace WorkflowTrace { get; set; }
     public WorkflowTrace WorkflowTrace { get; set; }
-
-    //[Navigate(NavigateType.OneToMany, nameof(WorkflowStepHandler.WorkflowStepId))]
-    //public List<WorkflowStepHandler> StepHandlers { get; set; }
-
+    
     #region Method
     #region Method
 
 
     /// <summary>
     /// <summary>

+ 99 - 38
src/Hotline/KnowledgeBase/KnowledgeDomainService.cs

@@ -1,7 +1,9 @@
 using DotNetCore.CAP;
 using DotNetCore.CAP;
+using Hotline.Caching.Interfaces;
 using Hotline.FlowEngine;
 using Hotline.FlowEngine;
 using Hotline.FlowEngine.WorkflowModules;
 using Hotline.FlowEngine.WorkflowModules;
 using Hotline.FlowEngine.Workflows;
 using Hotline.FlowEngine.Workflows;
+using Hotline.Settings;
 using Hotline.Share.Dtos.Knowledge;
 using Hotline.Share.Dtos.Knowledge;
 using Hotline.Share.Enums.KnowledgeBase;
 using Hotline.Share.Enums.KnowledgeBase;
 using Hotline.Share.Mq;
 using Hotline.Share.Mq;
@@ -21,6 +23,9 @@ namespace Hotline.KnowledgeBase
         private readonly IRepository<KnowledgePv> _knowledgePVRepository;
         private readonly IRepository<KnowledgePv> _knowledgePVRepository;
         private readonly IKnowledgeWorkFlowRepository _knowledgeWorkFlowRepository;
         private readonly IKnowledgeWorkFlowRepository _knowledgeWorkFlowRepository;
         private readonly ICapPublisher _capPublisher;
         private readonly ICapPublisher _capPublisher;
+        private readonly ISystemSettingCacheManager _systemSettingCacheManager;
+        private readonly IRepository<KnowledgeRelationType> _relationTypeRepository;
+        private readonly IRepository<KnowledgeType> _knowledgeTypeRepository;
 
 
         /// <summary>
         /// <summary>
         /// 
         /// 
@@ -30,14 +35,26 @@ namespace Hotline.KnowledgeBase
         /// <param name="knowledgePVRepository"></param>
         /// <param name="knowledgePVRepository"></param>
         /// <param name="knowledgeWorkFlowRepository"></param>
         /// <param name="knowledgeWorkFlowRepository"></param>
         /// <param name="capPublisher"></param>
         /// <param name="capPublisher"></param>
-        public KnowledgeDomainService(IKnowledgeRepository knowledgeRepository, IMapper mapper, IRepository<KnowledgePv> knowledgePVRepository
-            , IKnowledgeWorkFlowRepository knowledgeWorkFlowRepository, ICapPublisher capPublisher)
+        /// <param name="systemSettingCacheManager"></param>
+        /// <param name="relationTypeRepository"></param>
+        /// <param name="knowledgeTypeRepository"></param>
+        public KnowledgeDomainService(IKnowledgeRepository knowledgeRepository,
+            IMapper mapper,
+            IRepository<KnowledgePv> knowledgePVRepository,
+            IKnowledgeWorkFlowRepository knowledgeWorkFlowRepository,
+            ICapPublisher capPublisher, ISystemSettingCacheManager systemSettingCacheManager,
+            IRepository<KnowledgeRelationType> relationTypeRepository,
+            IRepository<KnowledgeType> knowledgeTypeRepository
+            )
         {
         {
             _knowledgeRepository = knowledgeRepository;
             _knowledgeRepository = knowledgeRepository;
             _mapper = mapper;
             _mapper = mapper;
             _knowledgePVRepository = knowledgePVRepository;
             _knowledgePVRepository = knowledgePVRepository;
             _knowledgeWorkFlowRepository = knowledgeWorkFlowRepository;
             _knowledgeWorkFlowRepository = knowledgeWorkFlowRepository;
             _capPublisher = capPublisher;
             _capPublisher = capPublisher;
+            _systemSettingCacheManager = systemSettingCacheManager;
+            _relationTypeRepository = relationTypeRepository;
+            _knowledgeTypeRepository = knowledgeTypeRepository;
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -49,8 +66,8 @@ namespace Hotline.KnowledgeBase
         {
         {
             var know = await _knowledgeRepository.Queryable(false, false, false)
             var know = await _knowledgeRepository.Queryable(false, false, false)
                 .Includes(x => x.Workflow, d => d.Steps)
                 .Includes(x => x.Workflow, d => d.Steps)
-                .Includes(x=>x.SourceOrganize)
-                .Includes(x=>x.KnowledgeType).FirstAsync(p => p.Id == Id);
+                .Includes(x => x.SourceOrganize)
+                .Includes(x => x.KnowledgeType).FirstAsync(p => p.Id == Id);
             if (know is null)
             if (know is null)
                 throw UserFriendlyException.SameMessage("知识查询失败!");
                 throw UserFriendlyException.SameMessage("知识查询失败!");
             return know;
             return know;
@@ -112,7 +129,7 @@ namespace Hotline.KnowledgeBase
             knowledgeFlow.WorkflowId = workflowId;
             knowledgeFlow.WorkflowId = workflowId;
             knowledgeFlow.Flowed(flowedUserIds, flowedOrgIds, handlerUsers, handlerOrgs);
             knowledgeFlow.Flowed(flowedUserIds, flowedOrgIds, handlerUsers, handlerOrgs);
             await _knowledgeWorkFlowRepository.UpdateAsync(knowledgeFlow, cancellationToken);
             await _knowledgeWorkFlowRepository.UpdateAsync(knowledgeFlow, cancellationToken);
-            var knowledge = await _knowledgeRepository.Queryable().Where(x=>x.Id == workflowExternalId).FirstAsync(cancellationToken);
+            var knowledge = await _knowledgeRepository.Queryable().Where(x => x.Id == workflowExternalId).FirstAsync(cancellationToken);
             if (knowledge == null)
             if (knowledge == null)
                 throw new UserFriendlyException($"无效知识编号, knowledgeId: {workflowExternalId}", "无效知识编号");
                 throw new UserFriendlyException($"无效知识编号, knowledgeId: {workflowExternalId}", "无效知识编号");
             knowledge.WorkflowId = workflowId;
             knowledge.WorkflowId = workflowId;
@@ -203,26 +220,31 @@ namespace Hotline.KnowledgeBase
         /// <exception cref="UserFriendlyException"></exception>
         /// <exception cref="UserFriendlyException"></exception>
         public async Task TerminateWorkKnowledge(Workflow workflow, CancellationToken cancellationToken)
         public async Task TerminateWorkKnowledge(Workflow workflow, CancellationToken cancellationToken)
         {
         {
-	        //根据审批主表知识ID查询当前知识
-	        var knowledge = await _knowledgeRepository.GetAsync(p => p.Id == workflow.ExternalId, cancellationToken);
-	        if (knowledge == null)
-		        throw new UserFriendlyException($"知识查询失败");
-
-	        //修改主表审批状态  根据流程ID查询审批主表
-	        var workFlow = await _knowledgeWorkFlowRepository.GetAsync(p => p.KnowledgeId == workflow.ExternalId, cancellationToken);
-	        if (workFlow == null)
-		        throw new UserFriendlyException($"知识查询失败, workflowId: {workflow.ExternalId}", "无效流程编号");
-
-			//修改业务数据状态
-			workFlow.WorkFlowApplyStatus = EKnowledgeWorkFlowStatus.Revoke;
-	        workFlow.ActualOpinion = workflow.ActualOpinion;
-	        await _knowledgeWorkFlowRepository.UpdateAsync(workFlow, cancellationToken);
+            //根据审批主表知识ID查询当前知识
+            var knowledge = await _knowledgeRepository.GetAsync(p => p.Id == workflow.ExternalId, cancellationToken);
+            if (knowledge == null)
+                throw new UserFriendlyException($"知识查询失败");
 
 
+            //修改主表审批状态  根据流程ID查询审批主表
+            var workFlow = await _knowledgeWorkFlowRepository.GetAsync(p => p.KnowledgeId == workflow.ExternalId, cancellationToken);
+            if (workFlow == null)
+                throw new UserFriendlyException($"知识查询失败, workflowId: {workflow.ExternalId}", "无效流程编号");
 
 
-            //现有知识状态更改为已撤回
-            knowledge.Status = EKnowledgeStatus.Revert;
-            await _knowledgeRepository.UpdateAsync(knowledge, cancellationToken);
+            //修改业务数据状态
+            workFlow.WorkFlowApplyStatus = EKnowledgeWorkFlowStatus.Revoke;
+            workFlow.ActualOpinion = workflow.ActualOpinion;
+            await _knowledgeWorkFlowRepository.UpdateAsync(workFlow, cancellationToken);
 
 
+            if (workflow.ModuleCode == WorkflowModuleConsts.KnowledgeOffshelf)
+            {
+                knowledge.Status = EKnowledgeStatus.OnShelf;
+                await _knowledgeRepository.UpdateAsync(knowledge, cancellationToken);
+            }
+            else {
+				//现有知识状态更改为已撤回
+				knowledge.Status = EKnowledgeStatus.Revert;
+				await _knowledgeRepository.UpdateAsync(knowledge, cancellationToken);
+			}
 			//switch (workFlow.WorkflowModuleStatus)
 			//switch (workFlow.WorkflowModuleStatus)
    //         {
    //         {
    //             case EKnowledgeApplyType.Add:
    //             case EKnowledgeApplyType.Add:
@@ -236,8 +258,8 @@ namespace Hotline.KnowledgeBase
    //                 await _knowledgeRepository.UpdateAsync(knowledge, cancellationToken);
    //                 await _knowledgeRepository.UpdateAsync(knowledge, cancellationToken);
    //                 break;
    //                 break;
 
 
-                   
-			//}
+
+            //}
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -250,20 +272,20 @@ namespace Hotline.KnowledgeBase
         public async Task EndWorkKnowledge(Workflow workflow, CancellationToken cancellationToken)
         public async Task EndWorkKnowledge(Workflow workflow, CancellationToken cancellationToken)
         {
         {
 
 
-	        //修改主表审批状态  根据流程ID查询审批主表
-	        var workFlow = await _knowledgeWorkFlowRepository.GetAsync(p => p.KnowledgeId == workflow.ExternalId, cancellationToken);
-	        if (workFlow == null)
-		        throw new UserFriendlyException($"知识查询失败, workflowId: {workflow.ExternalId}", "无效流程编号");
+            //修改主表审批状态  根据流程ID查询审批主表
+            var workFlow = await _knowledgeWorkFlowRepository.GetAsync(p => p.KnowledgeId == workflow.ExternalId, cancellationToken);
+            if (workFlow == null)
+                throw new UserFriendlyException($"知识查询失败, workflowId: {workflow.ExternalId}", "无效流程编号");
 
 
-	        //根据审批主表知识ID查询当前知识
-	        //  var knowledge = await _knowledgeRepository.GetAsync(p => p.Id == workFlow.KnowledgeId, cancellationToken);
+            //根据审批主表知识ID查询当前知识
+            //  var knowledge = await _knowledgeRepository.GetAsync(p => p.Id == workFlow.KnowledgeId, cancellationToken);
 
 
-	        workFlow.HandleTime = DateTime.Now;
-	        workFlow.WorkFlowApplyStatus = EKnowledgeWorkFlowStatus.Success;
-	        workFlow.ActualOpinion = workflow.ActualOpinion;
-	        await _knowledgeWorkFlowRepository.UpdateAsync(workFlow, cancellationToken);
+            workFlow.HandleTime = DateTime.Now;
+            workFlow.WorkFlowApplyStatus = EKnowledgeWorkFlowStatus.Success;
+            workFlow.ActualOpinion = workflow.ActualOpinion;
+            await _knowledgeWorkFlowRepository.UpdateAsync(workFlow, cancellationToken);
 
 
-			var knowledge = await _knowledgeRepository.Queryable(false, false, false)
+            var knowledge = await _knowledgeRepository.Queryable(false, false, false)
                 .Includes(it => it.SystemOrganize)
                 .Includes(it => it.SystemOrganize)
                 .Includes(it => it.HotspotType)
                 .Includes(it => it.HotspotType)
                 .Includes(it => it.User)
                 .Includes(it => it.User)
@@ -315,14 +337,53 @@ namespace Hotline.KnowledgeBase
 
 
                     isSendType = "2";
                     isSendType = "2";
                     break;
                     break;
-            }
+                case WorkflowModuleConsts.KnowledgeOffshelf://下架
+	                //知识先下架                          
+	                knowledge.Status = EKnowledgeStatus.OffShelf;
+	                knowledge.OffShelfTime = System.DateTime.Now;
+	                knowledge.WorkflowId = workflow.Id;
+	                await _knowledgeRepository.UpdateAsync(knowledge, cancellationToken);
+	                isSendType = "2";
+	                break;
+			}
 
 
             #region 数据推送
             #region 数据推送
             var pushKnowledge = _mapper.Map<KnowledgeSendDto>(knowledge);
             var pushKnowledge = _mapper.Map<KnowledgeSendDto>(knowledge);
             pushKnowledge.CategoryCode = "01";
             pushKnowledge.CategoryCode = "01";
             pushKnowledge.CategoryName = "公共服务";
             pushKnowledge.CategoryName = "公共服务";
-			//推送
-			if (isSendType == "0")  //新增知识推送
+
+            //高效办成一件事配置
+            var cancelPublishOrderEnabled = _systemSettingCacheManager.GetSetting(SettingConstants.EfficientlyAccomplish)?.SettingValue[0];
+            //获取到配置,执行下面
+            if (!string.IsNullOrEmpty(cancelPublishOrderEnabled))
+            {
+                var isEfficientlyAccomplish = false;
+                //根据知识ID查询所有的知识分类
+                var typeList = await _relationTypeRepository.Queryable().Where(p => p.KnowledgeId == knowledge.Id).ToListAsync(cancellationToken);
+                if (typeList != null && typeList.Any())
+                {
+                    //遍历知识分类
+                    foreach (var item in typeList)
+                    {
+                        //查询知识分类是否满足高效办成一件事
+                        if (await _knowledgeTypeRepository.AnyAsync(p => p.Id == item.KnowledgeTypeId && p.SpliceName.StartsWith(cancelPublishOrderEnabled), cancellationToken))
+                        {
+                            //满足,跳出遍历
+                            isEfficientlyAccomplish = true;
+                            break;
+                        }
+                    }
+                }
+                //是高效,处理默认值
+                if (isEfficientlyAccomplish)
+                {
+                    pushKnowledge.CategoryCode = "25";
+                    pushKnowledge.CategoryName = "高效办成一件事";
+                }
+            }
+
+            //推送
+            if (isSendType == "0")  //新增知识推送
 
 
                 await _capPublisher.PublishAsync(EventNames.HotlineKnowledgeAdd, pushKnowledge, cancellationToken: cancellationToken);
                 await _capPublisher.PublishAsync(EventNames.HotlineKnowledgeAdd, pushKnowledge, cancellationToken: cancellationToken);
             else if (isSendType == "1")  //修改知识推送
             else if (isSendType == "1")  //修改知识推送

+ 1 - 1
src/Hotline/OrderTranspond/TranspondCityRawData.cs

@@ -57,7 +57,7 @@ namespace Hotline.OrderTranspond
         /// <summary>
         /// <summary>
         /// 市州办理意见
         /// 市州办理意见
         /// </summary>
         /// </summary>
-        [SugarColumn(ColumnDescription = "接收方工单编号")]
+        [SugarColumn(ColumnDescription = "接收方工单编号", ColumnDataType = "varchar(8000)")]
         public string? ActualOpinion { get; set; }
         public string? ActualOpinion { get; set; }
     }
     }
 }
 }

+ 2 - 1
src/Hotline/Orders/OrderDomainService.cs

@@ -459,6 +459,7 @@ public class OrderDomainService : IOrderDomainService, IScopeDependency
         var scheduling = await _schedulingRepository.Queryable().Includes(x => x.SchedulingUser)
         var scheduling = await _schedulingRepository.Queryable().Includes(x => x.SchedulingUser)
             .Where(x => x.SchedulingTime == time && x.WorkingTime <= DateTime.Now.TimeOfDay && x.OffDutyTime >= DateTime.Now.TimeOfDay && x.AtWork == true)
             .Where(x => x.SchedulingTime == time && x.WorkingTime <= DateTime.Now.TimeOfDay && x.OffDutyTime >= DateTime.Now.TimeOfDay && x.AtWork == true)
             .OrderBy(x => x.SendOrderNum).FirstAsync(cancellationToken);
             .OrderBy(x => x.SendOrderNum).FirstAsync(cancellationToken);
+        var centerOrgName = _systemSettingCacheManager.GetSetting(SettingConstants.CenterOrgName)?.SettingValue[0];
         if (scheduling is null)
         if (scheduling is null)
             return new FlowStepHandler
             return new FlowStepHandler
             {
             {
@@ -467,7 +468,7 @@ public class OrderDomainService : IOrderDomainService, IScopeDependency
                 UserId = AppDefaults.SendPoolId,
                 UserId = AppDefaults.SendPoolId,
                 Username = "待派单池",
                 Username = "待派单池",
                 OrgId = OrgSeedData.CenterId,
                 OrgId = OrgSeedData.CenterId,
-                OrgName = "市民热线服务中心"
+                OrgName = centerOrgName
             };
             };
         scheduling.SendOrderNum++;
         scheduling.SendOrderNum++;
         await _schedulingRepository.UpdateAsync(scheduling, cancellationToken);
         await _schedulingRepository.UpdateAsync(scheduling, cancellationToken);

+ 1 - 1
src/Hotline/SeedData/OrgSeedData.cs

@@ -7,7 +7,7 @@ namespace Hotline.SeedData
     public class OrgSeedData : ISeedData<SystemOrganize>
     public class OrgSeedData : ISeedData<SystemOrganize>
     {
     {
         public static readonly string CenterId = "001";
         public static readonly string CenterId = "001";
-        public static readonly string CenterName = "市民热线服务中心";//12345政务服务便民热线
+        public static readonly string CenterName = "热线中心";//12345政务服务便民热线
 
 
         public IEnumerable<SystemOrganize> HasData() =>
         public IEnumerable<SystemOrganize> HasData() =>
             new[]
             new[]

+ 16 - 0
src/Hotline/Settings/SettingConstants.cs

@@ -585,5 +585,21 @@ namespace Hotline.Settings
         /// 取消发布功能总开关
         /// 取消发布功能总开关
         /// </summary>
         /// </summary>
         public const string CancelPublishOrderEnabled = "CancelPublishOrderEnabled";
         public const string CancelPublishOrderEnabled = "CancelPublishOrderEnabled";
+
+        /// <summary>
+        /// 高效办成一件事
+        /// </summary>
+        public const string EfficientlyAccomplish = "EfficientlyAccomplish";
+
+        /// <summary>
+        /// 热线中心名称
+        /// </summary>
+        public const string CenterOrgName = "CenterOrgName";
+
+        /// <summary>
+        /// 保存回访详情时发送延迟消息同步通话记录
+        /// 如果回访通话记录有多条, 需要关联通话时长最长的那条
+        /// </summary>
+        public const string VisitCallDelaySecond = "VisitCallDelaySecond";
     }
     }
 }
 }