Browse Source

合并冲突

Dun.Jason 6 months ago
parent
commit
3287c5c96e
53 changed files with 2232 additions and 724 deletions
  1. 40 24
      src/Hotline.Api/Controllers/Bi/BiOrderController.cs
  2. 84 4
      src/Hotline.Api/Controllers/DataSharingController.cs
  3. 14 2
      src/Hotline.Api/Controllers/ExportWordController.cs
  4. 251 182
      src/Hotline.Api/Controllers/OrderController.cs
  5. 1 1
      src/Hotline.Api/Controllers/OrderTerminateController.cs
  6. 8 0
      src/Hotline.Api/Controllers/OrgController.cs
  7. 66 2
      src/Hotline.Api/Controllers/PushMessageController.cs
  8. 60 7
      src/Hotline.Api/Controllers/WebPortalController.cs
  9. BIN
      src/Hotline.Api/Template/AssignmentForm.doc
  10. 2 2
      src/Hotline.Api/config/appsettings.Development.json
  11. 2 1
      src/Hotline.Api/config/appsettings.json
  12. 18 18
      src/Hotline.Application.Contracts/Validators/Order/OrderStartFlowDtoValidator.cs
  13. 14 14
      src/Hotline.Application.Tests/Application/OrderApplicationTest.cs
  14. 1 0
      src/Hotline.Application.Tests/Domain/ZiGongExpireTimeTest.cs
  15. 20 11
      src/Hotline.Application/FlowEngine/WorkflowApplication.cs
  16. 2 2
      src/Hotline.Application/Handlers/FlowEngine/WorkflowRecallHandler.cs
  17. 37 0
      src/Hotline.Application/Mappers/OrderMapperConfigs.cs
  18. 0 37
      src/Hotline.Application/Mappers/WorkflowMapperConfigs.cs
  19. 10 2
      src/Hotline.Application/Orders/IOrderApplication.cs
  20. 199 82
      src/Hotline.Application/Orders/OrderApplication.cs
  21. 7 0
      src/Hotline.Application/StatisticalReport/IOrderReportApplication.cs
  22. 31 0
      src/Hotline.Application/StatisticalReport/OrderReportApplication.cs
  23. 2 1
      src/Hotline.Repository.SqlSugar/Extensions/SqlSugarRepositoryExtensions.cs
  24. 1 0
      src/Hotline.Share/Dtos/CallCenter/QueryCallsFixedDto.cs
  25. 2 1
      src/Hotline.Share/Dtos/CallCenter/QueryTelOperationsFixedDto.cs
  26. 0 2
      src/Hotline.Share/Dtos/FlowEngine/BasicWorkflowDto.cs
  27. 7 2
      src/Hotline.Share/Dtos/FlowEngine/NextStepsDto.cs
  28. 50 92
      src/Hotline.Share/Dtos/FlowEngine/NextWorkflowDto.cs
  29. 31 8
      src/Hotline.Share/Dtos/FlowEngine/PreviousWorkflowDto.cs
  30. 0 5
      src/Hotline.Share/Dtos/FlowEngine/StartWorkflowDto.cs
  31. 255 8
      src/Hotline.Share/Dtos/Order/OrderDto.cs
  32. 135 24
      src/Hotline.Share/Dtos/Order/OrderStartFlowDto.cs
  33. 8 1
      src/Hotline.Share/Dtos/Order/OrderVisitDto.cs
  34. 42 25
      src/Hotline.Share/Dtos/Order/OrderWaitedDto.cs
  35. 80 0
      src/Hotline.Share/Dtos/Order/PublishedDto.cs
  36. 6 1
      src/Hotline.Share/Dtos/Order/QueryOrderDto.cs
  37. 5 0
      src/Hotline.Share/Dtos/OrderExportWord/OrderSubmissionForm.cs
  38. 71 59
      src/Hotline.Share/Dtos/Push/MessageDto.cs
  39. 1 0
      src/Hotline.Share/Enums/FlowEngine/EBusinessType.cs
  40. 7 1
      src/Hotline.Share/Enums/Push/EPushBusiness.cs
  41. 1 1
      src/Hotline.Share/Hotline.Share.csproj
  42. 3 2
      src/Hotline.Share/QueryFixedDto.cs
  43. 19 0
      src/Hotline.Share/Tools/IQueryFixedDto.cs
  44. 14 0
      src/Hotline.Share/Tools/StringExtensions.cs
  45. 1 0
      src/Hotline/Caching/Interfaces/ISystemSettingCacheManager.cs
  46. 2 0
      src/Hotline/Caching/Services/SystemSettingCacheManager.cs
  47. 23 1
      src/Hotline/FlowEngine/FlowAssignInfo.cs
  48. 12 1
      src/Hotline/FlowEngine/Workflows/IWorkflowDomainService.cs
  49. 2 2
      src/Hotline/FlowEngine/Workflows/Workflow.cs
  50. 572 90
      src/Hotline/FlowEngine/Workflows/WorkflowDomainService.cs
  51. 8 1
      src/Hotline/Settings/TimeLimitDomain/ExpireTimeSupplier/HourSupplier.cs
  52. 4 4
      src/XF.Domain.Repository/Entity.cs
  53. 1 1
      src/XF.Domain/Extensions/OrgExtensions.cs

+ 40 - 24
src/Hotline.Api/Controllers/Bi/BiOrderController.cs

@@ -1,5 +1,4 @@
-using AngleSharp.Text;
-using Hotline.Application.ExportExcel;
+using Hotline.Application.ExportExcel;
 using Hotline.Application.FlowEngine;
 using Hotline.Application.Orders;
 using Hotline.Application.StatisticalReport;
@@ -11,6 +10,7 @@ using Hotline.FlowEngine.Workflows;
 using Hotline.Orders;
 using Hotline.Repository.SqlSugar.Extensions;
 using Hotline.Schedulings;
+using Hotline.SeedData;
 using Hotline.Settings;
 using Hotline.Settings.Hotspots;
 using Hotline.Settings.TimeLimitDomain;
@@ -19,24 +19,16 @@ using Hotline.Share.Dtos;
 using Hotline.Share.Dtos.Bi;
 using Hotline.Share.Dtos.Bigscreen;
 using Hotline.Share.Dtos.CallCenter;
-using Hotline.Share.Dtos.FlowEngine.Workflow;
 using Hotline.Share.Dtos.Order;
 using Hotline.Share.Enums.CallCenter;
 using Hotline.Share.Enums.FlowEngine;
 using Hotline.Share.Enums.Order;
 using Hotline.Share.Requests;
-using Hotline.Share.Tools;
 using Hotline.Tools;
-using Mapster;
 using MapsterMapper;
-using MediatR;
-using Microsoft.AspNetCore.Authorization;
 using Microsoft.AspNetCore.Mvc;
-using NPOI.SS.Formula.Functions;
 using SqlSugar;
-using System.ComponentModel;
 using System.Data;
-using Hotline.SeedData;
 using XF.Domain.Authentications;
 using XF.Domain.Exceptions;
 using XF.Domain.Repository;
@@ -690,25 +682,49 @@ namespace Hotline.Api.Controllers.Bi
 
             var IsCenter = _sessionContext.OrgIsCenter;
 
-            var (total, items) = await _orderVisitDetailRepository.Queryable()
-                .Includes(x => x.OrderVisit, d => d.Order)
-                .Includes(x => x.OrderVisit, d => d.Employee)
-                //.Where(x => x.VisitOrgCode == dto.OrgCode)
-                .Where(x => x.OrderVisit.VisitState == EVisitState.Visited)
-                .Where(x => x.OrderVisit.VisitTime >= dto.StartTime.Value)
-                .Where(x => x.OrderVisit.VisitTime <= dto.EndTime.Value)
-                .Where(x => SqlFunc.JsonListObjectAny(x.OrgNoSatisfiedReason, "Key", dto.DissatisfiedKey))
-                .WhereIF(dto.VisitOrgName.NotNullOrEmpty(), x => x.VisitOrgName.Contains(dto.VisitOrgName))
-                .WhereIF(IsCenter == false, x => x.VisitOrgCode.StartsWith(_sessionContext.RequiredOrgId))
-                .WhereIF(IsCenter == true, x => x.VisitOrgCode.StartsWith(dto.OrgCode))
-                .WhereIF(!string.IsNullOrEmpty(dto.Keyword), x => x.OrderVisit.Order.No.Contains(dto.Keyword) || x.OrderVisit.Order.Title.Contains(dto.Keyword))
-                .OrderBy(x => x.OrderVisit.VisitTime)
+            var (total, items) = await _orderReportApplication.BiQueryVisitNoSatisfiedDetail(dto)
                 .ToPagedListAsync(dto.PageIndex, dto.PageSize, HttpContext.RequestAborted);
 
-
             return new PagedDto<OrderVisitDetailDto>(total, _mapper.Map<IReadOnlyList<OrderVisitDetailDto>>(items));
         }
 
+
+        /// <summary>
+        /// 部门不满意统计明细--导出
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [HttpPost("visit-nosatisfied-detail-export")]
+        public async Task<FileStreamResult> BiQueryVisitNoSatisfiedDetailExport([FromBody] ExportExcelDto<BiQueryVisitNoSatisfiedDetailDto> dto)
+        {
+            if (!dto.QueryDto.StartTime.HasValue || !dto.QueryDto.EndTime.HasValue) throw UserFriendlyException.SameMessage("请选择时间!");
+
+            var query = _orderReportApplication.BiQueryVisitNoSatisfiedDetail(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);
+
+            dynamic? dynamicClass = DynamicClassHelper.CreateDynamicClass(dto.ColumnInfos);
+
+            var dtos = dataDtos
+                .Select(stu => _mapper.Map(stu, typeof(OrderVisitDetailDto), dynamicClass))
+                .Cast<object>()
+                .ToList();
+
+            var stream = ExcelHelper.CreateStream(dtos);
+
+            return ExcelStreamResult(stream, "部门不满意统计明细");
+        }
+
         /// <summary>
         /// 部门延期统计
         /// </summary>

+ 84 - 4
src/Hotline.Api/Controllers/DataSharingController.cs

@@ -1,4 +1,5 @@
-using Hotline.Application.Bulletin;
+using DocumentFormat.OpenXml.Wordprocessing;
+using Hotline.Application.Bulletin;
 using Hotline.Article;
 using Hotline.Caching.Interfaces;
 using Hotline.FlowEngine.Workflows;
@@ -16,6 +17,7 @@ using Hotline.Share.Enums.Article;
 using Hotline.Share.Enums.FlowEngine;
 using Hotline.Share.Enums.Order;
 using Hotline.Share.Enums.Push;
+using Hotline.WebPortal;
 using MapsterMapper;
 using MediatR;
 using Microsoft.AspNetCore.Authorization;
@@ -40,6 +42,7 @@ namespace Hotline.Api.Controllers
         private readonly IRepository<WorkflowTrace> _workflowTraceRepository;
         private readonly IRepository<Hotspot> _hotspotTypeRepository;
         private readonly IMediator _mediator;
+        private readonly IRepository<OldPublicData> _oldPublicDataRepository;
 
         /// <summary>
         /// 
@@ -52,6 +55,7 @@ namespace Hotline.Api.Controllers
         /// <param name="workflowTraceRepository"></param>
         /// <param name="hotspotTypeRepository"></param>
         /// <param name="mediator"></param>
+        /// <param name="oldPublicDataRepository"></param>
         public DataSharingController(IMapper mapper,
             IOrderRepository orderRepository,
             IRepository<Bulletin> bulletinRepository,
@@ -59,7 +63,8 @@ namespace Hotline.Api.Controllers
             ISystemDicDataCacheManager sysDicDataCacheManager,
             IRepository<WorkflowTrace> workflowTraceRepository,
             IRepository<Hotspot> hotspotTypeRepository,
-            IMediator mediator)
+            IMediator mediator,
+            IRepository<OldPublicData> oldPublicDataRepository)
         {
             _mapper = mapper;
             _orderRepository = orderRepository;
@@ -69,6 +74,7 @@ namespace Hotline.Api.Controllers
             _workflowTraceRepository = workflowTraceRepository;
             _hotspotTypeRepository = hotspotTypeRepository;
             _mediator = mediator;
+            _oldPublicDataRepository = oldPublicDataRepository;
         }
 
         /// <summary>
@@ -179,7 +185,7 @@ namespace Hotline.Api.Controllers
         public async Task<PagedDto<PublishDto>> GetOrderByListOpen([FromBody] GetOrderList dto)
         {
             var (total, items) = await _orderRepository.Queryable()
-                .Where(p => p.IsPublicity == true)
+               .Where(p => p.IsPublicity == true)
                 .WhereIF(!string.IsNullOrEmpty(dto.No), p => p.No == dto.No)
                 .WhereIF(!string.IsNullOrEmpty(dto.Title), p => p.Title.Contains(dto.Title))
                 .WhereIF(!string.IsNullOrEmpty(dto.Mobile), p => p.Contact.Contains(dto.Mobile))
@@ -193,6 +199,80 @@ namespace Hotline.Api.Controllers
             return new PagedDto<PublishDto>(total, _mapper.Map<IReadOnlyList<PublishDto>>(items));
         }
 
+        /// <summary>
+        /// 查询工单发布后公开的数据
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [HttpPost("get_order_list_publish_all")]
+        [AllowAnonymous]
+        public async Task<PagedDto<PublishOrderAllDto>> GetOrderByListAllOpen([FromBody] GetOrderList dto)
+        {
+            var queryNew = _orderRepository.Queryable()
+                .Where(p => p.IsPublicity == true)
+                .WhereIF(!string.IsNullOrEmpty(dto.No), p => p.No == dto.No)
+                .WhereIF(!string.IsNullOrEmpty(dto.Title), p => p.Title.Contains(dto.Title))
+                .WhereIF(!string.IsNullOrEmpty(dto.Mobile), p => p.Contact.Contains(dto.Mobile))
+                .WhereIF(!string.IsNullOrEmpty(dto.AcceptTypeCode), p => p.AcceptTypeCode == dto.AcceptTypeCode)
+                .WhereIF(dto.StartTime.HasValue, p => p.CreationTime >= dto.StartTime)
+                .WhereIF(dto.EndTime.HasValue, p => p.CreationTime < dto.EndTime)
+                .WhereIF(!string.IsNullOrEmpty(dto.AreaCode), p => p.AreaCode == dto.AreaCode)
+                .OrderByDescending(p => p.CreationTime)
+               .Select(p => new PublishOrderAllDto
+               {
+                   OrderId = p.Id,
+                   FromName = p.FromName,
+                   Contact = p.Contact,
+                   OrderNo = p.No,
+                   Title = p.Title,
+                   SourceChannel = p.SourceChannel,
+                   SourceChannelCode = p.SourceChannelCode,
+                   AcceptType = p.AcceptType,
+                   AcceptTypeCode = p.AcceptTypeCode,
+                   HotspotName = p.HotspotName,
+                   Content = p.Content,
+                   State = p.Status >= EOrderStatus.Filed ? "办理完成" : "办理中",
+                   ActualOpinion = p.ActualOpinion,
+                   AcceptTime = p.CreationTime,
+                   PubDate = p.OrderPublish.CreationTime,
+                   FiledTime = p.FiledTime
+               });
+
+            var queryold = _oldPublicDataRepository.Queryable()
+                      .WhereIF(!string.IsNullOrEmpty(dto.No), p => p.OrderNo == dto.No)
+                      .WhereIF(!string.IsNullOrEmpty(dto.Title), p => p.Title.Contains(dto.Title))
+                      .WhereIF(!string.IsNullOrEmpty(dto.Mobile), p => p.Contact.Contains(dto.Mobile))
+                      .WhereIF(!string.IsNullOrEmpty(dto.AcceptTypeCode), p => p.AcceptTypeCode == dto.AcceptTypeCode)
+                      .WhereIF(dto.StartTime.HasValue, p => p.PubDate >= dto.StartTime)
+                      .WhereIF(dto.EndTime.HasValue, p => p.PubDate < dto.EndTime)
+                      .OrderByDescending(p => p.PubDate)
+                       .Select(p => new PublishOrderAllDto
+                       {
+                           OrderId = p.OrderId,
+                           FromName = p.FromName,
+                           Contact = p.Contact,
+                           OrderNo = p.OrderNo,
+                           Title = p.Title,
+                           SourceChannel = p.SourceChannel,
+                           SourceChannelCode = p.SourceChannelCode,
+                           AcceptType = p.AcceptType,
+                           AcceptTypeCode = p.AcceptTypeCode,
+                           HotspotName = p.HotspotName,
+                           Content = p.Content,
+                           State = p.State == "1" ? "办理完成" : "办理中",
+                           ActualOpinion = p.ActualOpinion,
+                           AcceptTime = p.CreationTime,
+                           PubDate = p.PubDate,
+                           FiledTime = p.FiledTime
+                       });
+
+            var (total, items) = await _orderRepository.UnionAll(queryNew, queryold)
+                .OrderByDescending(p => p.PubDate)
+                 .ToPagedListAsync(dto.PageIndex, dto.PageSize, HttpContext.RequestAborted);
+
+            return new PagedDto<PublishOrderAllDto>(total, items);
+        }
+
         /// <summary>
         /// 根据工单编号和密码查询流程
         /// </summary>
@@ -379,7 +459,7 @@ namespace Hotline.Api.Controllers
             if (dto.SendType == "2")
             {
                 messageDto.TemplateCode = "1011";
-                messageDto.PushBusiness= EPushBusiness.OrderExpire;
+                messageDto.PushBusiness = EPushBusiness.OrderExpire;
             }
 
             await _mediator.Publish(new PushMessageNotify(messageDto), HttpContext.RequestAborted);

+ 14 - 2
src/Hotline.Api/Controllers/ExportWordController.cs

@@ -1,8 +1,10 @@
 using Hotline.Application.ExportWord;
+using Hotline.Configurations;
 using Hotline.Orders;
 using Hotline.Share.Dtos.OrderExportWord;
 using MapsterMapper;
 using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Options;
 using XF.Domain.Repository;
 
 namespace Hotline.Api.Controllers
@@ -15,13 +17,15 @@ namespace Hotline.Api.Controllers
         private readonly IRepository<OrderVisit> _orderVisitRepository;
         private readonly IRepository<OrderVisitDetail> _orderVisitedDetailRepository;
         private readonly ILogger<ExportWordController> _logger;
+        private readonly IOptionsSnapshot<AppConfiguration> _appOptions;
 
         public ExportWordController(IOrderRepository orderRepository,
             IWordHelperService wordHelperService,
             IMapper mapper,
            IRepository<OrderVisit> orderVisitRepository,
            IRepository<OrderVisitDetail> orderVisitedDetailRepository,
-           ILogger<ExportWordController> logger)
+           ILogger<ExportWordController> logger,
+            IOptionsSnapshot<AppConfiguration> appOptions)
         {
             _orderRepository = orderRepository;
             _wordHelperService = wordHelperService;
@@ -29,10 +33,11 @@ namespace Hotline.Api.Controllers
             _orderVisitRepository = orderVisitRepository;
             _orderVisitedDetailRepository = orderVisitedDetailRepository;
             _logger = logger;
+            _appOptions = appOptions;
         }
 
         /// <summary>
-        /// 工单交办单导出
+        /// 工单交办单导出         
         /// </summary>
         /// <returns></returns>
         [HttpPost("order_submission_form")]
@@ -49,6 +54,13 @@ namespace Hotline.Api.Controllers
 
                 var exportTest = _mapper.Map<OrderSubmissionForm>(order);
 
+                if (_appOptions.Value.IsZiGong)
+                    exportTest.CityName = "自贡市";
+                else if (_appOptions.Value.IsYiBin)
+                    exportTest.CityName = "宜宾市";
+                else if (_appOptions.Value.IsLuZhou)
+                    exportTest.CityName = "泸州市";
+
                 //查询回访信息
                 var visitData = await _orderVisitRepository.GetAsync(p => p.OrderId == order.Id, HttpContext.RequestAborted);
                 if (visitData != null)

+ 251 - 182
src/Hotline.Api/Controllers/OrderController.cs

@@ -58,6 +58,7 @@ using MiniExcelLibs;
 using SqlSugar;
 using StackExchange.Redis;
 using System.Text;
+using MongoDB.Driver.Linq;
 using XF.Domain.Authentications;
 using XF.Domain.Cache;
 using XF.Domain.Entities;
@@ -65,6 +66,9 @@ using XF.Domain.Exceptions;
 using XF.Domain.Repository;
 using XF.Utility.EnumExtensions;
 using Newtonsoft.Json;
+using XF.Domain.Extensions;
+using Order = Hotline.Orders.Order;
+using WorkflowStep = Hotline.FlowEngine.Workflows.WorkflowStep;
 
 namespace Hotline.Api.Controllers;
 
@@ -877,15 +881,15 @@ public class OrderController : BaseController
             .Includes(d => d.Order)
             .Includes(d => d.Employee)
             .Includes(d => d.OrderVisitDetails)
-            .WhereIF(dto.VisitState == EVisitStateQuery.NoVisit,
+            .WhereIF(dto.VisitStateQuery == EVisitStateQuery.NoVisit,
                 d => d.VisitState == EVisitState.WaitForVisit ||
                      d.VisitState == EVisitState.NoSatisfiedWaitForVisit)
-            .WhereIF(dto.VisitState == EVisitStateQuery.Visited, d => d.VisitState == EVisitState.Visited)
-            .WhereIF(dto.VisitState == EVisitStateQuery.SMSUnsatisfied, m => m.VisitState == EVisitState.SMSUnsatisfied)
-            .WhereIF(dto.VisitState == EVisitStateQuery.SMSVisiting, m => m.VisitState == EVisitState.SMSVisiting)
+            .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(!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, x => x.VisitType == dto.VisitType)
+            .WhereIF(dto.VisitType != null, d => d.VisitType == dto.VisitType)
             .WhereIF(dto.FiledType != null && dto.FiledType == FiledType.CenterFiled, d => d.Order.ProcessType == EProcessType.Zhiban)
             .WhereIF(dto.FiledType != null && dto.FiledType == FiledType.OrgFiled, d => d.Order.ProcessType == EProcessType.Jiaoban)
             .WhereIF(dto.IsCountersign != null && dto.IsCountersign == true, d => d.Order.CounterSignType != null)
@@ -895,8 +899,8 @@ public class OrderController : BaseController
             .WhereIF(dto.IsProvince != null && dto.IsProvince == true, d => d.Order.IsProvince == true)
             .WhereIF(dto.IsProvince != null && dto.IsProvince == false, d => d.Order.IsProvince == false)
             .WhereIF(dto.IsEffectiveAiVisit != null, d => d.IsEffectiveAiVisit == dto.IsEffectiveAiVisit)
-            .WhereIF(dto.FromPhone.NotNullOrEmpty(), m => m.Order.FromPhone == dto.FromPhone)
-            .WhereIF(dto.Contact.NotNullOrEmpty(), m => m.Order.Contact == dto.Contact)
+            .WhereIF(dto.FromPhone.NotNullOrEmpty(), d => d.Order.FromPhone == dto.FromPhone)
+            .WhereIF(dto.Contact.NotNullOrEmpty(), d => d.Order.Contact == dto.Contact)
             .WhereIF(dto.VoiceEvaluate.Any(), d => d.OrderVisitDetails.Any(m => dto.VoiceEvaluate.Contains(m.VoiceEvaluate.Value)))
             .WhereIF(dto.SeatEvaluate.Any(), d => d.OrderVisitDetails.Any(m => dto.SeatEvaluate.Contains(m.SeatEvaluate.Value)))
             .WhereIF(dto.OrgProcessingResults.Any(),
@@ -910,6 +914,9 @@ public class OrderController : BaseController
                 d => (d.Order.ExpiredTime > DateTime.Now && d.Order.Status < EOrderStatus.Filed) ||
                      (d.Order.ExpiredTime > d.Order.ActualHandleTime && d.Order.Status >= EOrderStatus.Filed)) //否 超期
             .OrderByDescending(x => x.PublishTime)
+                d => d.OrderVisitDetails.Any(m => dto.OrgHandledAttitude.Contains(SqlFunc.JsonField(m.OrgHandledAttitude, "Key"))))
+            .WhereIF(dto.Channel.NotNullOrEmpty(), d => d.Order.SourceChannelCode == dto.Channel)
+            .OrderByDescending(d => d.PublishTime)
             .ToPagedListAsync(dto.PageIndex, dto.PageSize, HttpContext.RequestAborted);
         return new PagedDto<OrderVisitDto>(total, _mapper.Map<IReadOnlyList<OrderVisitDto>>(items));
     }
@@ -1631,7 +1638,7 @@ public class OrderController : BaseController
     //[Permission(EPermission.ApplyDelay)]
     [HttpPost("delay")]
     [LogFilter("新增工单延期申请")]
