Quellcode durchsuchen

Merge branch 'test' into dev

田爽 vor 6 Monaten
Ursprung
Commit
c5a38697b7
58 geänderte Dateien mit 1768 neuen und 804 gelöschten Zeilen
  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 DocumentFormat.OpenXml.Wordprocessing;
 using DotNetCore.CAP;
 using Hotline.Ai.CallOut;
 using Hotline.Ai.Jths;
@@ -661,7 +662,7 @@ namespace Hotline.Api.Controllers
                                     x.VisitContent = seatVisitContent;
                                 });
 
-                                await _orderVisitDetailRepository.UpdateRangeAsync(seatDetail, HttpContext.RequestAborted);
+                               
 
                                 //处理结果
                                 orgDetail.ForEach(x =>
@@ -678,8 +679,26 @@ namespace Hotline.Api.Controllers
                                     {
                                         //x.OrgNoSatisfiedReason = new List<Kv>() { new Kv() { Key = "7", Value = "未回复" } };
                                         //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
                                     {
@@ -687,6 +706,7 @@ namespace Hotline.Api.Controllers
                                         aiOrderVisitDetail.OrderVisit.IsEffectiveAiVisit = true;
                                     }
                                 });
+                                await _orderVisitDetailRepository.UpdateRangeAsync(seatDetail, HttpContext.RequestAborted);
                                 await _orderVisitDetailRepository.UpdateRangeAsync(orgDetail, HttpContext.RequestAborted);
 
                                 //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)
         {
             //验证是否有重复电话
-            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);
 

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

@@ -230,6 +230,8 @@ namespace Hotline.Api.Controllers
 				var waitedDataList = await _orderRepository
 					.Queryable(hasHandledStep: false, isAdmin: isAdmin)
 					.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.Source < ESource.MLSQ || d.Source > ESource.WZSC)
 					.Where(d => d.Status != EOrderStatus.BackToProvince && d.Status < EOrderStatus.Filed)
@@ -406,6 +408,8 @@ namespace Hotline.Api.Controllers
 				var waitedDataList = await _orderRepository
 					.Queryable(hasHandledStep: false, isAdmin: isAdmin)
 					.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.Source < ESource.MLSQ || d.Source > ESource.WZSC)
 					.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 System.Text;
 using System.Threading;
+using Hotline.Configurations;
+using Microsoft.Extensions.Options;
 using XF.Domain.Authentications;
 using XF.Domain.Exceptions;
 using XF.Domain.Repository;
 using XF.Utility.EnumExtensions;
 using static Microsoft.EntityFrameworkCore.DbLoggerCategory;
+using Hotline.Share.Dtos.Schedulings;
 
 namespace Hotline.Api.Controllers
 {
@@ -82,9 +85,10 @@ namespace Hotline.Api.Controllers
         private readonly IBulletinApplication _bulletinApplication;
         private readonly IRepository<KnowledgeCollectGroup> _knowledgeCollectGroupRepository;
         private readonly IRepository<KnowledgePv> _knowledgePvepository;
+        private readonly IOptionsSnapshot<AppConfiguration> _appOptions;
 
 
-        public KnowledgeController(
+		public KnowledgeController(
            IKnowledgeRepository knowledgeRepository,
            ISessionContext sessionContext,
            IKnowledgeDomainService knowledgeDomainService,
@@ -115,7 +119,8 @@ namespace Hotline.Api.Controllers
             IRepository<KnowledgePv> knowledgePvepository,
             IRepository<KnowledgeWord> knowledgeWordRepository,
             IRepository<KnowledgeHotWord> knowledgeWordHotRepository,
-            IRepository<KnowledgeHotWord> knowledgeHotWordRepository)
+           IOptionsSnapshot<AppConfiguration> appOptions,
+			IRepository<KnowledgeHotWord> knowledgeHotWordRepository)
         {
             _knowledgeRepository = knowledgeRepository;
             _sessionContext = sessionContext;
@@ -147,7 +152,8 @@ namespace Hotline.Api.Controllers
             _knowledgePvepository = knowledgePvepository;
             _knowledgeWordRepository = knowledgeWordRepository;
             _knowledgeHotWordRepository = knowledgeHotWordRepository;
-        }
+            _appOptions = appOptions;
+		}
 
         #endregion
 
@@ -208,21 +214,34 @@ namespace Hotline.Api.Controllers
         [Permission(EPermission.KnowledgeOffShelf)]
         [HttpPut("offshelf")]
         [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)
             {
-                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
                 throw UserFriendlyException.SameMessage("知识下架失败");
@@ -294,6 +313,20 @@ namespace Hotline.Api.Controllers
             else
                 knowledge.FileJson = new List<Share.Dtos.File.FileJson>();
             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);
             if (dto.Data.KnowledgeType.Any())
             {
@@ -407,13 +440,13 @@ namespace Hotline.Api.Controllers
         [LogFilter("删除知识")]
         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)
                 throw UserFriendlyException.SameMessage("无效知识库数据");
             if (knowledge.Status == EKnowledgeStatus.OnShelf || knowledge.Status == EKnowledgeStatus.Auditing)
                 throw UserFriendlyException.SameMessage("知识库数据不可删除");
-            if (knowledge.Status == EKnowledgeStatus.Drafts)
+            if (knowledge.Status == EKnowledgeStatus.Drafts || knowledge.Status == EKnowledgeStatus.Revert)
             {
                 await _knowledgeRepository.RemoveAsync(knowledge, false, HttpContext.RequestAborted);
             }
@@ -422,8 +455,8 @@ namespace Hotline.Api.Controllers
                 var startDto = _mapper.Map<StartWorkflowDto>(dto.Workflow);
                 startDto.DefinitionModuleCode = WorkflowModuleConsts.KnowledgeDelete;
                 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")]
         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)
                 .Add("tabNames", tabNames)
-                .Build();
+                .Add("tabNewDraftsNames", tabNewDraftsNames)
+                .Add("tabAuditingNames", tabAuditingNames)
+				.Build();
         }
 
         /// <summary>
@@ -935,14 +987,26 @@ namespace Hotline.Api.Controllers
         }
 
         /// <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)
+        //[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);
             if (knowledge == null)
@@ -958,7 +1022,7 @@ namespace Hotline.Api.Controllers
             await _knowledgeDomainService.AddWorkFlowAsync(id, eKnowledgeApplyType, HttpContext.RequestAborted);
 
             dto.DefinitionModuleCode = moduleCode;
-            dto.Title = knowledge.Title;
+			//dto.Title = knowledge.Title;
             return await _workflowApplication.StartWorkflowAsync(dto, id, cancellationToken: HttpContext.RequestAborted);
         }
         #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 System.Threading;
 using Hotline.Caching.Services;
+using Hotline.CallCenter.Calls;
+using Hotline.CallCenter.BlackLists;
+using Hotline.Share.Mq;
 
 namespace Hotline.Api.Controllers;
 
@@ -382,15 +385,24 @@ public class OrderController : BaseController
     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)
             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);
         orderPublish.OrderId = order.Id;
@@ -422,17 +434,30 @@ public class OrderController : BaseController
 
         // 取消发布功能开关
         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)
-            .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;
         }
 
@@ -767,16 +792,28 @@ public class OrderController : BaseController
     /// <param name="dto"></param>
     /// <returns></returns>
     [HttpPut("publish/cancel")]
-    public async Task<string> PublishCancelAsync([FromBody]PublishCancelInDto dto)
+    [LogFilter("取消发布")]
+    public async Task<string> PublishCancelAsync([FromBody] PublishCancelInDto dto)
     {
         var enabled = _systemSettingCacheManager.CancelPublishOrderEnabled;
         if (enabled == false) return "取消发布功能已被关闭";
+
         var publish = await _orderPublishRepository.GetAsync(dto.OrderPublishId)
-            ?? throw UserFriendlyException.SameMessage("发布单不存在");
+                      ?? throw UserFriendlyException.SameMessage("发布单不存在");
         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);
+        order.Status = EOrderStatus.Filed;
+        // 被取消的工单继续由之前发布的用户继续发布;
+        order.WaitForPublisherId = publish.CreatorId;
+        await _orderRepository.UpdateAsync(order);
         return "取消成功";
     }
+
     #endregion
 
     #region 工单回访
@@ -799,7 +836,7 @@ public class OrderController : BaseController
             .WhereIF(dto.VisitStateQuery == EVisitStateQuery.Visited, d => d.VisitState == EVisitState.Visited)
             .WhereIF(dto.VisitStateQuery == EVisitStateQuery.SMSUnsatisfied, d => d.VisitState == EVisitState.SMSUnsatisfied)
             .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.No), d => d.No == dto.No)
             .WhereIF(dto.VisitType != null, d => d.VisitType == dto.VisitType)
@@ -864,6 +901,7 @@ public class OrderController : BaseController
     /// </summary>
     /// <returns></returns>
     [HttpGet("visit/{id}")]
+    [AllowAnonymous]
     public async Task<object> VisitInfo(string id)
     {
         var orderVisit = await _orderVisitRepository.Queryable()
@@ -922,20 +960,25 @@ public class OrderController : BaseController
         //获取系统配置智能回访语音URL头
         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);
         if (orderVisit.VisitState == EVisitState.WaitForVisit)
@@ -993,7 +1036,17 @@ public class OrderController : BaseController
     [HttpPost("visit")]
     [LogFilter("工单回访")]
     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>
     /// 批量保存回访
@@ -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 _orderVisitRepository.Updateable()
                     .Where(m => m.Id == visit.VisitId)