-    public async Task ApplyDelay([FromBody] DelayStartFlowDto dto)
+    public async Task ApplyDelay([FromBody] StartWorkflowDto<ApplyDelayDto> dto)
     {
         var delaydto = dto.Data;
 
@@ -1972,7 +1979,7 @@ public class OrderController : BaseController
             //.LeftJoin<OrderScreen>((x, s) => x.Id == s.VisitDetailId && s.IsDeleted == false)
             .Includes(x => x.OrderScreens)
             .Where(x => x.OrderScreens.Any(s => s.Status == EScreenStatus.SendBack && s.SendBackApply == true) || x.OrderScreens.Any() == false
-                //|| x.OrderScreens.Any(s => (s.Status != EScreenStatus.SendBack && s.SendBackApply != true)) == false
+            //|| x.OrderScreens.Any(s => (s.Status != EScreenStatus.SendBack && s.SendBackApply != true)) == false
             )
             .WhereIF(dto.ScreenSendBack is 1, x => x.OrderScreens.Any(s => s.Status == EScreenStatus.SendBack && s.SendBackApply == true))
             .WhereIF(dto.ScreenSendBack is 2, x => x.OrderScreens.Any(s => (s.Status != EScreenStatus.SendBack && s.SendBackApply != true)) == false)
@@ -2054,83 +2061,53 @@ public class OrderController : BaseController
     [HttpGet("screen")]
     public async Task<PagedDto<OrderScreenListDto>> ScreenList([FromQuery] ScreenListDto dto)
     {
-        var handler = dto.TabStatus is EScreenStatus.Apply;
-        var isAdmin = _orderDomainService.IsCheckAdmin();
-        ISugarQueryable<OrderScreen> query;
+        var (total, items) = await  _orderApplication.OrderScreenList(dto)
+            .ToPagedListAsync(dto.PageIndex, dto.PageSize, HttpContext.RequestAborted);
+        return new PagedDto<OrderScreenListDto>(total, _mapper.Map<IReadOnlyList<OrderScreenListDto>>(items));
+    }
 
-        if (dto.source == 1)
-        {
-            query = _orderScreenRepository.Queryable(hasHandled: !handler, isAdmin: isAdmin);
-        }
-        else
-        {
-            query = _orderScreenRepository.Queryable(isAdmin: isAdmin)
-                .WhereIF(!isAdmin, x => x.CreatorOrgId.StartsWith(_sessionContext.RequiredOrgId));
-        }
 
-        query = query
-            .Includes(d => d.Order)
-            .Includes(d => d.VisitDetail)
-            .Includes(d => d.Visit, v => v.Order)
-            .Includes(d => d.Workflow)
-            .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)));
-        }
+	/// <summary>
+	/// 工单甄别列表导出
+	/// </summary>
+	/// <returns></returns>
+	[HttpPost("screen_list/_export")]
+    public async Task<FileStreamResult> ScreenListExport([FromBody] ExportExcelDto<ScreenListDto> dto)
+    {
+	    var query = _orderApplication.OrderScreenList(dto.QueryDto);
+	    List<OrderScreen> data;
+	    if (dto.IsExportAll)
+	    {
+		    data = await query.ToListAsync(HttpContext.RequestAborted);
+	    }
+	    else
+	    {
+		    var (_, items) = await query.ToPagedListAsync(dto.QueryDto, HttpContext.RequestAborted);
+		    data = items;
+	    }
 
-        if (dto.TabStatus.HasValue && dto.Status == EScreenStatus.MyHandle)
-        {
-            query.Where(d => (d.Status != EScreenStatus.Apply));
-        }
+	    var dataDtos = _mapper.Map<ICollection<OrderScreenListDto>>(data);
 
-        var (total, items) = await 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.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.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(dto.CreationTime.HasValue && dto.EndCreationTime.HasValue,
-                x => x.Order!.CreationTime >= dto.CreationTime && x.Order!.CreationTime <= dto.EndCreationTime)
-            .WhereIF(dto.CurrentHandleTime.HasValue && dto.EndCurrentHandleTime.HasValue,
-                x => x.Order!.CurrentHandleTime >= dto.CurrentHandleTime && x.Order!.ActualHandleTime <= dto.EndCurrentHandleTime)
-            .WhereIF(dto.FiledTime.HasValue && dto.EndFiledTime.HasValue,
-                x => x.Order!.FiledTime >= dto.FiledTime && x.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)
-            .ToPagedListAsync(dto.PageIndex, dto.PageSize, HttpContext.RequestAborted);
-        return new PagedDto<OrderScreenListDto>(total, _mapper.Map<IReadOnlyList<OrderScreenListDto>>(items));
+	    dynamic? dynamicClass = DynamicClassHelper.CreateDynamicClass(dto.ColumnInfos);
+
+	    var dtos = dataDtos
+		    .Select(stu => _mapper.Map(stu, typeof(OrderScreenListDto), dynamicClass))
+		    .Cast<object>()
+		    .ToList();
+
+	    var stream = ExcelHelper.CreateStream(dtos);
+
+	    return ExcelStreamResult(stream, "工单甄别列表数据");
     }
 
 
-    /// <summary>
-    /// 开始工单甄别流程
-    /// </summary>
-    [Permission(EPermission.ApplyScreen)]
+	/// <summary>
+	/// 开始工单甄别流程
+	/// </summary>
+	[Permission(EPermission.ApplyScreen)]
     [HttpPost("screen/startflow")]
     [LogFilter("开始工单甄别流程")]
-    public async Task StartFlow([FromBody] ScreenStartFlowDto dto)
+    public async Task StartFlow([FromBody] StartWorkflowDto<OrderScreenDto> dto)
     {
         var screenAny = await _orderScreenRepository.AnyAsync(x =>
             x.VisitDetailId == dto.Data.VisitDetailId &&
@@ -3130,7 +3107,7 @@ public class OrderController : BaseController
                 cancellationToken: HttpContext.RequestAborted);
 
             List<OrderRemarksDto> remarks = workflow.Steps.Where(x => !string.IsNullOrEmpty(x.Remark)).Select(x => new OrderRemarksDto
-                { Remark = x.Remark, RemarkTime = x.CreationTime, RemarkUser = x.CreatorName }).ToList();
+            { Remark = x.Remark, RemarkTime = x.CreationTime, RemarkUser = x.CreatorName }).ToList();
             dto.OrderRemarks = remarks;
             if (order.Status == EOrderStatus.SendBack || order.Status == EOrderStatus.SendBackAudit || order.Status == EOrderStatus.BackToUnAccept)
             {
@@ -3185,7 +3162,7 @@ public class OrderController : BaseController
             "不同意" : orderTerminateList.Any(x => x.Status == ETerminateStatus.Approval || x.Status == ETerminateStatus.SendBack) ? "审批中" : null;
 
 
-		return _sessionContext.OrgIsCenter ? dto : dto.DataMask();
+        return _sessionContext.OrgIsCenter ? dto : dto.DataMask();
     }
 
     /// <summary>
@@ -3497,9 +3474,9 @@ public class OrderController : BaseController
     /// 开始工单办理流程
     /// </summary>
     [HttpPost("startflow")]
-    public async Task StartFlow([FromBody] OrderStartFlowDto dto)
+    public async Task StartFlow([FromBody] StartWorkflowDto<OrderHandleFlowDto> dto)
     {
-        var order = await _orderDomainService.GetOrderAsync(dto.Data, cancellationToken: HttpContext.RequestAborted);
+        var order = await _orderDomainService.GetOrderAsync(dto.Data.OrderId, cancellationToken: HttpContext.RequestAborted);
         if (!string.IsNullOrEmpty(order.WorkflowId))
             throw new UserFriendlyException($"该工单已开启办理流程, No:{order.No}", "该工单已开启办理流程");
 
@@ -3533,23 +3510,33 @@ public class OrderController : BaseController
         }
 
         _mapper.Map(expiredTimeConfig, order);
+        
+        if (dto.Workflow.BusinessType is EBusinessType.Department or EBusinessType.DepartmentLeader)
+            order.ProcessType = EProcessType.Jiaoban;
+        
         await _orderRepository.UpdateAsync(order, HttpContext.RequestAborted);
 
         try
         {
-            // 平均派单
-            var averageSendOrder = bool.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.AverageSendOrder).SettingValue[0]);
-            if (dto.Workflow.BusinessType == EBusinessType.Send && averageSendOrder)
-            {
-                var handler = await _orderDomainService.AverageOrder(HttpContext.RequestAborted);
-                dto.Workflow.NextHandlers = new List<FlowStepHandler> { handler };
-            }
+            // // 平均派单
+            // var averageSendOrder = bool.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.AverageSendOrder).SettingValue[0]);
+            // if (dto.Workflow.BusinessType == EBusinessType.Send && averageSendOrder)
+            // {
+            //     var handler = await _orderDomainService.AverageOrder(HttpContext.RequestAborted);
+            //     dto.Workflow.NextHandlers = new List<FlowStepHandler> { handler };
+            // }
+            //
+            // var startDto = _mapper.Map<StartWorkflowDto>(dto.Workflow);
+            // startDto.DefinitionModuleCode = WorkflowModuleConsts.OrderHandle;
+            // startDto.Title = order.Title;
+            // await _workflowApplication.StartWorkflowAsync(startDto, order.Id, order.ExpiredTime, HttpContext.RequestAborted);
 
             var startDto = _mapper.Map<StartWorkflowDto>(dto.Workflow);
             startDto.DefinitionModuleCode = WorkflowModuleConsts.OrderHandle;
             startDto.Title = order.Title;
-            startDto.IsForwarded = dto.IsForwarded;
-            await _workflowApplication.StartWorkflowAsync(startDto, order.Id, order.ExpiredTime, HttpContext.RequestAborted);
+            var startStep = await _workflowDomainService.StartAsync(startDto, order.Id, order.ExpiredTime, HttpContext.RequestAborted);
+
+            await HandleOrderAsync(order, startStep, dto.Data, dto.Workflow, HttpContext.RequestAborted);
         }
         catch (Exception e)
         {
@@ -3557,8 +3544,12 @@ public class OrderController : BaseController
             throw new UserFriendlyException($"工单开启流程失败!, {e.Message}, {e.StackTrace}", "工单开启流程失败");
         }
 
-        //开启流程处理事件,处理市州互转
-        await _publisher.PublishAsync(new OrderStartWorkflowNotify(order.Id), PublishStrategy.ParallelWhenAll, HttpContext.RequestAborted);
+        if (_appOptions.Value.IsYiBin && dto.Data.Transpond.HasValue && dto.Data.Transpond.Value)
+        {
+            //开启流程处理事件,处理市州互转
+            await _publisher.PublishAsync(new OrderStartWorkflowNotify(order.Id), PublishStrategy.ParallelWhenAll,
+                HttpContext.RequestAborted);
+        }
     }
 
     /// <summary>
@@ -3566,31 +3557,36 @@ public class OrderController : BaseController
     /// </summary>
     /// <returns></returns>
     [HttpGet("startflow")]
-    public async Task<NextStepsDto> GetFlowStartOptions()
+    public async Task<NextStepsDto> GetFlowStartOptions([FromQuery]string? orderId)
     {
-        return await _workflowApplication.GetStartStepsAsync(WorkflowModuleConsts.OrderHandle,
+        var outDto = await _workflowApplication.GetStartStepsAsync(WorkflowModuleConsts.OrderHandle,
             HttpContext.RequestAborted);
+        if (orderId.NotNullOrEmpty())
+        { 
+            outDto.Opinion = await _typeCache.GetAsync($"tmp_opinion_{orderId}{_sessionContext.UserId}", HttpContext.RequestAborted);
+        }
+        return outDto;
     }
 
     /// <summary>
     /// 工单办理
     /// </summary>
     [HttpPost("handle")]
-    public async Task Handle([FromBody] NextWorkflowDto dto)
+    public async Task Handle([FromBody] NextWorkflowDto<OrderHandleFlowDto> dto)
     {
         var order = await _orderApplication.SaveOrderWorkflowInfo(dto, HttpContext.RequestAborted);
 
+        var workflow = await _workflowDomainService.GetWorkflowAsync(dto.WorkflowDto.WorkflowId, withSteps: true, withTraces: true,
+            cancellationToken: HttpContext.RequestAborted);
         //1.是否是判断节点  2.是否存在历史派单节点  3.存在获取上个派单节点  4.不存在走平均派单
-        if (dto.BusinessType == EBusinessType.Send)
+        if (dto.Data.OrderAssignMode is EOrderAssignMode.AdjoinLevel && dto.WorkflowDto.BusinessType == EBusinessType.Send)
         {
-            var workflow = await _workflowDomainService.GetWorkflowAsync(dto.WorkflowId, withTraces: true,
-                cancellationToken: HttpContext.RequestAborted);
             var sendOrderTraces = workflow.Traces.Where(x => x.BusinessType == EBusinessType.Send);
             if (sendOrderTraces.Any())
             {
                 var sendOrderTrace = workflow.Traces.Where(x => x.BusinessType == EBusinessType.Send).OrderByDescending(x => x.CreationTime)
                     .FirstOrDefault();
-                dto.NextHandlers = new List<FlowStepHandler>
+                dto.WorkflowDto.NextHandlers = new List<FlowStepHandler>
                 {
                     new FlowStepHandler()
                     {
@@ -3607,12 +3603,116 @@ public class OrderController : BaseController
                 if (averageSendOrder)
                 {
                     var handler = await _orderDomainService.AverageOrder(HttpContext.RequestAborted);
-                    dto.NextHandlers = new List<FlowStepHandler> { handler };
+                    dto.WorkflowDto.NextHandlers = new List<FlowStepHandler> { handler };
                 }
             }
         }
 
-        await _workflowApplication.NextAsync(dto, order.ExpiredTime, HttpContext.RequestAborted);
+        //await _workflowApplication.NextAsync(dto.WorkflowDto, order.ExpiredTime, HttpContext.RequestAborted);
+        var startStep = workflow.Steps.First(d => d.Id == dto.WorkflowDto.StepId);
+        await HandleOrderAsync(order, startStep, dto.Data, dto.WorkflowDto, HttpContext.RequestAborted);
+
+        if (_appOptions.Value.IsZiGong && dto.Data.Transpond.HasValue && dto.Data.Transpond.Value)
+        {
+            var count = await _transpondCityRawDataRepository.Queryable()
+                .CountAsync(m => m.OrderCode == order.No && m.CityName == order.TranspondCityName
+                                                         && m.Direction == ETranspondDirection.Out, HttpContext.RequestAborted);
+            //处理市州互转
+            if (count == 0)
+                await _publisher.PublishAsync(new OrderStartWorkflowNotify(order.Id), PublishStrategy.ParallelWhenAll, HttpContext.RequestAborted);
+        }
+    }
+
+    private async Task HandleOrderAsync(Order order, WorkflowStep startStep, OrderHandleFlowDto orderHandleFlowDto, BasicWorkflowDto workflowDto, CancellationToken cancellationToken)
+    {
+        switch (orderHandleFlowDto.OrderAssignMode)
+        {
+            case EOrderAssignMode.AdjoinLevel:
+                var nextDto = _mapper.Map<NextWorkflowDto>(workflowDto);
+                nextDto.WorkflowId = startStep.WorkflowId;
+                nextDto.StepId = startStep.Id;
+
+                // 平均派单
+                var averageSendOrder = bool.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.AverageSendOrder)
+                    .SettingValue[0]);
+                if (workflowDto.BusinessType == EBusinessType.Send && averageSendOrder)
+                {
+                    var handler = await _orderDomainService.AverageOrder(cancellationToken);
+                    nextDto.NextHandlers = new List<FlowStepHandler> { handler };
+                }
+
+                await _workflowDomainService.NextAsync(nextDto, order.ExpiredTime, cancellationToken);
+                break;
+            case EOrderAssignMode.CrossLevel:
+                if (!orderHandleFlowDto.CrossOrgIds.Any())
+                    throw new UserFriendlyException("跨级指派参数异常");
+
+                var orgIds = orderHandleFlowDto.CrossOrgIds;
+                orgIds.Add(workflowDto.NextHandlers.First().OrgId);
+                var orgs = await _organizeRepository.Queryable()
+                    .Where(d => orgIds.Contains(d.Id))
+                    .ToListAsync(HttpContext.RequestAborted);
+                var maxLevel = orgs.MaxBy(d => d.Level).Level;
+
+                //办理开始节点或派单节点至一级部门
+                var levelOneOrgs = orgs.Where(d => d.Level == 1).ToList();
+                var unhandleSteps = await HandleWorkflowStepAsync(startStep, levelOneOrgs, order.ExpiredTime, cancellationToken);
+
+                //依次办理路过节点
+                for (int i = 1; i < maxLevel; i++)
+                {
+                    var tempSteps = new List<WorkflowStep>();
+                    foreach (var unhandleStep in unhandleSteps)
+                    {
+                        var hasLowerLevel = orgs.Any(d => d.Id.StartsWith(unhandleStep.HandlerOrgId));
+                        if (!hasLowerLevel) continue;
+
+                        var currentLevel = unhandleStep.HandlerOrgId.CalcOrgLevel();
+                        var lowerLevel = currentLevel++;
+                        var handleOrgs = orgs.Where(d => d.Level == lowerLevel).ToList();
+                        if (i != 1)
+                            handleOrgs = handleOrgs.Where(d => d.Id.StartsWith(unhandleStep.HandlerOrgId)).ToList();
+                        var nextSteps = await HandleWorkflowStepAsync(unhandleStep, handleOrgs, order.ExpiredTime, cancellationToken);
+                        tempSteps.AddRange(nextSteps);
+                    }
+
+                    unhandleSteps = tempSteps;
+                }
+
+                break;
+            case EOrderAssignMode.MainAndSecondary:
+                break;
+            default:
+                throw new ArgumentOutOfRangeException();
+        }
+    }
+
+    private async Task<List<WorkflowStep>> HandleWorkflowStepAsync(WorkflowStep unhandleStep, List<SystemOrganize> handleOrgs, DateTime? expiredTime, CancellationToken cancellationToken)
+    {
+        var isStartCountersign = handleOrgs.Count > 1;
+        var handlers = handleOrgs.Select(d => new FlowStepHandler
+        {
+            OrgId = d.Id,
+            OrgName = d.Name,
+            Key = d.Id,
+            Value = d.Name
+        }).ToList();
+        var nextflowDto = new NextWorkflowDto
+        {
+            WorkflowId = unhandleStep.WorkflowId,
+            StepId = unhandleStep.Id,
+            NextStepCode = unhandleStep.Code,
+            NextStepName = unhandleStep.Name,
+            FlowDirection = EFlowDirection.CenterToOrg,
+            HandlerType = unhandleStep.HandlerType,
+            StepType = unhandleStep.StepType,
+            NextHandlers = handlers,
+            IsStartCountersign = isStartCountersign,
+            BusinessType = unhandleStep.BusinessType,
+            Opinion = "跨级派单,自动办理"
+        };
+
+        return await _workflowDomainService.NextAsync(nextflowDto, expiredTime, cancellationToken);
     }
 
     /// <summary>
@@ -3655,7 +3755,7 @@ public class OrderController : BaseController
     [HttpPost("steps/temp")]
     public async Task TempSaveAsync([FromBody] StepTempInDto dto)
     {
-        //await _typeCache.SetAsync($"tmp_opinion_{dto.OrderId}{_sessionContext.UserId}", dto.Opinion, TimeSpan.FromDays(3), HttpContext.RequestAborted);
+        await _typeCache.SetAsync($"tmp_opinion_{dto.OrderId}{_sessionContext.UserId}", dto.Opinion, TimeSpan.FromDays(3), HttpContext.RequestAborted);
     }
 
     /// <summary>
@@ -3742,8 +3842,9 @@ public class OrderController : BaseController
             PushTypeOptions = _sysDicDataCacheManager.GetSysDicDataCache(SysDicTypeConsts.PushType),
             OrderStatusOptions = EnumExts.GetDescriptions<EOrderStatus>(),
             CurrentStepOptions = definition?.Steps.Select(x => new Kv(x.Code, x.Name)),
-            IdentityTypeOptions = EnumExts.GetDescriptions<EIdentityType>()
-        };
+            IdentityTypeOptions = EnumExts.GetDescriptions<EIdentityType>(),
+            OrderTags = _sysDicDataCacheManager.GetSysDicDataCache(SysDicTypeConsts.OrderTag)
+		};
         return rsp;
     }
 
@@ -3907,64 +4008,18 @@ public class OrderController : BaseController
     /// 查询坐席待办
     /// </summary>
     [HttpGet("waited/sign")]
-    public async Task<PagedDto<OrderDto>> QueryWaitedForSeat([FromQuery] QueryOrderWaitedDto dto)
+    public async Task<List<OrderListOutDto>> QueryWaitedForSeat([FromQuery] QueryOrderWaitedDto dto)
     {
-        EOrderStatus[] handleStatuses = EnumExts.GetFields<EOrderStatus>().Select(d => (EOrderStatus)d.Key).ToArray();
-        handleStatuses = handleStatuses.WhereIF(dto.IsHandled.HasValue,
-                d => dto.IsHandled!.Value
-                    ? d is not EOrderStatus.WaitForAccept and not EOrderStatus.BackToUnAccept and not EOrderStatus.SpecialToUnAccept
-                        and not EOrderStatus.HandOverToUnAccept
-                    : d is EOrderStatus.WaitForAccept or EOrderStatus.BackToUnAccept or EOrderStatus.SpecialToUnAccept
-                        or EOrderStatus.HandOverToUnAccept)
-            .ToArray();
-
-        var query = _orderRepository.Queryable();
-        if (dto.IsHandled.HasValue)
-        {
-            var hasHandled = dto.IsHandled.Value;
-            query = query.Where(d => SqlFunc.Subqueryable<WorkflowTrace>()
-                                         .Where(step => step.ExternalId == d.Id &&
-                                                        (hasHandled || step.Status != EWorkflowStepStatus.Handled) &&
-                                                        (!hasHandled || step.Status == EWorkflowStepStatus.Handled &&
-                                                            step.TraceState != EWorkflowTraceState.StepRemoveByPrevious) &&
-                                                        ((step.FlowAssignType == EFlowAssignType.User && !string.IsNullOrEmpty(step.HandlerId) &&
-                                                          step.HandlerId == _sessionContext.RequiredUserId) ||
-                                                         (step.FlowAssignType == EFlowAssignType.Org && !string.IsNullOrEmpty(step.HandlerOrgId) &&
-                                                          step.HandlerOrgId == _sessionContext.RequiredOrgId) ||
-                                                         (step.FlowAssignType == EFlowAssignType.Role && !string.IsNullOrEmpty(step.RoleId) &&
-                                                          _sessionContext.Roles.Contains(step.RoleId)))).Any() ||
-                                     (string.IsNullOrEmpty(d.WorkflowId) &&
-                                      (string.IsNullOrEmpty(d.SignerId) || d.SignerId == _sessionContext.RequiredUserId))
-            );
-        }
-
-        var (total, items) = await query
-            .WhereIF(dto.IsProvince.HasValue, d => d.IsProvince == dto.IsProvince)
-            .WhereIF(dto.IsHandled.HasValue, d => handleStatuses.Contains(d.Status))
-            .WhereIF(!string.IsNullOrEmpty(dto.Keyword), d => d.Title.StartsWith(dto.Keyword!))
-            .WhereIF(!string.IsNullOrEmpty(dto.No), d => d.No == dto.No)
-            .WhereIF(!string.IsNullOrEmpty(dto.AreaCode), d => d.AreaCode == dto.AreaCode)
-            .WhereIF(dto.IsCounterSign.HasValue && dto.IsCounterSign == true, d => d.CounterSignType.HasValue)
-            .WhereIF(dto.IsCounterSign.HasValue && dto.IsCounterSign == false, d => d.CounterSignType == null)
-            .WhereIF(dto.ExpiredOrAlmostOverdue.HasValue && dto.ExpiredOrAlmostOverdue == true,
-                d => (d.ExpiredTime < DateTime.Now && d.Status < EOrderStatus.Filed) ||
-                     (d.ExpiredTime < d.ActualHandleTime && d.Status >= EOrderStatus.Filed)) //超期 未办
-            .WhereIF(dto.ExpiredOrAlmostOverdue.HasValue && dto.ExpiredOrAlmostOverdue == false,
-                d => d.NearlyExpiredTime < DateTime.Now && d.ExpiredTime > DateTime.Now) //即将超期 未办
-            .WhereIF(dto.StartTime.HasValue, d => d.CreationTime >= dto.StartTime)
-            .WhereIF(dto.EndTime.HasValue, d => d.CreationTime <= dto.EndTime)
-            //.Where(d => (string.IsNullOrEmpty(d.WorkflowId) && (string.IsNullOrEmpty(d.SignerId) || d.SignerId == _sessionContext.RequiredUserId)))
-            //.Where(d => string.IsNullOrEmpty(d.SignerId) || d.SignerId == _sessionContext.RequiredUserId)
-            .WhereIF(dto.IsUrgent.HasValue, d => d.IsUrgent == dto.IsUrgent.Value)
-            .WhereIF(dto.Status.HasValue, d => d.Status == dto.Status)
-            .Where(x => x.Source < ESource.MLSQ || x.Source > ESource.WZSC)
-            .Where(x => x.Status != EOrderStatus.BackToProvince && x.Status < EOrderStatus.Filed)
-            .OrderBy(d => d.Status)
-            .OrderByIF(dto.IsHandled == true, d => d.StartTime, OrderByType.Desc)
-            .OrderByIF(dto.IsHandled == false, d => new { IsUrgent = d.IsUrgent, CreationTime = d.CreationTime }, OrderByType.Desc)
-            .ToPagedListAsync(dto, HttpContext.RequestAborted);
+        return await _orderApplication
+            .QueryWaitedForSeat(dto)
+            .ToFixedListAsync(dto, HttpContext.RequestAborted);
+    }
 
-        return new PagedDto<OrderDto>(total, _mapper.Map<IReadOnlyList<OrderDto>>(items));
+    [HttpGet("waited/sign/count")]
+    public async Task<int> QueryWaitedForSeatCount([FromQuery] QueryOrderWaitedDto dto)
+    {
+        return await _orderApplication.QueryWaitedForSeat(dto)
+            .CountAsync(HttpContext.RequestAborted);
     }
 
     /// <summary>
@@ -4752,10 +4807,13 @@ public class OrderController : BaseController
             //	ETimeType.WorkDay,
             //	dto.TimeLimit.Value, order.AcceptTypeCode);
             await _orderRepository.Updateable().SetColumns(o => new Orders.Order()
-                {
-                    ExpiredTime = expiredTime.ExpiredTime, NearlyExpiredTime = expiredTime.NearlyExpiredTime,
-                    NearlyExpiredTimeOne = expiredTime.NearlyExpiredTimeOne, ProcessType = processType, Status = EOrderStatus.Special
-                })
+            {
+                ExpiredTime = expiredTime.ExpiredTime,
+                NearlyExpiredTime = expiredTime.NearlyExpiredTime,
+                NearlyExpiredTimeOne = expiredTime.NearlyExpiredTimeOne,
+                ProcessType = processType,
+                Status = EOrderStatus.Special
+            })
                 .Where(o => o.Id == order.Id).ExecuteCommandAsync(HttpContext.RequestAborted);
             var orderDto = _mapper.Map<OrderDto>(order);
             await _capPublisher.PublishAsync(Hotline.Share.Mq.EventNames.HotlineOrderExpiredTimeUpdate, orderDto,
@@ -4911,10 +4969,11 @@ public class OrderController : BaseController
 
                 endTime = expiredTime.EndTime;
                 await _orderRepository.Updateable().SetColumns(o => new Orders.Order()
-                    {
-                        ExpiredTime = expiredTime.EndTime, NearlyExpiredTime = expiredTime.NearlyExpiredTime,
-                        NearlyExpiredTimeOne = expiredTime.NearlyExpiredTimeOne
-                    })
+                {
+                    ExpiredTime = expiredTime.EndTime,
+                    NearlyExpiredTime = expiredTime.NearlyExpiredTime,
+                    NearlyExpiredTimeOne = expiredTime.NearlyExpiredTimeOne
+                })
                     .Where(o => o.Id == order.Id).ExecuteCommandAsync(HttpContext.RequestAborted);
                 var orderDto = _mapper.Map<OrderDto>(order);
                 await _capPublisher.PublishAsync(Hotline.Share.Mq.EventNames.HotlineOrderExpiredTimeUpdate, orderDto,
@@ -5019,10 +5078,13 @@ public class OrderController : BaseController
                 ? EProcessType.Zhiban
                 : EProcessType.Jiaoban;
             await _orderRepository.Updateable().SetColumns(o => new Orders.Order()
-                {
-                    ExpiredTime = expiredTime.ExpiredTime, NearlyExpiredTime = expiredTime.NearlyExpiredTime,
-                    NearlyExpiredTimeOne = expiredTime.NearlyExpiredTimeOne, ProcessType = processType, Status = EOrderStatus.Special
-                })
+            {
+                ExpiredTime = expiredTime.ExpiredTime,
+                NearlyExpiredTime = expiredTime.NearlyExpiredTime,
+                NearlyExpiredTimeOne = expiredTime.NearlyExpiredTimeOne,
+                ProcessType = processType,
+                Status = EOrderStatus.Special
+            })
                 .Where(o => o.Id == order.Id).ExecuteCommandAsync(HttpContext.RequestAborted);
             var orderDto = _mapper.Map<OrderDto>(order);
             await _capPublisher.PublishAsync(Hotline.Share.Mq.EventNames.HotlineOrderExpiredTimeUpdate, orderDto,
@@ -5139,10 +5201,13 @@ public class OrderController : BaseController
                     ? EProcessType.Zhiban
                     : EProcessType.Jiaoban;
                 await _orderRepository.Updateable().SetColumns(o => new Orders.Order()
-                    {
-                        ExpiredTime = expiredTime.ExpiredTime, NearlyExpiredTime = expiredTime.NearlyExpiredTime,
-                        NearlyExpiredTimeOne = expiredTime.NearlyExpiredTimeOne, ProcessType = processType, Status = EOrderStatus.Special
-                    })
+                {
+                    ExpiredTime = expiredTime.ExpiredTime,
+                    NearlyExpiredTime = expiredTime.NearlyExpiredTime,
+                    NearlyExpiredTimeOne = expiredTime.NearlyExpiredTimeOne,
+                    ProcessType = processType,
+                    Status = EOrderStatus.Special
+                })
                     .Where(o => o.Id == order.Id).ExecuteCommandAsync(HttpContext.RequestAborted);
                 var orderDto = _mapper.Map<OrderDto>(order);
                 await _capPublisher.PublishAsync(Hotline.Share.Mq.EventNames.HotlineOrderExpiredTimeUpdate, orderDto,
@@ -5295,7 +5360,7 @@ public class OrderController : BaseController
                 d => d.Title.Contains(dto.Keyword!) || d.No.Contains(dto.Keyword!))
             //.WhereIF(!string.IsNullOrEmpty(dto.Content), d => d.Content.Contains(dto.Content!))
             .WhereIF(!string.IsNullOrEmpty(dto.AcceptType), d => d.AcceptTypeCode == dto.AcceptType) //受理类型
-            //.WhereIF(dto.AcceptTypes.Any(), d => dto.AcceptTypes.Contains(d.AcceptTypeCode)) //受理类型
+                                                                                                     //.WhereIF(dto.AcceptTypes.Any(), d => dto.AcceptTypes.Contains(d.AcceptTypeCode)) //受理类型
             .WhereIF(!string.IsNullOrEmpty(dto.Channel), d => d.SourceChannelCode == dto.Channel) //来源渠道
             .WhereIF(!string.IsNullOrEmpty(dto.Hotspot), d => d.HotspotSpliceName != null && d.HotspotSpliceName.Contains(dto.Hotspot))
             .WhereIF(!string.IsNullOrEmpty(dto.TransferPhone), d => d.TransferPhone.Contains(dto.TransferPhone!))
@@ -5386,8 +5451,8 @@ public class OrderController : BaseController
             SpecialReason = _sysDicDataCacheManager.GetSysDicDataCache(SysDicTypeConsts.SpecialReason),
             InstaShotSpecialReason = _sysDicDataCacheManager.GetSysDicDataCache(SysDicTypeConsts.InstaShotSpecialReason),
             Step = step,
-			IsTerminate = await _orderTerminateRepository.Queryable().Where(d=>d.OrderId == order.Id && d.Status == ETerminateStatus.End).AnyAsync(),
-			BaseTypeId = baseTypeId
+            IsTerminate = await _orderTerminateRepository.Queryable().Where(d => d.OrderId == order.Id && d.Status == ETerminateStatus.End).AnyAsync(),
+            BaseTypeId = baseTypeId
         };
         return rsp;
     }
@@ -5416,7 +5481,7 @@ public class OrderController : BaseController
                 : _sysDicDataCacheManager.GetSysDicDataCache(SysDicTypeConsts.SpecialReason),
             ReTransactErrorType = _sysDicDataCacheManager.GetSysDicDataCache(SysDicTypeConsts.ReTransactErrorType),
             IsTerminate = await _orderTerminateRepository.Queryable().Where(d => d.OrderId == order.Id && d.Status == ETerminateStatus.End).AnyAsync(),
-			Step = step,
+            Step = step,
             Orgs = orgs,
         };
         return rsp;
@@ -6422,7 +6487,11 @@ public class OrderController : BaseController
 
             return new
             {
-                Count = count, ErrorCount = errorCount, AddCount = addCount, ModifyCount = modifyCount, ErrorMessage = errorMessage.ToString()
+                Count = count,
+                ErrorCount = errorCount,
+                AddCount = addCount,
+                ModifyCount = modifyCount,
+                ErrorMessage = errorMessage.ToString()
             };
         }
     }
@@ -7023,7 +7092,7 @@ public class OrderController : BaseController
         {
             await _orderRepository.Updateable()
                 .SetColumns(o => new Orders.Order()
-                    { SignerId = dto.Handler.UserId, SignerName = dto.Handler.Username, Status = EOrderStatus.HandOverToUnAccept })
+                { SignerId = dto.Handler.UserId, SignerName = dto.Handler.Username, Status = EOrderStatus.HandOverToUnAccept })
                 .Where(o => o.Id == dto.OrderId).ExecuteCommandAsync(HttpContext.RequestAborted);
         }
         else

+ 1 - 1
src/Hotline.Api/Controllers/OrderTerminateController.cs

@@ -150,7 +150,7 @@ namespace Hotline.Api.Controllers
 		//[Permission(EPermission.ApplyTerminate)]
 		[HttpPost("startflow")]
 		[LogFilter("开始工单终止流程")]
-		public async Task StartFlow([FromBody] TerminateStartFlowDto dto)
+		public async Task StartFlow([FromBody] StartWorkflowDto<OrderTerminateDto> dto)
 		{
 			var screenAny = await _orderTerminateRepository.AnyAsync(x =>
 				x.OrderId == dto.Data.OrderId && x.Status == ETerminateStatus.Approval);

+ 8 - 0
src/Hotline.Api/Controllers/OrgController.cs

@@ -198,6 +198,14 @@ namespace Hotline.Api.Controllers
             }
         }
 
+        [HttpGet("cascade")]
+        public async Task<IReadOnlyList<SystemOrganize>> GetOrgsCascade([FromQuery]string? id)
+        {
+            return await _systemOrganizeRepository.Queryable()
+                .OrderBy(d => d.Id)
+                .ToTreeAsync(it => it.Children, it => it.ParentId, id);
+        }
+
 
         /// <summary>
         /// 新增页面基础数据

+ 66 - 2
src/Hotline.Api/Controllers/PushMessageController.cs

@@ -1,4 +1,5 @@
-using DotNetCore.CAP;
+using Amazon.Runtime.Internal.Transform;
+using DotNetCore.CAP;
 using Hotline.Permissions;
 using Hotline.Push;
 using Hotline.Push.FWMessage;
@@ -8,10 +9,15 @@ using Hotline.Share.Dtos.Push;
 using Hotline.Share.Dtos.Push.FWMessage;
 using Hotline.Share.Enums.Push;
 using Hotline.Share.Mq;
+using Hotline.Share.Tools;
+using Hotline.Users;
+using J2N.Text;
 using MapsterMapper;
 using Microsoft.AspNetCore.Authorization;
 using Microsoft.AspNetCore.Mvc;
 using Microsoft.IdentityModel.Tokens;
+using Swashbuckle.AspNetCore.SwaggerGen;
+using System.Text;
 using XF.Domain.Exceptions;
 using XF.Domain.Repository;
 using XF.Utility.EnumExtensions;
@@ -29,6 +35,7 @@ namespace Hotline.Api.Controllers
         private readonly IRepository<BatchSmsTask> _batchSmsTaskRepository;
         private readonly IRepository<BatchSmsTaskDetail> _batchSmsTaskDetailRepository;
         private readonly ICapPublisher _capPublisher;
+        private readonly IRepository<User> _userRepository;
 
         /// <summary>
         /// 
@@ -43,7 +50,8 @@ namespace Hotline.Api.Controllers
             IMessageCodeDomainService messageCodeDomainService,
             IRepository<BatchSmsTask> batchSmsTaskRepository,
             IRepository<BatchSmsTaskDetail> batchSmsTaskDetailRepository,
-            ICapPublisher capPublisher)
+            ICapPublisher capPublisher,
+            IRepository<User> userRepository)
         {
             _messageRepository = messageRepository;
             _mapper = mapper;
@@ -52,6 +60,7 @@ namespace Hotline.Api.Controllers
             _batchSmsTaskRepository = batchSmsTaskRepository;
             _batchSmsTaskDetailRepository = batchSmsTaskDetailRepository;
             _capPublisher = capPublisher;
+            _userRepository = userRepository;
         }
 
         #endregion
@@ -114,6 +123,61 @@ namespace Hotline.Api.Controllers
             await _pushDomainService.PushMsgAsync(dto, HttpContext.RequestAborted);
         }
 
+        /// <summary>
+        /// 发送短信
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [HttpPost("send")]
+        public async Task<string> SendMessage([FromBody] MessageInDto dto)
+        {
+            var telNumbers = dto.TelNumbers.Split(",");
+            var count = 0;
+            var errorSB = new StringBuilder("失败原因: ");
+            var errorCount = 0;
+            for (int i = 0;i < telNumbers.Length;i++)
+            {
+                var telNumber = telNumbers[i];
+                if (telNumber.IsPhoneNumber() == false)
+                {
+                    errorCount++;
+                    errorSB.Append($" {telNumber}不是合法的手机号码.");
+                    continue;
+                }
+
+                var inDto = new MessageDto
+                {
+                    TelNumber = telNumber,
+                    Content = dto.Content,
+                    PushBusiness = EPushBusiness.ManualSms,
+                    Name = "",
+                };
+                try
+                {
+                    await _pushDomainService.PushMsgAsync(inDto, HttpContext.RequestAborted);
+                }
+                catch (Exception e)
+                {
+                    errorSB.Append(e.Message);
+                    errorCount++;
+                    continue;
+                }
+                count++;
+            }
+            return $"成功发送: {count}, 失败: {errorCount}. " + (errorCount != 0 ? "" : errorSB.ToString());
+        }
+
+        /// <summary>
+        /// 手动发送短信页面基础数据
+        /// </summary>
+        /// <returns></returns>
+        [HttpGet("send/basedata")]
+        public async Task<dynamic> SendMessageBaseDataAsync()
+        {
+            var users = await _userRepository.Queryable()
+                .Select(m => new {m.Id,  m.PhoneNo, m.Name }).ToListAsync();
+            return new Dictionary<string, dynamic>() { { "contacts", users } };
+        }
 
         #endregion
 

+ 60 - 7
src/Hotline.Api/Controllers/WebPortalController.cs

@@ -42,6 +42,7 @@ namespace Hotline.Api.Controllers
         private readonly ISessionContext _sessionContext;
         private readonly IRepository<OrderVisitDetail> _orderVisitDetailRepository;
         private readonly IBulletinApplication _bulletinApplication;
+        private readonly IRepository<OldPublicData> _oldPublicDataRepository;
 
         public WebPortalController(IMapper mapper,
             IMediator mediator,
@@ -58,7 +59,8 @@ namespace Hotline.Api.Controllers
            IOrderApplication orderApplication,
            ISessionContext sessionContext,
             IRepository<OrderVisitDetail> orderVisitDetailRepository,
-            IBulletinApplication bulletinApplication
+            IBulletinApplication bulletinApplication,
+             IRepository<OldPublicData> oldPublicDataRepository
             )
         {
             _mapper = mapper;
@@ -77,6 +79,7 @@ namespace Hotline.Api.Controllers
             _sessionContext = sessionContext;
             _orderVisitDetailRepository = orderVisitDetailRepository;
             _bulletinApplication = bulletinApplication;
+            _oldPublicDataRepository = oldPublicDataRepository;
         }
 
         #region 通知
@@ -543,18 +546,29 @@ namespace Hotline.Api.Controllers
         [AllowAnonymous]
         public async Task<OpenResponse> GetOrderListByNum([FromBody] QueryOrderListByNumDto dto)
         {
-            var items = await _orderPublishRepository.Queryable()
+            var queryNew = _orderPublishRepository.Queryable()
                .Includes(p => p.Order)
                 .Where(p => p.PublishState == true)
                 .Where(p => p.Order.Id != null)
                 .OrderByDescending(p => p.CreationTime)
-               .Select(p => new
+               .Select(p => new DataListTopDto
                {
                    DataID = p.OrderId,
                    Title = p.Order.Title,
                    CreateDate = p.CreationTime
-               })
-                .Take(dto.Num)
+               });
+
+            var queryold = _oldPublicDataRepository.Queryable()
+                      .OrderByDescending(p => p.PubDate)
+                       .Select(p => new DataListTopDto
+                       {
+                           DataID = p.OrderId,
+                           Title = p.Title,
+                           CreateDate = p.PubDate
+                       });
+            var items = await _orderRepository.UnionAll(queryNew, queryold)
+              .OrderByDescending(p => p.CreateDate)
+               .Take(dto.Num)
                 .ToListAsync();
             var data = _mapper.Map<IReadOnlyList<DataListTopDto>>(items);
             return OpenResponse.Ok(WebPortalDeResponse<IReadOnlyList<DataListTopDto>>.Success(data, "成功"));