@@ -1237,22 +1295,22 @@ public class OrderController : BaseController
     /// <param name="dto"></param>
     /// <returns></returns>
     [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)
     {
         var (total, items) = await _orderVisitedDetailRepository.Queryable()
@@ -1591,7 +1649,7 @@ public class OrderController : BaseController
 
         //验证延期次数
         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 =>
                 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 _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)
         {
-	    
-			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);
             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 == "省审批"))
                     {
                         result.Steps.Remove(result.Steps.First(x => x.Value == "省审批"));
                     }
                 }
+
                 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 == false, d => d.DelayState == EDelayState.Examining)
             .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)
             .ToPagedListAsync(dto.PageIndex, dto.PageSize, HttpContext.RequestAborted);
 
@@ -1883,14 +1946,16 @@ public class OrderController : BaseController
                 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;
-	}
+    }
 
     /// <summary>
     /// 延期页面基础信息
@@ -1902,6 +1967,7 @@ public class OrderController : BaseController
         var rsp = new
         {
             DelayState = EnumExts.GetDescriptions<EDelayState>(),
+            QueryDelayState = EnumExts.GetDescriptions<EQueryDelayState>(),
             TimeType = EnumExts.GetDescriptions<ETimeType>()
         };
         return rsp;
@@ -1929,8 +1995,9 @@ public class OrderController : BaseController
             // dto.CreationTimeStart = _timeLimitDomainService.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));
     }
 
@@ -1941,49 +2008,49 @@ public class OrderController : BaseController
     [HttpPost("mayscreen/_export")]
     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)
     {
         var (total, items) = await _orderApplication.OrderScreenList(dto)
@@ -2022,7 +2089,7 @@ public class OrderController : BaseController
 
         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 == "中心初审"));
             }
         }
-        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>
@@ -2227,11 +2296,12 @@ public class OrderController : BaseController
 
                 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;
             }
         }
@@ -2250,6 +2320,7 @@ public class OrderController : BaseController
         {
             ScreenStatus = EnumExts.GetDescriptions<EScreenStatus>(),
             OrderScreenType = EnumExts.GetDescriptions<EOrderScreenType>(),
+            QueryScreenType = EnumExts.GetDescriptions<EQueryOrderScreenType>(),
             ScreenType = _sysDicDataCacheManager.GetSysDicDataCache(SysDicTypeConsts.ScreenType),
             CounterSignType = EnumExts.GetDescriptions<ECounterSignType>(),
             AcceptType = _sysDicDataCacheManager.GetSysDicDataCache(SysDicTypeConsts.AcceptType),
@@ -3274,7 +3345,7 @@ public class OrderController : BaseController
     [HttpPost("add-anonymous")]
     [AllowAnonymous]
     [LogFilter("省平台调用记录")]
-	public async Task<AddOrderResponse> AddAnonymous([FromBody] AddOrderDto dto)
+    public async Task<AddOrderResponse> AddAnonymous([FromBody] AddOrderDto dto)
     {
         return await _orderApplication.ReceiveOrderFromExternalAsync(dto, HttpContext.RequestAborted);
     }
@@ -3468,6 +3539,11 @@ public class OrderController : BaseController
         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) && order.OrderExtension is null)
+            {
+                throw UserFriendlyException.SameMessage("该工单不存在拓展信息,不能推送至全国12315平台!");
+            }
+
             var timeResult = await _expireTime.CalcEndTime(DateTime.Now, ETimeType.WorkDay, 45, 80, 50);
             expiredTimeConfig = new ExpiredTimeWithConfig
             {
@@ -3588,18 +3664,18 @@ public class OrderController : BaseController
         BasicWorkflowDto workflowDto, CancellationToken cancellationToken)
     {
         var isAutoFillSummaryOpinion = bool.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.IsAutoFillSummaryOpinion).SettingValue[0]);
-        
+
         switch (orderHandleFlowDto.OrderAssignMode)
         {
             case EOrderAssignMode.AdjoinLevel:
                 var nextDto = _mapper.Map<NextWorkflowDto>(workflowDto);
                 nextDto.WorkflowId = startStep.WorkflowId;
                 nextDto.StepId = startStep.Id;
-                
+
                 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);
                         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;
             case EOrderAssignMode.CrossLevel:
                 if (!orderHandleFlowDto.CrossSteps.Any())
@@ -3637,7 +3713,7 @@ public class OrderController : BaseController
                 orderHandleFlowDto.CrossSteps = orderHandleFlowDto.CrossSteps.OrderBy(d => d.Sort).ToList();
                 var stepCount = orderHandleFlowDto.CrossSteps.Count;
                 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 tempSteps = new List<WorkflowStep>();
@@ -3833,7 +3909,7 @@ public class OrderController : BaseController
             IdentityTypeOptions = EnumExts.GetDescriptions<EIdentityType>(),
             OrderTags = _sysDicDataCacheManager.GetSysDicDataCache(SysDicTypeConsts.OrderTag),
             FocusOnEvents = _sysDicDataCacheManager.GetSysDicDataCache(SysDicTypeConsts.FocusOnEvent)
-    };
+        };
         return rsp;
     }
 
@@ -3948,19 +4024,43 @@ public class OrderController : BaseController
     }
 
     /// <summary>
-    /// 批量归档
+    /// 批量归档(暂时只支持派单组节点操作)
     /// </summary>
+    /// <returns>成功总量</returns>
     [HttpPost("batch-file")]
-    public async Task BatchFile([FromBody]OrderBatchFileDto dto)
+    public async Task<string> BatchFile([FromBody] OrderBatchFileDto dto)
     {
         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))
             .ToListAsync(HttpContext.RequestAborted);
+        var success = 0;
         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);
+            success++;
         }
+
+        return $"批量办理{dto.OrderIds.Count}件,成功{success}件";
     }
 
     #endregion
@@ -3985,7 +4085,7 @@ public class OrderController : BaseController
         {
             query.WhereIF(dto.QueryType is 1, d => d.IsForwarded == false)
                 .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());
         }
 
@@ -3995,7 +4095,10 @@ public class OrderController : BaseController
                         d.Status != EOrderStatus.SpecialToUnAccept &&
                         d.Status != EOrderStatus.HandOverToUnAccept)
             .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(!string.IsNullOrEmpty(dto.Keyword), d => d.Title.StartsWith(dto.Keyword))
             .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.IsUrgent.HasValue, d => d.IsUrgent == dto.IsUrgent!.Value)
             .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: "startTime", SortRule: 0 }, d => d.StartTime, OrderByType.Asc) //受理时间升序
             .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(dto.IsUrgent.HasValue, d => d.IsUrgent == dto.IsUrgent!.Value)
             .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: 1 }, d => d.StartTime, OrderByType.Desc) //受理时间降序
             .OrderByIF(dto is { SortField: "expiredTime", SortRule: 0 }, d => d.ExpiredTime, OrderByType.Asc) //期满时间升序
@@ -4239,6 +4342,7 @@ public class OrderController : BaseController
             .Queryable()
             .Includes(d => d.Workflow)
             .FirstAsync(d => d.Id == workflow.ExternalId);
+        dto.ExpiredTime = order.ExpiredTime;
         var (currentStep, prevStep, isOrgToCenter, isSecondToFirstOrgLevel) = await _workflowApplication.GetPreviousInformationAsync(
             dto.WorkflowId, _sessionContext.RequiredUserId, _sessionContext.RequiredOrgId, _sessionContext.Roles, HttpContext.RequestAborted);
         var audit = new OrderSendBackAudit
@@ -4305,7 +4409,6 @@ public class OrderController : BaseController
                 audit.State = ESendBackAuditState.End;
                 audit.AuditUser = "默认通过";
                 audit.AuditTime = DateTime.Now;
-                dto.ExpiredTime = order.ExpiredTime;
                 var flowDirection = await _workflowApplication.PreviousAsync(dto, HttpContext.RequestAborted);
                 var processType = flowDirection == EFlowDirection.OrgToCenter || flowDirection == EFlowDirection.CenterToCenter
                     ? EProcessType.Zhiban
@@ -4363,6 +4466,23 @@ public class OrderController : BaseController
         if (sendBack.State == ESendBackAuditState.End)
         {
             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,
             //    ISessionContext current, CancellationToken cancellationToken);
             sendBack.SendBackData.ExpiredTime = order.ExpiredTime;
@@ -4416,6 +4536,23 @@ public class OrderController : BaseController
             if (sendBack.State == ESendBackAuditState.End)
             {
                 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;
                 var flowDirection = await _workflowApplication.PreviousAsync(sendBack.SendBackData,
                     sendBack.WorkflowUserId, sendBack.WorkflowOrgId, sendBack.WorkflowRoleIds.ToArray(),
@@ -4746,17 +4883,27 @@ public class OrderController : BaseController
             .AnyAsync();
         if (specialAny) throw UserFriendlyException.SameMessage("工单已存在待审批特提信息!");
         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);
         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;
-		var step = await _workflowDomainService.FindLastStepAsync(model.WorkflowId, HttpContext.RequestAborted);
+        var step = await _workflowDomainService.FindLastStepAsync(model.WorkflowId, HttpContext.RequestAborted);
         model.StepName = step.Name;
         model.StepCode = step.Code;
         model.Status = order.Status;
@@ -4796,6 +4943,7 @@ public class OrderController : BaseController
                 {
                     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();
                 }
             }
 
@@ -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("工单重办")]
     public async Task Add([FromBody] OrderReTransactDto dto)
     {
@@ -4957,14 +5105,15 @@ public class OrderController : BaseController
 
         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);
         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.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.StepCode = step.Code;
         model.State = 1;