@@ -570,7 +584,8 @@ namespace Hotline.Api.Controllers
         public async Task<OpenResponse> GetOrderList([FromBody] QueryOrderListDto dto)
         {
             RefAsync<int> total = 0;
-            var items = await _orderPublishRepository.Queryable()
+
+            var queryNew = _orderPublishRepository.Queryable()
                  .Includes(d => d.Order)
                 .Where(d => d.PublishState == true && d.Order.Id != null)
                 .WhereIF(!string.IsNullOrEmpty(dto.FlowCode), d => d.Order.No == dto.FlowCode)
@@ -581,7 +596,45 @@ namespace Hotline.Api.Controllers
                 .WhereIF(!string.IsNullOrEmpty(dto.FlowEDate), d => d.Order.StartTime <= DateTime.Parse(DateTime.Parse(dto.FlowEDate).ToString("yyyy-MM-dd 00:00:00")))// dto.FlowEDate
                 .WhereIF(!string.IsNullOrEmpty(dto.FlowFrom), d => d.Order.FromName.Contains(dto.FlowFrom))
                 .OrderByDescending(p => p.CreationTime)
-                .ToPageListAsync(dto.PageIndex, dto.PageSize, total);
+               .Select(p => new OrderListDto
+               {
+                   FlowID = p.OrderId,
+                   FlowCode = p.No,
+                   FlowPwd = p.Order.Password,
+                   FlowTitle = p.Order.Title,
+                   FlowFromName = p.Order.SourceChannel,
+                   FlowPurTypeName = p.Order.AcceptType,
+                   ConTypeName = p.Order.HotspotName,
+                   FlowAddDate = p.Order.CreationTime,
+                   RSFlagName = p.Order.Status >= EOrderStatus.Filed ? "办理完成" : "办理中",
+                   PubDate = p.CreationTime
+               });
+
+            var queryold = _oldPublicDataRepository.Queryable()
+                       .WhereIF(!string.IsNullOrEmpty(dto.FlowCode), d => d.OrderNo == dto.FlowCode)
+                .WhereIF(!string.IsNullOrEmpty(dto.FlowName), d => d.Title.Contains(dto.FlowName))
+                .WhereIF(!string.IsNullOrEmpty(dto.FlowSType), d => d.AcceptTypeCode == dto.FlowSType)
+                .WhereIF(!string.IsNullOrEmpty(dto.FlowSDate), d => d.PubDate >= DateTime.Parse(DateTime.Parse(dto.FlowSDate).ToString("yyyy-MM-dd 00:00:00")))//dto.FlowSDate
+                .WhereIF(!string.IsNullOrEmpty(dto.FlowEDate), d => d.PubDate <= DateTime.Parse(DateTime.Parse(dto.FlowEDate).ToString("yyyy-MM-dd 00:00:00")))// dto.FlowEDate
+                .WhereIF(!string.IsNullOrEmpty(dto.FlowFrom), d => d.FromName.Contains(dto.FlowFrom))
+                      .OrderByDescending(p => p.PubDate)
+                       .Select(p => new OrderListDto
+                       {
+                           FlowID = p.OrderId,
+                           FlowCode = p.OrderNo,
+                           FlowPwd = p.OrderPwd,
+                           FlowTitle = p.Title,
+                           FlowFromName = p.SourceChannel,
+                           FlowPurTypeName = p.AcceptType,
+                           ConTypeName = p.HotspotName,
+                           FlowAddDate = p.AcceptTime,
+                           RSFlagName = p.State == "1" ? "办理完成" : "办理中",
+                           PubDate = p.PubDate
+                       });
+
+            var items = await _orderRepository.UnionAll(queryNew, queryold)
+            .OrderByDescending(p => p.PubDate)
+              .ToPageListAsync(dto.PageIndex, dto.PageSize, total);
 
             //计算总页数
             int nPageCount = Convert.ToInt32(Math.Ceiling(Convert.ToDouble(total) / dto.PageSize));

BIN
src/Hotline.Api/Template/AssignmentForm.doc


+ 2 - 2
src/Hotline.Api/config/appsettings.Development.json

@@ -91,13 +91,13 @@
     }
   },
   "ConnectionStrings": {
-    "Hotline": "PORT=5432;DATABASE=hotline;HOST=110.188.24.182;PASSWORD=fengwo11!!;USER ID=dev;"
+    "Hotline": "PORT=5432;DATABASE=hotline_dev;HOST=110.188.24.182;PASSWORD=fengwo11!!;USER ID=dev;"
   },
   "Cache": {
     "Host": "110.188.24.182",
     "Port": 50179,
     "Password": "fengwo123!$!$",
-    "Database": 3 //release:3, dev:5
+    "Database": 5 //release:3, dev:5
   },
   "Swagger": true,
   "Cors": {

+ 2 - 1
src/Hotline.Api/config/appsettings.json

@@ -24,7 +24,8 @@
       "Ip": "222.213.23.229"
     },
     "XingTang": {
-      "DbConnectionString": "server=123.56.10.71;Database=callcenter_db;Uid=root;Pwd=Lhw1981!(*!"
+      //"DbConnectionString": "server=123.56.10.71;Database=callcenter_db;Uid=root;Pwd=Lhw1981!(*!"
+      "DbConnectionString": "server=110.188.24.182;Database=callcenter_db;Uid=dev;Pwd=fengwo123!@#"
     }
   },
   "ConnectionStrings": {

+ 18 - 18
src/Hotline.Application.Contracts/Validators/Order/OrderStartFlowDtoValidator.cs

@@ -1,18 +1,18 @@
-using FluentValidation;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using Hotline.Share.Dtos.Order;
-
-namespace Hotline.Application.Contracts.Validators.Order
-{
-    public class OrderStartFlowDtoValidator : AbstractValidator<OrderStartFlowDto>
-    {
-        public OrderStartFlowDtoValidator()
-        {
-            RuleFor(d => d.Data).NotEmpty();
-        }
-    }
-}
+// using FluentValidation;
+// using System;
+// using System.Collections.Generic;
+// using System.Linq;
+// using System.Text;
+// using System.Threading.Tasks;
+// using Hotline.Share.Dtos.Order;
+//
+// namespace Hotline.Application.Contracts.Validators.Order
+// {
+//     public class OrderStartFlowDtoValidator : AbstractValidator<OrderStartFlowDto>
+//     {
+//         public OrderStartFlowDtoValidator()
+//         {
+//             RuleFor(d => d.Data).NotEmpty();
+//         }
+//     }
+// }

+ 14 - 14
src/Hotline.Application.Tests/Application/OrderApplicationTest.cs

@@ -76,21 +76,21 @@ public class OrderApplicationTest
             .ExecuteCommandAsync();
         var time = DateTime.Now;
         var dicSystem = _systemDicDataCacheManager.GetSysDicDataCache(SysDicTypeConsts.TranspondCity).First();
-        var dto = new NextWorkflowDto
+        var dto = new NextWorkflowDto<OrderHandleFlowDto>
         {
-            RealCommunicationAddress = realCommunicationAddress,
-            WorkflowId = workflowId,
-            RealHandlerName = realHandlerName,
-            RealHandlerPhone = realHandlerPhone,
-            RealCommunicationMode = ERealCommunicationMode.Locale,
-            RealCommunicationTime = time,
-            RealIsContacted = true,
-            RealContactLocale = true,
-            IsOther = true,
-            OtherRemark = "备注",
-            TranspondCityId = dicSystem.Id,
-            TranspondCityName = dicSystem.DicDataName,
-            TranspondCityValue = dicSystem.DicDataValue,
+            // RealCommunicationAddress = realCommunicationAddress,
+            // WorkflowId = workflowId,
+            // RealHandlerName = realHandlerName,
+            // RealHandlerPhone = realHandlerPhone,
+            // RealCommunicationMode = ERealCommunicationMode.Locale,
+            // RealCommunicationTime = time,
+            // RealIsContacted = true,
+            // RealContactLocale = true,
+            // IsOther = true,
+            // OtherRemark = "备注",
+            // TranspondCityId = dicSystem.Id,
+            // TranspondCityName = dicSystem.DicDataName,
+            // TranspondCityValue = dicSystem.DicDataValue,
         };
         var order = await _orderApplication.SaveOrderWorkflowInfo(dto, new CancellationToken());
         order = await _orderRepository.GetAsync(order.Id);

+ 1 - 0
src/Hotline.Application.Tests/Domain/ZiGongExpireTimeTest.cs

@@ -66,6 +66,7 @@ public class ZiGongExpireTimeTest
 
     [Theory]
     [InlineData("求助三个工作日", "2024-09-12 22:01:28", "CenterToOrg", "2024-09-20 08:30:00")]
+    [InlineData("咨询小三问题", "2024-10-20 14:21:53", "CenterToCenter", "2024/10/21 14:21:53")]
     public async Task CalcExpiredTime_Release_Test(string title, string beginTxt, string flowTxt, string expected)
     {
         var beginTime = DateTime.Parse(beginTxt);

+ 20 - 11
src/Hotline.Application/FlowEngine/WorkflowApplication.cs

@@ -178,8 +178,7 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
         var counterSignType = _workflowDomainService.GetCounterSignType(dto.IsStartCountersign);
 
         //办理开始节点
-        await _workflowDomainService.HandleStepAsync(startStep, workflow, dto, flowAssignInfo.FlowAssignType,
-            counterSignType, expiredTime, cancellationToken);
+        await _workflowDomainService.HandleStepAsync(startStep, workflow, dto, counterSignType, expiredTime, cancellationToken);
 
         if (dto.Files.Any())
             startStep.FileJson =
@@ -493,8 +492,7 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
 
         foreach (var step in unhandleSteps)
         {
-            await _workflowDomainService.HandleStepAsync(step, workflow, dto, null,
-                null, null, cancellationToken);
+            await _workflowDomainService.HandleStepAsync(step, workflow, dto, null, null, cancellationToken);
 
             var trace = unhandleTraces.First(d => d.StepId == step.Id);
             _mapper.Map(dto, trace);
@@ -606,8 +604,10 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
             //动态生成下一步
             var nextStepOption = await GetDynamicStepAsync(currentStep.InstancePolicy.Value, currentStep.StepType,
                 currentStep.BusinessType, settingHandle?.SettingValue[0], settingLead?.SettingValue[0], cancellationToken);
-            dto.Steps = new List<NextStepOption> { nextStepOption
-};
+            dto.Steps = new List<NextStepOption>
+            {
+                nextStepOption
+            };
             return dto;
         }
 
@@ -750,10 +750,11 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
             stepOption.InputRealHandler = false;
 
             //已归档工单,撤回至中心看作otc,撤回至部门看作cto
+            //todo 依据归档节点的busiType判断
             stepOption.FlowDirection = isWorkflowFiled
                 ? stepDefine.BusinessType is EBusinessType.Seat or EBusinessType.Send
                     ? EFlowDirection.FiledToCenter
-                    : stepDefine.BusinessType is EBusinessType.Department
+                    : stepDefine.BusinessType is EBusinessType.Department or EBusinessType.DepartmentLeader
                         ? EFlowDirection.FiledToOrg
                         : null
                 //: CheckFlowDirection(currentStep.BusinessType, stepDefine.BusinessType);
@@ -1292,13 +1293,15 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
                     isLead = _sessionContext.Roles.Any(x => x == leadRoleCode);
                     if (!isLead)
                     {
-                        isSkip = await _userRepository.Queryable().AnyAsync(x => x.OrgId == _sessionContext.RequiredOrgId && x.Roles.Any(r => r.Name == leadRoleCode), cancellationToken);
+                        isSkip = await _userRepository.Queryable()
+                            .AnyAsync(x => x.OrgId == _sessionContext.RequiredOrgId && x.Roles.Any(r => r.Name == leadRoleCode), cancellationToken);
                         if (isSkip)
                         {
                             roleId = leadRoleCode;
                             roleName = leadRoleName;
                         }
                     }
+
                     if (isLead || !isSkip)
                     {
                         //上级部门Id
@@ -1306,6 +1309,7 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
                         roleId = handleRoleCode;
                         roleName = handleRoleName;
                     }
+
                     items = await _organizeRepository.Queryable()
                         .Where(d => d.Id == upperOrgId)
                         .Select(d => new FlowStepHandler
@@ -1333,13 +1337,15 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
                 isLead = _sessionContext.Roles.Any(x => x == leadRoleCode);
                 if (!isLead)
                 {
-                    isSkip = await _userRepository.Queryable().AnyAsync(x => x.OrgId == _sessionContext.RequiredOrgId && x.Roles.Any(r => r.Name == leadRoleCode), cancellationToken);
+                    isSkip = await _userRepository.Queryable()
+                        .AnyAsync(x => x.OrgId == _sessionContext.RequiredOrgId && x.Roles.Any(r => r.Name == leadRoleCode), cancellationToken);
                     if (isSkip)
                     {
                         roleId = leadRoleCode;
                         roleName = leadRoleName;
                     }
                 }
+
                 if (isLead || !isSkip)
                 {
                     //上级部门Id
@@ -1347,6 +1353,7 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
                     roleId = handleRoleCode;
                     roleName = handleRoleName;
                 }
+
                 items = await _organizeRepository.Queryable()
                     .Where(d => d.Id == upperOrgId)
                     .Select(d => new FlowStepHandler
@@ -1383,7 +1390,9 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
                 businessType = _sessionContext.OrgIsCenter ? EBusinessType.Send : EBusinessType.Department;
                 orgLevel = _sessionContext.OrgIsCenter ? 0 : 1;
                 //上级部门Id
-                upperOrgId = _sessionContext.OrgIsCenter ? _sessionContext.RequiredOrgId.Substring(0, 3) : _sessionContext.RequiredOrgId.Substring(0, 6);
+                upperOrgId = _sessionContext.OrgIsCenter
+                    ? _sessionContext.RequiredOrgId.Substring(0, 3)
+                    : _sessionContext.RequiredOrgId.Substring(0, 6);
                 items = await _organizeRepository.Queryable()
                     .Where(d => d.Id == upperOrgId)
                     .Select(d => new FlowStepHandler
@@ -1462,7 +1471,6 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
     /// 会签 动态策略
     /// </summary>
     /// <returns></returns>
-
     private async Task<NextStepOption> GetDynamicStepAsync(
         EDynamicPolicyCountersign policy, EStepType stepType,
         EBusinessType currentBusinessType, CancellationToken cancellationToken)
@@ -1597,6 +1605,7 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
             Items = items
         };
     }
+
     /// <summary>
     /// 查询下一节点办理对象类型(user or org)及实际办理对象
     /// </summary>

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

@@ -78,7 +78,8 @@ public class WorkflowRecallHandler : INotificationHandler<RecallNotify>
                         withExtension: true, cancellationToken: cancellationToken);
                     //order.UpdateHandlingStatus(workflow.IsInCountersign);
                     _mapper.Map(workflow, order);
-                    if (notification.TargetStep.StepType is EStepType.Start)
+                    order.FileEmpty();
+					if (notification.TargetStep.StepType is EStepType.Start)
                     {
                         //if (!bool.TryParse(
                         //        _systemSettingCacheManager.GetSetting(SettingConstants.IsRecallToSeatDesignated)?.SettingValue[0],
@@ -86,7 +87,6 @@ public class WorkflowRecallHandler : INotificationHandler<RecallNotify>
                         //    isRecallToSeatDesignated = false;
                         //var isRecallToSeatDesignated = bool.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.IsRecallToSeatDesignated).SettingValue[0]);
                         order.Status = EOrderStatus.SpecialToUnAccept;
-                        order.FileEmpty();
                         //if (isRecallToSeatDesignated)
                         //{
                         //    if (data.HandlerType is EHandlerType.Role or EHandlerType.AssignedUser)

+ 37 - 0
src/Hotline.Application/Mappers/OrderMapperConfigs.cs

@@ -172,5 +172,42 @@ public class OrderMapperConfigs : IRegister
         config.ForType<OrderScreen, OrderScreenListDto>()
             .IgnoreIf((s, d) => s.VisitDetail == null, d => d.VisitDetail)
             ;
+        
+        config.ForType<OrderHandleFlowDto, Workflow>()
+            .Map(d => d.RealHandlerPhone, s => s.RealHandlerPhone)
+            .Map(d => d.RealHandlerName, s => s.RealHandlerName)
+            .Map(d => d.RealCommunicationMode, s => s.RealCommunicationMode)
+            .Map(d => d.RealCommunicationTime, s => s.RealCommunicationTime)
+            .Map(d => d.RealCommunicationAddress, s => s.RealCommunicationAddress)
+            .Map(d => d.RealIsContacted, s => s.RealIsContacted)
+            .Map(d => d.RealContactLocale, s => s.RealContactLocale)
+            .IgnoreNonMapped(true);
+
+        config.ForType<OrderHandleFlowDto, Order>()
+            .Map(src => src.RealCommunicationAddress, dest => dest.RealCommunicationAddress)
+            .IgnoreIf((src, dest) => string.IsNullOrEmpty(src.RealCommunicationAddress), dest => dest.RealCommunicationAddress)
+            .Map(src => src.RealHandlerPhone , dest => dest.RealHandlerPhone)
+            .IgnoreIf((src, dest) => string.IsNullOrEmpty(src.RealHandlerPhone), dest => dest.RealHandlerPhone)
+            .Map(src => src.RealHandlerName, dest => dest.RealHandlerName)
+            .IgnoreIf((src, dest) => string.IsNullOrEmpty(src.RealHandlerName), dest => dest.RealHandlerName)
+            .Map(src => src.RealCommunicationMode, dest => dest.RealCommunicationMode)
+            .IgnoreIf((src, dest) => src.RealCommunicationMode == null, dest => dest.RealCommunicationMode)
+            .Map(src => src.RealCommunicationTime, dest => dest.RealCommunicationTime)
+            .IgnoreIf((src, dest) => src.RealCommunicationTime == null, dest => dest.RealCommunicationTime)
+            .Map(src => src.RealIsContacted, dest => dest.RealIsContacted)
+            .IgnoreIf((src, dest) => src.RealIsContacted == null, dest => dest.RealIsContacted)
+            .Map(src => src.RealContactLocale, dest => dest.RealContactLocale)
+            .IgnoreIf((src, dest) => src.RealContactLocale == null, dest => dest.RealContactLocale)
+            .Map(src => src.IsOther, dest => dest.IsOther)
+            .IgnoreIf((src, dest) => src.IsOther == null, dest => dest.IsOther)
+            .Map(src => src.OtherRemark, dest => dest.OtherRemark)
+            .IgnoreIf((src, dest) => string.IsNullOrEmpty(src.OtherRemark), dest => dest.OtherRemark)
+            .Map(src => src.TranspondCityId, dest => dest.TranspondCityId)
+            .IgnoreIf((src, dest) => string.IsNullOrEmpty(src.TranspondCityId), dest => dest.TranspondCityId)
+            .Map(src => src.TranspondCityName, dest => dest.TranspondCityName)
+            .IgnoreIf((src, dest) => string.IsNullOrEmpty(src.TranspondCityName), dest => dest.TranspondCityName)
+            .Map(src => src.TranspondCityValue, dest => dest.TranspondCityValue)
+            .IgnoreIf((src, dest) => string.IsNullOrEmpty(src.TranspondCityValue), dest => dest.TranspondCityValue)
+            .IgnoreNonMapped(true);
     }
 }

+ 0 - 37
src/Hotline.Application/Mappers/WorkflowMapperConfigs.cs

@@ -71,43 +71,6 @@ public class WorkflowMapperConfigs : IRegister
             //.Ignore(d => d.Traces)
             ;
 
-        config.ForType<NextWorkflowDto, Workflow>()
-            .Map(d => d.RealHandlerPhone, s => s.RealHandlerPhone)
-            .Map(d => d.RealHandlerName, s => s.RealHandlerName)
-            .Map(d => d.RealCommunicationMode, s => s.RealCommunicationMode)
-            .Map(d => d.RealCommunicationTime, s => s.RealCommunicationTime)
-            .Map(d => d.RealCommunicationAddress, s => s.RealCommunicationAddress)
-            .Map(d => d.RealIsContacted, s => s.RealIsContacted)
-            .Map(d => d.RealContactLocale, s => s.RealContactLocale)
-            .IgnoreNonMapped(true);
-
-        config.ForType<NextWorkflowDto, Order>()
-            .Map(src => src.RealCommunicationAddress, dest => dest.RealCommunicationAddress)
-            .IgnoreIf((src, dest) => src.RealCommunicationAddress.IsNullOrEmpty(), dest => dest.RealCommunicationAddress)
-            .Map(src => src.RealHandlerPhone , dest => dest.RealHandlerPhone)
-            .IgnoreIf((src, dest) => src.RealHandlerPhone.IsNullOrEmpty(), dest => dest.RealHandlerPhone)
-            .Map(src => src.RealHandlerName, dest => dest.RealHandlerName)
-            .IgnoreIf((src, dest) => src.RealHandlerName.IsNullOrEmpty(), dest => dest.RealHandlerName)
-            .Map(src => src.RealCommunicationMode, dest => dest.RealCommunicationMode)
-            .IgnoreIf((src, dest) => src.RealCommunicationMode == null, dest => dest.RealCommunicationMode)
-            .Map(src => src.RealCommunicationTime, dest => dest.RealCommunicationTime)
-            .IgnoreIf((src, dest) => src.RealCommunicationTime == null, dest => dest.RealCommunicationTime)
-            .Map(src => src.RealIsContacted, dest => dest.RealIsContacted)
-            .IgnoreIf((src, dest) => src.RealIsContacted == null, dest => dest.RealIsContacted)
-            .Map(src => src.RealContactLocale, dest => dest.RealContactLocale)
-            .IgnoreIf((src, dest) => src.RealContactLocale == null, dest => dest.RealContactLocale)
-            .Map(src => src.IsOther, dest => dest.IsOther)
-            .IgnoreIf((src, dest) => src.IsOther == null, dest => dest.IsOther)
-            .Map(src => src.OtherRemark, dest => dest.OtherRemark)
-            .IgnoreIf((src, dest) => src.OtherRemark.IsNullOrEmpty(), dest => dest.OtherRemark)
-            .Map(src => src.TranspondCityId, dest => dest.TranspondCityId)
-            .IgnoreIf((src, dest) => src.TranspondCityId.IsNullOrEmpty(), dest => dest.TranspondCityId)
-            .Map(src => src.TranspondCityName, dest => dest.TranspondCityName)
-            .IgnoreIf((src, dest) => src.TranspondCityName.IsNullOrEmpty(), dest => dest.TranspondCityName)
-            .Map(src => src.TranspondCityValue, dest => dest.TranspondCityValue)
-            .IgnoreIf((src, dest) => src.TranspondCityValue.IsNullOrEmpty(), dest => dest.TranspondCityValue)
-            .IgnoreNonMapped(true);
-
         config.ForType<Workflow, WorkflowStep>()
             .Map(d => d.WorkflowId, s => s.Id)
             .Map(d => d.ExternalId, s => s.ExternalId)

+ 10 - 2
src/Hotline.Application/Orders/IOrderApplication.cs

@@ -122,7 +122,7 @@ namespace Hotline.Application.Orders
         /// </summary>
         /// <param name="dto"></param>
         /// <returns></returns>
-        Task<Order> SaveOrderWorkflowInfo(NextWorkflowDto dto, CancellationToken cancellationToken);
+        Task<Order> SaveOrderWorkflowInfo(NextWorkflowDto<OrderHandleFlowDto> dto, CancellationToken cancellationToken);
 
         #endregion
 
@@ -313,5 +313,13 @@ namespace Hotline.Application.Orders
         /// <returns></returns>
         ISugarQueryable<OrderTerminate> OrderTerminateList(OrderTerminateListDto dto);
 
-	}
+        /// <summary>
+        /// 甄别列表
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        ISugarQueryable<OrderScreen> OrderScreenList(ScreenListDto dto);
+
+        ISugarQueryable<OrderListOutDto> QueryWaitedForSeat(QueryOrderWaitedDto dto);
+    }
 }

+ 199 - 82
src/Hotline.Application/Orders/OrderApplication.cs

@@ -65,6 +65,7 @@ using Hotline.Share.Tools;
 using Hotline.EventBus;
 using Hotline.Orders.Notifications;
 using Hotline.OrderTranspond;
+using XF.Utility.EnumExtensions;
 using Newtonsoft.Json;
 
 namespace Hotline.Application.Orders;
@@ -80,8 +81,8 @@ public class OrderApplication : IOrderApplication, IScopeDependency
     private readonly IRepository<OrderVisitDetail> _orderVisitedDetailRepository;
     private readonly IOrderDomainService _orderDomainService;
     private readonly IWorkflowDomainService _workflowDomainService;
-
-    private readonly IOrderRepository _orderRepository;
+    private readonly ISessionContext _sessionContext;
+	private readonly IOrderRepository _orderRepository;
 
     //private readonly ITimeLimitDomainService _timeLimitDomainService;
     private readonly IMapper _mapper;
@@ -102,7 +103,7 @@ public class OrderApplication : IOrderApplication, IScopeDependency
     private readonly IRepository<WorkflowTrace> _workflowTraceRepository;
     private readonly IRepository<SystemDicData> _systemDicDataRepository;
     private readonly IRepository<OrderPublish> _orderPublishRepository;
-    private readonly IRepository<OrderScreen> _orderScreenRepository;
+    private readonly IOrderScreenRepository _orderScreenRepository;
     private readonly IRepository<OrderSendBackAudit> _orderSendBackAuditRepository;
     private readonly ICalcExpireTime _expireTime;
     private readonly IRepository<OrderObserve> _orderObserveRepository;
@@ -132,7 +133,7 @@ public class OrderApplication : IOrderApplication, IScopeDependency
         IRepository<SystemDicData> systemDicDataRepository,
         IRepository<WorkflowTrace> workflowTraceRepository,
         IRepository<OrderPublish> orderPublishRepository,
-        IRepository<OrderScreen> orderScreenRepository,
+        IOrderScreenRepository orderScreenRepository,
         IRepository<OrderSendBackAudit> orderSendBackAuditRepository,
         ICalcExpireTime expireTime,
         IMediator mediator,
@@ -179,6 +180,7 @@ public class OrderApplication : IOrderApplication, IScopeDependency
         _transpondCityRawDataRepository = transpondCityRawDataRepository;
         _orderObserveRepository = orderObserveRepository;
         _orderTerminateRepository = orderTerminateRepository;
+        _sessionContext = sessionContext;
 	}
 
     /// <summary>
@@ -432,7 +434,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 })
@@ -659,7 +661,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);
@@ -703,10 +705,10 @@ public class OrderApplication : IOrderApplication, IScopeDependency
     /// </summary>
     /// <param name="dto"></param>
     /// <returns></returns>
-    public async Task<Order> SaveOrderWorkflowInfo(NextWorkflowDto dto, CancellationToken cancellationToken)
+    public async Task<Order> SaveOrderWorkflowInfo(NextWorkflowDto<OrderHandleFlowDto> dto, CancellationToken cancellationToken)
     {
         var order = await _orderRepository.Queryable()
-                        .FirstAsync(d => d.WorkflowId == dto.WorkflowId, cancellationToken)
+                        .FirstAsync(d => d.Id == dto.Data.OrderId, cancellationToken)
                     ?? throw UserFriendlyException.SameMessage("无效工单编号");
         if (await _orderSendBackAuditRepository.AnyAsync(x => x.OrderId == order.Id && x.State == ESendBackAuditState.Apply,
                 cancellationToken))
@@ -715,7 +717,7 @@ public class OrderApplication : IOrderApplication, IScopeDependency
         }
 
         ExpiredTimeWithConfig? expiredTimeConfig = null;
-        if (dto.NextHandlers.Any(d => d.Key == "001171" || d.Key == "001178"))
+        if (dto.WorkflowDto.NextHandlers.Any(d => d.Key == "001171" || d.Key == "001178"))
         {
             var timeResult = await _expireTime.CalcEndTime(DateTime.Now, ETimeType.WorkDay, 45, 80, 50);
             expiredTimeConfig = new ExpiredTimeWithConfig
@@ -731,7 +733,7 @@ public class OrderApplication : IOrderApplication, IScopeDependency
             order.CenterToOrg(
                 expiredTimeConfig.TimeText, expiredTimeConfig.Count,
                 expiredTimeConfig.TimeType, expiredTimeConfig.ExpiredTime,
-                expiredTimeConfig.NearlyExpiredTime, expiredTimeConfig.NearlyExpiredTimeOne, dto.Opinion,
+                expiredTimeConfig.NearlyExpiredTime, expiredTimeConfig.NearlyExpiredTimeOne, dto.WorkflowDto.Opinion,
                 _sessionContextProvider.SessionContext.RequiredUserId, _sessionContextProvider.SessionContext.UserName,
                 canUpdateOrderSender);
             //TODO发送短信即将超期
@@ -740,14 +742,14 @@ public class OrderApplication : IOrderApplication, IScopeDependency
             _capPublisher.PublishDelay(expiredTimeConfig.ExpiredTime - DateTime.Now.AddHours(1), EventNames.HotlineOrderAutomaticDelay,
                 new PublishAutomaticDelayDto() { OrderId = order.Id });
         }
-        else if (dto.FlowDirection is EFlowDirection.CenterToOrg)
+        else if (dto.WorkflowDto.FlowDirection is EFlowDirection.CenterToOrg)
         {
             expiredTimeConfig = await _expireTime.CalcExpiredTime(DateTime.Now, EFlowDirection.CenterToOrg, order.Adapt<OrderTimeClacInfo>());
             var canUpdateOrderSender = bool.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.CanUpdateOrderSender).SettingValue[0]);
             order.CenterToOrg(
                 expiredTimeConfig.TimeText, expiredTimeConfig.Count,
                 expiredTimeConfig.TimeType, expiredTimeConfig.ExpiredTime,
-                expiredTimeConfig.NearlyExpiredTime, expiredTimeConfig.NearlyExpiredTimeOne, dto.Opinion,
+                expiredTimeConfig.NearlyExpiredTime, expiredTimeConfig.NearlyExpiredTimeOne, dto.WorkflowDto.Opinion,
                 _sessionContextProvider.SessionContext.RequiredUserId, _sessionContextProvider.SessionContext.UserName,
                 canUpdateOrderSender);
             //TODO发送短信即将超期
@@ -756,7 +758,7 @@ public class OrderApplication : IOrderApplication, IScopeDependency
             _capPublisher.PublishDelay(expiredTimeConfig.ExpiredTime - DateTime.Now.AddHours(1), EventNames.HotlineOrderAutomaticDelay,
                 new PublishAutomaticDelayDto() { OrderId = order.Id });
         }
-        else if (dto.FlowDirection is EFlowDirection.CenterToCenter)
+        else if (dto.WorkflowDto.FlowDirection is EFlowDirection.CenterToCenter)
         {
             if (_appOptions.Value.IsZiGong == false)
             {
@@ -771,35 +773,24 @@ public class OrderApplication : IOrderApplication, IScopeDependency
                     new PublishAutomaticDelayDto() { OrderId = order.Id });
             }
         }
-        else if (dto.FlowDirection is EFlowDirection.CenterToFile) //中心直接归档,中心意见为实际办理意见
+        else if (dto.WorkflowDto.FlowDirection is EFlowDirection.CenterToFile) //中心直接归档,中心意见为实际办理意见
         {
-            order.CenterToOrgOpinion = dto.Opinion;
+            order.CenterToOrgOpinion = dto.WorkflowDto.Opinion;
             await _orderRepository.UpdateAsync(order, cancellationToken);
         }
 
         if (expiredTimeConfig is not null)
             _mapper.Map(expiredTimeConfig, order);
 
-        if (dto.LeaderSMSKey != null)
+        if (dto.Data.LeaderSMSKey != null)
         {
-            var dic = _sysDicDataCacheManager.GetSysDicDataCache(SysDicTypeConsts.LeaderSMS).First(m => m.Id == dto.LeaderSMSKey);
+            var dic = _sysDicDataCacheManager.GetSysDicDataCache(SysDicTypeConsts.LeaderSMS).First(m => m.Id == dto.Data.LeaderSMSKey);
             _capPublisher.Publish(EventNames.HotlineLeaderSMS, new PublishLeaderSMSDto(order.Id, dic.DicDataName, dic.DicDataValue));
         }
 
-        _mapper.Map(dto, order);
+        _mapper.Map(dto.Data, order);
         await _orderRepository.UpdateAsync(order, cancellationToken);
 
-        if (_appOptions.Value.IsZiGong && dto.Transpond.HasValue && dto.Transpond.Value == true)
-        {
-            var count = await _transpondCityRawDataRepository.Queryable()
-                .Where(m => m.OrderCode == order.No && m.CityName == order.TranspondCityName
-                                                    && m.Direction == ETranspondDirection.Out)
-                .CountAsync();
-            //处理市州互转
-            if (count == 0)
-                await _publisher.PublishAsync(new OrderStartWorkflowNotify(order.Id), PublishStrategy.ParallelWhenAll, cancellationToken);
-        }
-
         return order;
     }
 
@@ -857,7 +848,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);
@@ -987,30 +978,30 @@ public class OrderApplication : IOrderApplication, IScopeDependency
             .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(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(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(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), 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.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(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)) //无甄别
@@ -1025,8 +1016,8 @@ public class OrderApplication : IOrderApplication, IScopeDependency
                      (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(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))
@@ -1046,6 +1037,7 @@ public class OrderApplication : IOrderApplication, IScopeDependency
                 d => d.Title.Contains(dto.ContentRetrieval) || d.Content.Contains(dto.ContentRetrieval))
             .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)
             .OrderByDescending(d => d.CreationTime);
     }
 
@@ -1340,9 +1332,9 @@ public class OrderApplication : IOrderApplication, IScopeDependency
         if (IsCenter && list != null)
         {
             data = await list.GroupBy(x => new
-                {
-                    VisitOrgCode = x.VisitOrgCode.Substring(SqlFunc.MappingColumn<int>("0"), SqlFunc.MappingColumn<int>("6"))
-                })
+            {
+                VisitOrgCode = x.VisitOrgCode.Substring(SqlFunc.MappingColumn<int>("0"), SqlFunc.MappingColumn<int>("6"))
+            })
                 .Select(x => new VisitAndOrgSatisfactionStatisticsDto()
                 {
                     OrgCode = x.VisitOrgCode.Substring(SqlFunc.MappingColumn<int>("0"), SqlFunc.MappingColumn<int>("6")),
@@ -1391,9 +1383,9 @@ public class OrderApplication : IOrderApplication, IScopeDependency
         else
         {
             data = await list.GroupBy(x => new
-                {
-                    x.VisitOrgCode
-                })
+            {
+                x.VisitOrgCode
+            })
                 .Select(x => new VisitAndOrgSatisfactionStatisticsDto()
                 {
                     OrgCode = x.VisitOrgCode,
@@ -1657,10 +1649,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());
             }
@@ -1672,10 +1664,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());
             }