@@ -4994,6 +5143,18 @@ public class OrderController : BaseController
 
         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
             {
                 WorkflowId = dto.WorkflowId!,
@@ -5111,6 +5272,18 @@ public class OrderController : BaseController
         var order = await _orderRepository.GetAsync(x => x.Id == special.OrderId);
         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
             {
                 WorkflowId = special.WorkflowId!,
@@ -5148,6 +5321,7 @@ public class OrderController : BaseController
                     expiredTime = await _expireTime.CalcEndTime(DateTime.Now, order.AcceptTypeCode);
                 }
             }
+
             if (expiredTime.ExpiredTime < order.ExpiredTime)
             {
                 expiredTime.ExpiredTime = order.ExpiredTime.Value;
@@ -5246,6 +5420,18 @@ public class OrderController : BaseController
             var order = await _orderRepository.GetAsync(x => x.Id == special.OrderId);
             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
                 {
                     WorkflowId = special.WorkflowId!,
@@ -5283,12 +5469,14 @@ public class OrderController : BaseController
                         expiredTime = await _expireTime.CalcEndTime(DateTime.Now, order.AcceptTypeCode);
                     }
                 }
+
                 if (expiredTime.ExpiredTime < order.ExpiredTime)
                 {
                     expiredTime.ExpiredTime = order.ExpiredTime.Value;
                     expiredTime.NearlyExpiredTime = order.NearlyExpiredTime.Value;
                     expiredTime.NearlyExpiredTimeOne = order.NearlyExpiredTimeOne.Value;
                 }
+
                 var processType = special.FlowDirection is EFlowDirection.OrgToCenter or EFlowDirection.CenterToCenter or EFlowDirection.FiledToCenter
                     ? EProcessType.Zhiban
                     : 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.CallCenter.Ivrs;
+using Hotline.CallCenter.Tels;
+using Hotline.FlowEngine.WorkflowModules;
+using Hotline.Orders;
 using Hotline.Permissions;
+using Hotline.Realtimes;
 using Hotline.Repository.SqlSugar.Extensions;
 using Hotline.Settings;
 using Hotline.Settings.CommonOpinions;
@@ -11,10 +17,16 @@ using Hotline.Share.Dtos.Settings;
 using Hotline.Share.Enums.Settings;
 using Hotline.Share.Requests;
 using Hotline.Share.Tools;
+using Hotline.Users;
+using Hotline.YbEnterprise.Sdk;
 using MapsterMapper;
 using Microsoft.AspNetCore.Authorization;
 using Microsoft.AspNetCore.Mvc;
+using NPOI.SS.Formula.Functions;
+using StackExchange.Redis;
+using Wex.Sdk;
 using XF.Domain.Authentications;
+using XF.Domain.Cache;
 using XF.Domain.Exceptions;
 using XF.Domain.Repository;
 using XF.Utility.EnumExtensions;
@@ -39,6 +51,9 @@ namespace Hotline.Api.Controllers
         private readonly ISystemDicDataCacheManager _systemDicDataCacheManager;
         private readonly IRepository<SystemLog> _systemLogRepository;
         private readonly ISystemMobilAreaApplication _systemMobilAreaApplication;
+        private readonly IServiceScopeFactory _serviceScopeFactory;
+        private readonly IRedisCachingProvider _redisCaching;
+        private readonly IEasyCachingProvider _easyCaching;
 
         /// <summary>
         /// 系统管理相关接口
@@ -69,7 +84,10 @@ namespace Hotline.Api.Controllers
             ISessionContext sessionContext,
             ISystemDicDataCacheManager systemDicDataCacheManager,
             IRepository<SystemLog> systemLogRepository,
-            ISystemMobilAreaApplication systemMobilAreaApplication
+            ISystemMobilAreaApplication systemMobilAreaApplication,
+            IServiceScopeFactory serviceScopeFactory,
+            IRedisCachingProvider redisCaching,
+            IEasyCachingProvider easyCaching
             )
         {
             _mapper = mapper;
@@ -85,6 +103,9 @@ namespace Hotline.Api.Controllers
             _systemDicDataCacheManager = systemDicDataCacheManager;
             _systemLogRepository = systemLogRepository;
             _systemMobilAreaApplication = systemMobilAreaApplication;
+            _serviceScopeFactory = serviceScopeFactory;
+            _redisCaching = redisCaching;
+            _easyCaching = easyCaching;
         }
 
         #region 菜单管理
@@ -595,5 +616,54 @@ namespace Hotline.Api.Controllers
         {
             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.CallCenter.Tels;
 using Hotline.Identity.Accounts;
@@ -254,6 +255,21 @@ public class UserController : BaseController
         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>

+ 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 => p.BulletinTypeId == dto.NoticeType)
                  .WhereIF(!string.IsNullOrEmpty(dto.Condition), p => p.Title.Contains(dto.Condition))
-                 .OrderByDescending(p => p.BulletinTime)
+                 .OrderByDescending(p => p.CreationTime)
                  .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,
                      NoticeContent = it.Content,
                      NoticeTypeName = it.BulletinTypeName,
                      NoticeTitle = it.Title,
                      NoticeBMName = it.SourceOrgName,
-                     NoticeCreateDate = it.BulletinTime,
+                     NoticeCreateDate = it.CreationTime,
                      VideoUrl = "",
                      WNLT_FullCode = ""
                  })
@@ -159,12 +159,12 @@ namespace Hotline.Api.Controllers
                 .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.BulletinDisplayLocation), p => SqlFunc.JsonListObjectAny(p.DisplayLocation, "Key", dto.BulletinDisplayLocation))
-                .OrderByDescending(p => p.BulletinTime)
+                .OrderByDescending(p => p.CreationTime)
                 .Select(it => new
                 {
                     DataID = it.Id,
                     it.Title,
-                    CreateDate = it.BulletinTime,
+                    CreateDate = it.CreationTime,
                     it.Content,
                     it.BulletinTypeName
                 })
@@ -197,7 +197,7 @@ namespace Hotline.Api.Controllers
                     NoticeTypeName = data.BulletinTypeName,
                     NoticeTitle = data.Title,
                     NoticeBMName = data.SourceOrgName,
-                    NoticeCreateDate = data.BulletinTime,
+                    NoticeCreateDate = data.CreationTime,
                     NoticeRCount = data.ReadedNum,
                     WNED_VideoUrl = "",
                     NoticeContent = data.Content
@@ -237,13 +237,13 @@ namespace Hotline.Api.Controllers
                     .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
                 {
-                    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,
                     NoticeTitle = it.Title,
-                    NoticeCreateDate = it.BulletinTime
+                    NoticeCreateDate = it.CreationTime
                 })
                 .ToListAsync();
             //数据为空返回空数据
@@ -301,17 +301,17 @@ namespace Hotline.Api.Controllers
                 .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")
                  .WhereIF(!string.IsNullOrEmpty(dto.Condition), p => p.Title.Contains(dto.Condition))
-                 .OrderByDescending(p => p.BulletinTime)
+                 .OrderByDescending(p => p.CreationTime)
                  .Select(it => new
                  {
-                     Page = SqlFunc.RowNumber($"{it.BulletinTime} desc "),
+                     Page = SqlFunc.RowNumber($"{it.CreationTime} desc "),
                      NoticeID = it.Id,
                      Content = it.Content,
                      NoticeTypeID = it.BulletinTypeId,
                      NoticeTypeName = it.BulletinTypeName,
                      NoticeTitle = it.Title,
                      NoticeBMName = it.SourceOrgName,
-                     NoticeCreateDate = it.BulletinTime,
+                     NoticeCreateDate = it.CreationTime,
                      TypeCode = "",
                  })
                 .ToPageListAsync(dto.PageIndex, dto.PageSize, total);
@@ -565,13 +565,14 @@ namespace Hotline.Api.Controllers
         public async Task<OpenResponse> GetOrderListByNum([FromBody] QueryOrderListByNumDto dto)
         {
             var queryNew = _orderRepository.Queryable()
+                .LeftJoin<OrderPublish>((p, op) => p.Id == op.OrderId)
                 .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,
                    Title = p.Title,
-                   CreateDate = p.CreationTime
+                   CreateDate = op.CreationTime
                });
 
             var queryold = _oldPublicDataRepository.Queryable()
@@ -580,7 +581,7 @@ namespace Hotline.Api.Controllers
                        {
                            DataID = p.OrderId,
                            Title = p.Title,
-                           CreateDate = p.PubDate
+                           CreateDate = p.CreationTime
                        });
             var items = await _orderRepository.UnionAll(queryNew, queryold)
               .OrderByDescending(p => p.CreateDate)
@@ -761,6 +762,7 @@ namespace Hotline.Api.Controllers
                 var orderPublish = await _orderPublishRepository.GetAsync(p => p.OrderId == data.Id, HttpContext.RequestAborted);
                 if (orderPublish != null)
                 {
+                    orderDetail.PubFlag = "1";
                     orderDetail.FlowTitle = orderPublish.ArrangeTitle;
                     orderDetail.FlowContent = orderPublish.ArrangeContent;
                     orderDetail.FlowResult = orderPublish.ArrangeOpinion;
@@ -826,6 +828,7 @@ namespace Hotline.Api.Controllers
                     var orderPublish = await _orderPublishRepository.GetAsync(p => p.OrderId == data.Id, HttpContext.RequestAborted);
                     if (orderPublish != null)
                     {
+                        orderDetail.PubFlag = "1";
                         orderDetail.FlowTitle = orderPublish.ArrangeTitle;
                         orderDetail.FlowContent = orderPublish.ArrangeContent;
                         orderDetail.FlowResult = orderPublish.ArrangeOpinion;
@@ -922,7 +925,7 @@ namespace Hotline.Api.Controllers
 
             //数据查询
             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
                 {
                     it.AcceptType,
@@ -938,12 +941,13 @@ namespace Hotline.Api.Controllers
                  .ToListAsync();
 
             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
                  {
                      HotspotId = it.HotspotId.Substring(SqlFunc.MappingColumn<int>("0"), SqlFunc.MappingColumn<int>("2")),
                  })
                  .MergeTable()
+                 .Where(it => it.HotspotId != "18")
                  .GroupBy(it => it.HotspotId)//对新表进行分组
                  .Select(it => new
                  {
@@ -960,8 +964,6 @@ namespace Hotline.Api.Controllers
                      num = it.num
                  }).ToListAsync();
 
-
-
             ////数据查询-查询总数前5的数据
             //var listHot = await _orderRepository.Queryable()
             //    .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,
               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();
             return OpenResponse.Ok(WebPortalDeResponse<GetStatistDto>.Success(getStatistDto));
@@ -1127,7 +1129,7 @@ namespace Hotline.Api.Controllers
             var list = _systemDicDataCacheManager.GetSysDicDataCache(SysDicTypeConsts.KnowledgeBaseTags).Select(p => new
             {
                 SDICT_Name = p.DicDataName,
-                SDICT_ID = p.DicDataValue
+                SDICT_ID = p.DicDataName
             }).ToList();
 
             var rsp = new
@@ -1154,9 +1156,16 @@ namespace Hotline.Api.Controllers
                 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()
                 .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)))
                 .OrderByDescending(p => p.CreationTime)
                 .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));
             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()
     {
         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.Identity.Accounts;
 using Hotline.Identity.Roles;
+using Hotline.Orders;
 using Hotline.Share.Dtos.CallCenter;
+using Hotline.Share.Dtos.Order;
 using Hotline.Share.Enums.CallCenter;
 using Hotline.Users;
 using Microsoft.Extensions.DependencyInjection;
@@ -14,10 +16,12 @@ namespace Hotline.Application.Tests.Application;
 public class DefaultCallApplicationTest
 {
     private readonly XingTangCallApplication _defaultCallApplication;
+    private readonly IOrderVisitRepository _orderVisitRepository;
 
-    public DefaultCallApplicationTest(XingTangCallApplication defaultCallApplication)
+    public DefaultCallApplicationTest(XingTangCallApplication defaultCallApplication, IOrderVisitRepository orderVisitRepository)
     {
         _defaultCallApplication = defaultCallApplication;
+        _orderVisitRepository = orderVisitRepository;
     }
 
     [Fact]
@@ -61,4 +65,25 @@ public class DefaultCallApplicationTest
             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()
     {
         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.FlowEngine;
 using Hotline.Share.Dtos.Order;
+using Hotline.Share.Dtos.Order.Publish;
 using Hotline.Share.Dtos.Users;
 using Hotline.Share.Enums.FlowEngine;
 using Hotline.Share.Enums.Order;
@@ -34,6 +35,7 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 using XF.Domain.Authentications;
+using XF.Domain.Exceptions;
 using XF.Domain.Repository;
 
 namespace Hotline.Application.Tests.Controller;
@@ -204,9 +206,38 @@ public class OrderControllerTest : TestBase
 
         await _orderController.PublishCancelAsync(new PublishCancelInDto { OrderPublishId = publish.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;
 public static class TestSettingConstants
 {
+    public const string ZuoXiAccountName = "UnitTestZuoXi";
     public const string PaiDanYuanAccountName = "UnitTestPDY";
     public const string FirstOrgAccountName = "cs";
     public const string SecondOrgAccountName = "cs21";

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

@@ -58,7 +58,7 @@ public class TestBase
 
     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)

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

@@ -6,6 +6,7 @@ using Hotline.Orders;
 using Hotline.Repository.SqlSugar.Extensions;
 using Hotline.Share.Dtos;
 using Hotline.Share.Dtos.CallCenter;
+using Hotline.Share.Dtos.Order;
 using Hotline.Share.Dtos.TrCallCenter;
 using Hotline.Share.Enums.CallCenter;
 using Hotline.Share.Tools;
@@ -34,6 +35,7 @@ public abstract class DefaultCallApplication : ICallApplication
     private readonly ISessionContext _sessionContext;
     private readonly IMapper _mapper;
     private readonly ILogger<DefaultCallApplication> _logger;
+    private readonly IOrderVisitRepository _orderVisitRepository;
 
     public DefaultCallApplication(
         IRepository<Tel> telRepository,
@@ -47,7 +49,8 @@ public abstract class DefaultCallApplication : ICallApplication
         IUserCacheManager userCacheManager,
         ISessionContext sessionContext,
         IMapper mapper,
-        ILogger<DefaultCallApplication> logger)
+        ILogger<DefaultCallApplication> logger,
+        IOrderVisitRepository orderVisitRepository)
     {
         _telRepository = telRepository;
         _telGroupRepository = telGroupRepository;
@@ -61,6 +64,7 @@ public abstract class DefaultCallApplication : ICallApplication
         _sessionContext = sessionContext;
         _mapper = mapper;
         _logger = logger;
+        _orderVisitRepository = orderVisitRepository;
     }
 
     public DefaultCallApplication()
@@ -323,6 +327,8 @@ public abstract class DefaultCallApplication : ICallApplication
     public virtual async Task<CallNative> GetCallAsync(string callId, CancellationToken cancellationToken)
     {
         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);
     }
 
@@ -359,6 +365,25 @@ public abstract class DefaultCallApplication : ICallApplication
             .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 临时方案
 
     public virtual Task<TrCallRecord?> GetTianrunCallAsync(string callId, CancellationToken cancellationToken)
@@ -408,5 +433,6 @@ public abstract class DefaultCallApplication : ICallApplication
     /// <returns></returns>
     public abstract List<Kv> GetTelOperationOptions();
 
+
     #endregion
 }

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

@@ -3,6 +3,7 @@ using Hotline.CallCenter.Calls;
 using Hotline.CallCenter.Tels;
 using Hotline.Share.Dtos;
 using Hotline.Share.Dtos.CallCenter;
+using Hotline.Share.Dtos.Order;
 using Hotline.Share.Dtos.TrCallCenter;
 using Hotline.Share.Enums.CallCenter;
 
@@ -146,5 +147,11 @@ namespace Hotline.Application.CallCenter
         /// </summary>
         /// <returns></returns>
         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,
             ISessionContext sessionContext,
             IMapper mapper,
-            ILogger<XingTangCallApplication> logger
+            ILogger<XingTangCallApplication> logger,
+            IOrderVisitRepository orderVisitRepository
         ) : base(telRepository, telGroupRepository, workRepository, telRestRepository, callNativeRepository,
             teloperationRepository, callidRelationRepository, cacheWork, userCacheManager, sessionContext, mapper,
-            logger)
+            logger, orderVisitRepository)
         {
             // _telRepository = telRepository;
             // _telGroupRepository = telGroupRepository;

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

@@ -29,11 +29,11 @@ namespace Hotline.Application.FlowEngine
         /// </summary>
         Task<string> StartWorkflowToStartStepAsync(StartWorkflowDto dto, string externalId, DateTime? expiredTime = null,
             CancellationToken cancellationToken = default);
-        
+
         /// <summary>
         /// 查询下一节点办理对象类型(user or org)及实际办理对象
         /// </summary>
-		Task<FlowAssignInfo> GetNextStepFlowAssignInfoAsync(Workflow workflow, WorkflowStep currentStep,
+        Task<FlowAssignInfo> GetNextStepFlowAssignInfoAsync(Workflow workflow, WorkflowStep currentStep,
             BasicWorkflowDto dto, StepDefine nextStepDefine, bool isNextDynamic, CancellationToken cancellationToken);
 
         /// <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.RequiredOrgId, _sessionContextProvider.SessionContext.OrgName,
             _sessionContextProvider.SessionContext.OrgAreaCode, _sessionContextProvider.SessionContext.OrgAreaName,
-            _sessionContextProvider.SessionContext.OrgIsCenter, _sessionContextProvider.SessionContext.Roles, cancellationToken);
+            _sessionContextProvider.SessionContext.OrgIsCenter, _sessionContextProvider.SessionContext.Roles,
+            cancellationToken);
     }
 
     /// <summary>
@@ -365,7 +366,7 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
     }
 
     /// <summary>
-    /// 撤回至任意节点
+    /// 撤回至之前任意节点
     /// </summary>
     public async Task RecallAsync(RecallDto dto, DateTime? expiredTime, bool isOrderFiled, EWorkflowTraceType traceType,
         CancellationToken cancellationToken)
@@ -957,7 +958,7 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
                         UserId = d.Id,
                         Username = d.Name,
                         OrgId = d.OrgId,
-                        OrgName = d.Organization.Name,
+                        OrgName = d.Organization?.Name,
                         RoleId = defineTypeItem.Key,
                         RoleName = defineTypeItem.Value
                     })
@@ -1097,10 +1098,11 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
                 throw new ArgumentOutOfRangeException(nameof(policy), policy, null);
         }
 