@@ -1829,10 +1821,10 @@ public class OrderApplication : IOrderApplication, IScopeDependency
         if (IsCenter && list != null)
         {
             data = await list.GroupBy((x, h) => new
-                {
-                    HotspotName = h.HotSpotName,
-                    HotspotId = h.Id
-                })
+            {
+                HotspotName = h.HotSpotName,
+                HotspotId = h.Id
+            })
                 .Select((x, h) => new VisitAndHotspotSatisfactionStatisticsDto()
                 {
                     HotspotName = h.HotSpotName,
@@ -1867,10 +1859,10 @@ public class OrderApplication : IOrderApplication, IScopeDependency
         else
         {
             data = await list.GroupBy((x, h) => new
-                {
-                    HotspotName = h.HotSpotName,
-                    HotspotId = h.Id
-                })
+            {
+                HotspotName = h.HotSpotName,
+                HotspotId = h.Id
+            })
                 .Select((x, h) => new VisitAndHotspotSatisfactionStatisticsDto()
                 {
                     HotspotName = h.HotSpotName,
@@ -2266,15 +2258,83 @@ public class OrderApplication : IOrderApplication, IScopeDependency
         return query;
     }
 
+    #region 甄别
+    public ISugarQueryable<OrderScreen> OrderScreenList(ScreenListDto dto) {
+	    var handler = dto.TabStatus is EScreenStatus.Apply;
+	    var isAdmin = _orderDomainService.IsCheckAdmin();
+		ISugarQueryable<OrderScreen> query;
+
+		if (dto.source == 1)
+		{
+			query = _orderScreenRepository.Queryable(hasHandled: !handler, isAdmin: isAdmin);
+		}
+		else
+		{
+			query = _orderScreenRepository.Queryable(isAdmin: isAdmin)
+				.WhereIF(!isAdmin, x => x.CreatorOrgId.StartsWith(_sessionContext.RequiredOrgId));
+		}
+
+		query = query
+			.Includes(d => d.Order)
+			.Includes(d => d.VisitDetail)
+			.Includes(d => d.Visit, v => v.Order)
+			.Includes(d => d.Workflow)
+			.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.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.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(dto.CreationTime.HasValue && dto.EndCreationTime.HasValue,
+                x => x.Order!.CreationTime >= dto.CreationTime && x.Order!.CreationTime <= dto.EndCreationTime)
+            .WhereIF(dto.CurrentHandleTime.HasValue && dto.EndCurrentHandleTime.HasValue,
+                x => x.Order!.CurrentHandleTime >= dto.CurrentHandleTime && x.Order!.ActualHandleTime <= dto.EndCurrentHandleTime)
+            .WhereIF(dto.FiledTime.HasValue && dto.EndFiledTime.HasValue,
+                x => x.Order!.FiledTime >= dto.FiledTime && x.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);
+	}
+	#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)
+	#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("工单外部编号不能为空");
@@ -2449,20 +2509,20 @@ public class OrderApplication : IOrderApplication, IScopeDependency
             }).ToListAsync();
 
         var res = (from t1 in items
-            join t2 in items2 on t1.UserId equals t2.UserId into t1_t2
-            from item in t1_t2.DefaultIfEmpty()
-            select new SendOrderReportOutDto
-            {
-                UserId = t1.UserId,
-                UserName = t1.UserName,
-                SendOrderNum = t1.SendOrderNum,
-                NoSendOrderNum = t1.NoSendOrderNum,
-                ReSendOrderNum = t1_t2.Select(x => x.ReSendOrderNum).FirstOrDefault(),
-                ChainRate = t1_t2.Select(x => x.ReSendOrderNum).FirstOrDefault() > 0
-                    ? ((double.Parse(t1.SendOrderNum.ToString()) - double.Parse(t1_t2.Select(x => x.ReSendOrderNum).FirstOrDefault().ToString())) /
-                        double.Parse(t1.SendOrderNum.ToString()) * 100).ToString("F2") + "%"
-                    : "100.00%",
-            }).ToList();
+                   join t2 in items2 on t1.UserId equals t2.UserId into t1_t2
+                   from item in t1_t2.DefaultIfEmpty()
+                   select new SendOrderReportOutDto
+                   {
+                       UserId = t1.UserId,
+                       UserName = t1.UserName,
+                       SendOrderNum = t1.SendOrderNum,
+                       NoSendOrderNum = t1.NoSendOrderNum,
+                       ReSendOrderNum = t1_t2.Select(x => x.ReSendOrderNum).FirstOrDefault(),
+                       ChainRate = t1_t2.Select(x => x.ReSendOrderNum).FirstOrDefault() > 0
+                           ? ((double.Parse(t1.SendOrderNum.ToString()) - double.Parse(t1_t2.Select(x => x.ReSendOrderNum).FirstOrDefault().ToString())) /
+                               double.Parse(t1.SendOrderNum.ToString()) * 100).ToString("F2") + "%"
+                           : "100.00%",
+                   }).ToList();
         return res;
     }
 
@@ -2554,8 +2614,65 @@ public class OrderApplication : IOrderApplication, IScopeDependency
             .WhereIF(dto.Status.HasValue, d => d.Status == dto.Status)
             .WhereIF(dto.StartTime.HasValue && dto.EndTime.HasValue, d => d.Order.StartTime >= dto.StartTime && d.Order.StartTime <= dto.EndTime)
             .OrderByDescending(d => d.CreationTime);
+    }
+    #endregion
 
-	}
-	#endregion
+    public ISugarQueryable<OrderListOutDto> QueryWaitedForSeat(QueryOrderWaitedDto dto)
+    {
+        EOrderStatus[] handleStatuses = EnumExts.GetFields<EOrderStatus>().Select(d => (EOrderStatus)d.Key).ToArray();
+        handleStatuses = handleStatuses.WhereIF(dto.IsHandled.HasValue,
+                d => dto.IsHandled!.Value
+                    ? d is not EOrderStatus.WaitForAccept and not EOrderStatus.BackToUnAccept and not EOrderStatus.SpecialToUnAccept and not EOrderStatus.HandOverToUnAccept
+                    : d is EOrderStatus.WaitForAccept or EOrderStatus.BackToUnAccept or EOrderStatus.SpecialToUnAccept or EOrderStatus.HandOverToUnAccept)
+            .ToArray();
+
+        var query = _orderRepository.Queryable();
+        if (dto.IsHandled.HasValue)
+        {
+            var hasHandled = dto.IsHandled.Value;
+            query = query.Where(d => SqlFunc.Subqueryable<WorkflowTrace>() 
+            .Where(step => step.ExternalId == d.Id && 
+                (hasHandled || step.Status != EWorkflowStepStatus.Handled) && 
+                (!hasHandled || step.Status == EWorkflowStepStatus.Handled && 
+                step.TraceState != EWorkflowTraceState.StepRemoveByPrevious) && 
+                ((step.FlowAssignType == EFlowAssignType.User && !string.IsNullOrEmpty(step.HandlerId) && 
+                step.HandlerId == _sessionContext.RequiredUserId) || 
+                (step.FlowAssignType == EFlowAssignType.Org && !string.IsNullOrEmpty(step.HandlerOrgId) && 
+                step.HandlerOrgId == _sessionContext.RequiredOrgId) || 
+                (step.FlowAssignType == EFlowAssignType.Role && !string.IsNullOrEmpty(step.RoleId) && 
+                _sessionContext.Roles.Contains(step.RoleId)))).Any() || 
+                (string.IsNullOrEmpty(d.WorkflowId) && 
+                (string.IsNullOrEmpty(d.SignerId) || d.SignerId == _sessionContext.RequiredUserId))
+            );
+        }
 
+        return query
+            // 交办件:已派单其他节点的工单,该选项卡下工单若办结就不显示
+            .WhereIF(dto.TypeCode.HasValue && dto.TypeCode == 1, d => d.ProcessType == EProcessType.Jiaoban && d.Status < EOrderStatus.Filed)
+            // 办结件:当前登录坐席作为最初受理人已办结的工单
+            .WhereIF(dto.TypeCode.HasValue && dto.TypeCode == 2, d=> d.Status >= EOrderStatus.Filed && d.AcceptorId == _sessionContext.RequiredUserId)
+            .WhereIF(dto.IsProvince.HasValue, d => d.IsProvince == dto.IsProvince) 
+            .WhereIF(dto.IsHandled.HasValue, d => handleStatuses.Contains(d.Status)) 
+            .WhereIF(!string.IsNullOrEmpty(dto.Keyword), d => d.Title.StartsWith(dto.Keyword!)) 
+            .WhereIF(!string.IsNullOrEmpty(dto.No), d => d.No == dto.No) 
+            .WhereIF(!string.IsNullOrEmpty(dto.AreaCode), d => d.AreaCode == dto.AreaCode) 
+            .WhereIF(dto.IsCounterSign.HasValue && dto.IsCounterSign == true, d => d.CounterSignType.HasValue) 
+            .WhereIF(dto.IsCounterSign.HasValue && dto.IsCounterSign == false, d => d.CounterSignType == null) 
+            .WhereIF(dto.ExpiredOrAlmostOverdue.HasValue && dto.ExpiredOrAlmostOverdue == true,
+               d => (d.ExpiredTime < DateTime.Now && d.Status < EOrderStatus.Filed) ||
+                    (d.ExpiredTime < d.ActualHandleTime && d.Status >= EOrderStatus.Filed)) //超期 未办
+            .WhereIF(dto.ExpiredOrAlmostOverdue.HasValue && dto.ExpiredOrAlmostOverdue == false,
+               d => d.NearlyExpiredTime < DateTime.Now && d.ExpiredTime > DateTime.Now) //即将超期 未办
+            .WhereIF(dto.StartTime.HasValue, d => d.CreationTime >= dto.StartTime)
+            .WhereIF(dto.EndTime.HasValue, d => d.CreationTime <= dto.EndTime)
+           //.Where(d => (string.IsNullOrEmpty(d.WorkflowId) && (string.IsNullOrEmpty(d.SignerId) || d.SignerId == _sessionContext.RequiredUserId)))
+           //.Where(d => string.IsNullOrEmpty(d.SignerId) || d.SignerId == _sessionContext.RequiredUserId)
+            .WhereIF(dto.IsUrgent.HasValue, d => d.IsUrgent == dto.IsUrgent.Value)
+            .Where(x => x.Source < ESource.MLSQ || x.Source > ESource.WZSC)
+            .Where(x => x.Status != EOrderStatus.BackToProvince && x.Status < EOrderStatus.Filed)
+            .OrderBy(d => d.Status)
+            .OrderByIF(dto.IsHandled == true, d => d.StartTime, OrderByType.Desc)
+            .OrderByIF(dto.IsHandled == false, d => new { IsUrgent = d.IsUrgent, CreationTime = d.CreationTime }, OrderByType.Desc)
+            .Select<OrderListOutDto>();
+    }
 }

+ 7 - 0
src/Hotline.Application/StatisticalReport/IOrderReportApplication.cs

@@ -114,6 +114,13 @@ namespace Hotline.Application.StatisticalReport
         /// <returns></returns>
         Task<DataTable> ExportQueryVisitNoSatisfiedAsync(IReadOnlyList<SystemDicData> dissatisfiedReason, List<dynamic>? list, List<string> addColumnName);
 
+        /// <summary>
+        /// 部门不满意统计明细
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        ISugarQueryable<OrderVisitDetail> BiQueryVisitNoSatisfiedDetail(BiQueryVisitNoSatisfiedDetailDto dto);
+
         /// <summary>
         /// 未签收统计
         /// </summary>

+ 31 - 0
src/Hotline.Application/StatisticalReport/OrderReportApplication.cs

@@ -29,6 +29,10 @@ using XF.Domain.Authentications;
 using XF.Domain.Dependency;
 using XF.Domain.Exceptions;
 using XF.Domain.Repository;
+using Hotline.Share.Dtos;
+using Hotline.Share.Tools;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
 
 namespace Hotline.Application.StatisticalReport
 {
@@ -1723,6 +1727,33 @@ namespace Hotline.Application.StatisticalReport
             return dataTable;
         }
 
+        /// <summary>
+        /// 部门不满意统计明细
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [HttpGet("visit-nosatisfied-detail")]
+        public ISugarQueryable<OrderVisitDetail> BiQueryVisitNoSatisfiedDetail( BiQueryVisitNoSatisfiedDetailDto dto)
+        {
+            var IsCenter = _sessionContext.OrgIsCenter;
+
+            var query = _orderVisitDetailRepository.Queryable()
+                .Includes(x => x.OrderVisit, d => d.Order)
+                .Includes(x => x.OrderVisit, d => d.Employee)
+                //.Where(x => x.VisitOrgCode == dto.OrgCode)
+                .Where(x => x.OrderVisit.VisitState == EVisitState.Visited)
+                .Where(x => x.OrderVisit.VisitTime >= dto.StartTime.Value)
+                .Where(x => x.OrderVisit.VisitTime <= dto.EndTime.Value)
+                .Where(x => SqlFunc.JsonListObjectAny(x.OrgNoSatisfiedReason, "Key", dto.DissatisfiedKey))
+                .WhereIF(dto.VisitOrgName.NotNullOrEmpty(), x => x.VisitOrgName.Contains(dto.VisitOrgName))
+                .WhereIF(IsCenter == false, x => x.VisitOrgCode.StartsWith(_sessionContext.RequiredOrgId))
+                .WhereIF(IsCenter == true, x => x.VisitOrgCode.StartsWith(dto.OrgCode))
+                .WhereIF(!string.IsNullOrEmpty(dto.Keyword), x => x.OrderVisit.Order.No.Contains(dto.Keyword) || x.OrderVisit.Order.Title.Contains(dto.Keyword))
+                .OrderBy(x => x.OrderVisit.VisitTime);
+
+            return query;
+        }
+
         /// <summary>
         /// 未签收统计
         /// </summary>

+ 2 - 1
src/Hotline.Repository.SqlSugar/Extensions/SqlSugarRepositoryExtensions.cs

@@ -6,6 +6,7 @@ using System.Text;
 using System.Threading.Tasks;
 using Hotline.Share;
 using Hotline.Share.Requests;
+using Hotline.Share.Tools;
 using Microsoft.AspNetCore.Http;
 using SqlSugar;
 using XF.Domain.Entities;
@@ -34,7 +35,7 @@ namespace Hotline.Repository.SqlSugar.Extensions
         /// 分批次查询固定数量
         /// </summary>
         /// <returns></returns>
-        public static Task<List<TEntity>> ToFixedListAsync<TEntity>(this ISugarQueryable<TEntity> query, QueryFixedDto dto, CancellationToken cancellationToken)
+        public static Task<List<TEntity>> ToFixedListAsync<TEntity>(this ISugarQueryable<TEntity> query, IQueryFixedDto dto, CancellationToken cancellationToken)
         where TEntity : class, new()
         {
             if (dto.QueryCount == 0) dto.QueryCount = 50;

+ 1 - 0
src/Hotline.Share/Dtos/CallCenter/QueryCallsFixedDto.cs

@@ -4,6 +4,7 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 using Hotline.Share.Enums.CallCenter;
+using Hotline.Share.Tools;
 
 namespace Hotline.Share.Dtos.CallCenter
 {

+ 2 - 1
src/Hotline.Share/Dtos/CallCenter/QueryTelOperationsFixedDto.cs

@@ -1,4 +1,5 @@
-using System;
+using Hotline.Share.Tools;
+using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Text;

+ 0 - 2
src/Hotline.Share/Dtos/FlowEngine/BasicWorkflowDto.cs

@@ -78,8 +78,6 @@ public class BasicWorkflowDto : EndWorkflowDto
     /// 备注
     /// </summary>
     public string? Remark { get; set; }
-
-
 }
 
 public class External

+ 7 - 2
src/Hotline.Share/Dtos/FlowEngine/NextStepsDto.cs

@@ -40,6 +40,11 @@ public class NextStepsDto
     public int? CurrentOrgLevel { get; set; }
 
     public IReadOnlyList<KeyValuePair<int, string>> TimeTypeOptions { get; set; }
+
+    /// <summary>
+    /// 临时保存的办理意见
+    /// </summary>
+    public string? Opinion { get; set; }
 }
 
 public class NextStepsDto<TSteps> : NextStepsDto
@@ -69,9 +74,9 @@ public class NextStepsWithOpinionDto<TSteps> : NextStepsDto<TSteps>
 public class StepTempInDto
 {
     /// <summary>
-    /// 节点Id
+    /// 工单Id
     /// </summary>
-    public string StepId { get; set; }
+    public string OrderId { get; set; }
 
     /// <summary>
     /// 意见

+ 50 - 92
src/Hotline.Share/Dtos/FlowEngine/NextWorkflowDto.cs

@@ -21,101 +21,59 @@ public class NextWorkflowDto : BasicWorkflowDto
     ///// </summary>
     //public DateTime StepExpiredTime { get; set; }
 
-    #region 手动填入办理人信息
-
-    /// <summary>
-    /// 真实办理人姓名(手动填写)
-    /// </summary>
-    public string? RealHandlerName { get; set; }
-
-    /// <summary>
-    /// 真实办理人电话(手动填写)
-    /// </summary>
-    public string? RealHandlerPhone { get; set; }
-
-    /// <summary>
-    /// 沟通方式(手动填写)
-    /// </summary>
-    public ERealCommunicationMode? RealCommunicationMode { get; set; }
-
-    /// <summary>
-    /// 沟通时间(手动填写)
-    /// </summary>
-    public DateTime? RealCommunicationTime { get; set; }
-
-    /// <summary>
-    /// 沟通地点(手动填写)
-    /// </summary>
-    public string? RealCommunicationAddress { get; set; }
-
-    /// <summary>
-    /// 已与市民沟通
-    /// 已与市民电话联系,确认办理结果
-    /// </summary>
-    [Description("已与市民电话联系,确认办理结果")]
-    public bool? RealIsContacted { get; set; }
-
-    /// <summary>
-    /// 已与市民现场沟通
-    /// 已赴现场处置,将处理结果告知市民
-    /// </summary>
-    [Description("已赴现场处置,将处理结果告知市民")]
-    public bool? RealContactLocale { get; set; }
-
-    #endregion
-
-    #region task_298
-    /// <summary>
-    /// 其它
-    /// </summary>
-    [Description("其它")]
-    public bool? IsOther { get; set; }
-
-    /// <summary>
-    /// 其它原因
-    /// </summary>
-    [Description("其它原因")]
-    public string? OtherRemark { get; set; }
-
-    /// <summary>
-    /// 是否紧急
-    /// </summary>
-    public bool? IsUrgent { get; set; }
-
-    /// <summary>
-    /// 是否推诿
-    /// </summary>
-    public bool? IsEvasive { get; set; }
-
-    /// <summary>
-    /// 是否不积极
-    /// </summary>
-    public bool? IsInactively { get; set; }
-
-    /// <summary>
-    /// 领导短信Key
-    /// </summary>
-    public string? LeaderSMSKey { get; set; }
-
-    /// <summary>
-    /// 是否市州转办
-    /// </summary>
-    public bool? Transpond { get; set; }
-
-    /// <summary>
-    /// 市州转办信息
-    /// </summary>
-    public string? TranspondCityName { get; set; }
-
+    // #region 手动填入办理人信息
+    //
+    // /// <summary>
+    // /// 真实办理人姓名(手动填写)
+    // /// </summary>
+    // public string? RealHandlerName { get; set; }
+    //
+    // /// <summary>
+    // /// 真实办理人电话(手动填写)
+    // /// </summary>
+    // public string? RealHandlerPhone { get; set; }
+    //
+    // /// <summary>
+    // /// 沟通方式(手动填写)
+    // /// </summary>
+    // public ERealCommunicationMode? RealCommunicationMode { get; set; }
+    //
+    // /// <summary>
+    // /// 沟通时间(手动填写)
+    // /// </summary>
+    // public DateTime? RealCommunicationTime { get; set; }
+    //
+    // /// <summary>
+    // /// 沟通地点(手动填写)
+    // /// </summary>
+    // public string? RealCommunicationAddress { get; set; }
+    //
+    // /// <summary>
+    // /// 已与市民沟通
+    // /// 已与市民电话联系,确认办理结果
+    // /// </summary>
+    // [Description("已与市民电话联系,确认办理结果")]
+    // public bool? RealIsContacted { get; set; }
+    //
+    // /// <summary>
+    // /// 已与市民现场沟通
+    // /// 已赴现场处置,将处理结果告知市民
+    // /// </summary>
+    // [Description("已赴现场处置,将处理结果告知市民")]
+    // public bool? RealContactLocale { get; set; }
+    //
+    // #endregion
+}
+
+public class NextWorkflowDto<TData>
+{
     /// <summary>
-    /// 市州转办信息
+    /// 业务数据
     /// </summary>
-    public string? TranspondCityId { get; set; }
+    public TData Data { get; set; }
 
     /// <summary>
-    /// 市州转办信息
+    /// 流程数据
     /// </summary>
-    public string? TranspondCityValue { get; set; }
-
-    #endregion
+    public NextWorkflowDto WorkflowDto { get; set; }
 }

+ 31 - 8
src/Hotline.Share/Dtos/FlowEngine/PreviousWorkflowDto.cs

@@ -11,17 +11,40 @@ public class PreviousWorkflowDto : EndWorkflowIdDto
     /// 期满时间
     /// </summary>
     public DateTime? ExpiredTime { get; set; }
+    
+    /// <summary>
+    /// 指定办理对象
+    /// </summary>
+    public FlowStepHandler? Handler { get; set; }
+}
 
-    ///// <summary>
-    ///// 根据办理者类型不同,此字段为不同内容
-    ///// <example>
-    ///// 部门等级/分类为:depCodes, 角色为:userIds
-    ///// </example>
-    ///// </summary>
-    //public List<FlowStepHandler> NextHandlers { get; set; } = new();
+/// <summary>
+/// 目标节点指派策略信息(流程流转时,指派的目标节点)
+/// </summary>
+public class TargetStepAssignPolicyInfo
+{
+    public ETargetStepAssignPolicy TargetStepAssignPolicy { get; set; }
+    
+    /// <summary>
+    /// 指定办理对象(非指定办理对象时无效)
+    /// </summary>
+    public FlowStepHandler? Handler { get; set; }
+}
 
+public enum ETargetStepAssignPolicy
+{
+    /// <summary>
+    /// 按配置
+    /// </summary>
+    Config = 0,
+    
+    /// <summary>
+    /// 保持目标节点的办理对象不变
+    /// </summary>
+    TargetStep = 1,
+    
     /// <summary>
     /// 指定办理对象
     /// </summary>
-    public FlowStepHandler? Handler { get; set; }
+    AssignHandler = 2,
 }

+ 0 - 5
src/Hotline.Share/Dtos/FlowEngine/StartWorkflowDto.cs

@@ -12,11 +12,6 @@
         /// 流程title
         /// </summary>
         public string Title { get; set; }
-
-        /// <summary>
-        /// 是否转办
-        /// </summary>
-        public bool IsForwarded { get; set; }
     }
 
     public class StartWorkflowDto<TData>

+ 255 - 8
src/Hotline.Share/Dtos/Order/OrderDto.cs

@@ -79,12 +79,19 @@ namespace Hotline.Share.Dtos.Order
 
         public string? TagNames { get; set; }
 
-        #region 流程信息
-
         /// <summary>
-        /// 工单开始时间(受理/接办时间=流程开启时间)
+        /// 工单标签(自贡)
         /// </summary>
-        public DateTime? StartTime { get; set; }
+        public string? OrderTag { get; set; }
+
+        public string? OrderTagCode { get; set; }
+
+		#region 流程信息
+
+		/// <summary>
+		/// 工单开始时间(受理/接办时间=流程开启时间)
+		/// </summary>
+		public DateTime? StartTime { get; set; }
 
         /// <summary>
         /// 交办时间(中心交部门办理时间)
@@ -119,10 +126,23 @@ namespace Hotline.Share.Dtos.Order
         /// </summary>
         public double AllDuration { get; set; }
 
-        /// <summary>
-        /// 办结时长(秒) 归档时间-受理时间(工单创建时间)
-        /// </summary>
-        public double? CreationTimeHandleDuration { get; set; }
+
+        public string AllDurationHour => GetAllDurationHour();
+
+
+		public string GetAllDurationHour() {
+
+			if (Status >= EOrderStatus.Filed)
+			{
+                return  Math.Round(AllDuration / 60, 2).ToString() + "小时";
+			}
+            return "-";
+        }
+
+		/// <summary>
+		/// 办结时长(秒) 归档时间-受理时间(工单创建时间)
+		/// </summary>
+		public double? CreationTimeHandleDuration { get; set; }
 
         /// <summary>
         /// 办结工作日时长(秒)归档时间-受理时间(工单创建时间)
@@ -1196,4 +1216,231 @@ namespace Hotline.Share.Dtos.Order
 	    public string OrderId { get; set; }
     }
 
+
+    public class OrderListOutDto
+    {
+        /// <summary>
+        /// 工单状态
+        /// </summary>
+        public EOrderStatus Status { get; set; }
+
+        public string StatusText => Status.GetDescription();
+
+        /// <summary>
+        /// 过期状态
+        /// </summary>
+        public EExpiredStatus? ExpiredStatus => CalculateExpiredState();
+
+        /// <summary>
+        /// 归档时间(暂为流程结束时间,因流程结束自动归档)
+        /// </summary>
+        public DateTime? FiledTime { get; set; }
+
+        /// <summary>
+        /// 过期时间
+        /// </summary>
+        public DateTime? ExpiredTime { get; set; }
+
+        /// <summary>
+        /// 即将超期时间
+        /// </summary>
+        public DateTime? NearlyExpiredTime { get; set; }
+
+        /// <summary>
+        /// 即将超期第一级
+        /// </summary>
+        public DateTime? NearlyExpiredTimeOne { get; set; }
+
+
+        public EExpiredStatus? CalculateExpiredState()
+        {
+            DateTime? dateTime = DateTime.Now;
+            if (Status >= EOrderStatus.Filed)
+            {
+                dateTime = FiledTime;
+            }
+            //ExpiredStatus
+            if (ExpiredTime.HasValue)
+            {
+                if (NearlyExpiredTimeOne.HasValue && dateTime < NearlyExpiredTimeOne)
+                {
+                    return EExpiredStatus.Normal;
+                }
+                else if (!NearlyExpiredTimeOne.HasValue && dateTime < NearlyExpiredTime)
+                {
+                    return EExpiredStatus.Normal;
+                }
+                else if (NearlyExpiredTimeOne.HasValue && dateTime > NearlyExpiredTimeOne && dateTime < NearlyExpiredTime)
+                {
+                    return EExpiredStatus.GoingToExpiredOne;
+                }
+                else if (dateTime > NearlyExpiredTime && dateTime < ExpiredTime)
+                {
+                    return EExpiredStatus.GoingToExpired;
+                }
+                else
+                {
+                    return EExpiredStatus.Expired;
+                }
+            }
+            return null;
+        }
+
+        /// <summary>
+        /// 过期状态
+        /// </summary>
+        public string ExpiredStatusText => ExpiredStatus?.GetDescription() ?? string.Empty;
+
+        /// <summary>
+        /// 工单编码(20220101000001)
+        /// </summary>
+        public string No { get; set; }
+
+
+        /// <summary>
+        /// 会签类型
+        /// </summary>
+        public ECounterSignType? CounterSignType { get; set; }
+
+        public string? CounterSignTypeText => CounterSignType?.GetDescription() ?? "未会签";
+
+        /// <summary>
+        /// 签收人id
+        /// </summary>
+        public string? SignerId { get; set; }
+
+        /// <summary>
+        /// 签收人名称
+        /// </summary>
+        public string? SignerName { get; set; }
+
+        public DateTime CreationTime { get; set; }
+
+        public string? ContactMask { get; set; }
+
+
+        /// <summary>
+        /// 紧急程度
+        /// </summary>
+        public EEmergencyLevel EmergencyLevel => IsUrgent
+            ? EEmergencyLevel.Emergency
+            : EEmergencyLevel.UnEmergency;
+
+        /// <summary>
+        /// 是否可签收
+        /// </summary>
+        public bool CanSign => string.IsNullOrEmpty(SignerId);
+
+        /// <summary>
+        /// 待结束会签编号
+        /// </summary>
+        public string? CountersignId { get; set; }
+
+        /// <summary>
+        /// 是否可以延期
+        /// </summary>
+        public bool IsCanDelay { get; set; }
+
+        /// <summary>
+        /// 是否可以取消延期
+        /// </summary>
+        public bool IsCanCancelDelay { get; set; }
+
+        /// <summary>
+        /// 最后一次延期天数
+        /// </summary>
+        public string? DelayString { get; set; }
+
+        /// <summary>
+        /// 是否可退回
+        /// </summary>
+        public bool CanPrevious { get; set; }
+
+        /// <summary>
+        /// 是否可办理
+        /// </summary>
+        public bool CanHandle { get; set; }
+
+        /// <summary>
+        /// 退回意见
+        /// </summary>
+        public string? PreviousOpinion { get; set; }
+
+        /// <summary>
+        /// 是否可代办
+        /// </summary>
+        public bool CanInsteadHandle { get; set; }
+
+        /// <summary>
+        /// 是否紧急
+        /// </summary>
+        public bool IsUrgent { get; set; }
+
+        public string IsUrgentText => IsUrgent ? "紧急" : "";
+
+        /// <summary>
+        /// 发布范围
+        /// </summary>
+        public bool? PublishState { get; set; }
+
+        /// <summary>
+        /// 实际办理节点code(会签状态此字段保存最外层会签发起节点code)
+        /// </summary>
+        public string? ActualHandleStepCode { get; set; }
+
+        /// <summary>
+        /// 实际办理节点名称(会签状态此字段保存最外层会签发起节点名称)
+        /// </summary>
+        public string? ActualHandleStepName { get; set; }
+
+        /// <summary>
+        /// 重办次数
+        /// </summary>
+        public int ReTransactNum { get; set; }
+
+        /// <summary>
+        /// 热点分类
+        /// </summary>
+        public string HotspotName { get; set; }
+
+        /// <summary>
+        /// 来源渠道
+        /// </summary>
+        public string SourceChannel { get; set; }
+
+        public string SourceChannelCode { get; set; }
+
+        /// <summary>
+        /// 受理类型
+        /// </summary>
+        public string AcceptType { get; set; }
+        public string AcceptTypeCode { get; set; }
+
+        /// <summary>
+        /// 是否为省工单
+        /// </summary>
+        public bool IsProvince { get; set; }
+
+        public string IsProvinceText => IsProvince ? "省工单" : "市工单";
+
+        /// <summary>
+        /// 工单标题
+        /// </summary>
+        public string Title { get; set; }
+
+        /// <summary>
+        /// 工单开始时间(受理/接办时间=流程开启时间)
+        /// </summary>
+        public DateTime? StartTime { get; set; }
+
+        /// <summary>
+        /// 受理人id
+        /// </summary>
+        public string AcceptorId { get; set; }
+
+        /// <summary>
+        /// 受理人名称
+        /// </summary>
+        public string AcceptorName { get; set; }
+    }
 }