+        var centerOrgName = _systemSettingCacheManager.GetSetting(SettingConstants.CenterOrgName)?.SettingValue[0];
         return new NextStepOption
         {
             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);
         }
 
+        var centerOrgName = _systemSettingCacheManager.GetSetting(SettingConstants.CenterOrgName)?.SettingValue[0];
         return new NextStepOption
         {
             Key = orgLevel.ToString(),
-            Value = orgLevel == 0 ? "市民热线服务中心" : $"{orgLevel.ToChinese()}级部门 {roleName}",
+            Value = orgLevel == 0 ? centerOrgName : $"{orgLevel.ToChinese()}级部门 {roleName}",
             FlowDirection = flowDirection,
             StepType = stepType,
             BusinessType = businessType,
@@ -1593,10 +1596,11 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
                 throw new ArgumentOutOfRangeException(nameof(policy), policy, null);
         }
 
+        var centerOrgName = _systemSettingCacheManager.GetSetting(SettingConstants.CenterOrgName)?.SettingValue[0];
         return new NextStepOption
         {
             Key = orgLevel.ToString(),
-            Value = orgLevel == 0 ? "市民热线服务中心" : $"{orgLevel.ToChinese()}级部门",
+            Value = orgLevel == 0 ? centerOrgName : $"{orgLevel.ToChinese()}级部门",
             FlowDirection = flowDirection,
             StepType = stepType,
             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.KnowledgeUpdate://修改知识库
                 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);
                     await _knowledgeRepository.UpdateAsync(knowledge, cancellationToken);
                     if (isReviewPass)

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

@@ -233,7 +233,8 @@ public class WorkflowNextHandler : INotificationHandler<NextStepNotify>
                     break;
                 case WorkflowModuleConsts.KnowledgeAdd:
                 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 knowledge = await _knowledgeRepository.Queryable().Where(x => x.Id == workflow.ExternalId).FirstAsync(cancellationToken);
                     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.KnowledgeUpdate:
                     case WorkflowModuleConsts.KnowledgeDelete:
-                    case WorkflowModuleConsts.TelRestApply:
+                    case WorkflowModuleConsts.KnowledgeOffshelf:
+					case WorkflowModuleConsts.TelRestApply:
                         break;
                     case WorkflowModuleConsts.OrderTerminate:
 	                    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.KnowledgeUpdate:
                 case WorkflowModuleConsts.KnowledgeDelete:
-                case WorkflowModuleConsts.TelRestApply:
+                case WorkflowModuleConsts.KnowledgeOffshelf:
+				case WorkflowModuleConsts.TelRestApply:
                     break;
             }
         }

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

@@ -215,7 +215,8 @@ namespace Hotline.Application.Handlers.FlowEngine
                     case WorkflowModuleConsts.KnowledgeAdd:
                     case WorkflowModuleConsts.KnowledgeUpdate:
                     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,
                             cancellationToken);
                         break;

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

@@ -1,6 +1,8 @@
 using DotNetCore.CAP;
+using Hotline.Application.CallCenter;
 using Hotline.CallCenter.Calls;
 using Hotline.Orders;
+using Hotline.Share.Dtos.Order;
 using Hotline.Share.Mq;
 using System;
 using System.Collections.Generic;
@@ -12,11 +14,11 @@ using XF.Domain.Repository;
 
 namespace Hotline.Application.Handlers.Order
 {
-    public class OrderRelateCallHandler: ICapSubscribe,ITransientDependency
+    public class OrderRelateCallHandler : ICapSubscribe, ITransientDependency
     {
         private readonly IOrderRepository _orderRepository;
         private readonly IRepository<TrCallRecord> _trCallRecordRepository;
-        
+
 
         public OrderRelateCallHandler(IOrderRepository orderRepository, IRepository<TrCallRecord> trCallRecordRepository)
         {
@@ -31,10 +33,10 @@ namespace Hotline.Application.Handlers.Order
         /// <param name="cancellationToken"></param>
         /// <returns></returns>
         [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);
-            if (order!=null)
+            if (order != null)
             {
                 var callRecords = await _trCallRecordRepository.Queryable().Where(p => p.OtherAccept == order.CallId && string.IsNullOrEmpty(p.OtherAccept) == false).ToListAsync(cancellationToken);
                 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.IsPublic.HasValue, x => x.IsPublic == pagedDto.IsPublic)
                 .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(pagedDto.CreateOrgId), x => x.SourceOrganizeId != null && x.SourceOrganizeId.EndsWith(pagedDto.CreateOrgId!))
                 .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);
             //返回数据
             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)
         {
-            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<KnowledgeRelationType>((p, k, r) => r.KnowledgeId == k.Id)
                 .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.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)
                 .OrderByDescending((p, k, r) => p.CreationTime);
 
@@ -281,7 +291,7 @@ namespace Hotline.Application.Knowledge
                 .Where(x => x.IsDeleted == false)
                 .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))
-                .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(dto.HotspotName), x => x.HotspotType.HotSpotFullName.EndsWith(dto.HotspotName!))
                 .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.Opinion)
             .Ignore(d => d.FileJson)
-            //.Ignore(d => d.StepExpiredTime)
+            .Ignore(d => d.StepExpiredTime)
             .Ignore(d => d.Workflow)
             .Ignore(d => d.WorkflowTrace)
             //.IgnoreIf((d, s) => s.StepHandlers == null || !s.StepHandlers.Any(), d => d.StepHandlers)
             ;
 
         config.ForType<WorkflowStep, WorkflowTrace>()
+            .Ignore(d=>d.Workflow)
             .Ignore(d => d.ParentId)
             .Ignore(d => d.TraceType)
             .Map(d => d.StepId, s => s.Id)

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

@@ -78,6 +78,7 @@ namespace Hotline.Application.Orders
 
         /// <summary>
         /// 回访保存
+        /// 调用本方法前, 先调用 _callApplication.GetOrSetCallIdAsync 转换 CallId
         /// </summary>
         /// <param name="dto"></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.Dynamic.Core;
 using System.Threading;
+using Hotline.Application.CallCenter;
 
 namespace Hotline.Application.Orders;
 
@@ -113,8 +114,10 @@ public class OrderApplication : IOrderApplication, IScopeDependency
     private readonly IRepository<OrderObserve> _orderObserveRepository;
     private readonly IOrderTerminateRepository _orderTerminateRepository;
     private readonly IRepository<OrderPublishHistory> _orderPublishHistoryRepository;
+    private readonly IOrderDelayRepository _orderDelayRepository;
+    private readonly IRepository<OrderSecondaryHandling> _orderSecondaryHandlingRepository;
 
-    public OrderApplication(
+	public OrderApplication(
         IOrderDomainService orderDomainService,
         IOrderRepository orderRepository,
         IWorkflowDomainService workflowDomainService,
@@ -150,7 +153,9 @@ public class OrderApplication : IOrderApplication, IScopeDependency
         IRepository<TranspondCityRawData> transpondCityRawDataRepository,
         IRepository<OrderObserve> orderObserveRepository,
         IOrderTerminateRepository orderTerminateRepository,
-        IRepository<OrderPublishHistory> orderPublishHistoryRepository)
+        IRepository<OrderPublishHistory> orderPublishHistoryRepository,
+        IRepository<OrderSecondaryHandling> orderSecondaryHandlingRepository,
+		IOrderDelayRepository orderDelayRepository)
     {
         _orderDomainService = orderDomainService;
         _workflowDomainService = workflowDomainService;
@@ -188,7 +193,9 @@ public class OrderApplication : IOrderApplication, IScopeDependency
         _orderTerminateRepository = orderTerminateRepository;
         _orderPublishHistoryRepository = orderPublishHistoryRepository;
         _sessionContext = sessionContext;
-    }
+        _orderDelayRepository = orderDelayRepository;
+        _orderSecondaryHandlingRepository = orderSecondaryHandlingRepository;
+	}
 
     /// <summary>
     /// 更新工单办理期满时间(延期调用,其他不调用)
@@ -430,8 +437,8 @@ public class OrderApplication : IOrderApplication, IScopeDependency
                                  _sessionContextProvider.SessionContext.Roles.Contains(step.RoleId))))
                 .Any())
             // .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 &&
                                (!string.IsNullOrEmpty(step.HandlerOrgId) && step.HandlerOrgId.StartsWith(orgCode)))
                 .Any())
@@ -486,7 +493,7 @@ public class OrderApplication : IOrderApplication, IScopeDependency
         var seg = new Segment();
         ICollection<WordInfo> splitWords = seg.DoSegment(inputStr);
         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);
             if (word is { WordType: WordType.SimplifiedChinese, Word.Length: > 1 })
@@ -713,7 +720,7 @@ public class OrderApplication : IOrderApplication, IScopeDependency
             visit.VisitTime = dto.VisitTime;
             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 detaildto = dto.OrderVisitDetailDto.FirstOrDefault(x => x.Id == detail.Id);
@@ -759,7 +766,7 @@ public class OrderApplication : IOrderApplication, IScopeDependency
     /// <returns></returns>
     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)
                     ?? throw UserFriendlyException.SameMessage("无效工单编号");
         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;
         var settingBase = _systemSettingCacheManager.GetSetting(SettingConstants.CityBaseConfiguration)?.SettingValue[0];
         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);
             expiredTimeConfig = new ExpiredTimeWithConfig
             {
@@ -837,8 +844,7 @@ public class OrderApplication : IOrderApplication, IScopeDependency
             await _orderRepository.UpdateAsync(order, cancellationToken);
         }
 