+ 135 - 24
src/Hotline.Share/Dtos/Order/OrderStartFlowDto.cs

@@ -1,40 +1,157 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using Hotline.Share.Dtos.Article;
+using System.ComponentModel;
 using Hotline.Share.Dtos.FlowEngine;
 using Hotline.Share.Enums.FlowEngine;
 
 namespace Hotline.Share.Dtos.Order
 {
-    public class OrderStartFlowDto : StartWorkflowDto<string>
+    /// <summary>
+    /// 工单开启/办理流程dto
+    /// </summary>
+    public class OrderHandleFlowDto
     {
+        public string OrderId { get; set; }
+
         /// <summary>
-        /// 是否转办
+        /// 派单方式
         /// </summary>
-        public bool IsForwarded { get; set; }
-    }
+        public EOrderAssignMode OrderAssignMode { get; set; }
 
-    public class ScreenStartFlowDto : StartWorkflowDto<OrderScreenDto>
-    {
+        /// <summary>
+        /// 跨级转派得下级办理部门/主协办得协办部门
+        /// </summary>
+        public List<string> CrossOrgIds { get; set; }
 
+        /// <summary>
+        /// 抄送对象
+        /// </summary>
+        public List<FlowStepHandler> CopyToHandlers { get; set; }
+        
+        #region 手动填入办理人信息
+
+        /// <summary>
+        /// 真实办理人姓名(手动填写)
+        /// </summary>
+        public string? RealHandlerName { get; set; }
+
+        /// <summary>
+        /// 真实办理人电话(手动填写)
+        /// </summary>
+        public string? RealHandlerPhone { get; set; }
+
+        /// <summary>
+        /// 沟通方式(手动填写)
+        /// </summary>
+        public ERealCommunicationMode? RealCommunicationMode { get; set; }
+
+        /// <summary>
+        /// 沟通时间(手动填写)
+        /// </summary>
+        public DateTime? RealCommunicationTime { get; set; }
+
+        /// <summary>
+        /// 沟通地点(手动填写)
+        /// </summary>
+        public string? RealCommunicationAddress { get; set; }
+
+        /// <summary>
+        /// 已与市民沟通
+        /// 已与市民电话联系,确认办理结果
+        /// </summary>
+        [Description("已与市民电话联系,确认办理结果")]
+        public bool? RealIsContacted { get; set; }
+
+        /// <summary>
+        /// 已与市民现场沟通
+        /// 已赴现场处置,将处理结果告知市民
+        /// </summary>
+        [Description("已赴现场处置,将处理结果告知市民")]
+        public bool? RealContactLocale { get; set; }
+
+        #endregion
+        
+        #region task_298
+
+        /// <summary>
+        /// 是否市州转办
+        /// </summary>
+        public bool? Transpond { get; set; }
+        
+        /// <summary>
+        /// 其它
+        /// </summary>
+        [Description("其它")]
+        public bool? IsOther { get; set; }
+
+        /// <summary>
+        /// 其它原因
+        /// </summary>
+        [Description("其它原因")]
+        public string? OtherRemark { get; set; }
+
+        /// <summary>
+        /// 是否紧急
+        /// </summary>
+        public bool? IsUrgent { get; set; }
+
+        /// <summary>
+        /// 是否推诿
+        /// </summary>
+        public bool? IsEvasive { get; set; }
+
+        /// <summary>
+        /// 是否不积极
+        /// </summary>
+        public bool? IsInactively { get; set; }
+
+        /// <summary>
+        /// 领导短信Key
+        /// </summary>
+        public string? LeaderSMSKey { get; set; }
+
+        /// <summary>
+        /// 市州转办信息
+        /// </summary>
+        public string? TranspondCityName { get; set; }
+
+        /// <summary>
+        /// 市州转办信息
+        /// </summary>
+        public string? TranspondCityId { get; set; }
+
+        /// <summary>
+        /// 市州转办信息
+        /// </summary>
+        public string? TranspondCityValue { get; set; }
+
+        #endregion
     }
 
-    public class TerminateStartFlowDto : StartWorkflowDto<OrderTerminateDto>
+    public enum EOrderAssignMode
     {
+        /// <summary>
+        /// 逐级
+        /// </summary>
+        AdjoinLevel = 0,
+
+        /// <summary>
+        /// 跨级
+        /// </summary>
+        CrossLevel = 1,
 
+        /// <summary>
+        /// 主协办
+        /// </summary>
+        MainAndSecondary = 2,
     }
 
     public class TerminateNextFlowDto
-	{
-	    public OrderTerminateDto Data { get; set; }
+    {
+        public OrderTerminateDto Data { get; set; }
 
-	    public NextWorkflowDto NextWorkflow { get; set; }
+        public NextWorkflowDto NextWorkflow { get; set; }
     }
 
-	public class ScreenNextFlowDto
+    public class ScreenNextFlowDto
     {
         public OrderScreenDto Data { get; set; }
 
@@ -47,10 +164,4 @@ namespace Hotline.Share.Dtos.Order
 
         public NextWorkflowDto NextWorkflow { get; set; }
     }
-
-    public class DelayStartFlowDto : StartWorkflowDto<ApplyDelayDto>
-    {
-
-    }
-
-}
+}

+ 8 - 1
src/Hotline.Share/Dtos/Order/OrderVisitDto.cs

@@ -19,7 +19,7 @@ namespace Hotline.Share.Dtos.Order
     }
     public record QueryOrderVisitDto : PagedKeywordRequest
     {
-        public EVisitStateQuery VisitState { get; set; }
+        public EVisitStateQuery VisitStateQuery { get; set; }
 
         /// <summary>
         /// 归档方式
@@ -91,6 +91,13 @@ namespace Hotline.Share.Dtos.Order
         /// </summary>
         public List<string> OrgHandledAttitude { get; set; } = new();
 
+        /// <summary>
+        /// 来源渠道
+        /// </summary>
+        public string Channel { get; set; }
+
+	}
+
         /// <summary>
         /// 是否超期
         /// </summary>

+ 42 - 25
src/Hotline.Share/Dtos/Order/OrderWaitedDto.cs

@@ -5,6 +5,7 @@ using System.Text;
 using System.Threading.Tasks;
 using Hotline.Share.Enums.Order;
 using Hotline.Share.Requests;
+using Hotline.Share.Tools;
 
 namespace Hotline.Share.Dtos.Order
 {
@@ -24,12 +25,17 @@ namespace Hotline.Share.Dtos.Order
         public DateTime? StepExpiredTime { get; set; }
     }
 
-    public record QueryOrderWaitedDto : PagedKeywordRequest 
+    public record QueryOrderWaitedDto : PagedKeywordRequest, IQueryFixedDto
     {
-	    public bool? IsProvince { get; set; }
-	    public bool? IsHandled { get; set; }
-	    public bool? IsCounterSign { get; set; }
-	    public bool? ExpiredOrAlmostOverdue { get; set; }
+        public bool? IsProvince { get; set; }
+
+        /// <summary>
+        /// true: 工单已办
+        /// false: 工单待办
+        /// </summary>
+        public bool? IsHandled { get; set; }
+        public bool? IsCounterSign { get; set; }
+        public bool? ExpiredOrAlmostOverdue { get; set; }
         public string? No { get; set; }
 
         /// <summary>
@@ -48,21 +54,32 @@ namespace Hotline.Share.Dtos.Order
         public EOrderStatus? Status { get; set; }
 	}
 
+        public int QueryIndex { get; set; }
+
+        public int QueryCount { get; set; }
+
+        /// <summary>
+        /// 1: 交办件
+        /// 2: 直办件
+        /// </summary>
+        public int? TypeCode { get; set; }
+    }
+
     /// <summary>
     /// 中心待办
     /// </summary>
     public record QueryOrderWaitedCenterDto : PagedKeywordRequest
-	{
-		/// <summary>
-		/// 编号
-		/// </summary>
-		public string? No { get; set; }
+    {
+        /// <summary>
+        /// 编号
+        /// </summary>
+        public string? No { get; set; }
 
         /// <summary>
         /// 标题
         /// </summary>
         public string? Title { get; set; }
-        
+
         /// <summary>
         /// 开始生成时间
         /// </summary>
@@ -73,16 +90,16 @@ namespace Hotline.Share.Dtos.Order
         /// </summary>
         public DateTime? EndCreationTime { get; set; }
 
-		/// <summary>
-		/// 结束受理时间
-		/// </summary>
-		public DateTime? StartTimeEnd { get; set; }
+        /// <summary>
+        /// 结束受理时间
+        /// </summary>
+        public DateTime? StartTimeEnd { get; set; }
+
+        /// <summary>
+        /// 开始受理时间
+        /// </summary>
+        public DateTime? StartTimeSt { get; set; }
 
-		/// <summary>
-		/// 开始受理时间
-		/// </summary>
-		public DateTime? StartTimeSt { get; set; }
-        
         /// <summary>
         /// 当前节点名称
         /// </summary>
@@ -108,15 +125,15 @@ namespace Hotline.Share.Dtos.Order
         /// </summary>
         public string? CenterToOrgHandlerName { get; set; }
 
-		/// <summary>
-		/// 过期状态
-		/// </summary>
-		public EExpiredStatus? ExpiredStatus { get; set; }
+        /// <summary>
+        /// 过期状态
+        /// </summary>
+        public EExpiredStatus? ExpiredStatus { get; set; }
 
         /// <summary>
         /// 是否紧急
         /// </summary>
         public bool? IsUrgent { get; set; }
 
-	}
+    }
 }

+ 80 - 0
src/Hotline.Share/Dtos/Order/PublishedDto.cs

@@ -606,4 +606,84 @@ public record AboutToExpireListDto : PagedKeywordRequest
     /// 0 全部  1 是  2 否
     /// </summary>
     public int? Delay { get; set; }
+}
+
+public class PublishOrderAllDto
+{
+    /// <summary>
+    /// 工单id
+    /// </summary>
+    public string OrderId { get; set; }
+
+    /// <summary>
+    /// 来电/信人姓名
+    /// </summary>
+    public string? FromName { get; set; }
+
+    /// <summary>
+    /// 联系电话
+    /// </summary>
+    public string? Contact { get; set; }
+
+    /// <summary>
+    /// 工单编号
+    /// </summary>
+    public string OrderNo { get; set; }
+
+    /// <summary>
+    /// 工单标题
+    /// </summary>
+    public string Title { get; set; }
+
+    /// <summary>
+    /// 来源渠道(电话、网站、APP等)
+    /// </summary>
+    public string? SourceChannel { get; set; }
+
+    public string? SourceChannelCode { get; set; }
+
+    /// <summary>
+    /// 受理类型(咨询、投诉等)
+    /// </summary>
+    public string? AcceptType { get; set; }
+
+    /// <summary>
+    /// 受理类型代码
+    /// </summary>
+    public string? AcceptTypeCode { get; set; }
+
+    /// <summary>
+    /// 热点名称
+    /// </summary>
+    public string? HotspotName { get; set; }
+
+    /// <summary>
+    /// 诉求内容
+    /// </summary>
+    public string Content { get; set; }
+
+    /// <summary>
+    /// 工单状态 0:办理中;1办理完成
+    /// </summary>
+    public string State { get; set; }
+
+    /// <summary>
+    /// 实际办理意见(办理中...or 最终办理意见)
+    /// </summary>
+    public string ActualOpinion { get; set; }
+
+    /// <summary>
+    /// 工单创建时间
+    /// </summary>
+    public DateTime? AcceptTime { get; set; }
+
+    /// <summary>
+    /// 发布时间
+    /// </summary>
+    public DateTime? PubDate { get; set; }
+
+    /// <summary>
+    /// 归档时间
+    /// </summary>
+    public DateTime? FiledTime { get; set; }
 }

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

@@ -184,7 +184,12 @@ namespace Hotline.Share.Dtos.Order
         /// 省交办编号
         /// </summary>
         public string? ReceiveProvinceNo { get; set; }
-    }
+
+        /// <summary>
+        /// 工单标签Code
+        /// </summary>
+        public string? OrderTagCode { get; set; }
+	}
 
 
 	public enum FiledType

+ 5 - 0
src/Hotline.Share/Dtos/OrderExportWord/OrderSubmissionForm.cs

@@ -4,6 +4,11 @@ namespace Hotline.Share.Dtos.OrderExportWord
 {
     public class OrderSubmissionForm : IWordExportTemplate
     {
+        /// <summary>
+        /// 市州名称
+        /// </summary>
+        public string CityName { get; set; }
+
         /// <summary>
         /// 编号
         /// </summary>

+ 71 - 59
src/Hotline.Share/Dtos/Push/MessageDto.cs

@@ -1,63 +1,75 @@
 using Hotline.Share.Enums.Push;
 
-namespace Hotline.Share.Dtos.Push
+namespace Hotline.Share.Dtos.Push;
+
+public class MessageDto
+{
+    /// <summary>
+    /// 消息推送业务
+    /// </summary>
+    public EPushBusiness PushBusiness { get; set; }
+
+    /// <summary>
+    /// 接收姓名
+    /// </summary>
+    public string Name { get; set; }
+
+    /// <summary>
+    /// 接收手机号码
+    /// </summary>
+    public string TelNumber { get; set; }
+
+    /// <summary>
+    /// 外部业务唯一编号
+    /// </summary>
+    public string? ExternalId { get; set; }
+
+    /// <summary>
+    /// 关联工单编号
+    /// </summary>
+    public string? OrderId { get; set; }
+
+    /// <summary>
+    /// 推送平台
+    /// </summary>
+    public EPushPlatform? PushPlatform { get; set; } = EPushPlatform.Sms;
+
+    /// <summary>
+    /// 模板
+    /// </summary>
+    public string? TemplateCode { get; set; }
+
+    /// <summary>
+    /// 内容
+    /// </summary>
+    public string? Content { get; set; }
+
+    /// <summary>
+    /// 备注
+    /// </summary>
+    public string? Remark { get; set; }
+
+    /// <summary>
+    /// 参数
+    /// </summary>
+    public List<string>? Params { get; set; }
+    public bool IsSmsReply { get; set; }
+    public string? SmsReplyContent { get; set; }
+    /// <summary>
+    /// 消息处理类型  0:推送状态修改,1:发送状态,2:短信回复
+    /// </summary>
+    public string Type { get; set; }
+}
+
+public class MessageInDto
 {
-    public class MessageDto
-    {
-        /// <summary>
-        /// 消息推送业务
-        /// </summary>
-        public EPushBusiness PushBusiness { get; set; }
-
-        /// <summary>
-        /// 接收姓名
-        /// </summary>
-        public string Name { get; set; }
-
-        /// <summary>
-        /// 接收手机号码
-        /// </summary>
-        public string TelNumber { get; set; }
-
-        /// <summary>
-        /// 外部业务唯一编号
-        /// </summary>
-        public string? ExternalId { get; set; }
-
-        /// <summary>
-        /// 关联工单编号
-        /// </summary>
-        public string? OrderId { get; set; }
-
-        /// <summary>
-        /// 推送平台
-        /// </summary>
-        public EPushPlatform? PushPlatform { get; set; } = EPushPlatform.Sms;
-
-        /// <summary>
-        /// 模板
-        /// </summary>
-        public string? TemplateCode { get; set; }
-
-        /// <summary>
-        /// 内容
-        /// </summary>
-        public string? Content { get; set; }
-
-        /// <summary>
-        /// 备注
-        /// </summary>
-        public string? Remark { get; set; }
-
-        /// <summary>
-        /// 参数
-        /// </summary>
-        public List<string>? Params { get; set; }
-        public bool IsSmsReply { get; set; }
-        public string? SmsReplyContent { get; set; }
-        /// <summary>
-        /// 消息处理类型  0:推送状态修改,1:发送状态,2:短信回复
-        /// </summary>
-        public string Type { get; set; }
-    }
+    /// <summary>
+    /// 接收手机号码(多个逗号分隔)
+    /// </summary>
+    public string TelNumbers { get; set; }
+
+    /// <summary>
+    /// 内容
+    /// </summary>
+    public string? Content { get; set; }
 }

+ 1 - 0
src/Hotline.Share/Enums/FlowEngine/EBusinessType.cs

@@ -31,5 +31,6 @@ public enum EBusinessType
     /// <summary>
     /// 归档节点
     /// </summary>
+    [Description("归档节点")]
     File = 99,
 }

+ 7 - 1
src/Hotline.Share/Enums/Push/EPushBusiness.cs

@@ -67,10 +67,16 @@ public enum EPushBusiness
     [Description("撤销短信")]
     OrderRevocationSms = 9,
 
-
     /// <summary>
     /// 自动延期
     /// </summary>
     [Description("自动延期")]
     AutomaticDelay = 10,
+
+    /// <summary>
+    /// 手动发送短信
+    /// </summary>
+    [Description("手动发送短信")]
+    ManualSms = 11,
+
 }

+ 1 - 1
src/Hotline.Share/Hotline.Share.csproj

@@ -7,7 +7,7 @@
     <GenerateDocumentationFile>True</GenerateDocumentationFile>
     <NoWarn>$(NoWarn);1591;8618;</NoWarn>
     <GeneratePackageOnBuild>True</GeneratePackageOnBuild>
-    <Version>1.0.106</Version>
+    <Version>1.0.107</Version>
   </PropertyGroup>
 
   <ItemGroup>

+ 3 - 2
src/Hotline.Share/QueryFixedDto.cs

@@ -1,4 +1,5 @@
-using System;
+using Hotline.Share.Tools;
+using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Text;
@@ -6,7 +7,7 @@ using System.Threading.Tasks;
 
 namespace Hotline.Share
 {
-    public class QueryFixedDto
+    public class QueryFixedDto : IQueryFixedDto
     {
         /// <summary>
         /// 查询批次

+ 19 - 0
src/Hotline.Share/Tools/IQueryFixedDto.cs

@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Hotline.Share.Tools;
+public interface IQueryFixedDto
+{
+    /// <summary>
+    /// 查询批次
+    /// </summary>
+    int QueryIndex { get; set; }
+
+    /// <summary>
+    /// 单次总数据量
+    /// </summary>
+    int QueryCount { get; set; }
+}

+ 14 - 0
src/Hotline.Share/Tools/StringExtensions.cs

@@ -39,4 +39,18 @@ public static class StringExtensions
                     .Select(s => s.Trim())
                     .ToList();
     }
+
+    /// <summary>
+    /// 是否手机号码
+    /// </summary>
+    /// <param name="value"></param>
+    /// <returns></returns>
+    public static bool IsPhoneNumber(this string value)
+    {
+        if (string.IsNullOrWhiteSpace(value))
+            return false;
+
+        var phoneNumberPattern = @"^1[3-9]\d{9}$";
+        return Regex.IsMatch(value, phoneNumberPattern);
+    }
 }

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

@@ -14,5 +14,6 @@ namespace Hotline.Caching.Interfaces
         int RingTimes { get; }
         string RecordPrefix { get; }
         string DefaultVisitEmployeeId { get; }
+        int FixedQueryCount { get; }
     }
 }

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

@@ -53,5 +53,7 @@ namespace Hotline.Caching.Services
         public string RecordPrefix => GetSetting(SettingConstants.RecordPrefix)?.SettingValue[0].Trim().ToString();
 
         public string DefaultVisitEmployeeId => GetSetting(SettingConstants.DefaultVisitEmployeeId)?.SettingValue[0].Trim().ToString();
+
+        public int FixedQueryCount => int.Parse(GetSetting(SettingConstants.FixedQueryCount)?.SettingValue[0]);
     }
 }

+ 23 - 1
src/Hotline/FlowEngine/FlowAssignInfo.cs

@@ -1,6 +1,10 @@
-using Hotline.Share.Dtos;
+using Hotline.FlowEngine.Definitions;
+using Hotline.FlowEngine.Workflows;
+using Hotline.Share.Dtos;
+using Hotline.Share.Dtos.FlowEngine;
 using Hotline.Share.Enums.FlowEngine;
 using XF.Domain.Entities;
+using XF.Domain.Exceptions;
 
 namespace Hotline.FlowEngine;
 
@@ -11,6 +15,11 @@ public class FlowAssignInfo
     /// </summary>
     public EFlowAssignType FlowAssignType { get; set; }
 
+    /// <summary>
+    /// 办理对象
+    /// </summary>
+    public FlowStepHandler Handler { get; set; }
+
     /// <summary>
     /// 办理对象(UserIds/OrgCodes)
     /// </summary>
@@ -75,5 +84,18 @@ public class FlowAssignInfo
             EHandlerType.AssignedOrg => EFlowAssignType.Org,
             _ => throw new ArgumentOutOfRangeException(nameof(handlerType), handlerType, null)
         };
+}
 
+public class StepAssignInfo
+{
+    /// <summary>
+    /// 流程指派类型
+    /// </summary>
+    public EFlowAssignType FlowAssignType { get; set; }
+
+    /// <summary>
+    /// 办理对象
+    /// </summary>
+    public FlowStepHandler Handler { get; set; }
+    
 }

+ 12 - 1
src/Hotline/FlowEngine/Workflows/IWorkflowDomainService.cs

@@ -26,6 +26,17 @@ namespace Hotline.FlowEngine.Workflows
             bool isNextDynamic, FlowAssignInfo flowAssignInfo, ECounterSignType? counterSignType, DateTime? expiredTime,
             CancellationToken cancellationToken);
 
+        /// <summary>
+        /// new
+        /// </summary>
+        Task<WorkflowStep> StartAsync(StartWorkflowDto dto, string externalId, DateTime? expiredTime, CancellationToken cancellationToken = default);
+
+        /// <summary>
+        /// new
+        /// </summary>
+        Task<List<WorkflowStep>> NextAsync(NextWorkflowDto dto,
+            DateTime? expiredTime = null, CancellationToken cancellationToken = default);
+
         /// <summary>
         /// 查询工作流
         /// </summary>
@@ -203,7 +214,7 @@ namespace Hotline.FlowEngine.Workflows
         /// 办理节点
         /// </summary>
         Task HandleStepAsync(WorkflowStep step, Workflow workflow, BasicWorkflowDto dto,
-            EFlowAssignType? flowAssignType, ECounterSignType? counterSignType, DateTime? expiredTime,
+            ECounterSignType? counterSignType, DateTime? expiredTime,
             CancellationToken cancellationToken);
 
         /// <summary>

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