-        if (expiredTimeConfig is not null)
-            _mapper.Map(expiredTimeConfig, order);
+        
 
         if (dto.Data.LeaderSMSKey != null)
         {
@@ -847,6 +853,10 @@ public class OrderApplication : IOrderApplication, IScopeDependency
         }
 
         _mapper.Map(dto.Data, order);
+
+        if (expiredTimeConfig is not null)
+            _mapper.Map(expiredTimeConfig, order);
+
         await _orderRepository.UpdateAsync(order, cancellationToken);
 
         return order;
@@ -854,6 +864,7 @@ public class OrderApplication : IOrderApplication, IScopeDependency
 
     /// <summary>
     /// 回访保存
+    /// 调用本方法前, 先调用 _callApplication.GetOrSetCallIdAsync 转换 CallId
     /// </summary>
     /// <param name="dto"></param>
     /// <param name="cancellationToken"></param>
@@ -906,7 +917,7 @@ public class OrderApplication : IOrderApplication, IScopeDependency
             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 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());
         }
 
-            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;
     }
@@ -1326,8 +1338,8 @@ public class OrderApplication : IOrderApplication, IScopeDependency
         var quer = _orderRepository.Queryable()
             .InnerJoin<SystemOrganize>((x, so) => x.ActualHandleOrgCode == so.Id && so.Level == 1)
             .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.TypeId != null && dto.TypeId == 1, (x, so) => x.IdentityType == EIdentityType.Citizen)
             .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)
                 .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);
         }
 
@@ -1372,7 +1384,7 @@ public class OrderApplication : IOrderApplication, IScopeDependency
         var quer = _orderRepository.Queryable()
             .InnerJoin<SystemOrganize>((x, so) => x.ActualHandleOrgCode == so.Id && so.Level == 1)
             .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))
             .Where((x, so) =>
                 (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() });
         quer = _orderRepository.UnionAll(quer, queryCountersignOrder).MergeTable()
             .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;
     }
 
@@ -1412,7 +1424,7 @@ public class OrderApplication : IOrderApplication, IScopeDependency
         return _orderSpecialDetailRepository.Queryable()
             .Includes(x => x.OrderSpecial)
             .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.EndTime)
             .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");
 
         //计算小计
-        for (int i = 0;i < dt.Rows.Count;i++)
+        for (int i = 0; i < dt.Rows.Count; i++)
         {
             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());
             }
@@ -1801,10 +1813,10 @@ public class OrderApplication : IOrderApplication, IScopeDependency
         DataRow totalRow = dt.NewRow();
         if (dt.Columns[0].ColumnName == "HotspotName") totalRow["HotspotName"] = "合计";
         else totalRow["一级热点"] = "合计";
-        for (int i = 1;i < dt.Columns.Count;i++)
+        for (int i = 1; i < dt.Columns.Count; i++)
         {
             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());
             }
@@ -2318,7 +2330,7 @@ public class OrderApplication : IOrderApplication, IScopeDependency
     public ISugarQueryable<OrderScreenAuditVo> OrderScreenAudit(OrderScreenAuditPagedRequest dto)
     {
         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(!string.IsNullOrEmpty(dto.AuditUserName), x => x.HandlerName == dto.AuditUserName)
             .Where(x => x.HandlerName != null && x.HandlerName != "")
@@ -2410,10 +2422,10 @@ public class OrderApplication : IOrderApplication, IScopeDependency
         else
         {
             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.VisitDetail)
             .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)
                 .ToList())
             .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.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.CreationTimeEnd.HasValue, d => d.CreationTime <= dto.CreationTimeEnd)
             .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,
-                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,
-                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,
-                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,
-                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)
             .Includes(x => x.OrderVisit)
             .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)) ==
                      false)
             .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(dto.IsProvince.HasValue, x => x.OrderVisit.Order!.IsProvince == dto.IsProvince)
             .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),
             //    x => SqlFunc.JsonField(x.OrgNoSatisfiedReason, "Key") == dto.OrgNoSatisfiedReason)
             .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))
             throw new UserFriendlyException("工单外部编号不能为空");
@@ -2703,25 +2728,37 @@ public class OrderApplication : IOrderApplication, IScopeDependency
             }
 
             _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);
                 var isPaiDan = await _workflowDomainService.RecallToCenterFirstToSendAsync(order.WorkflowId, "省工单重派", order.Status >= EOrderStatus.Filed,
-                    order.ExpiredTime, cancellationToken);
+                    order.ExpiredTime, nextHandler, cancellationToken);
                 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)
                 {
                     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>
@@ -3007,113 +3045,113 @@ public class OrderApplication : IOrderApplication, IScopeDependency
     /// <param name="isExport"></param>
     /// <returns></returns>
 	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();
-		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 centerOrgName = _systemSettingCacheManager.GetSetting(SettingConstants.CenterOrgName)?.SettingValue[0];
             var orderData = _orderRepository.Queryable()
               .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)
@@ -1524,7 +1525,7 @@ namespace Hotline.Application.StatisticalReport
                })
                 .Select((it, o) => new DepartmentAcceptanceTypeStatisticsDto
                 {
-                    OrgName = it.OrgCode == "001" ? "市民热线服务中心" : o.Name,
+                    OrgName = it.OrgCode == "001" ? centerOrgName : o.Name,
                     OrgCode = it.OrgCode,
                     OrgType = o.OrgType == EOrgType.County ? "区县部门" : "市直部门",
                     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);
 
             var isCenter = _sessionContext.OrgIsCenter;
+            var centerOrgName = _systemSettingCacheManager.GetSetting(SettingConstants.CenterOrgName)?.SettingValue[0];
             //工单未超期
             var queryOrderNoSigen = _orderRepository.Queryable()
                .LeftJoin<WorkflowStep>((o, w) => o.Id == w.ExternalId)
@@ -1786,7 +1788,7 @@ namespace Hotline.Application.StatisticalReport
                 })
                  .Select((it, sy) => new OrderNoSigenDto
                  {
-                     OrgName = it.OrgCode == "001" ? "市民热线服务中心" : sy.Name,
+                     OrgName = it.OrgCode == "001" ? centerOrgName : sy.Name,
                      OrgCode = it.OrgCode,
                      OrderNoSigen = SqlFunc.AggregateSum(1),
                      CounterNoSign = 0
@@ -1814,7 +1816,7 @@ namespace Hotline.Application.StatisticalReport
                  })
                   .Select((it, sy) => new OrderNoSigenDto
                   {
-                      OrgName = it.OrgCode == "001" ? "市民热线服务中心" : sy.Name,
+                      OrgName = it.OrgCode == "001" ? centerOrgName : sy.Name,
                       OrgCode = it.OrgCode,
                       OrderNoSigen = 0,
                       CounterNoSign = SqlFunc.AggregateSum(1)

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

@@ -303,8 +303,19 @@ namespace Hotline.Application.Subscribers
             }
             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;
                 if (isPaiDan)
                 {
@@ -330,6 +341,7 @@ namespace Hotline.Application.Subscribers
             if (exists)
                 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 model = new OrderUrge();
             model.InitId();
@@ -339,7 +351,7 @@ namespace Hotline.Application.Subscribers
             model.ApplyContent = dto.RemindReasion;
             model.OrderId = order.Id;
             model.OrgId = "001";
-            model.OrgName = "市民热线服务中心";
+            model.OrgName = centerOrgName;
             model.RemindTime = dto.RemindTime;
             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);
             if (order != null)
             {
+                var centerOrgName = _systemSettingCacheManager.GetSetting(SettingConstants.CenterOrgName)?.SettingValue[0];
                 var model = new OrderUrge();
                 model.InitId();
                 model.State = 0;
@@ -386,7 +399,7 @@ namespace Hotline.Application.Subscribers
                 model.OrderId = order.Id;
                 model.ReplyLimitTime = dto.WarnTimebf;
                 model.OrgId = "001";
-                model.OrgName = "市民热线服务中心";
+                model.OrgName = centerOrgName;
 
                 //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();
-                if (order != null)
+                if (order != null && order.Status<= EOrderStatus.Filed)
                 {
                     //判断是否有发布数据
                     var orderPublish = await _orderPublishRepository.Queryable()
@@ -667,11 +680,10 @@ namespace Hotline.Application.Subscribers
                     orderVisit = new OrderVisit();
                     orderVisit.No = order.No;
                     orderVisit.OrderId = order.Id;
-                    orderVisit.VisitState = EVisitState.WaitForVisit;
                     orderVisit.PublishTime = DateTime.Now;
                     orderVisit.IsCanHandle = true;
                     orderVisit.EmployeeId = order.CenterToOrgHandlerId;
-                    orderVisit.VisitState = EVisitState.Visited;
+                    orderVisit.VisitState = EVisitState.None;
                     orderVisit.VisitTime = dto.VisitTime;
                     orderVisit.VisitType = dto.VisitType;
                     orderVisit.IsCanAiVisit = false;
@@ -708,9 +720,9 @@ namespace Hotline.Application.Subscribers
 
 
                     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;
     }
 
-    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);
         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,
                     OrgLevelOneName = x.OrderVisit.Order.OrgLevelOneName,
                     CurrentHandleOrgName = x.OrderVisit.Order.CurrentHandleOrgName,
-                    CreationTime = x.CreationTime,
+                    CreationTime = x.OrderVisit.Order.CreationTime,
                     Title = x.OrderVisit.Order.Title,
                     VisitUser = x.OrderVisit.Employee.Name,
                     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 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
     {
@@ -229,7 +230,7 @@ namespace Hotline.Share.Dtos.Article
         /// 审核时间
         /// </summary>
         public DateTime? ExaminTime { get; set; }
-	}
+    }
 
     public record CircularReadGroupDto
     {
@@ -295,7 +296,7 @@ namespace Hotline.Share.Dtos.Article
         public List<CircularReadGroupDto> CircularReadGroups { get; set; }
     }
 
-    public record UpdateCircularDto:AddCircularDto
+    public record UpdateCircularDto : AddCircularDto
     {
         public string Id { get; set; }
     }
@@ -376,10 +377,10 @@ namespace Hotline.Share.Dtos.Article
         /// </summary>
         public DateTime LoseEfficacyTime { get; set; }
 
-		/// <summary>
-		/// 发布范围  EPushRange
-		/// </summary>
-		public List<Kv> PushRanges { get; set; }
+        /// <summary>
+        /// 发布范围  EPushRange
+        /// </summary>
+        public List<Kv> PushRanges { get; set; }
 
         /// <summary>
         /// 显示位置(多选) 位枚举  BulletinDisplayLocation
@@ -407,13 +408,13 @@ namespace Hotline.Share.Dtos.Article
         public bool IsArrive { get; set; }
 
     }
-    public record UpdateBulletinDto:AddBulletinDto
+    public record UpdateBulletinDto : AddBulletinDto
     {
         public string Id { get; set; }
     }
 
 
-    public record QueryCircularListRequestDto:PagedRequest
+    public record QueryCircularListRequestDto : PagedRequest
     {
         /// <summary>
         /// 标题
@@ -455,7 +456,7 @@ namespace Hotline.Share.Dtos.Article
         public string Reason { get; set; }
     }
 
-    public record QueryBulletinListRequestDto:PagedRequest
+    public record QueryBulletinListRequestDto : PagedRequest
     {
         /// <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>
         /// 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>
         /// Id

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

@@ -74,12 +74,17 @@ namespace Hotline.Share.Dtos.Knowledge
         /// <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>
 		/// 摘要

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

@@ -1024,6 +1024,8 @@ namespace Hotline.Share.Dtos.Order
         /// </summary>
         public bool IsSecret { get; set; }
 
+        public string IsSecretText => IsSecret ? "保密" : "";
+
         /// <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? VisitContent { get; set; }
         public DateTime? VisitTime { get; set; }
+
+        /// <summary>
+        /// 通话Id
+        /// </summary>
+        public string? CallId { get; set; }
     }
 
     public class DistributionVisitRspDto

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

@@ -210,6 +210,11 @@ namespace Hotline.Share.Dtos.Order
         /// </summary>
         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 EDelayState? DelayState { get; set; }
-    }
+
+        /// <summary>
+        /// 延期状态快捷查询
+        /// </summary>
+
+        public EQueryDelayState? QueryDelayState { get; set; }
+
+		/// <summary>
+		/// 0 全部  1 我的
+		/// </summary>
+		public int? DataScope { get; set; }
+	}
 
     public record ApplySuperviseDto
     {
@@ -510,6 +526,11 @@ namespace Hotline.Share.Dtos.Order
 		/// </summary>
 		public int? DataScope { get; set; }
 
+        /// <summary>
+        /// 甄别类型查询条件
+        /// </summary>
+        public EQueryOrderScreenType? QueryScreenType { get; set; }
+
 		/// <summary>
 		/// 甄别申请状态选项卡
 		/// </summary>
@@ -770,6 +791,11 @@ namespace Hotline.Share.Dtos.Order
 		/// 甄别类型
 		/// </summary>
 		public EOrderScreenType? ScreenType { get; set; }
+
+		/// <summary>
+		/// 受理坐席名字或工号(×)
+		/// </summary>
+		public string? NameOrNo { get; set; }
 	}
 
     public record UrgeListDto : PagedKeywordRequest
@@ -829,4 +855,52 @@ namespace Hotline.Share.Dtos.Order
         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 class QueryKnowledgeInfo
+    {
+        public string Id { get; set; }
+    }
+
     /// <summary>
     /// 知识实体
     /// </summary>

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

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

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

@@ -7,10 +7,10 @@ namespace Hotline.Share.Enums.KnowledgeBase;
 /// </summary>
 public enum EKnowledgeStatus
 {
-    /// <summary>
-    /// 草稿
-    /// </summary>
-    [Description("草稿")]
+	/// <summary>
+	/// 待提交
+/// </summary>
+	[Description("待提交")]
     Drafts = 0,
 
     /// <summary>
@@ -48,4 +48,16 @@ public enum EKnowledgeStatus
 	/// </summary>
 	[Description("已过期")]
 	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>
         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; }
 
-        [SugarColumn(ColumnDataType = "varchar(8000)")]
+        [SugarColumn(ColumnDataType = "text")]
         public string Content { get; set; }
 
         public string BulletinTypeId { get; set; }

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

@@ -26,5 +26,11 @@ namespace Hotline.Caching.Interfaces
         /// </summary>
         bool AutomaticPublishOrder { 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]);
 
+        /// <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
         {
             get

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

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

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

@@ -96,8 +96,8 @@ namespace Hotline.FlowEngine.Workflows
         /// 特提至中心(优先派单组其次坐席)
         /// </summary>
         /// <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>
         ///// 跳转(直接将流程跳转至任意节点)
@@ -193,7 +193,9 @@ namespace Hotline.FlowEngine.Workflows
         /// <param name="countersignId"></param>
         /// <param name="cancellationToken"></param>
         /// <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>
         /// 办理节点
@@ -292,5 +294,8 @@ namespace Hotline.FlowEngine.Workflows
         /// </summary>
         Task JumpToEndAsync(ISessionContext current,string workflowId, string opinion, List<FileDto> files, DateTime? expiredTime,
             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
 
+    [Navigate(NavigateType.ManyToOne, nameof(WorkflowId))]
+    public Workflow Workflow { get; set; }
+    
     #region method
 
     /// <summary>

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

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

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

@@ -21,7 +21,11 @@ using XF.Domain.Exceptions;
 using XF.Domain.Repository;
 using System.Diagnostics;
 using System.Text;
+using Hotline.Configurations;
+using Hotline.Orders;
 using Hotline.Share.Dtos.File;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Options;
 
 namespace Hotline.FlowEngine.Workflows
 {
@@ -39,6 +43,7 @@ namespace Hotline.FlowEngine.Workflows
         private readonly ISystemSettingCacheManager _systemSettingCacheManager;
         private readonly IWfModuleCacheManager _wfModuleCacheManager;
         private readonly ISessionContextProvider _sessionContextProvider;
+        private readonly IOptionsSnapshot<AppConfiguration> _appOptions;
 
         public WorkflowDomainService(
             IWorkflowRepository workflowRepository,
@@ -51,6 +56,7 @@ namespace Hotline.FlowEngine.Workflows
             IMapper mapper,
             Publisher publisher,
             ILogger<WorkflowDomainService> logger,
+            IOptionsSnapshot<AppConfiguration> appOptions,
             IFileRepository fileRepository)
         {
             _workflowRepository = workflowRepository;
@@ -64,6 +70,7 @@ namespace Hotline.FlowEngine.Workflows
             _systemSettingCacheManager = systemSettingCacheManager;
             _wfModuleCacheManager = wfModuleCacheManager;
             _sessionContextProvider = sessionContextProvider;
+            _appOptions = appOptions;
         }
 
         public async Task<Workflow> CreateWorkflowAsync(WorkflowModule wfModule, string title, string userId,
@@ -418,6 +425,19 @@ namespace Hotline.FlowEngine.Workflows
 
             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);
 
             #endregion
@@ -436,10 +456,15 @@ namespace Hotline.FlowEngine.Workflows
             //检查是否流转到流程终点
             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);
                 return new List<WorkflowStep>();
@@ -532,22 +557,13 @@ namespace Hotline.FlowEngine.Workflows
                 query = query.Includes(d => d.Countersigns, x => x.Members);
             if (withSteps)
                 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);
             if (workflow is null)
                 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)
             {
                 workflow.Traces = await _workflowTraceRepository.Queryable()
@@ -556,14 +572,6 @@ namespace Hotline.FlowEngine.Workflows
                     .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;
         }
 
@@ -595,16 +603,20 @@ namespace Hotline.FlowEngine.Workflows
                 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 =>
             //    d.IsCountersignEndStep && d.CountersignStartStepId == unCompletedCountersign.StartStepId);
             //return (workflow, existCountersignEndStep ? null : unCompletedCountersign.Id, canPrevious);
 
-            return (workflow, unCompletedCountersign.Id, canHandle, canPrevious, unhandlePreviousTrace);
+            return (workflow, unCompletedCountersign?.Id ?? null, canHandle, canPrevious, unhandlePreviousTrace);
         }
 
         /// <summary>
@@ -888,8 +900,8 @@ namespace Hotline.FlowEngine.Workflows
             string applicantId, string applicantName,
             string applicantOrgId, string applicantOrgName,
             string applicantOrgAreaCode, string applicantOrgAreaName,