@@ -794,7 +794,7 @@ public partial class Workflow
         switch (type)
         {
             case EFlowAssignType.Org:
-                var orgCodes = handler.GetHigherOrgCodes(true).ToList();
+                var orgCodes = handler.GetHigherOrgIds(true).ToList();
                 FlowedOrgIds.AddRange(orgCodes);
                 FlowedOrgIds = FlowedOrgIds.Distinct().ToList();
                 break;
@@ -817,7 +817,7 @@ public partial class Workflow
         switch (type)
         {
             case EFlowAssignType.Org:
-                var orgCodes = handlers.SelectMany(d => d.GetHigherOrgCodes(true)).ToList();
+                var orgCodes = handlers.SelectMany(d => d.GetHigherOrgIds(true)).ToList();
                 FlowedOrgIds.AddRange(orgCodes);
                 FlowedOrgIds = FlowedOrgIds.Distinct().ToList();
                 break;

+ 572 - 90
src/Hotline/FlowEngine/Workflows/WorkflowDomainService.cs

@@ -28,10 +28,7 @@ namespace Hotline.FlowEngine.Workflows
         private readonly IRepository<WorkflowStep> _workflowStepRepository;
         private readonly IRepository<WorkflowTrace> _workflowTraceRepository;
         private readonly IRepository<WorkflowSupplement> _workflowSupplementRepository;
-
         private readonly IRepository<WorkflowCountersign> _workflowCountersignRepository;
-
-        //private readonly IRepository<WorkflowStepHandler> _workflowStepHandlerRepository;
         private readonly ISessionContext _sessionContext;
         private readonly IMapper _mapper;
         private readonly Publisher _publisher;
@@ -39,6 +36,7 @@ namespace Hotline.FlowEngine.Workflows
         private readonly IFileRepository _fileRepository;
         private readonly IRepository<User> _userRepository;
         private readonly ISystemSettingCacheManager _systemSettingCacheManager;
+        private readonly IWfModuleCacheManager _wfModuleCacheManager;
 
         public WorkflowDomainService(
             IWorkflowRepository workflowRepository,
@@ -46,8 +44,8 @@ namespace Hotline.FlowEngine.Workflows
             IRepository<WorkflowTrace> workflowTraceRepository,
             IRepository<WorkflowSupplement> workflowSupplementRepository,
             IRepository<WorkflowCountersign> workflowCountersignRepository,
-            //IRepository<WorkflowStepHandler> workflowStepHandlerRepository,
             ISystemSettingCacheManager systemSettingCacheManager,
+            IWfModuleCacheManager wfModuleCacheManager,
             ISessionContextProvider sessionContextProvider,
             IMapper mapper,
             Publisher publisher,
@@ -59,13 +57,13 @@ namespace Hotline.FlowEngine.Workflows
             _workflowTraceRepository = workflowTraceRepository;
             _workflowSupplementRepository = workflowSupplementRepository;
             _workflowCountersignRepository = workflowCountersignRepository;
-            //_workflowStepHandlerRepository = workflowStepHandlerRepository;
             _sessionContext = sessionContextProvider.SessionContext;
             _mapper = mapper;
             _publisher = publisher;
             _logger = logger;
             _fileRepository = fileRepository;
             _systemSettingCacheManager = systemSettingCacheManager;
+            _wfModuleCacheManager = wfModuleCacheManager;
         }
 
         public async Task<Workflow> CreateWorkflowAsync(WorkflowModule wfModule, string title, string userId,
@@ -86,8 +84,8 @@ namespace Hotline.FlowEngine.Workflows
                 Traces = new(),
                 WorkflowDefinition = definition,
                 ExternalId = externalId ?? string.Empty,
-                //FlowedOrgIds = new List<string> { orgId },
-                FlowedUserIds = new List<string> { userId },
+                // //FlowedOrgIds = new List<string> { orgId },
+                // FlowedUserIds = new List<string> { userId },
                 FlowType = definition.FlowType,
             };
 
@@ -121,33 +119,13 @@ namespace Hotline.FlowEngine.Workflows
 
             //firststeps
             var firstSteps = await CreateNextStepsAsync(workflow, startStep, dto, firstStepDefine,
-                isNextDynamic, flowAssignInfo, expiredTime, dto.IsStartCountersign, cancellationToken);
-            //if (firstSteps.Any())
-            //    workflow.Steps.AddRange(firstSteps);
-
-            //var counterSignType = GetCounterSignType(startStep.BusinessType);
-            ////办理开始节点
-            //await HandleStepAsync(startStep, workflow, dto, flowAssignInfo.FlowAssignType, counterSignType,
-            //    cancellationToken);
-
-            ////赋值当前节点的下级办理节点
-            //if (dto.IsStartCountersign)
-            //    startStep.CreateCountersignSteps(firstSteps);
+                isNextDynamic, flowAssignInfo.FlowAssignType, expiredTime, dto.IsStartCountersign, cancellationToken);
 
             await _workflowStepRepository.UpdateAsync(startStep, cancellationToken);
 
             //handle trace
             var trace = await NextTraceAsync(workflow, dto, startStep, cancellationToken);
 
-            //todo 计算办理工作时长
-
-            ////更新当前办理节点信息
-            //workflow.UpdateWorkflowCurrentStepInfo(dto.IsStartCountersign,
-            //    current.RequiredUserId, current.UserName,
-            //    current.RequiredOrgId, current.OrgName,
-            //    current.OrgAreaCode, current.OrgAreaName,
-            //    startStep, firstSteps.First());
-
             //指派实际办理节点
             UpdateActualStep(workflow, dto, firstStepDefine, firstSteps);
 
@@ -158,24 +136,367 @@ namespace Hotline.FlowEngine.Workflows
             if (dto.IsStartCountersign && !workflow.IsInCountersign)
                 workflow.StartCountersign(startStep.Id, counterSignType);
 
-            ////更新实际办理节点信息
-            //workflow.UpdateWorkflowActualHandleInfo(startStep,
-            //    current.RequiredUserId, current.UserName,
-            //    current.RequiredOrgId, current.OrgName,
-            //    current.OrgAreaCode, current.OrgAreaName,
-            //    current.OrgLevel);
+            await _workflowRepository.UpdateAsync(workflow, cancellationToken);
+
+            //publish
+            await _publisher.PublishAsync(new StartWorkflowNotify(workflow, dto, flowAssignInfo, trace),
+                PublishStrategy.ParallelWhenAll, cancellationToken);
+        }
+
+        /// <summary>
+        /// new(开启流程并停留在开始节点,开始节点为待办节点,指派给当前操作人)
+        /// </summary>
+        public async Task<WorkflowStep> StartAsync(StartWorkflowDto dto,
+            string externalId, DateTime? expiredTime, CancellationToken cancellationToken = default)
+        {
+            //     var validator = new StartWorkflowDtoValidator();
+            // var validResult = await validator.ValidateAsync(dto, cancellationToken);
+            // if (!validResult.IsValid)
+            //     throw new UserFriendlyException(
+            //         $"非法参数, {string.Join(',', validResult.Errors.Select(d => d.ErrorMessage))}");
+
+            var wfModule = await GetWorkflowModuleAsync(dto.DefinitionModuleCode, cancellationToken);
+            var definition = wfModule.Definition;
+            if (definition == null)
+                throw new UserFriendlyException("无效模板编码");
+            if (definition.Status is not EDefinitionStatus.Enable)
+                throw new UserFriendlyException("该模板不可用");
+
+            //如果发起会签需检查是否支持发起会签
+            var startStepDefine = definition.FindStartStepDefine();
+
+            //下一节点是否为动态节点
+            var isNextDynamic = startStepDefine.InstanceMode is EInstanceMode.Dynamic &&
+                                !DynamicShouldTerminal(startStepDefine, _sessionContext.OrgLevel);
+            var firstStepDefine = isNextDynamic
+                ? startStepDefine
+                : definition.FindStepDefine(dto.NextStepCode);
+            if (firstStepDefine is null)
+                throw new UserFriendlyException("未查询到下一步节点配置");
+
+            //1. 如果不是按角色指派,handlers必填 2. 如果按角色指派,handlers可以不选
+            if (firstStepDefine.HandlerType is not EHandlerType.Role && !dto.NextHandlers.Any())
+                throw UserFriendlyException.SameMessage("未指派办理人");
+
+            if (dto.IsStartCountersign)
+            {
+                if (!startStepDefine.CanStartCountersign)
+                    throw new UserFriendlyException("当前节点不支持发起会签");
+                //if (startStepDefine.HandlerType is EHandlerType.Role)
+                //    throw new UserFriendlyException("当前节点不支持发起会签");
+                //即使当前节点支持发起会签,但下一节点为信息汇总节点、结束节点时也不可发起会签
+                if (firstStepDefine.StepType is EStepType.Summary or EStepType.End)
+                    throw new UserFriendlyException("下一节点不允许发起会签");
+                //下一节点是会签汇总节点也不允许发起会签
+                if (dto.BackToCountersignEnd)
+                    throw new UserFriendlyException("下一节点不允许发起会签");
+            }
+
+            var workflow = await CreateWorkflowAsync(wfModule, dto.Title,
+                _sessionContext.RequiredUserId, _sessionContext.RequiredOrgId,
+                externalId, cancellationToken);
+
+            var startStep = CreateStartStep(workflow, startStepDefine, dto,
+                new FlowStepHandler
+                {
+                    Key = _sessionContext.RequiredUserId,
+                    Value = _sessionContext.UserName,
+                    UserId = _sessionContext.UserId,
+                    Username = _sessionContext.UserName,
+                    OrgId = _sessionContext.RequiredOrgId,
+                    OrgName = _sessionContext.OrgName,
+                    // RoleId = defineHandler.Key,
+                    // RoleName = defineHandler.Value,
+                }, expiredTime);
+
+            if (dto.Files.Any())
+                startStep.FileJson =
+                    await _fileRepository.AddFileAsync(dto.Files, workflow.ExternalId, startStep.Id, cancellationToken);
+
+            await _workflowStepRepository.AddAsync(startStep, cancellationToken);
+            workflow.Steps.Add(startStep);
+
+            //starttrace
+            var startTrace = _mapper.Map<WorkflowTrace>(startStep);
+            startTrace.StepId = startStep.Id;
+            startTrace.TraceType = EWorkflowTraceType.Normal;
+            await _workflowTraceRepository.AddAsync(startTrace, cancellationToken);
+            workflow.Traces.Add(startTrace);
+            startStep.WorkflowTrace = startTrace;
+
+            //todo refactor:新增&更新整合
+            //更新受理人信息
+            workflow.UpdateAcceptor(
+                _sessionContext.RequiredUserId,
+                _sessionContext.UserName,
+                _sessionContext.StaffNo,
+                _sessionContext.RequiredOrgId,
+                _sessionContext.OrgName);
+
+            //发起会签时记录顶层会签节点(必须在update currentStep之后)
+            var counterSignType = GetCounterSignType(dto.IsStartCountersign);
+            if (dto.IsStartCountersign && !workflow.IsInCountersign)
+                workflow.StartCountersign(startStep.Id, counterSignType);
+
+            await _workflowRepository.UpdateAsync(workflow, cancellationToken);
+
+            return startStep;
+        }
+
+        /// <summary>
+        /// new
+        /// </summary>
+        public async Task<List<WorkflowStep>> NextAsync(NextWorkflowDto dto,
+            DateTime? expiredTime = null, CancellationToken cancellationToken = default)
+        {
+            var workflow = await GetWorkflowAsync(dto.WorkflowId, withDefine: true, withSteps: true,
+            withTraces: true, withCountersigns: true, cancellationToken: cancellationToken);
+            CheckWhetherRunnable(workflow.Status);
+
+        //var currentStep = _workflowDomainService.FindCurrentStepWaitForHandle(workflow,
+        //    current.RequiredUserId, current.RequiredOrgId, current.Roles);
+        var currentStep = workflow.Steps.FirstOrDefault(d => d.Id == dto.StepId);
+        if (currentStep == null)
+            throw new UserFriendlyException(
+                $"未找到对应节点, workflowId: {dto.WorkflowId}, stepId: {dto.StepId}", "未找到对应节点");
+        if (currentStep.Status is EWorkflowStepStatus.Handled)
+            throw new UserFriendlyException("该状态不支持继续办理");
+
+        var currentStepDefine = GetStepDefine(workflow.WorkflowDefinition, currentStep.Code);
+
+        //下一节点是否为动态节点
+        var isNextDynamic = currentStepDefine.InstanceMode is EInstanceMode.Dynamic &&
+                            !DynamicShouldTerminal(currentStepDefine, _sessionContext.OrgLevel);
+
+        StepDefine nextStepDefine;
+        if (isNextDynamic
+            || (currentStep.IsInCountersign() && !currentStep.IsTopCountersignEndStep(workflow.TopCountersignStepId))
+            || dto.IsStartCountersign)
+        {
+            //下一步配置为当前节点配置
+            nextStepDefine = currentStepDefine;
+        }
+        else
+        {
+            //下一步配置为下一步节点配置
+            nextStepDefine = GetStepDefine(workflow.WorkflowDefinition, dto.NextStepCode);
+        }
+
+        //需求:按角色选择办理人可以不选,表示该角色下所有人都可以办理,同时依据配置:是否本部门人办理显示待选办理人。角色下只要一人办理即可(即:角色下不发起会签)
+        if (nextStepDefine.HandlerType != EHandlerType.Role && !dto.NextHandlers.Any())
+            throw new UserFriendlyException("未指定节点处理者");
+
+        if (dto.IsStartCountersign)
+        {
+            if (!currentStepDefine.CanStartCountersign)
+                throw new UserFriendlyException("当前节点不支持发起会签");
+            //if (currentStepDefine.HandlerType is EHandlerType.Role)
+            //    throw new UserFriendlyException("当前节点不支持发起会签");
+            //即使当前节点支持发起会签,但下一节点为信息汇总节点、结束节点时也不可发起会签
+            if (nextStepDefine.StepType is EStepType.Summary or EStepType.End)
+                throw new UserFriendlyException("下一节点不允许发起会签");
+            //下一节点是会签汇总节点也不允许发起会签
+            if (dto.BackToCountersignEnd)
+                throw new UserFriendlyException("下一节点不允许发起会签");
+        }
+
+        var flowAssignInfo =
+            await GetNextStepFlowAssignInfoAsync(workflow, currentStep, dto, nextStepDefine, isNextDynamic, cancellationToken);
+        
+
+         #region 办理当前节点
+
+            if (dto.Files != null && dto.Files.Any())
+                currentStep.FileJson = await _fileRepository.AddFileAsync(dto.Files, workflow.ExternalId,
+                    currentStep.Id, cancellationToken);
+
+            //(currentStep.IsInCountersign() && !dto.BackToCountersignEnd) || dto.IsStartCountersign;
+            var isStartCountersign = currentStep.CountersignPosition switch
+            {
+                ECountersignPosition.None => dto.IsStartCountersign,
+                ECountersignPosition.Multi => !dto.BackToCountersignEnd,
+                ECountersignPosition.Single => !dto.BackToCountersignEnd,
+                ECountersignPosition.End => dto.IsStartCountersign,
+                _ => throw new ArgumentOutOfRangeException()
+            };
+
+            var counterSignType = GetCounterSignType(dto.IsStartCountersign);
+
+            var updateSteps = new List<WorkflowStep> { currentStep };
+
+            //结束当前会签流程
+            if (currentStep.IsCountersignEndStep)
+            {
+                var countersignStartStep =
+                    workflow.Steps.FirstOrDefault(d => d.Id == currentStep.CountersignStartStepId);
+                if (countersignStartStep is null)
+                    throw new UserFriendlyException(
+                        $"未查询到会签开始step, workflowId: {workflow.Id}, currentStepId: {currentStep.Id}",
+                        "未查询到会签开始节点");
+
+                if (countersignStartStep.IsStartCountersign)
+                {
+                    var currentCountersign =
+                        workflow.Countersigns.FirstOrDefault(d => d.Id == countersignStartStep.StartCountersignId);
+                    if (currentCountersign is null)
+                        throw new UserFriendlyException(
+                            $"未查询到对应会签信息,workflowId:{workflow.Id}, countersignId:{currentStep.CountersignId}",
+                            "无效会签编号");
+
+                    //结束step会签信息
+                    countersignStartStep.CountersignEnd();
+                    updateSteps.Add(countersignStartStep);
+
+                    //结束会签
+                    currentCountersign.End(currentStep.Id, currentStep.Code, currentStep.BusinessType,
+                        _sessionContext.RequiredUserId, _sessionContext.UserName,
+                        _sessionContext.RequiredOrgId, _sessionContext.OrgName,
+                        _sessionContext.OrgAreaCode, _sessionContext.OrgAreaName);
+                    await _workflowCountersignRepository.UpdateAsync(currentCountersign, cancellationToken);
+                }
+            }
+
+            await HandleStepAsync(currentStep, workflow, dto, counterSignType, expiredTime, cancellationToken);
+
+            //创建会签数据
+            if (isStartCountersign)
+            {
+                var exists = workflow.Countersigns.Any(d =>
+                    !d.IsCompleted() && d.StarterId == _sessionContext.RequiredUserId);
+                if (exists)
+                    throw new UserFriendlyException("该用户在当前流程存在未结束会签");
+                await StartCountersignAsync(_sessionContext, workflow, currentStep, dto, flowAssignInfo.FlowAssignType,
+                    counterSignType, expiredTime, cancellationToken);
+            }
+
+            currentStep.IsActualHandled = CheckIsActualHandle(workflow, currentStep, nextStepDefine, dto);
+
+            _mapper.Map(dto, workflow);
+            
+            //会签办理节点办理时更新会签members字段
+            if (currentStep.CountersignPosition is ECountersignPosition.Multi or ECountersignPosition.Single)
+            {
+                if (!string.IsNullOrEmpty(currentStep.CountersignId))
+                {
+                    //会签中正常办理节点,更新会签members办理状态
+                    var countersign =
+                        workflow.Countersigns.FirstOrDefault(d =>
+                            !d.IsCompleted() && d.Id == currentStep.CountersignId);
+                    if (countersign is not null)
+                    {
+                        //throw new UserFriendlyException(
+                        //    $"会签数据异常, workflowId: {currentStep.WorkflowId}, countersignId: {currentStep.CountersignId}",
+                        //    "会签数据异常");
+                        countersign.MemberHandled(_sessionContext.RequiredUserId, _sessionContext.RequiredOrgId);
+                        //update cs
+                        await _workflowCountersignRepository.UpdateNav(countersign)
+                            .Include(d => d.Members)
+                            .ExecuteCommandAsync();
+                    }
+                }
+            }
+
+            await _workflowStepRepository.UpdateRangeAsync(updateSteps, cancellationToken);
+
+            //更新traces
+            var updateTraces = new List<WorkflowTrace>();
+            foreach (var updateStep in updateSteps)
+            {
+                var updateTrace = workflow.Traces.First(d => d.Id == updateStep.Id);
+                _mapper.Map(updateStep, updateTrace);
+                updateTraces.Add(updateTrace);
+            }
+
+            await _workflowTraceRepository.UpdateRangeAsync(updateTraces, cancellationToken);
 
+            //var trace = await NextTraceAsync(workflow, dto, currentStep, cancellationToken);
+
+            #endregion
+
+            #region 处理流程
+
+            //检查会签是否结束,并更新当前会签节点字段
+            var isCountersignOver = false;
+            if (workflow.IsInCountersign && currentStep.IsCountersignEndStep)
+            {
+                isCountersignOver = workflow.CheckIfCountersignOver();
+                if (isCountersignOver)
+                    workflow.EndCountersign();
+            }
+
+            if (workflow.ActualHandleStepId == currentStep.Id)
+            {
+                //更新实际办理节点信息
+                workflow.UpdateActualStepWhenHandle(currentStep, _sessionContext.OrgAreaCode, _sessionContext.OrgAreaName, _sessionContext.OrgLevel);
+            }
+
+            if (workflow.CurrentStepId == currentStep.Id)
+            {
+                workflow.UpdateCurrentStepWhenHandle(currentStep, _sessionContext.OrgAreaCode, _sessionContext.OrgAreaName, _sessionContext.OrgLevel);
+            }
+
+            //检查是否流转到流程终点
+            if (nextStepDefine.StepType is EStepType.End)
+            {
+                var endTrace = await EndAsync(workflow, dto, nextStepDefine, currentStep, expiredTime, cancellationToken);
+                return new List<WorkflowStep>();
+            }
+
+            //创建下一/N个节点(会签汇总节点:会签未全部办理时不创建,最后一个会签办理节点创建会签汇总节点)
+            var nextSteps = await CreateNextStepsAsync(workflow, currentStep, dto,
+                nextStepDefine, isNextDynamic, flowAssignInfo.FlowAssignType, expiredTime, isStartCountersign,
+                cancellationToken);
+
+            ////赋值当前节点的下级办理节点
+            //if (dto.IsStartCountersign
+            //   //|| (currentStep.IsInCountersign() &&
+            //   //    !currentStep.IsTopCountersignEndStep(workflow.TopCountersignStepId))
+            //   )
+            //{
+            //    currentStep.CreateCountersignSteps(nextSteps);
+            //    await _workflowStepRepository.UpdateAsync(currentStep, cancellationToken);
+            //}
+
+            // //更新办理对象(nextSteps无元素表示当前节点为会签办理节点且当前会签没有全部办理完成)
             // workflow.UpdateHandlers(current.RequiredUserId, current.RequiredOrgId,
-            //     flowAssignInfo.FlowAssignType, flowAssignInfo.HandlerObjects, true);
+            //     flowAssignInfo.FlowAssignType, flowAssignInfo.HandlerObjects, nextSteps.Any());
+
+            //todo 计算办理工作时长
+
+            //指派实际办理节点
+            UpdateActualStep(workflow, dto, nextStepDefine, nextSteps);
+
+            //更新实际办理节点
+            UpdateCurrentStep(workflow, dto, nextStepDefine, nextSteps);
+
+            //发起会签时记录顶层会签节点
+            if (dto.IsStartCountersign && !workflow.IsInCountersign)
+                workflow.StartCountersign(currentStep.Id, counterSignType);
 
             //更新指派信息
             //workflow.Assign(flowAssignInfo.FlowAssignType, flowAssignInfo.GetHandlerIds());
 
+            //更新会签实际办理对象信息
+            if (currentStep.IsActualHandled)
+                workflow.AddCsActualHandler(_sessionContext.RequiredUserId, _sessionContext.RequiredOrgId);
+
             await _workflowRepository.UpdateAsync(workflow, cancellationToken);
 
-            //publish
-            await _publisher.PublishAsync(new StartWorkflowNotify(workflow, dto, flowAssignInfo, trace),
-                PublishStrategy.ParallelWhenAll, cancellationToken);
+            #endregion
+
+            #region 流转记录
+
+            //var trace = await NextTraceAsync(workflow, dto, currentStep, cancellationToken);
+
+            #endregion
+
+            var currentTrace = workflow.Traces.First(d => d.Id == currentStep.Id);
+            await _publisher.PublishAsync(
+                new NextStepNotify(workflow, dto, flowAssignInfo, currentTrace, nextStepDefine,
+                    _sessionContext.RequiredOrgId, expiredTime.HasValue), PublishStrategy.ParallelWhenAll,
+                cancellationToken);
+            
+            return nextSteps;
         }
 
         public async Task<Workflow> GetWorkflowAsync(string workflowId,
@@ -235,8 +556,7 @@ namespace Hotline.FlowEngine.Workflows
         /// <summary>
         /// 查询工作流包含当前用户结束会签权限(是否可结束)
         /// </summary>
-        public async Task<(Workflow Workflow, string? CountersignId, bool CanHandle, bool CanPrevious, WorkflowTrace?
-                Trace)>
+        public async Task<(Workflow Workflow, string? CountersignId, bool CanHandle, bool CanPrevious, WorkflowTrace? Trace)>
             GetWorkflowHandlePermissionAsync(
                 string workflowId, string userId, string orgId, string[] roleIds,
                 CancellationToken cancellationToken = default)
@@ -379,8 +699,7 @@ namespace Hotline.FlowEngine.Workflows
                 }
             }
 
-            await HandleStepAsync(currentStep, workflow, dto, flowAssignInfo.FlowAssignType,
-                counterSignType, expiredTime, cancellationToken);
+            await HandleStepAsync(currentStep, workflow, dto, counterSignType, expiredTime, cancellationToken);
 
             //创建会签数据
             if (isStartCountersign)
@@ -492,7 +811,7 @@ namespace Hotline.FlowEngine.Workflows
 
             //创建下一/N个节点(会签汇总节点:会签未全部办理时不创建,最后一个会签办理节点创建会签汇总节点)
             var nextSteps = await CreateNextStepsAsync(workflow, currentStep, dto,
-                nextStepDefine, isNextDynamic, flowAssignInfo, expiredTime, isStartCountersign,
+                nextStepDefine, isNextDynamic, flowAssignInfo.FlowAssignType, expiredTime, isStartCountersign,
                 cancellationToken);
 
             ////赋值当前节点的下级办理节点
@@ -953,21 +1272,32 @@ namespace Hotline.FlowEngine.Workflows
                         EBusinessType.Seat => EFlowDirection.CenterToCenter,
                         EBusinessType.Send => EFlowDirection.CenterToCenter,
                         EBusinessType.Department => EFlowDirection.CenterToOrg,
+                        EBusinessType.DepartmentLeader => EFlowDirection.CenterToOrg,
                         EBusinessType.File => EFlowDirection.CenterToFile,
                         _ => throw new ArgumentOutOfRangeException(nameof(directionStepBusinessType),
                             directionStepBusinessType, null)
                     };
                 case EBusinessType.Department:
-                case EBusinessType.File:
+                case EBusinessType.DepartmentLeader:
                     return directionStepBusinessType switch
                     {
                         EBusinessType.Seat => EFlowDirection.OrgToCenter,
                         EBusinessType.Send => EFlowDirection.OrgToCenter,
                         EBusinessType.Department => EFlowDirection.OrgToOrg,
+                        EBusinessType.DepartmentLeader => EFlowDirection.OrgToOrg,
                         EBusinessType.File => EFlowDirection.OrgToFile,
                         _ => throw new ArgumentOutOfRangeException(nameof(directionStepBusinessType),
                             directionStepBusinessType, null)
                     };
+                case EBusinessType.File:
+                    return directionStepBusinessType switch
+                    {
+                        EBusinessType.Seat => EFlowDirection.FiledToCenter,
+                        EBusinessType.Send => EFlowDirection.FiledToCenter,
+                        EBusinessType.Department => EFlowDirection.FiledToOrg,
+                        EBusinessType.DepartmentLeader => EFlowDirection.FiledToOrg,
+                        _ => throw new ArgumentOutOfRangeException(nameof(directionStepBusinessType), directionStepBusinessType, null)
+                    };
                 default:
                     throw new ArgumentOutOfRangeException(nameof(sourceStepBusinessType), sourceStepBusinessType, null);
             }