-            bool applicantIsCenter,
-            string[] applicantRoleIds, CancellationToken cancellationToken)
+            bool applicantIsCenter, string[] applicantRoleIds,
+            CancellationToken cancellationToken)
         {
             //ValidatePermission(workflow, operater.OrgId, operater.Id);
 
@@ -946,8 +958,10 @@ namespace Hotline.FlowEngine.Workflows
             //    applicantOrgAreaCode, applicantOrgAreaName,
             //    applicantIsCenter, cancellationToken);
             var trace = workflow.Traces.First(t => t.StepId == currentStep.Id);
-            _mapper.Map(dto, trace);
+            // _mapper.Map(dto, trace);
             trace.FileJson = currentStep.FileJson;
+            trace.IsSms = dto.AcceptSms;
+            trace.Opinion = dto.Opinion;
 
             //HandleTrace(trace, dto.Opinion, current);
 
@@ -963,9 +977,9 @@ namespace Hotline.FlowEngine.Workflows
                 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
             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);
             }
 
+            await _workflowStepRepository.AddAsync(newPrevStep, cancellationToken);
+            await CreateTraceAsync(workflow, newPrevStep, EWorkflowTraceType.Previous, cancellationToken);
+
             //remove workflow.steps
             await _workflowStepRepository.RemoveRangeAsync(removeSteps, cancellationToken);
 
@@ -986,18 +1003,18 @@ namespace Hotline.FlowEngine.Workflows
             //更新实际办理节点信息
             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
             {
-                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,
                 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();
             if (endStepDefine is null)
                 throw new UserFriendlyException("未正确配置结束节点");
@@ -1653,7 +1679,7 @@ namespace Hotline.FlowEngine.Workflows
         /// </summary>
         /// <returns>true 派单组  false 话务部</returns>
         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;
             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,
                     dto.NextHandlers.Select(d => new Kv(d.Key, d.Value)).ToList(), cancellationToken);
                 await RecallAsync(workflow, dto, targetStepDefine, flowAssignInfo, EWorkflowTraceType.Recall, expiredTime, isOrderFiled,
@@ -2081,8 +2112,7 @@ namespace Hotline.FlowEngine.Workflows
                 var nextHandler = dto.NextHandlers.First();
                 workflow.UpdateActualStepWhenAssign(nextStep, nextHandler);
             }
-
-
+            
             //与实际办理节点的接办状态保持一致
             workflow.ActualHandleStepAcceptTime = nextStep.AcceptTime;
         }
@@ -2512,7 +2542,22 @@ namespace Hotline.FlowEngine.Workflows
         /// 复制一个节点为待接办
         /// </summary>
         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);
             newStep.Reset();
@@ -2525,6 +2570,8 @@ namespace Hotline.FlowEngine.Workflows
             newStep.StartCountersignId = step.StartCountersignId;
             newStep.CountersignId = step.CountersignId;
             newStep.IsStartedCountersignEnd = step.IsStartedCountersignEnd;
+            newStep.StepExpiredTime = expiredTime;
+            newStep.InitId();
 
             //退回场景:指派给原办理人,其余场景:按照原节点原始指派方式复制 //todo 重构为参数传入办理对象
             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);
             }
 
-            await _workflowStepRepository.AddAsync(newStep, cancellationToken);
-            //await _workflowStepRepository.AddNav(newStep)
-            //    .Include(d => d.StepHandlers)
-            //    .ExecuteCommandAsync();
-
-
-            await CreateTraceAsync(workflow, newStep, traceType, cancellationToken);
-
             return newStep;
         }
 
@@ -3091,12 +3130,29 @@ namespace Hotline.FlowEngine.Workflows
         /// <param name="countersignId"></param>
         /// <param name="cancellationToken"></param>
         /// <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);
             if (countersign is null)
                 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会签发起节点变为待办节点
             if (countersign.IsCompleted())
                 throw new UserFriendlyException("该会签已结束");
@@ -3105,9 +3161,9 @@ namespace Hotline.FlowEngine.Workflows
                 withCountersigns: true, cancellationToken: cancellationToken);
             if (!workflow.IsInCountersign)
                 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)
                 throw new UserFriendlyException("未查询到发起会签节点");
             if (startCountersignStep.IsStartedCountersignEnd)
@@ -3136,7 +3192,8 @@ namespace Hotline.FlowEngine.Workflows
 
                 //cp会签发起节点变为待办节点
                 //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
                 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
 {
-    [Navigate(NavigateType.ManyToOne, nameof(WorkflowId))]
-    public Workflow Workflow { get; set; }
-
-    [Navigate(NavigateType.OneToOne, nameof(Id),
+   [Navigate(NavigateType.OneToOne, nameof(Id),
         nameof(Workflows.WorkflowTrace.StepId))]
     public WorkflowTrace WorkflowTrace { get; set; }
-
-    //[Navigate(NavigateType.OneToMany, nameof(WorkflowStepHandler.WorkflowStepId))]
-    //public List<WorkflowStepHandler> StepHandlers { get; set; }
-
+    
     #region Method
 
     /// <summary>

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

@@ -1,7 +1,9 @@
 using DotNetCore.CAP;
+using Hotline.Caching.Interfaces;
 using Hotline.FlowEngine;
 using Hotline.FlowEngine.WorkflowModules;
 using Hotline.FlowEngine.Workflows;
+using Hotline.Settings;
 using Hotline.Share.Dtos.Knowledge;
 using Hotline.Share.Enums.KnowledgeBase;
 using Hotline.Share.Mq;
@@ -21,6 +23,9 @@ namespace Hotline.KnowledgeBase
         private readonly IRepository<KnowledgePv> _knowledgePVRepository;
         private readonly IKnowledgeWorkFlowRepository _knowledgeWorkFlowRepository;
         private readonly ICapPublisher _capPublisher;
+        private readonly ISystemSettingCacheManager _systemSettingCacheManager;
+        private readonly IRepository<KnowledgeRelationType> _relationTypeRepository;
+        private readonly IRepository<KnowledgeType> _knowledgeTypeRepository;
 
         /// <summary>
         /// 
@@ -30,14 +35,26 @@ namespace Hotline.KnowledgeBase
         /// <param name="knowledgePVRepository"></param>
         /// <param name="knowledgeWorkFlowRepository"></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;
             _mapper = mapper;
             _knowledgePVRepository = knowledgePVRepository;
             _knowledgeWorkFlowRepository = knowledgeWorkFlowRepository;
             _capPublisher = capPublisher;
+            _systemSettingCacheManager = systemSettingCacheManager;
+            _relationTypeRepository = relationTypeRepository;
+            _knowledgeTypeRepository = knowledgeTypeRepository;
         }
 
         /// <summary>
@@ -49,8 +66,8 @@ namespace Hotline.KnowledgeBase
         {
             var know = await _knowledgeRepository.Queryable(false, false, false)
                 .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)
                 throw UserFriendlyException.SameMessage("知识查询失败!");
             return know;
@@ -112,7 +129,7 @@ namespace Hotline.KnowledgeBase
             knowledgeFlow.WorkflowId = workflowId;
             knowledgeFlow.Flowed(flowedUserIds, flowedOrgIds, handlerUsers, handlerOrgs);
             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)
                 throw new UserFriendlyException($"无效知识编号, knowledgeId: {workflowExternalId}", "无效知识编号");
             knowledge.WorkflowId = workflowId;
@@ -203,26 +220,31 @@ namespace Hotline.KnowledgeBase
         /// <exception cref="UserFriendlyException"></exception>
         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)
    //         {
    //             case EKnowledgeApplyType.Add:
@@ -236,8 +258,8 @@ namespace Hotline.KnowledgeBase
    //                 await _knowledgeRepository.UpdateAsync(knowledge, cancellationToken);
    //                 break;
 
-                   
-			//}
+
+            //}
         }
 
         /// <summary>
@@ -250,20 +272,20 @@ namespace Hotline.KnowledgeBase
         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.HotspotType)
                 .Includes(it => it.User)
@@ -315,14 +337,53 @@ namespace Hotline.KnowledgeBase
 
                     isSendType = "2";
                     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 数据推送
             var pushKnowledge = _mapper.Map<KnowledgeSendDto>(knowledge);
             pushKnowledge.CategoryCode = "01";
             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);
             else if (isSendType == "1")  //修改知识推送

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

@@ -57,7 +57,7 @@ namespace Hotline.OrderTranspond
         /// <summary>
         /// 市州办理意见
         /// </summary>
-        [SugarColumn(ColumnDescription = "接收方工单编号")]
+        [SugarColumn(ColumnDescription = "接收方工单编号", ColumnDataType = "varchar(8000)")]
         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)
             .Where(x => x.SchedulingTime == time && x.WorkingTime <= DateTime.Now.TimeOfDay && x.OffDutyTime >= DateTime.Now.TimeOfDay && x.AtWork == true)
             .OrderBy(x => x.SendOrderNum).FirstAsync(cancellationToken);
+        var centerOrgName = _systemSettingCacheManager.GetSetting(SettingConstants.CenterOrgName)?.SettingValue[0];
         if (scheduling is null)
             return new FlowStepHandler
             {
@@ -467,7 +468,7 @@ public class OrderDomainService : IOrderDomainService, IScopeDependency
                 UserId = AppDefaults.SendPoolId,
                 Username = "待派单池",
                 OrgId = OrgSeedData.CenterId,
-                OrgName = "市民热线服务中心"
+                OrgName = centerOrgName
             };
         scheduling.SendOrderNum++;
         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 static readonly string CenterId = "001";
-        public static readonly string CenterName = "市民热线服务中心";//12345政务服务便民热线
+        public static readonly string CenterName = "热线中心";//12345政务服务便民热线
 
         public IEnumerable<SystemOrganize> HasData() =>
             new[]

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

@@ -585,5 +585,21 @@ namespace Hotline.Settings
         /// 取消发布功能总开关
         /// </summary>
         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";
     }
 }