@@ -1083,7 +1413,7 @@ namespace Hotline.FlowEngine.Workflows
         /// <summary>
         /// 撤回至开始节点
         /// </summary>
-        public async Task RecallToStartStepAsync(string workflowId, string opinion, bool isOrderFiled,DateTime? expiredTime,
+        public async Task RecallToStartStepAsync(string workflowId, string opinion, bool isOrderFiled, DateTime? expiredTime,
             CancellationToken cancellationToken)
         {
             //todo 1.当前待办节点删掉 2.当前待办trace更新(status, opinion) 3.复制startStep为待办 4.更新workflow(status, csStatus, handlers) 5.publish event
@@ -1122,13 +1452,14 @@ namespace Hotline.FlowEngine.Workflows
             var flowAssignInfo = await GetNextStepFlowAssignInfoByDefineAsync(targetStepDefine, dto.HandlerType, dto.IsStartCountersign,
                 dto.NextHandlers.Select(d => new Kv(d.Key, d.Value)).ToList(), cancellationToken);
             //flowAssignInfo.FlowAssignType = EFlowAssignType.Role;
-            await RecallAsync(workflow, dto, targetStepDefine, flowAssignInfo, EWorkflowTraceType.Recall, expiredTime, isOrderFiled, cancellationToken);
+            await RecallAsync(workflow, dto, targetStepDefine, flowAssignInfo, EWorkflowTraceType.Recall, expiredTime, isOrderFiled,
+                cancellationToken);
         }
 
         /// <summary>
         /// 撤回至派单节点
         /// </summary>
-        public async Task RecallToSendStepAsync(string workflowId, string opinion, bool isOrderFiled,DateTime? expiredTime,
+        public async Task RecallToSendStepAsync(string workflowId, string opinion, bool isOrderFiled, DateTime? expiredTime,
             CancellationToken cancellationToken)
         {
             var workflow = await GetWorkflowAsync(workflowId, withDefine: true, withSteps: true, withTraces: true, withCountersigns: true,
@@ -1166,7 +1497,8 @@ namespace Hotline.FlowEngine.Workflows
             var flowAssignInfo = await GetNextStepFlowAssignInfoByDefineAsync(targetStepDefine, dto.HandlerType, dto.IsStartCountersign,
                 dto.NextHandlers.Select(d => new Kv(d.Key, d.Value)).ToList(), cancellationToken);
             //flowAssignInfo.FlowAssignType = EFlowAssignType.Role;
-            await RecallAsync(workflow, dto, targetStepDefine, flowAssignInfo, EWorkflowTraceType.Recall, expiredTime, isOrderFiled, cancellationToken);
+            await RecallAsync(workflow, dto, targetStepDefine, flowAssignInfo, EWorkflowTraceType.Recall, expiredTime, isOrderFiled,
+                cancellationToken);
         }
 
         /// <summary>
@@ -1174,7 +1506,7 @@ namespace Hotline.FlowEngine.Workflows
         /// </summary>
         /// <returns></returns>
         public async Task RecallToCenterFirstToSendAsync(string workflowId, string opinion, bool isOrderFiled,
-            DateTime? expiredTime,  CancellationToken cancellationToken)
+            DateTime? expiredTime, CancellationToken cancellationToken)
         {
             var workflow = await GetWorkflowAsync(workflowId, withDefine: true, withSteps: true, withTraces: true, withCountersigns: true,
                 cancellationToken: cancellationToken);
@@ -1207,7 +1539,8 @@ namespace Hotline.FlowEngine.Workflows
                 };
                 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, cancellationToken);
+                await RecallAsync(workflow, dto, targetStepDefine, flowAssignInfo, EWorkflowTraceType.Recall, expiredTime, isOrderFiled,
+                    cancellationToken);
             }
             else
             {
@@ -1244,7 +1577,8 @@ namespace Hotline.FlowEngine.Workflows
                 var flowAssignInfo = await GetNextStepFlowAssignInfoByDefineAsync(targetStepDefine, dto.HandlerType, dto.IsStartCountersign,
                     dto.NextHandlers.Select(d => new Kv(d.Key, d.Value)).ToList(), cancellationToken);
                 //flowAssignInfo.FlowAssignType = EFlowAssignType.Role;
-                await RecallAsync(workflow, dto, targetStepDefine, flowAssignInfo, EWorkflowTraceType.Recall, expiredTime, isOrderFiled, cancellationToken);
+                await RecallAsync(workflow, dto, targetStepDefine, flowAssignInfo, EWorkflowTraceType.Recall, expiredTime, isOrderFiled,
+                    cancellationToken);
             }
         }
 
@@ -1542,7 +1876,7 @@ namespace Hotline.FlowEngine.Workflows
         /// 办理节点
         /// </summary>
         public async Task HandleStepAsync(WorkflowStep step, Workflow workflow,
-            BasicWorkflowDto dto, EFlowAssignType? flowAssignType, ECounterSignType? counterSignType,
+            BasicWorkflowDto dto, ECounterSignType? counterSignType,
             DateTime? expiredTime, CancellationToken cancellationToken)
         {
             if (step.Status is EWorkflowStepStatus.Handled)
@@ -1666,7 +2000,7 @@ namespace Hotline.FlowEngine.Workflows
         /// 创建下1/N个节点
         /// </summary>
         private async Task<List<WorkflowStep>> CreateNextStepsAsync(Workflow workflow, WorkflowStep currentStep,
-            BasicWorkflowDto dto, StepDefine nextStepDefine, bool isNextDynamic, FlowAssignInfo flowAssignInfo,
+            BasicWorkflowDto dto, StepDefine nextStepDefine, bool isNextDynamic, EFlowAssignType flowAssignType,
             DateTime? expiredTime, bool isStartCountersign,
             CancellationToken cancellationToken)
         {
@@ -1683,13 +2017,13 @@ namespace Hotline.FlowEngine.Workflows
                         {
                             //依据会签策略创建会签下一级节点
                             nextSteps = await CreateCountersignStepsAsync(workflow, nextStepDefine, currentStep, dto,
-                                flowAssignInfo.FlowAssignType, expiredTime, isStartCountersign, cancellationToken);
+                                flowAssignType, expiredTime, cancellationToken);
                         }
                         else
                         {
                             //创建普通节点(根据配置)
                             nextSteps = await CreateConfigStepsAsync(workflow, nextStepDefine, currentStep, dto,
-                                flowAssignInfo, EWorkflowTraceType.Normal, expiredTime, cancellationToken);
+                                flowAssignType, EWorkflowTraceType.Normal, expiredTime, cancellationToken);
                         }
                     }
                     else
@@ -1708,7 +2042,7 @@ namespace Hotline.FlowEngine.Workflows
                         {
                             //依据会签策略创建会签下一级节点
                             nextSteps = await CreateCountersignStepsAsync(workflow, nextStepDefine, currentStep, dto,
-                                flowAssignInfo.FlowAssignType, expiredTime, isStartCountersign, cancellationToken);
+                                flowAssignType, expiredTime, cancellationToken);
                         }
                     }
                 }
@@ -1724,7 +2058,7 @@ namespace Hotline.FlowEngine.Workflows
                     {
                         //依据会签策略创建会签下一级节点
                         nextSteps = await CreateCountersignStepsAsync(workflow, nextStepDefine, currentStep, dto,
-                            flowAssignInfo.FlowAssignType, expiredTime, isStartCountersign, cancellationToken);
+                            flowAssignType, expiredTime, cancellationToken);
                     }
                 }
             }
@@ -1732,18 +2066,18 @@ namespace Hotline.FlowEngine.Workflows
             {
                 //依据会签策略创建会签下一级节点
                 nextSteps = await CreateCountersignStepsAsync(workflow, nextStepDefine, currentStep, dto,
-                    flowAssignInfo.FlowAssignType, expiredTime, isStartCountersign, cancellationToken);
+                    flowAssignType, expiredTime, cancellationToken);
             }
             else if (isNextDynamic)
             {
                 //创建动态下一级节点
-                nextSteps = await CreateDynamicStepsAsync(workflow, nextStepDefine, currentStep, dto, flowAssignInfo,
+                nextSteps = await CreateDynamicStepsAsync(workflow, nextStepDefine, currentStep, dto, flowAssignType,
                     expiredTime, cancellationToken);
             }
             else
             {
                 //创建普通节点(根据配置)
-                nextSteps = await CreateConfigStepsAsync(workflow, nextStepDefine, currentStep, dto, flowAssignInfo,
+                nextSteps = await CreateConfigStepsAsync(workflow, nextStepDefine, currentStep, dto, flowAssignType,
                     EWorkflowTraceType.Normal, expiredTime, cancellationToken);
             }
 
@@ -1768,7 +2102,7 @@ namespace Hotline.FlowEngine.Workflows
             StepDefine nextStepDefine,
             WorkflowStep prevStep,
             BasicWorkflowDto dto,
-            FlowAssignInfo flowAssignInfo,
+            EFlowAssignType flowAssignType,
             DateTime? expiredTime,
             CancellationToken cancellationToken)
         {
@@ -1789,7 +2123,7 @@ namespace Hotline.FlowEngine.Workflows
             };
 
             return await CreateStepsAsync(workflow, nextStepDefine, prevStep, dto,
-                flowAssignInfo.FlowAssignType, dto.NextHandlers, null, EWorkflowStepStatus.WaitForAccept,
+                flowAssignType, dto.NextHandlers, null, EWorkflowStepStatus.WaitForAccept,
                 ECountersignPosition.None, false, EWorkflowTraceType.Normal, handlerType, expiredTime,
                 cancellationToken: cancellationToken);
         }
@@ -1801,28 +2135,14 @@ namespace Hotline.FlowEngine.Workflows
             BasicWorkflowDto dto,
             EFlowAssignType flowAssignType,
             DateTime? expiredTime,
-            bool isStartCountersign,
             CancellationToken cancellationToken = default
         )
         {
             //var countersignId = dto.IsStartCountersign ? prevStep.StartCountersignId : prevStep.CountersignId;
             var countersignId = prevStep.StartCountersignId;
 
-            var handlerType = stepDefine.CountersignPolicy switch
-            {
-                EDynamicPolicyCountersign.OrgUpCenterTop => EHandlerType.OrgLevel,
-                EDynamicPolicyCountersign.OrgUp => EHandlerType.OrgLevel,
-                EDynamicPolicyCountersign.OrgUpHandleCenterTop => EHandlerType.OrgLevel,
-                EDynamicPolicyCountersign.OrgUpHandle => EHandlerType.OrgLevel,
-                EDynamicPolicyCountersign.OrgUpLeadCenterTop => EHandlerType.OrgLevel,
-                EDynamicPolicyCountersign.OrgUpLead => EHandlerType.OrgLevel,
-                EDynamicPolicyCountersign.ArriveCenter => EHandlerType.OrgLevel,
-                EDynamicPolicyCountersign.ArriveOneOrg => EHandlerType.OrgLevel,
-                EDynamicPolicyCountersign.OrgDownCenterTop => EHandlerType.OrgLevel,
-                EDynamicPolicyCountersign.OrgDown => EHandlerType.OrgLevel,
-                null => throw new ArgumentOutOfRangeException(),
-                _ => throw new ArgumentOutOfRangeException()
-            };
+            //当前策略均为orglevel
+            var handlerType = EHandlerType.OrgLevel;
 
             var nextStepCountersignPosition = dto.NextHandlers.Count > 1
                 ? ECountersignPosition.Multi
@@ -2351,7 +2671,7 @@ namespace Hotline.FlowEngine.Workflows
                 OrgId = targetStep.HandlerOrgId,
                 OrgName = targetStep.HandlerOrgName
             });
-            
+
             //取消维护workflow得冗余字段(FlowedOrgIds, HandlerOrgs)
             //workflow.Assign(flowAssignInfo.FlowAssignType, flowAssignInfo.GetHandlerIds());
             //workflow.ResetHandlers(flowAssignInfo.FlowAssignType, flowAssignInfo.HandlerObjects);
@@ -2417,7 +2737,7 @@ namespace Hotline.FlowEngine.Workflows
 
             var step = CreateStep(workflow, endStepDefine, prevStep, EFlowAssignType.User, handler,
                 null, null, EWorkflowStepStatus.WaitForAccept,
-                ECountersignPosition.None, expiredTime, endStepDefine.Name, true);
+                ECountersignPosition.None, expiredTime, endStepDefine.Name, true, businessType: EBusinessType.File);
 
             //step.Accept(_sessionContext.RequiredUserId, _sessionContext.UserName,
             //    _sessionContext.RequiredOrgId, _sessionContext.OrgName,
@@ -2439,7 +2759,7 @@ namespace Hotline.FlowEngine.Workflows
             StepDefine stepDefine,
             WorkflowStep prevStep,
             BasicWorkflowDto dto,
-            FlowAssignInfo flowAssignInfo,
+            EFlowAssignType flowAssignType,
             EWorkflowTraceType traceType,
             DateTime? expiredTime,
             CancellationToken cancellationToken)
@@ -2460,8 +2780,8 @@ namespace Hotline.FlowEngine.Workflows
                 handlers = dto.NextHandlers;
             }
 
-            return await CreateStepsAsync(workflow, stepDefine, prevStep, dto, /*dto.IsStartCountersign,*/
-                flowAssignInfo.FlowAssignType, handlers, null,
+            return await CreateStepsAsync(workflow, stepDefine, prevStep, dto,
+                flowAssignType, handlers, null,
                 EWorkflowStepStatus.WaitForAccept, ECountersignPosition.None,
                 true, traceType, null, expiredTime, cancellationToken);
         }
@@ -2491,18 +2811,11 @@ namespace Hotline.FlowEngine.Workflows
                 var step = CreateStep(workflow, stepDefine, prevStep, flowAssignType,
                     handler, dto.NextStepCode, countersignId, stepStatus, csPosition, expiredTime,
                     dto.NextStepName, isOrigin, isMain, handlerType, dto.BusinessType);
-
-                //var stepHandler = stepHandlers.First(d => d.GetHandler().Key == handler.Key);
-                //step.StepHandlers = new List<WorkflowStepHandler> { stepHandler };
-
                 steps.Add(step);
             }
 
             await _workflowStepRepository.AddRangeAsync(steps, cancellationToken);
             workflow.Steps.AddRange(steps);
-            //await _workflowStepRepository.AddNav(steps)
-            //    .Include(d => d.StepHandlers)
-            //    .ExecuteCommandAsync();
 
             //create traces todo add range traces
             foreach (var step in steps)
@@ -2871,6 +3184,175 @@ namespace Hotline.FlowEngine.Workflows
             }
         }
 
+        //new
+
+        /// <summary>
+        /// 查询流程业务模块
+        /// </summary>
+        /// <param name="code"></param>
+        /// <param name="cancellationToken"></param>
+        /// <returns></returns>
+        /// <exception cref="UserFriendlyException"></exception>
+        private async Task<WorkflowModule> GetWorkflowModuleAsync(string code, CancellationToken cancellationToken)
+        {
+            var wfModule = await _wfModuleCacheManager.GetWorkflowModuleAsync(code, cancellationToken);
+            if (wfModule == null)
+                throw UserFriendlyException.SameMessage("无效流程模块编码");
+            if (wfModule.Definition is null)
+                throw new UserFriendlyException($"{code} 未配置流程模板", "未配置流程模板");
+            return wfModule;
+        }
+        //
+        // private StepAssignInfo GetStepAssignInfo(TargetStepAssignPolicyInfo targetStepAssignPolicyInfo, bool anyHandlers,
+        //     StepDefine? stepDefine = null, WorkflowStep? targetStep = null, FlowStepHandler? handler = null)
+        // {
+        //     switch (targetStepAssignPolicyInfo.TargetStepAssignPolicy)
+        //     {
+        //         case ETargetStepAssignPolicy.Config:
+        //             if (stepDefine is null) throw new UserFriendlyException($"{nameof(stepDefine)} is null");
+        //             return new StepAssignInfo
+        //             {
+        //                 FlowAssignType = GetNextStepFlowAssignTypeByDefine(stepDefine, anyHandlers),
+        //                 //todo 
+        //                 // Handler = 
+        //             };
+        //         case ETargetStepAssignPolicy.TargetStep:
+        //             if (targetStep is null) throw new UserFriendlyException($"{nameof(targetStep)} is null");
+        //             if (targetStep.FlowAssignType is null) throw new UserFriendlyException($"targetStep.FlowAssignType is null");
+        //             return new StepAssignInfo
+        //             {
+        //                 FlowAssignType = targetStep.FlowAssignType.Value,
+        //                 Handler = targetStep.GetWorkflowStepHandler()
+        //             };
+        //         case ETargetStepAssignPolicy.AssignHandler:
+        //             if(handler is null) throw new UserFriendlyException($"{nameof(handler)} is null");
+        //             return new()
+        //             {
+        //                 FlowAssignType = EFlowAssignType.User,
+        //                 Handler = handler
+        //             };
+        //         default:
+        //             throw new ArgumentOutOfRangeException();
+        //     }
+        // }
+        //
+        // /// <summary>
+        // /// 按流程模板配置获取下一节点指派类型
+        // /// </summary>
+        // private EFlowAssignType GetNextStepFlowAssignTypeByDefine(StepDefine nextStepDefine, bool anyHandlers)
+        // {
+        //     switch (nextStepDefine.HandlerType)
+        //     {
+        //         case EHandlerType.Role:
+        //             return !anyHandlers ? EFlowAssignType.Role : EFlowAssignType.User;
+        //
+        //         case EHandlerType.OrgLevel:
+        //         case EHandlerType.OrgType:
+        //         case EHandlerType.AssignedOrg:
+        //             return EFlowAssignType.Org;
+        //         
+        //         case EHandlerType.AssignedUser:
+        //             return EFlowAssignType.User;
+        //         
+        //         default:
+        //             throw new ArgumentOutOfRangeException();
+        //     }
+        // }
+
+        /// <summary>
+    /// 查询下一节点办理对象类型(user or org)及实际办理对象
+    /// </summary>
+    private async Task<FlowAssignInfo> GetNextStepFlowAssignInfoAsync(Workflow workflow, WorkflowStep currentStep,
+        BasicWorkflowDto dto, StepDefine nextStepDefine, bool isNextDynamic, CancellationToken cancellationToken)
+    {
+        if (nextStepDefine.StepType is EStepType.End) return new();
+
+        var isStartCountersign = dto.IsStartCountersign;
+        var handlers = dto.NextHandlers.Select(d => new Kv(d.Key, d.Value)).ToList();
+
+        if (isStartCountersign)
+        {
+            var assignType = FlowAssignInfo.GetAssignType(dto.HandlerType, dto.NextHandlers.Any());
+            //按会签策略判断,目前所有策略为org
+            return FlowAssignInfo.Create(assignType, handlers, isStartCountersign);
+        }
+
+        //if (currentStep.IsInCountersign() && !currentStep.IsTopCountersignEndStep(workflow.TopCountersignStepId))
+        //    return FlowAssignInfo.Create(EFlowAssignType.Org, handlers, isStartCountersign);
+
+        if (currentStep.IsInCountersign())
+        {
+            if (currentStep.IsCountersignEndStep)
+            {
+                //汇总节点(非顶级)
+                if (!currentStep.IsTopCountersignEndStep(workflow.TopCountersignStepId))
+                {
+                    if (dto.BackToCountersignEnd)
+                    {
+                        var csStartStep = GetCsLoopStartStep(workflow, currentStep);
+                        var prevStep = workflow.Steps.FirstOrDefault(d => d.Id == csStartStep.PrevStepId);
+                        if (prevStep is null)
+                            throw new UserFriendlyException("未查询到目标节点的前一节点");
+                        return FlowAssignInfo.Create(prevStep.FlowAssignType.Value, prevStep.Handlers, isStartCountersign);
+                    }
+                }
+            }
+            else
+            {
+                if (dto.BackToCountersignEnd)
+                {
+                    var prevStep = workflow.Steps.FirstOrDefault(d => d.Id == currentStep.PrevStepId);
+                    if (prevStep is null)
+                        throw new UserFriendlyException($"未查询到当前节点的上级节点");
+                    return FlowAssignInfo.Create(prevStep.FlowAssignType.Value, prevStep.Handlers, isStartCountersign);
+                }
+                else
+                {
+                    var assignType = FlowAssignInfo.GetAssignType(dto.HandlerType, dto.NextHandlers.Any());
+                    //按会签策略判断,目前所有策略为org
+                    return FlowAssignInfo.Create(assignType, handlers, isStartCountersign);
+                }
+            }
+        }
+
+        if (isNextDynamic)
+        {
+            switch (currentStep.InstancePolicy)
+            {
+                case EDynamicPolicy.OrgUpCenterTop:
+                case EDynamicPolicy.OrgUp:
+                case EDynamicPolicy.OrgDownCenterTop:
+                case EDynamicPolicy.OrgDown:
+                case EDynamicPolicy.ArriveCenter:
+                case EDynamicPolicy.ArriveOneOrg:
+                    return FlowAssignInfo.Create(EFlowAssignType.Org, handlers, isStartCountersign);
+                case EDynamicPolicy.OrgUpHandleCenterTop:
+                case EDynamicPolicy.OrgUpHandle:
+                case EDynamicPolicy.OrgUpLeadCenterTop:
+                case EDynamicPolicy.OrgUpLead:
+                    return FlowAssignInfo.Create(EFlowAssignType.OrgAndRole, handlers, isStartCountersign);
+                default:
+                    throw new ArgumentOutOfRangeException();
+            }
+        }
+
+        return await GetNextStepFlowAssignInfoByDefineAsync(nextStepDefine, dto.HandlerType, isStartCountersign, handlers,
+            cancellationToken);
+    }
+        
+        private WorkflowStep GetCsLoopStartStep(Workflow workflow, WorkflowStep currentStep)
+        {
+            var startCountersignStep =
+                workflow.Steps.FirstOrDefault(d => d.Id == currentStep.CountersignStartStepId);
+            if (startCountersignStep is null)
+                throw new UserFriendlyException(
+                    $"未查询到会签开始节点,workflowId: {workflow.Id}, startStepId: {currentStep.CountersignStartStepId}",
+                    "未查询到会签开始节点,数据异常");
+            if (!startCountersignStep.IsCountersignEndStep)
+                return startCountersignStep;
+            return GetCsLoopStartStep(workflow, startCountersignStep);
+        }
+        
         #endregion
     }
 }

+ 8 - 1
src/Hotline/Settings/TimeLimitDomain/ExpireTimeSupplier/HourSupplier.cs

@@ -11,6 +11,13 @@ public class HourSupplier : IExpireTimeSupplier, IScopeDependency
 {
     public async Task<TimeResult> CalcEndTimeAsync(DateTime beginTime, TimeConfig timeConfig)
     {
-        return new TimeResult { EndTime = beginTime.AddHours(timeConfig.Count), RuleStr = timeConfig.Count + "个小时", NearlyExpiredTime = beginTime };
+        return new TimeResult
+        {
+            EndTime =
+            beginTime.AddHours(timeConfig.Count),
+            RuleStr = timeConfig.Count + "个小时",
+            NearlyExpiredTime = beginTime.AddHours(timeConfig.Count * ((double)timeConfig.Percentage / 100)),
+            NearlyExpiredTimeOne = beginTime.AddHours(timeConfig.Count * ((double)timeConfig.PercentageOne / 100))
+        };
     }
 }

+ 4 - 4
src/XF.Domain.Repository/Entity.cs

@@ -175,7 +175,7 @@ public abstract class WorkflowEntity : FullStateEntity, IWorkflow
         switch (type)
         {
             case EFlowAssignType.Org:
-                var orgCodes = handler.GetHigherOrgCodes(true).ToList();
+                var orgCodes = handler.GetHigherOrgIds(true).ToList();
                 FlowedOrgIds.AddRange(orgCodes);
                 FlowedOrgIds = FlowedOrgIds.Distinct().ToList();
                 break;
@@ -195,7 +195,7 @@ public abstract class WorkflowEntity : FullStateEntity, IWorkflow
         switch (type)
         {
             case EFlowAssignType.Org:
-                var orgCodes = handlers.SelectMany(d => d.GetHigherOrgCodes(true)).ToList();
+                var orgCodes = handlers.SelectMany(d => d.GetHigherOrgIds(true)).ToList();
                 FlowedOrgIds.AddRange(orgCodes);
                 FlowedOrgIds = FlowedOrgIds.Distinct().ToList();
                 break;
@@ -347,7 +347,7 @@ public abstract class PositionWorkflowEntity : PositionEntity, IWorkflow
         switch (type)
         {
             case EFlowAssignType.Org:
-                var orgCodes = handler.GetHigherOrgCodes(true).ToList();
+                var orgCodes = handler.GetHigherOrgIds(true).ToList();
                 FlowedOrgIds.AddRange(orgCodes);
                 FlowedOrgIds = FlowedOrgIds.Distinct().ToList();
                 break;
@@ -367,7 +367,7 @@ public abstract class PositionWorkflowEntity : PositionEntity, IWorkflow
         switch (type)
         {
             case EFlowAssignType.Org:
-                var orgCodes = handlers.SelectMany(d => d.GetHigherOrgCodes(true)).ToList();
+                var orgCodes = handlers.SelectMany(d => d.GetHigherOrgIds(true)).ToList();
                 FlowedOrgIds.AddRange(orgCodes);
                 FlowedOrgIds = FlowedOrgIds.Distinct().ToList();
                 break;

+ 1 - 1
src/XF.Domain/Extensions/OrgExtensions.cs

@@ -10,7 +10,7 @@ namespace XF.Domain.Extensions
 {
     public static class OrgExtensions
     {
-        public static List<string> GetHigherOrgCodes(this string orgCode, bool withSelf = false)
+        public static List<string> GetHigherOrgIds(this string orgCode, bool withSelf = false)
         {
             if (string.IsNullOrEmpty(orgCode))
                 return new();