Jelajahi Sumber

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

田爽 6 bulan lalu
induk
melakukan
aa014e0470
81 mengubah file dengan 2943 tambahan dan 1092 penghapusan
  1. 40 24
      src/Hotline.Api/Controllers/Bi/BiOrderController.cs
  2. 6 6
      src/Hotline.Api/Controllers/Bigscreen/SeatController.cs
  3. 3 2
      src/Hotline.Api/Controllers/DataSharingController.cs
  4. 14 2
      src/Hotline.Api/Controllers/ExportWordController.cs
  5. 319 216
      src/Hotline.Api/Controllers/OrderController.cs
  6. 1 1
      src/Hotline.Api/Controllers/OrderTerminateController.cs
  7. 8 0
      src/Hotline.Api/Controllers/OrgController.cs
  8. 66 2
      src/Hotline.Api/Controllers/PushMessageController.cs
  9. 0 8
      src/Hotline.Api/Controllers/SysController.cs
  10. 56 6
      src/Hotline.Api/Controllers/WebPortalController.cs
  11. 2 0
      src/Hotline.Api/StartupExtensions.cs
  12. TEMPAT SAMPAH
      src/Hotline.Api/Template/AssignmentForm.doc
  13. 2 34
      src/Hotline.Api/config/appsettings.Development.json
  14. 4 8
      src/Hotline.Application.Contracts/Validators/FlowEngine/BasicWorkflowDtoValidator.cs
  15. 1 0
      src/Hotline.Application.Contracts/Validators/FlowEngine/NextWorkflowDtoValidator.cs
  16. 12 0
      src/Hotline.Application.Contracts/Validators/FlowEngine/RecallDtoValidator.cs
  17. 17 21
      src/Hotline.Application.Contracts/Validators/FlowEngine/StartWorkflowDtoValidator.cs
  18. 18 18
      src/Hotline.Application.Contracts/Validators/Order/OrderStartFlowDtoValidator.cs
  19. 14 14
      src/Hotline.Application.Tests/Application/OrderApplicationTest.cs
  20. 9 116
      src/Hotline.Application/Bigscreen/SeatStateDataService.cs
  21. 128 0
      src/Hotline.Application/Bigscreen/SeatStateDataServiceBase.cs
  22. 124 0
      src/Hotline.Application/Bigscreen/YiBinSeatStateDataService.cs
  23. 7 0
      src/Hotline.Application/FlowEngine/IWorkflowApplication.cs
  24. 191 141
      src/Hotline.Application/FlowEngine/WorkflowApplication.cs
  25. 1 0
      src/Hotline.Application/Hotline.Application.csproj
  26. 5 0
      src/Hotline.Application/Mappers/MapperConfigs.cs
  27. 37 0
      src/Hotline.Application/Mappers/OrderMapperConfigs.cs
  28. 0 37
      src/Hotline.Application/Mappers/WorkflowMapperConfigs.cs
  29. 10 2
      src/Hotline.Application/Orders/IOrderApplication.cs
  30. 196 79
      src/Hotline.Application/Orders/OrderApplication.cs
  31. 48 41
      src/Hotline.Application/Orders/OrderScreenHandler/OrderScreenEndWorkflowHandler.cs
  32. 7 0
      src/Hotline.Application/StatisticalReport/IOrderReportApplication.cs
  33. 31 0
      src/Hotline.Application/StatisticalReport/OrderReportApplication.cs
  34. 0 1
      src/Hotline.Application/Subscribers/DatasharingSubscriber.cs
  35. 2 1
      src/Hotline.Repository.SqlSugar/Extensions/SqlSugarRepositoryExtensions.cs
  36. 1 0
      src/Hotline.Share/Dtos/CallCenter/QueryCallsFixedDto.cs
  37. 2 1
      src/Hotline.Share/Dtos/CallCenter/QueryTelOperationsFixedDto.cs
  38. 2 2
      src/Hotline.Share/Dtos/FlowEngine/BasicWorkflowDto.cs
  39. 5 0
      src/Hotline.Share/Dtos/FlowEngine/NextStepsDto.cs
  40. 50 87
      src/Hotline.Share/Dtos/FlowEngine/NextWorkflowDto.cs
  41. 31 8
      src/Hotline.Share/Dtos/FlowEngine/PreviousWorkflowDto.cs
  42. 42 0
      src/Hotline.Share/Dtos/Order/Handle/GetCrossLevelStepsDto.cs
  43. 266 8
      src/Hotline.Share/Dtos/Order/OrderDto.cs
  44. 29 0
      src/Hotline.Share/Dtos/Order/OrderScreenDto.cs
  45. 142 23
      src/Hotline.Share/Dtos/Order/OrderStartFlowDto.cs
  46. 11 1
      src/Hotline.Share/Dtos/Order/OrderVisitDto.cs
  47. 47 26
      src/Hotline.Share/Dtos/Order/OrderWaitedDto.cs
  48. 5 0
      src/Hotline.Share/Dtos/Order/Publish/QueryOrderPublishDto.cs
  49. 10 2
      src/Hotline.Share/Dtos/Order/QueryOrderDto.cs
  50. 9 0
      src/Hotline.Share/Dtos/Order/TargetStepInfo.cs
  51. 5 0
      src/Hotline.Share/Dtos/OrderExportWord/OrderSubmissionForm.cs
  52. 71 59
      src/Hotline.Share/Dtos/Push/MessageDto.cs
  53. 1 0
      src/Hotline.Share/Enums/FlowEngine/EBusinessType.cs
  54. 7 1
      src/Hotline.Share/Enums/Push/EPushBusiness.cs
  55. 1 1
      src/Hotline.Share/Hotline.Share.csproj
  56. 3 2
      src/Hotline.Share/QueryFixedDto.cs
  57. 5 0
      src/Hotline.Share/Requests/PagedKeywordRequest.cs
  58. 19 0
      src/Hotline.Share/Tools/IQueryFixedDto.cs
  59. 14 0
      src/Hotline.Share/Tools/StringExtensions.cs
  60. 12 0
      src/Hotline.XingTang/CallTelClient.cs
  61. 17 0
      src/Hotline.XingTang/ServiceCollectionExtensions.cs
  62. 48 0
      src/Hotline/Authentications/FakeSessionContext.cs
  63. 1 0
      src/Hotline/Caching/Interfaces/ISystemSettingCacheManager.cs
  64. 2 0
      src/Hotline/Caching/Services/SystemSettingCacheManager.cs
  65. 17 0
      src/Hotline/CallCenter/Tels/CallTelDomain/QueryTelRequest.cs
  66. 23 0
      src/Hotline/CallCenter/Tels/CallTelDomain/QueryTelResponse.cs
  67. 12 0
      src/Hotline/CallCenter/Tels/ICallTelClient.cs
  68. 23 1
      src/Hotline/FlowEngine/FlowAssignInfo.cs
  69. 12 1
      src/Hotline/FlowEngine/Workflows/IWorkflowDomainService.cs
  70. 3 3
      src/Hotline/FlowEngine/Workflows/Workflow.cs
  71. 5 5
      src/Hotline/FlowEngine/Workflows/WorkflowCountersign.cs
  72. 551 72
      src/Hotline/FlowEngine/Workflows/WorkflowDomainService.cs
  73. 7 1
      src/Hotline/Orders/Order.cs
  74. 7 0
      src/Hotline/Orders/OrderScreen.cs
  75. 5 0
      src/Hotline/Permissions/EPermission.cs
  76. 3 1
      src/Hotline/Settings/SystemOrganize.cs
  77. 25 0
      src/Tr.Sdk/CallTelClient.cs
  78. 5 1
      src/Tr.Sdk/Tr.Sdk.csproj
  79. 3 1
      src/Tr.Sdk/TrSdkStartupExtensions.cs
  80. 4 4
      src/XF.Domain.Repository/Entity.cs
  81. 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>

+ 6 - 6
src/Hotline.Api/Controllers/Bigscreen/SeatController.cs

@@ -1,26 +1,26 @@
 using Hotline.Application.Bigscreen;
 using Hotline.Caching.Interfaces;
+using Hotline.CallCenter.Tels;
+using Hotline.CallCenter.Tels.CallTelDomain;
 using Hotline.Settings;
 using Hotline.Share.Dtos.TrCallCenter;
 using Mapster;
 using Microsoft.AspNetCore.Authorization;
 using Microsoft.AspNetCore.Mvc;
-using Tr.Sdk;
-using Tr.Sdk.Tels;
 
 namespace Hotline.Api.Controllers.Bigscreen
 {
     public class SeatController : BaseController
     {
         private readonly ISeatStateDataService _seatStateDataService;
-        private readonly ITrClient _trClient;
+        private readonly ICallTelClient _callTelClient;
         private readonly ISystemSettingCacheManager _systemSettingCacheManager;
 
-        public SeatController(ISeatStateDataService seatStateDataService, ISystemSettingCacheManager systemSettingCacheManager, ITrClient trClient)
+        public SeatController(ISeatStateDataService seatStateDataService, ISystemSettingCacheManager systemSettingCacheManager, ICallTelClient callTelClient)
         {
             _seatStateDataService = seatStateDataService;
             _systemSettingCacheManager = systemSettingCacheManager;
-            _trClient = trClient;
+            _callTelClient = callTelClient;
         }
 
         /// <summary>
@@ -51,7 +51,7 @@ namespace Hotline.Api.Controllers.Bigscreen
         {
             var listenTels = _systemSettingCacheManager.GetSetting(SettingConstants.ListenTels)?.SettingValue;
             var callOuttQueueId = _systemSettingCacheManager.GetSetting(SettingConstants.CallOutQueueId)?.SettingValue;
-            var list = (await _trClient.QueryTelsAsync(new QueryTelRequest(), HttpContext.RequestAborted))
+            var list = (await _callTelClient.QueryTelsAsync(new QueryTelRequest(), HttpContext.RequestAborted))
                 .Where(m => listenTels.Contains(m.TelNo))
                 .ToList()
                 .Adapt<List<TelOutDto>>();

+ 3 - 2
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;
@@ -184,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))

+ 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)

+ 319 - 216
src/Hotline.Api/Controllers/OrderController.cs

@@ -35,14 +35,12 @@ using Hotline.Share.Dtos.FlowEngine.Workflow;
 using Hotline.Share.Dtos.Order;
 using Hotline.Share.Dtos.Order.Migration;
 using Hotline.Share.Dtos.Order.Publish;
-using Hotline.Share.Dtos.Push;
 using Hotline.Share.Dtos.Settings;
 using Hotline.Share.Enums.CallCenter;
 using Hotline.Share.Enums.FlowEngine;
 using Hotline.Share.Enums.Order;
 using Hotline.Share.Enums.Push;
 using Hotline.Share.Enums.Settings;
-using Hotline.Share.Mq;
 using Hotline.Share.Requests;
 using Hotline.Share.Tools;
 using Hotline.Tools;
@@ -56,15 +54,18 @@ using Microsoft.AspNetCore.Mvc;
 using Microsoft.Extensions.Options;
 using MiniExcelLibs;
 using SqlSugar;
-using StackExchange.Redis;
 using System.Text;
+using System.Text.Json;
+using Hotline.Share.Dtos.Order.Handle;
 using XF.Domain.Authentications;
 using XF.Domain.Cache;
 using XF.Domain.Entities;
 using XF.Domain.Exceptions;
 using XF.Domain.Repository;
 using XF.Utility.EnumExtensions;
-using Newtonsoft.Json;
+using Hotline.Application.Contracts.Validators.FlowEngine;
+using Hotline.Authentications;
+using Microsoft.AspNetCore.Components;
 
 namespace Hotline.Api.Controllers;
 
@@ -322,6 +323,12 @@ public class OrderController : BaseController
             .WhereIF(dto.FiledTimeStart.HasValue, d => d.FiledTime >= dto.FiledTimeStart)
             .WhereIF(dto.FiledTimeEnd.HasValue, d => d.FiledTime <= dto.FiledTimeEnd)
             .WhereIF(dto.QuerySelf.HasValue && dto.QuerySelf.Value, d => d.WaitForPublisherId == _sessionContext.RequiredUserId)
+            .WhereIF(dto.IsOverTime == true,
+                d => (d.ExpiredTime < DateTime.Now && d.Status < EOrderStatus.Filed) ||
+                     (d.ExpiredTime < d.ActualHandleTime && d.Status >= EOrderStatus.Filed)) //是 超期
+            .WhereIF(dto.IsOverTime == false,
+                d => (d.ExpiredTime > DateTime.Now && d.Status < EOrderStatus.Filed) ||
+                     (d.ExpiredTime > d.ActualHandleTime && d.Status >= EOrderStatus.Filed)) //否 超期
             .OrderByDescending(d => d.FiledTime)
             .ToPagedListAsync(dto.PageIndex, dto.PageSize, HttpContext.RequestAborted);
 
@@ -695,6 +702,12 @@ public class OrderController : BaseController
             .WhereIF(dto.StartTime.HasValue, d => d.CreationTime >= dto.StartTime)
             .WhereIF(dto.EndTime.HasValue, d => d.CreationTime <= dto.EndTime)
             .WhereIF(dto.Resolve.HasValue, x => x.Resolve == dto.Resolve)
+             .WhereIF(dto.IsOverTime == true,
+                d => (d.Order.ExpiredTime < DateTime.Now && d.Order.Status < EOrderStatus.Filed) ||
+                     (d.Order.ExpiredTime < d.Order.ActualHandleTime && d.Order.Status >= EOrderStatus.Filed)) //是 超期
+            .WhereIF(dto.IsOverTime == false,
+                d => (d.Order.ExpiredTime > DateTime.Now && d.Order.Status < EOrderStatus.Filed) ||
+                     (d.Order.ExpiredTime > d.Order.ActualHandleTime && d.Order.Status >= EOrderStatus.Filed)) //否 超期
             .OrderByDescending(d => d.CreationTime)
             .ToPagedListAsync(dto.PageIndex, dto.PageSize, HttpContext.RequestAborted);
 
@@ -865,15 +878,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)
@@ -883,17 +896,25 @@ 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(),
                 d => d.OrderVisitDetails.Any(m => dto.OrgProcessingResults.Contains(SqlFunc.JsonField(m.OrgProcessingResults, "Key"))))
             .WhereIF(dto.OrgHandledAttitude.Any(),
                 d => d.OrderVisitDetails.Any(q => dto.OrgHandledAttitude.Contains(SqlFunc.JsonField(q.OrgHandledAttitude, "Key"))))
+             .WhereIF(dto.IsOverTime == true,
+                d => (d.Order.ExpiredTime < DateTime.Now && d.Order.Status < EOrderStatus.Filed) ||
+                     (d.Order.ExpiredTime < d.Order.ActualHandleTime && d.Order.Status >= EOrderStatus.Filed)) //是 超期
+            .WhereIF(dto.IsOverTime == false,
+                d => (d.Order.ExpiredTime > DateTime.Now && d.Order.Status < EOrderStatus.Filed) ||
+                     (d.Order.ExpiredTime > d.Order.ActualHandleTime && d.Order.Status >= EOrderStatus.Filed)) //否 超期
             .WhereIF(dto.StartTime.HasValue,d=>d.VisitTime>= dto.StartTime)
             .WhereIF(dto.EndTime.HasValue,d=>d.VisitTime<= dto.EndTime)
             .OrderByDescending(x => x.PublishTime)
+            .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));
     }
@@ -1615,7 +1636,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;
 
@@ -1955,11 +1976,12 @@ public class OrderController : BaseController
             .Includes(x => x.OrderVisit, y => y.Employee)
             //.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
+            .Where(x => x.OrderScreens.Any(s => s.Status == EScreenStatus.SendBack && s.ScreenType == dto.ScreenType && s.SendBackApply == true) || x.OrderScreens.Any() == 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)
+            .WhereIF(dto.ScreenType == EOrderScreenType.Seat , x=>x.OrderVisit.Order.IsProvince == false)
+            .WhereIF(dto.ScreenSendBack is 1, x => x.OrderScreens.Any(s => s.Status == EScreenStatus.SendBack && s.ScreenType == dto.ScreenType && s.SendBackApply == true))
+            .WhereIF(dto.ScreenSendBack is 2, x => x.OrderScreens.Any(s => (s.Status != EScreenStatus.SendBack && s.ScreenType == dto.ScreenType && s.SendBackApply != true)) == false)
             .WhereIF(!string.IsNullOrEmpty(dto.No), x => x.OrderVisit.Order!.No!.Contains(dto.No!))
             .WhereIF(!string.IsNullOrEmpty(dto.Title), x => x.OrderVisit.Order!.Title!.Contains(dto.Title!))
             .WhereIF(dto.IsProvince.HasValue, x => x.OrderVisit.Order!.IsProvince == dto.IsProvince)
@@ -2007,12 +2029,14 @@ public class OrderController : BaseController
             query.WhereIF(!string.IsNullOrEmpty(dto.Keyword),
                     x => x.OrderVisit.Order.Title.Contains(dto.Keyword!) ||
                          x.OrderVisit.Order.No.Contains(dto.Keyword!))
-                .Where(x => x.VisitTarget == EVisitTarget.Org && (
+                .WhereIF(dto.ScreenType == EOrderScreenType.Org,x => x.VisitTarget == EVisitTarget.Org && (
                     SqlFunc.JsonField(x.OrgProcessingResults, "Key") == "1" ||
                     SqlFunc.JsonField(x.OrgProcessingResults, "Key") == "2" ||
                     SqlFunc.JsonField(x.OrgHandledAttitude, "Key") == "1" ||
                     SqlFunc.JsonField(x.OrgHandledAttitude, "Key") == "2"
-                ));
+                ))
+                .WhereIF(dto.ScreenType == EOrderScreenType.Seat, x => x.VisitTarget == EVisitTarget.Seat && (x.SeatEvaluate == ESeatEvaluate.VeryNoSatisfied || x.SeatEvaluate  == ESeatEvaluate.NoSatisfied))
+				;
         }
 
         var (total, items) = await query
@@ -2038,97 +2062,67 @@ 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 &&
+            x.VisitDetailId == dto.Data.VisitDetailId && x.ScreenType == dto.Data.ScreenType &&
             (x.Status == EScreenStatus.Apply || x.Status == EScreenStatus.Approval));
         if (screenAny)
             throw UserFriendlyException.SameMessage("该工单已提起甄别申请,正在审批过程中,不能申请");
 
-        var isNoPass = await _orderScreenRepository.AnyAsync(x => x.Status == EScreenStatus.Refuse && x.VisitDetailId == dto.Data.VisitDetailId);
+        var isNoPass = await _orderScreenRepository.AnyAsync(x => x.Status == EScreenStatus.Refuse && x.ScreenType == dto.Data.ScreenType && x.VisitDetailId == dto.Data.VisitDetailId);
         if (isNoPass)
             throw UserFriendlyException.SameMessage("该工单已被拒绝过甄别申请,不能再次申请");
 
         var setting = _systemSettingCacheManager.GetSetting(SettingConstants.ScreenApplyNum);
         int count = await _orderScreenRepository.CountAsync(x =>
-            x.OrderId == dto.Data.OrderId && x.Status == EScreenStatus.Refuse && x.VisitDetailId == dto.Data.VisitDetailId);
+            x.OrderId == dto.Data.OrderId && x.Status == EScreenStatus.Refuse && x.ScreenType == dto.Data.ScreenType && x.VisitDetailId == dto.Data.VisitDetailId);
         if (count > int.Parse(setting?.SettingValue[0]) && int.Parse(setting?.SettingValue[0]) > -1)
             throw UserFriendlyException.SameMessage("甄别申请已超过系统预定设置,不能申请");
 
@@ -2309,7 +2303,8 @@ public class OrderController : BaseController
         var rsp = new
         {
             ScreenStatus = EnumExts.GetDescriptions<EScreenStatus>(),
-            ScreenType = _sysDicDataCacheManager.GetSysDicDataCache(SysDicTypeConsts.ScreenType),
+            OrderScreenType = EnumExts.GetDescriptions<EOrderScreenType>(),
+			ScreenType = _sysDicDataCacheManager.GetSysDicDataCache(SysDicTypeConsts.ScreenType),
             CounterSignType = EnumExts.GetDescriptions<ECounterSignType>(),
             AcceptType = _sysDicDataCacheManager.GetSysDicDataCache(SysDicTypeConsts.AcceptType),
             SourceChannel = _sysDicDataCacheManager.GetSysDicDataCache(SysDicTypeConsts.SourceChannel),
@@ -2457,7 +2452,7 @@ public class OrderController : BaseController
                 }
             }
             var setting = _systemSettingCacheManager.GetSetting(SettingConstants.CityBaseConfiguration)?.SettingValue[0];
-            CityBaseConfiguration cityBase = JsonConvert.DeserializeObject<CityBaseConfiguration>(setting);
+            CityBaseConfiguration cityBase = JsonSerializer.Deserialize<CityBaseConfiguration>(setting);
             //推省上
             if (!string.IsNullOrEmpty(model.Id) && (cityBase.CityProvince.OrgId.Equals(model.OrgId) || cityBase.CityProvinceAssign.OrgId.Equals(model.OrgId)))
             {
@@ -2703,7 +2698,7 @@ public class OrderController : BaseController
                 }
             }
             var setting = _systemSettingCacheManager.GetSetting(SettingConstants.CityBaseConfiguration)?.SettingValue[0];
-            CityBaseConfiguration cityBase = JsonConvert.DeserializeObject<CityBaseConfiguration>(setting);
+            CityBaseConfiguration cityBase = JsonSerializer.Deserialize<CityBaseConfiguration>(setting);
             //推省上
             if (!string.IsNullOrEmpty(model.Id) && (cityBase.CityProvince.OrgId.Equals(model.OrgId) || cityBase.CityProvinceAssign.OrgId.Equals(model.OrgId)))
             {
@@ -2936,8 +2931,8 @@ public class OrderController : BaseController
             .Includes(d => d.OrderPublish)
             .Includes(d => d.OrderPushTypes)
             //.Includes(d => d.OrderScreens)
-            .Includes(d => d.OrderVisits, x => x.OrderVisitDetails)
-            .Includes(d => d.OrderVisits, x => x.Employee)
+            .Includes(d => d.OrderVisits.Where(x => x.VisitState == EVisitState.Visited).ToList(), x => x.OrderVisitDetails)
+            .Includes(d => d.OrderVisits.Where(x => x.VisitState == EVisitState.Visited).ToList(), x => x.Employee)
             .FirstAsync(d => d.Id == id);
         if (order == null) return new();
 
@@ -2973,8 +2968,6 @@ public class OrderController : BaseController
                     {
                         canInsteadHandle = false;
                     }
-
-                    ;
                 }
 
                 if (canInsteadHandle)
@@ -3126,7 +3119,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)
             {
@@ -3181,7 +3174,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>
@@ -3493,15 +3486,20 @@ public class OrderController : BaseController
     /// 开始工单办理流程
     /// </summary>
     [HttpPost("startflow")]
-    public async Task StartFlow([FromBody] OrderStartFlowDto dto)
-    {
-        var order = await _orderDomainService.GetOrderAsync(dto.Data, cancellationToken: HttpContext.RequestAborted);
+    public async Task StartFlow([FromBody] StartWorkflowDto<OrderHandleFlowDto> dto)
+    {
+        var validator = new BasicWorkflowDtoValidator();
+        var validResult = await validator.ValidateAsync(dto.Workflow, HttpContext.RequestAborted);
+        if (!validResult.IsValid)
+            throw new UserFriendlyException(
+                $"非法参数, {string.Join(',', validResult.Errors.Select(d => d.ErrorMessage))}");
+        var order = await _orderDomainService.GetOrderAsync(dto.Data.OrderId, cancellationToken: HttpContext.RequestAborted);
         if (!string.IsNullOrEmpty(order.WorkflowId))
             throw new UserFriendlyException($"该工单已开启办理流程, No:{order.No}", "该工单已开启办理流程");
 
         ExpiredTimeWithConfig expiredTimeConfig;
         var setting = _systemSettingCacheManager.GetSetting(SettingConstants.CityBaseConfiguration)?.SettingValue[0];
-        CityBaseConfiguration cityBase = JsonConvert.DeserializeObject<CityBaseConfiguration>(setting);
+        CityBaseConfiguration cityBase = JsonSerializer.Deserialize<CityBaseConfiguration>(setting);
         if (dto.Workflow.NextHandlers.Any(d => d.Key == cityBase.CityProvince.OrgId || d.Key == cityBase.CityProvinceAssign.OrgId))
         {
             var timeResult = await _expireTime.CalcEndTime(DateTime.Now, ETimeType.WorkDay, 45, 80, 50);
@@ -3527,7 +3525,7 @@ public class OrderController : BaseController
                 expiredTimeConfig = await _expireTime.CalcExpiredTime(DateTime.Now, EFlowDirection.CenterToOrg, order.Adapt<OrderTimeClacInfo>());
             }
         }
-
+        order.IsForwarded = dto.Workflow.IsForwarded;
         _mapper.Map(expiredTimeConfig, order);
 
         if (dto.Workflow.BusinessType is EBusinessType.Department or EBusinessType.DepartmentLeader)
@@ -3537,22 +3535,25 @@ public class OrderController : BaseController
 
         try
         {
-            // 平均派单
-            var averageSendOrder = bool.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.AverageSendOrder).SettingValue[0]);
-            if (dto.Workflow.BusinessType == EBusinessType.Send && averageSendOrder)
-            {
-	            if (!dto.Workflow.NextHandlers.Any())
-	            {
-					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.Transpond = 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)
         {
@@ -3560,8 +3561,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>
@@ -3569,31 +3574,42 @@ 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 validator = new BasicWorkflowDtoValidator();
+        var validResult = await validator.ValidateAsync(dto.Workflow, HttpContext.RequestAborted);
+        if (!validResult.IsValid)
+            throw new UserFriendlyException(
+                $"非法参数, {string.Join(',', validResult.Errors.Select(d => d.ErrorMessage))}");
         var order = await _orderApplication.SaveOrderWorkflowInfo(dto, HttpContext.RequestAborted);
 
+        var workflow = await _workflowDomainService.GetWorkflowAsync(dto.Workflow.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.Workflow.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)
+                var sendOrderTrace = workflow.Traces.Where(x => x.BusinessType == EBusinessType.Send)
+                    .OrderByDescending(x => x.CreationTime)
                     .FirstOrDefault();
-                dto.NextHandlers = new List<FlowStepHandler>
+                dto.Workflow.NextHandlers = new List<FlowStepHandler>
                 {
                     new FlowStepHandler()
                     {
@@ -3609,13 +3625,101 @@ public class OrderController : BaseController
                 var averageSendOrder = bool.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.AverageSendOrder).SettingValue[0]);
                 if (averageSendOrder)
                 {
-                    var handler = await _orderDomainService.AverageOrder(HttpContext.RequestAborted);
-                    dto.NextHandlers = new List<FlowStepHandler> { handler };
+	                if (!dto.Workflow.NextHandlers.Any())
+	                {
+						var handler = await _orderDomainService.AverageOrder(HttpContext.RequestAborted);
+						dto.Workflow.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.Workflow.StepId);
+        await HandleOrderAsync(order, startStep, dto.Data, dto.Workflow, 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)
+                {
+                    if (!nextDto.NextHandlers.Any()) {
+						var handler = await _orderDomainService.AverageOrder(cancellationToken);
+						nextDto.NextHandlers = new List<FlowStepHandler> { handler };
+					}
+                }
+
+                await _workflowDomainService.NextAsync(_sessionContext, nextDto, order.ExpiredTime, cancellationToken);
+                break;
+            case EOrderAssignMode.CrossLevel:
+                if (!orderHandleFlowDto.CrossSteps.Any())
+                    throw new UserFriendlyException("跨级指派参数异常");
+
+                orderHandleFlowDto.CrossSteps = orderHandleFlowDto.CrossSteps.OrderBy(d => d.Sort).ToList();
+                var stepCount = orderHandleFlowDto.CrossSteps.Count;
+                var unhandleSteps = new List<WorkflowStep> { startStep };
+                for (int i = 0; i < stepCount; i++)
+                {
+                    var crossStep = orderHandleFlowDto.CrossSteps[i];
+                    var tempSteps = new List<WorkflowStep>();
+                    foreach (var unhandleStep in unhandleSteps)
+                    {
+                        var lowerLevelHandlers = crossStep.NextHandlers.Where(d => d.OrgId.StartsWith(unhandleStep.HandlerOrgId)).ToList();
+                        if (!lowerLevelHandlers.Any()) continue;
+                        var nextflowDto = _mapper.Map<NextWorkflowDto>(crossStep);
+                        nextflowDto.WorkflowId = unhandleStep.WorkflowId;
+                        nextflowDto.StepId = unhandleStep.Id;
+                        nextflowDto.IsStartCountersign = lowerLevelHandlers.Count > 1;
+                        nextflowDto.NextHandlers = lowerLevelHandlers;
+                        nextflowDto.Opinion = "跨级派单,自动办理";
+
+                        var operater = new FakeSessionContext
+                        {
+                            OrgId = unhandleStep.HandlerOrgId,
+                            OrgName = unhandleStep.HandlerOrgName,
+                        };
+                        var nextSteps = await _workflowDomainService.NextAsync(operater, nextflowDto, order.ExpiredTime, cancellationToken);
+                        tempSteps.AddRange(nextSteps);
+                    }
+                    unhandleSteps = tempSteps;
+                }
+
+                break;
+            case EOrderAssignMode.MainAndSecondary:
+                break;
+            default:
+                throw new ArgumentOutOfRangeException();
+        }
+    }
+
+    /// <summary>
+    /// 跨级指派查询下一步可选节点及办理对象参数
+    /// </summary>
+    /// <returns></returns>
+    [HttpGet("nextsteps/cross")]
+    public async Task<NextStepsDto<NextStepOption>> GetCrossLevelStepsOptions([FromQuery] GetCrossLevelStepsDto dto)
+    {
+        return await _workflowApplication.GetCrossLevelStepsAsync(dto, HttpContext.RequestAborted);
     }
 
     /// <summary>
@@ -3652,13 +3756,15 @@ public class OrderController : BaseController
         return rsp;
     }
 
+
+
     /// <summary>
     /// 临时保存
     /// </summary>
     [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>
@@ -3745,8 +3851,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;
     }
 
@@ -3873,14 +3980,22 @@ public class OrderController : BaseController
         var isHandled = dto.IsHandled.HasValue && dto.IsHandled.Value;
 
         var isAdmin = _orderDomainService.IsCheckAdmin();
-
-        var (total, items) = await _orderRepository
+        var query = _orderRepository
             .Queryable(hasHandled: isHandled, isAdmin: isAdmin)
-            .Includes(d => d.OrderSpecials)
-            .Where(d => d.Status != EOrderStatus.WaitForAccept &&
+            .Includes(d => d.OrderSpecials);
+        if (dto.QueryType is 1  || dto.QueryType is 2)
+		{
+            query.WhereIF(dto.QueryType is 1, d => d.IsForwarded == false)
+	            .WhereIF(dto.QueryType is 2, d => d.IsForwarded == true)
+				.Where(d=>SqlFunc.Subqueryable<OrderSpecial>().Where(os=>os.OrderId == d.Id && os.SpecialType == ESpecialType.ReTransact).NotAny());
+        }
+
+		var (total, items) = await query
+			.Where(d => d.Status != EOrderStatus.WaitForAccept &&
                         d.Status != EOrderStatus.BackToUnAccept &&
                         d.Status != EOrderStatus.SpecialToUnAccept &&
                         d.Status != EOrderStatus.HandOverToUnAccept)
+		    .WhereIF(dto.QueryType is 3, d => SqlFunc.Subqueryable<OrderSpecial>().Where(os => os.OrderId == d.Id && os.SpecialType == ESpecialType.ReTransact).Any())
             .WhereIF(dto.IsProvince.HasValue, d => d.IsProvince == dto.IsProvince)
             .WhereIF(!string.IsNullOrEmpty(dto.Keyword), d => d.Title.StartsWith(dto.Keyword))
             .WhereIF(!string.IsNullOrEmpty(dto.No), d => d.No == dto.No)
@@ -3910,64 +4025,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>
@@ -4446,6 +4515,26 @@ public class OrderController : BaseController
         return rsp;
     }
 
+    /// <summary>
+    /// 查询工单退回目标步节点信息
+    /// </summary>
+    [HttpGet("previous/targetStep/{orderId}")]
+    public async Task<TargetStepInfo> GetPreviousTargetStepInfo(string orderId)
+    {
+        var order = await _orderDomainService.GetOrderAsync(orderId, cancellationToken: HttpContext.RequestAborted);
+        if (string.IsNullOrEmpty(order.WorkflowId))
+            throw UserFriendlyException.SameMessage("该工单未开启流程");
+        
+        var (currentStep, prevStep, isOrgToCenter, isSecondToFirstOrgLevel) = await _workflowApplication.GetPreviousInformationAsync(
+            order.WorkflowId, _sessionContext.RequiredUserId, _sessionContext.RequiredOrgId, _sessionContext.Roles, HttpContext.RequestAborted);
+
+        return new TargetStepInfo
+        {
+            CurrentBusinessType = currentStep.BusinessType,
+            TargetBusinessType = prevStep.BusinessType
+        };
+    }
+    
     #endregion
 
     #region 省工单退回
@@ -4755,10 +4844,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,
@@ -4793,7 +4885,7 @@ public class OrderController : BaseController
             }
 
             var setting = _systemSettingCacheManager.GetSetting(SettingConstants.CityBaseConfiguration)?.SettingValue[0];
-            CityBaseConfiguration cityBase = JsonConvert.DeserializeObject<CityBaseConfiguration>(setting);
+            CityBaseConfiguration cityBase = JsonSerializer.Deserialize<CityBaseConfiguration>(setting);
             if (order != null && (cityBase.CityProvince.OrgId.Equals(model.OrgId) ||
                                   cityBase.CityProvinceAssign.OrgId.Equals(model.OrgId) || cityBase.CityEnterprise.OrgId.Equals(model.OrgId) ||
                                   cityBase.PublicSecurity.OrgId.Equals(model.OrgId)))
@@ -4914,10 +5006,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,
@@ -5022,10 +5115,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,
@@ -5062,7 +5158,7 @@ public class OrderController : BaseController
             }
 
             var setting = _systemSettingCacheManager.GetSetting(SettingConstants.CityBaseConfiguration)?.SettingValue[0];
-            CityBaseConfiguration cityBase = JsonConvert.DeserializeObject<CityBaseConfiguration>(setting);
+            CityBaseConfiguration cityBase = JsonSerializer.Deserialize<CityBaseConfiguration>(setting);
             if (order != null && (cityBase.CityProvince.OrgId.Equals(special.OrgId) ||
                                   cityBase.CityProvinceAssign.OrgId.Equals(special.OrgId) || cityBase.CityEnterprise.OrgId.Equals(special.OrgId) ||
                                   cityBase.PublicSecurity.OrgId.Equals(special.OrgId)))
@@ -5142,10 +5238,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,
@@ -5181,7 +5280,7 @@ public class OrderController : BaseController
                     }
                 }
                 var setting = _systemSettingCacheManager.GetSetting(SettingConstants.CityBaseConfiguration)?.SettingValue[0];
-                CityBaseConfiguration cityBase = JsonConvert.DeserializeObject<CityBaseConfiguration>(setting);
+                CityBaseConfiguration cityBase = JsonSerializer.Deserialize<CityBaseConfiguration>(setting);
                 if (order != null && (cityBase.CityProvince.OrgId.Equals(special.OrgId) ||
                                       cityBase.CityProvinceAssign.OrgId.Equals(special.OrgId) || cityBase.CityEnterprise.OrgId.Equals(special.OrgId) ||
                                       cityBase.PublicSecurity.OrgId.Equals(special.OrgId)))
@@ -5298,7 +5397,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!))
@@ -5392,8 +5491,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;
     }
@@ -5422,7 +5521,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;
@@ -6428,7 +6527,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()
             };
         }
     }
@@ -7029,7 +7132,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
 

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

@@ -1,27 +1,19 @@
 using Hotline.Application.Systems;
 using Hotline.Caching.Interfaces;
 using Hotline.Permissions;
-using Hotline.Quality;
 using Hotline.Repository.SqlSugar.Extensions;
-using Hotline.Repository.SqlSugar.System;
 using Hotline.Settings;
 using Hotline.Settings.CommonOpinions;
 using Hotline.Share.Dtos;
 using Hotline.Share.Dtos.Dic;
 using Hotline.Share.Dtos.Menu;
-using Hotline.Share.Dtos.Quality;
 using Hotline.Share.Dtos.Settings;
-using Hotline.Share.Dtos.Trunk;
-using Hotline.Share.Enums.Quality;
 using Hotline.Share.Enums.Settings;
 using Hotline.Share.Requests;
 using Hotline.Share.Tools;
 using MapsterMapper;
 using Microsoft.AspNetCore.Authorization;
 using Microsoft.AspNetCore.Mvc;
-using Microsoft.AspNetCore.Mvc.Rendering;
-using System.Diagnostics.CodeAnalysis;
-using System.Text.RegularExpressions;
 using XF.Domain.Authentications;
 using XF.Domain.Exceptions;
 using XF.Domain.Repository;

+ 56 - 6
src/Hotline.Api/Controllers/WebPortalController.cs

@@ -548,18 +548,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, "成功"));
@@ -575,7 +586,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)
@@ -586,7 +598,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));

+ 2 - 0
src/Hotline.Api/StartupExtensions.cs

@@ -32,6 +32,7 @@ using Hotline.Share.Tools;
 using Hotline.Settings.TimeLimitDomain.ExpireTimeSupplier;
 using Hotline.Api.Middleware;
 using XF.Domain.Authentications;
+using Hotline.XingTang;
 
 namespace Hotline.Api;
 
@@ -165,6 +166,7 @@ internal static class StartupExtensions
                 break;
             case AppDefaults.CallCenterType.XingTang:
                 services.AddXingTangDb(callCenterConfiguration.XingTang)
+                    .AddXingTangSDK()
                     .AddScoped<ICallApplication, XingTangCallApplication>()
                     .AddScoped<CallIdManager>()
                     ;

TEMPAT SAMPAH
src/Hotline.Api/Template/AssignmentForm.doc


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

@@ -1,36 +1,4 @@
 {
-  "CityBaseConfiguration": {
-    "CityProvince": {
-      "UserId": "9fd11bfd-0c39-f1f0-80b1-3a1384977b8e",
-      "UserName": "省12345交办",
-      "OrgId": "001090",
-      "OrgName": "省12345交办"
-    },
-    "CityProvinceAssign": {
-      "UserId": "9fd11bfd-0c39-f1f0-80b1-3a1384977b8e",
-      "UserName": "省12345交办",
-      "OrgId": "001090",
-      "OrgName": "省12345交办"
-    },
-    "PublicSecurity": {
-      "UserId": "a5a57c15-fe42-5782-449b-3a1384976aaf",
-      "UserName": "市公安局110",
-      "OrgId": "001096",
-      "OrgName": "市公安局110"
-    },
-    "CityEnterprise": {
-      "UserId": "",
-      "UserName": "",
-      "OrgId": "",
-      "OrgName": ""
-    },
-    "ComprehensiveTreatment": {
-      "UserId": "",
-      "UserName": "",
-      "OrgId": "",
-      "OrgName": ""
-    }
-  },
   "AllowedHosts": "*",
   "AppConfiguration": {
     "AppScope": "ZiGong",
@@ -91,13 +59,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": {

+ 4 - 8
src/Hotline.Application.Contracts/Validators/FlowEngine/BasicWorkflowDtoValidator.cs

@@ -15,14 +15,10 @@ namespace Hotline.Application.Contracts.Validators.FlowEngine
         {
             RuleFor(d => d.BusinessType).NotNull();
             RuleFor(d => d.HandlerType).NotNull();
-        }
-    }
-
-    public class RecallDtoValidator : AbstractValidator<RecallDto>
-    {
-        public RecallDtoValidator()
-        {
-            Include(new BasicWorkflowDtoValidator());
+            RuleFor(d => d.Opinion)
+                .Cascade(CascadeMode.Stop)
+                .NotEmpty()
+                .MaximumLength(2000);
         }
     }
 }

+ 1 - 0
src/Hotline.Application.Contracts/Validators/FlowEngine/NextWorkflowDtoValidator.cs

@@ -12,6 +12,7 @@ namespace Hotline.Application.Contracts.Validators.FlowEngine
     {
         public NextWorkflowDtoValidator()
         {
+            Include(new BasicWorkflowDtoValidator());
             RuleFor(d => d.WorkflowId).NotEmpty();
             RuleFor(d => d.Opinion)
                 .Cascade(CascadeMode.Stop)

+ 12 - 0
src/Hotline.Application.Contracts/Validators/FlowEngine/RecallDtoValidator.cs

@@ -0,0 +1,12 @@
+using FluentValidation;
+using Hotline.Share.Dtos.FlowEngine;
+
+namespace Hotline.Application.Contracts.Validators.FlowEngine;
+
+public class RecallDtoValidator : AbstractValidator<RecallDto>
+{
+    public RecallDtoValidator()
+    {
+        Include(new BasicWorkflowDtoValidator());
+    }
+}

+ 17 - 21
src/Hotline.Application.Contracts/Validators/FlowEngine/StartWorkflowDtoValidator.cs

@@ -8,28 +8,24 @@ using Hotline.Share.Dtos.FlowEngine;
 using Hotline.Application.Contracts.Validators;
 using Hotline.Share.Enums.FlowEngine;
 
-namespace Hotline.Application.Contracts.Validators.FlowEngine
+namespace Hotline.Application.Contracts.Validators.FlowEngine;
+
+public class StartWorkflowDtoValidator : AbstractValidator<StartWorkflowDto>
 {
-    public class StartWorkflowDtoValidator : AbstractValidator<StartWorkflowDto>
+    public StartWorkflowDtoValidator()
     {
-        public StartWorkflowDtoValidator()
-        {
-            //RuleFor(d => d.DefinitionCode)
-            //    .NotEmpty().When(d => string.IsNullOrEmpty(d.DefinitionModuleCode));
-            //RuleFor(d => d.DefinitionModuleCode)
-            //    .NotEmpty().When(d => string.IsNullOrEmpty(d.DefinitionCode));
+        //RuleFor(d => d.DefinitionCode)
+        //    .NotEmpty().When(d => string.IsNullOrEmpty(d.DefinitionModuleCode));
+        //RuleFor(d => d.DefinitionModuleCode)
+        //    .NotEmpty().When(d => string.IsNullOrEmpty(d.DefinitionCode));
 
-            //RuleFor(d => d.DefinitionModuleCode).NotEmpty();
-            //RuleFor(d => d.Title).NotEmpty();
-            RuleFor(d => d.NextStepCode).NotEmpty();
-            RuleFor(d => d.IsSms).NotNull();
-            RuleFor(d => d.IsStartCountersign).NotNull();
-            RuleFor(d => d.NextHandlers.Count).LessThanOrEqualTo(1)
-                .Unless(d => d.IsStartCountersign);
-            RuleFor(d => d.Opinion)
-                .Cascade(CascadeMode.Stop)
-                .NotEmpty()
-                .MaximumLength(2000);
-        }
+        //RuleFor(d => d.DefinitionModuleCode).NotEmpty();
+        //RuleFor(d => d.Title).NotEmpty();
+        Include(new BasicWorkflowDtoValidator());
+        RuleFor(d => d.NextStepCode).NotEmpty();
+        RuleFor(d => d.IsSms).NotNull();
+        RuleFor(d => d.IsStartCountersign).NotNull();
+        RuleFor(d => d.NextHandlers.Count).LessThanOrEqualTo(1)
+            .Unless(d => d.IsStartCountersign);
     }
-}
+}

+ 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);

+ 9 - 116
src/Hotline.Application/Bigscreen/SeatStateDataService.cs

@@ -1,121 +1,14 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using Hotline.CallCenter.Calls;
-using SqlSugar;
+using Hotline.CallCenter.Calls;
+using Hotline.DI;
 using XF.Domain.Dependency;
 using XF.Domain.Repository;
 
-namespace Hotline.Application.Bigscreen
-{
-	public  class SeatStateDataService: ISeatStateDataService , IScopeDependency
-	{
-		private readonly IRepository<TrCallRecord> _callRepository;
-		public SeatStateDataService(IRepository<TrCallRecord> callRepository) {
-			_callRepository= callRepository;
-		}
-
-		public async Task<object> GetCall24(CancellationToken stoppingToken) {
-			List<string> timeList = new List<string>();
-			for (int i = 0; i < 24; i++)
-			{
-				var time = i < 10 ? $"0{i}" : i.ToString();
-				timeList.Add(time);
-			}
-			var call24 = await _callRepository.Queryable()
-				.Where(x => x.CreatedTime.Date == DateTime.Now.Date
-					&& x.CallOrderType == Share.Enums.CallCenter.ECallOrderType.Order
-				)
-				.Select(x => new { time = x.CreatedTime.ToString("hh"), x.CallDirection }).MergeTable()
-				.GroupBy(x => x.time)
-				.Select(x => new
-				{
-					Time = x.time.ToString(),
-					In = SqlFunc.AggregateSum(SqlFunc.IIF(x.CallDirection == Share.Enums.CallCenter.ECallDirection.In, 1, 0)),
-					Out = SqlFunc.AggregateSum(SqlFunc.IIF(x.CallDirection == Share.Enums.CallCenter.ECallDirection.Out, 1, 0)),
-				}).MergeTable().ToListAsync(stoppingToken);
-
-			var call24List = (from t1 in timeList
-				join t2 in call24 on t1 equals t2.Time into t1_t2
-				from item in t1_t2.DefaultIfEmpty()
-				select new { 
-					Time = t1 + ":00",
-					In = t1_t2.Select(x => x.In).FirstOrDefault() ,
-					Out = t1_t2.Select(x => x.Out).FirstOrDefault() 
-				}).ToList();
-			return call24List;
-		}
-
-		public async Task<object> GetCallTop10(CancellationToken stoppingToken)
-		{
-
-			var callTop10 = await _callRepository.Queryable()
-				.Where(x => x.CreatedTime.Date == DateTime.Now.Date
-					&& x.CallOrderType == Share.Enums.CallCenter.ECallOrderType.Order
-				)
-				.GroupBy(x => x.UserName)
-				.Select(x => new
-				{
-					UserName = x.UserName,
-					In = SqlFunc.AggregateSum(SqlFunc.IIF(x.CallDirection == Share.Enums.CallCenter.ECallDirection.In, 1, 0))
-				}).MergeTable().OrderByDescending(x => x.In).Take(10).ToListAsync(stoppingToken);
-			return callTop10;
-		}
+namespace Hotline.Application.Bigscreen;
 
-		public async Task<object> GetCallList(CancellationToken stoppingToken)
-		{
-			var callList = await _callRepository.Queryable()
-				.Where(x => x.CreatedTime.Date == DateTime.Now.Date
-                    && x.CallOrderType == Share.Enums.CallCenter.ECallOrderType.Order
-				)
-				.Select(x => new
-				{
-					InOn = SqlFunc.AggregateSum(SqlFunc.IIF(x.CallDirection == Share.Enums.CallCenter.ECallDirection.In && x.OnState == Share.Enums.CallCenter.EOnState.On, 1, 0)),
-					ValidOn = SqlFunc.AggregateSum(SqlFunc.IIF(x.CallDirection == Share.Enums.CallCenter.ECallDirection.In && x.OnState == Share.Enums.CallCenter.EOnState.On && x.Duration > 5, 1, 0)),
-					InNoOn = SqlFunc.AggregateSum(SqlFunc.IIF(x.CallDirection == Share.Enums.CallCenter.ECallDirection.In && x.OnState == Share.Enums.CallCenter.EOnState.NoOn, 1, 0)),
-					OutOn = SqlFunc.AggregateSum(SqlFunc.IIF(x.CallDirection == Share.Enums.CallCenter.ECallDirection.Out && x.OnState == Share.Enums.CallCenter.EOnState.On, 1, 0)),
-					InQueueNoOn = SqlFunc.AggregateSum(SqlFunc.IIF(x.CallDirection == Share.Enums.CallCenter.ECallDirection.In && x.OnState == Share.Enums.CallCenter.EOnState.NoOn && x.QueueTims > 0, 1, 0)),
-				}).MergeTable().ToListAsync(stoppingToken);
-			return callList;
-		}
-
-		public async Task<object> GetCallAverage(CancellationToken stoppingToken)
-		{
-			List<string> timeList = new List<string>();
-			for (int i = 0; i < 24; i++)
-			{
-				var time = i < 10 ? $"0{i}" : i.ToString();
-				timeList.Add(time);
-			}
-			var callAverage = await _callRepository.Queryable()
-				.Where(x => x.CreatedTime.Date == DateTime.Now.Date
-                    && x.CallOrderType == Share.Enums.CallCenter.ECallOrderType.Order
-				)
-				.Select(x => new { time = x.CreatedTime.ToString("hh"), x.CallDirection }).MergeTable()
-				.GroupBy(x => x.time)
-				.Select(x => new
-				{
-					Time = x.time.ToString(),
-					In = SqlFunc.AggregateSum(SqlFunc.IIF(x.CallDirection == Share.Enums.CallCenter.ECallDirection.In, 1, 0)),
-					InAverag = SqlFunc.AggregateSum(SqlFunc.IIF(x.CallDirection == Share.Enums.CallCenter.ECallDirection.In, 1, 0)) / 60,
-					Out = SqlFunc.AggregateSum(SqlFunc.IIF(x.CallDirection == Share.Enums.CallCenter.ECallDirection.Out, 1, 0)),
-					OutAverag = SqlFunc.AggregateSum(SqlFunc.IIF(x.CallDirection == Share.Enums.CallCenter.ECallDirection.Out, 1, 0)) / 60,
-				}).MergeTable().ToListAsync(stoppingToken);
-			var callAverageList = (from t1 in timeList
-				join t2 in callAverage on t1 equals t2.Time into t1_t2
-				from item in t1_t2.DefaultIfEmpty()
-				select new {
-					Time = t1 + ":00",
-					In = t1_t2.Select(x => x.In).FirstOrDefault(),
-					InAverag = t1_t2.Select(x => x.InAverag).FirstOrDefault(),
-					Out = t1_t2.Select(x => x.Out).FirstOrDefault(),
-					OutAverag = t1_t2.Select(x => x.OutAverag).FirstOrDefault() 
-				}).ToList();
-			return callAverageList;
-		}
-
-
-	}
+[Injection(AppScopes = EAppScope.ZiGong | EAppScope.LuZhou)]
+public class SeatStateDataService : SeatStateDataServiceBase, ISeatStateDataService, IScopeDependency
+{
+    public SeatStateDataService(IRepository<CallNative> repository) : base(repository)
+    {
+    }
 }

+ 128 - 0
src/Hotline.Application/Bigscreen/SeatStateDataServiceBase.cs

@@ -0,0 +1,128 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Hotline.CallCenter.Calls;
+using Hotline.Orders;
+using SqlSugar;
+using XF.Domain.Dependency;
+using XF.Domain.Repository;
+
+namespace Hotline.Application.Bigscreen
+{
+    public abstract class SeatStateDataServiceBase
+    {
+        private readonly IRepository<CallNative> _callRepository;
+
+        public SeatStateDataServiceBase(IRepository<CallNative> callRepository)
+        {
+            _callRepository = callRepository;
+        }
+
+        public virtual async Task<object> GetCall24(CancellationToken stoppingToken)
+        {
+            List<string> timeList = new List<string>();
+            for (int i = 0;i < 24;i++)
+            {
+                var time = i < 10 ? $"0{i}" : i.ToString();
+                timeList.Add(time);
+            }
+            var call24 = await _callRepository.Queryable()
+                .LeftJoin<Order>((x, o) => x.Id == o.CallId)
+                .Where((x, o) => x.CreationTime.Date == DateTime.Now.Date
+                    && string.IsNullOrEmpty(o.Id) == false
+                )
+                .Select(x => new { time = x.CreationTime.ToString("hh"), x.Direction }).MergeTable()
+                .GroupBy(x => x.time)
+                .Select(x => new
+                {
+                    Time = x.time.ToString(),
+                    In = SqlFunc.AggregateSum(SqlFunc.IIF(x.Direction == Share.Enums.CallCenter.ECallDirection.In, 1, 0)),
+                    Out = SqlFunc.AggregateSum(SqlFunc.IIF(x.Direction == Share.Enums.CallCenter.ECallDirection.Out, 1, 0)),
+                }).MergeTable().ToListAsync(stoppingToken);
+
+            var call24List = (from t1 in timeList
+                              join t2 in call24 on t1 equals t2.Time into t1_t2
+                              from item in t1_t2.DefaultIfEmpty()
+                              select new
+                              {
+                                  Time = t1 + ":00",
+                                  In = t1_t2.Select(x => x.In).FirstOrDefault(),
+                                  Out = t1_t2.Select(x => x.Out).FirstOrDefault()
+                              }).ToList();
+            return call24List;
+        }
+
+        public virtual async Task<object> GetCallTop10(CancellationToken stoppingToken)
+        {
+            var callTop10 = await _callRepository.Queryable()
+                .LeftJoin<Order>((x, o) => x.Id == o.CallId)
+                .Where((x, o) => x.CreationTime.Date == DateTime.Now.Date
+                    && string.IsNullOrEmpty(o.Id) == false
+                )
+                .GroupBy(x => x.UserName)
+                .Select(x => new
+                {
+                    UserName = x.UserName,
+                    In = SqlFunc.AggregateSum(SqlFunc.IIF(x.Direction == Share.Enums.CallCenter.ECallDirection.In, 1, 0))
+                }).MergeTable().OrderByDescending(x => x.In).Take(10).ToListAsync(stoppingToken);
+            return callTop10;
+        }
+
+        public virtual async Task<object> GetCallList(CancellationToken stoppingToken)
+        {
+            var callList = await _callRepository.Queryable()
+                .LeftJoin<Order>((x, o) => x.Id == o.CallId)
+                .Where((x, o) => x.CreationTime.Date == DateTime.Now.Date
+                    && string.IsNullOrEmpty(o.Id) == false
+                )
+                .Select(x => new
+                {
+                    InOn = SqlFunc.AggregateSum(SqlFunc.IIF(x.Direction == Share.Enums.CallCenter.ECallDirection.In && x.AnsweredTime != null, 1, 0)),
+                    ValidOn = SqlFunc.AggregateSum(SqlFunc.IIF(x.Direction == Share.Enums.CallCenter.ECallDirection.In && x.AnsweredTime != null && x.Duration > 5, 1, 0)),
+                    InNoOn = SqlFunc.AggregateSum(SqlFunc.IIF(x.Direction == Share.Enums.CallCenter.ECallDirection.In && x.AnsweredTime == null, 1, 0)),
+                    OutOn = SqlFunc.AggregateSum(SqlFunc.IIF(x.Direction == Share.Enums.CallCenter.ECallDirection.Out && x.AnsweredTime != null, 1, 0)),
+                    InQueueNoOn = SqlFunc.AggregateSum(SqlFunc.IIF(x.Direction == Share.Enums.CallCenter.ECallDirection.In && x.AnsweredTime == null && x.WaitDuration > 0, 1, 0)),
+                }).MergeTable().ToListAsync(stoppingToken);
+            return callList;
+        }
+
+        public virtual async Task<object> GetCallAverage(CancellationToken stoppingToken)
+        {
+            List<string> timeList = new List<string>();
+            for (int i = 0;i < 24;i++)
+            {
+                var time = i < 10 ? $"0{i}" : i.ToString();
+                timeList.Add(time);
+            }
+            var callAverage = await _callRepository.Queryable()
+                .LeftJoin<Order>((x, o) => x.Id == o.CallId)
+                .Where((x, o) => x.CreationTime.Date == DateTime.Now.Date
+                    && string.IsNullOrEmpty(o.Id) == false
+                )
+                .Select(x => new { time = x.CreationTime.ToString("hh"), x.Direction }).MergeTable()
+                .GroupBy(x => x.time)
+                .Select(x => new
+                {
+                    Time = x.time.ToString(),
+                    In = SqlFunc.AggregateSum(SqlFunc.IIF(x.Direction == Share.Enums.CallCenter.ECallDirection.In, 1, 0)),
+                    InAverag = SqlFunc.AggregateSum(SqlFunc.IIF(x.Direction == Share.Enums.CallCenter.ECallDirection.In, 1, 0)) / 60,
+                    Out = SqlFunc.AggregateSum(SqlFunc.IIF(x.Direction == Share.Enums.CallCenter.ECallDirection.Out, 1, 0)),
+                    OutAverag = SqlFunc.AggregateSum(SqlFunc.IIF(x.Direction == Share.Enums.CallCenter.ECallDirection.Out, 1, 0)) / 60,
+                }).MergeTable().ToListAsync(stoppingToken);
+            var callAverageList = (from t1 in timeList
+                                   join t2 in callAverage on t1 equals t2.Time into t1_t2
+                                   from item in t1_t2.DefaultIfEmpty()
+                                   select new
+                                   {
+                                       Time = t1 + ":00",
+                                       In = t1_t2.Select(x => x.In).FirstOrDefault(),
+                                       InAverag = t1_t2.Select(x => x.InAverag).FirstOrDefault(),
+                                       Out = t1_t2.Select(x => x.Out).FirstOrDefault(),
+                                       OutAverag = t1_t2.Select(x => x.OutAverag).FirstOrDefault()
+                                   }).ToList();
+            return callAverageList;
+        }
+    }
+}

+ 124 - 0
src/Hotline.Application/Bigscreen/YiBinSeatStateDataService.cs

@@ -0,0 +1,124 @@
+using Hotline.CallCenter.Calls;
+using Hotline.DI;
+using SqlSugar;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using XF.Domain.Dependency;
+using XF.Domain.Repository;
+
+namespace Hotline.Application.Bigscreen;
+
+[Injection(AppScopes = EAppScope.YiBin)]
+public class YiBinSeatStateDataService : SeatStateDataServiceBase, ISeatStateDataService, IScopeDependency
+{
+    private readonly IRepository<TrCallRecord> _callRepository;
+    public YiBinSeatStateDataService(IRepository<TrCallRecord> callRepository, IRepository<CallNative> repository) : base(repository)
+    {
+        _callRepository = callRepository;
+    }
+
+    public override async Task<object> GetCall24(CancellationToken stoppingToken)
+    {
+        List<string> timeList = new List<string>();
+        for (int i = 0;i < 24;i++)
+        {
+            var time = i < 10 ? $"0{i}" : i.ToString();
+            timeList.Add(time);
+        }
+        var call24 = await _callRepository.Queryable()
+            .Where(x => x.CreatedTime.Date == DateTime.Now.Date
+                && x.CallOrderType == Share.Enums.CallCenter.ECallOrderType.Order
+            )
+            .Select(x => new { time = x.CreatedTime.ToString("hh"), x.CallDirection }).MergeTable()
+            .GroupBy(x => x.time)
+            .Select(x => new
+            {
+                Time = x.time.ToString(),
+                In = SqlFunc.AggregateSum(SqlFunc.IIF(x.CallDirection == Share.Enums.CallCenter.ECallDirection.In, 1, 0)),
+                Out = SqlFunc.AggregateSum(SqlFunc.IIF(x.CallDirection == Share.Enums.CallCenter.ECallDirection.Out, 1, 0)),
+            }).MergeTable().ToListAsync(stoppingToken);
+
+        var call24List = (from t1 in timeList
+                          join t2 in call24 on t1 equals t2.Time into t1_t2
+                          from item in t1_t2.DefaultIfEmpty()
+                          select new
+                          {
+                              Time = t1 + ":00",
+                              In = t1_t2.Select(x => x.In).FirstOrDefault(),
+                              Out = t1_t2.Select(x => x.Out).FirstOrDefault()
+                          }).ToList();
+        return call24List;
+    }
+
+    public override async Task<object> GetCallTop10(CancellationToken stoppingToken)
+    {
+
+        var callTop10 = await _callRepository.Queryable()
+            .Where(x => x.CreatedTime.Date == DateTime.Now.Date
+                && x.CallOrderType == Share.Enums.CallCenter.ECallOrderType.Order
+            )
+            .GroupBy(x => x.UserName)
+            .Select(x => new
+            {
+                UserName = x.UserName,
+                In = SqlFunc.AggregateSum(SqlFunc.IIF(x.CallDirection == Share.Enums.CallCenter.ECallDirection.In, 1, 0))
+            }).MergeTable().OrderByDescending(x => x.In).Take(10).ToListAsync(stoppingToken);
+        return callTop10;
+    }
+
+    public override async Task<object> GetCallList(CancellationToken stoppingToken)
+    {
+        var callList = await _callRepository.Queryable()
+            .Where(x => x.CreatedTime.Date == DateTime.Now.Date
+                && x.CallOrderType == Share.Enums.CallCenter.ECallOrderType.Order
+            )
+            .Select(x => new
+            {
+                InOn = SqlFunc.AggregateSum(SqlFunc.IIF(x.CallDirection == Share.Enums.CallCenter.ECallDirection.In && x.OnState == Share.Enums.CallCenter.EOnState.On, 1, 0)),
+                ValidOn = SqlFunc.AggregateSum(SqlFunc.IIF(x.CallDirection == Share.Enums.CallCenter.ECallDirection.In && x.OnState == Share.Enums.CallCenter.EOnState.On && x.Duration > 5, 1, 0)),
+                InNoOn = SqlFunc.AggregateSum(SqlFunc.IIF(x.CallDirection == Share.Enums.CallCenter.ECallDirection.In && x.OnState == Share.Enums.CallCenter.EOnState.NoOn, 1, 0)),
+                OutOn = SqlFunc.AggregateSum(SqlFunc.IIF(x.CallDirection == Share.Enums.CallCenter.ECallDirection.Out && x.OnState == Share.Enums.CallCenter.EOnState.On, 1, 0)),
+                InQueueNoOn = SqlFunc.AggregateSum(SqlFunc.IIF(x.CallDirection == Share.Enums.CallCenter.ECallDirection.In && x.OnState == Share.Enums.CallCenter.EOnState.NoOn && x.QueueTims > 0, 1, 0)),
+            }).MergeTable().ToListAsync(stoppingToken);
+        return callList;
+    }
+
+    public override async Task<object> GetCallAverage(CancellationToken stoppingToken)
+    {
+        List<string> timeList = new List<string>();
+        for (int i = 0;i < 24;i++)
+        {
+            var time = i < 10 ? $"0{i}" : i.ToString();
+            timeList.Add(time);
+        }
+        var callAverage = await _callRepository.Queryable()
+            .Where(x => x.CreatedTime.Date == DateTime.Now.Date
+                && x.CallOrderType == Share.Enums.CallCenter.ECallOrderType.Order
+            )
+            .Select(x => new { time = x.CreatedTime.ToString("hh"), x.CallDirection }).MergeTable()
+            .GroupBy(x => x.time)
+            .Select(x => new
+            {
+                Time = x.time.ToString(),
+                In = SqlFunc.AggregateSum(SqlFunc.IIF(x.CallDirection == Share.Enums.CallCenter.ECallDirection.In, 1, 0)),
+                InAverag = SqlFunc.AggregateSum(SqlFunc.IIF(x.CallDirection == Share.Enums.CallCenter.ECallDirection.In, 1, 0)) / 60,
+                Out = SqlFunc.AggregateSum(SqlFunc.IIF(x.CallDirection == Share.Enums.CallCenter.ECallDirection.Out, 1, 0)),
+                OutAverag = SqlFunc.AggregateSum(SqlFunc.IIF(x.CallDirection == Share.Enums.CallCenter.ECallDirection.Out, 1, 0)) / 60,
+            }).MergeTable().ToListAsync(stoppingToken);
+        var callAverageList = (from t1 in timeList
+                               join t2 in callAverage on t1 equals t2.Time into t1_t2
+                               from item in t1_t2.DefaultIfEmpty()
+                               select new
+                               {
+                                   Time = t1 + ":00",
+                                   In = t1_t2.Select(x => x.In).FirstOrDefault(),
+                                   InAverag = t1_t2.Select(x => x.InAverag).FirstOrDefault(),
+                                   Out = t1_t2.Select(x => x.Out).FirstOrDefault(),
+                                   OutAverag = t1_t2.Select(x => x.OutAverag).FirstOrDefault()
+                               }).ToList();
+        return callAverageList;
+    }
+}

+ 7 - 0
src/Hotline.Application/FlowEngine/IWorkflowApplication.cs

@@ -8,8 +8,10 @@ using Hotline.Share.Dtos.File;
 using Hotline.Share.Dtos.FlowEngine;
 using Hotline.Share.Dtos.FlowEngine.Definition;
 using Hotline.Share.Dtos.Order;
+using Hotline.Share.Dtos.Order.Handle;
 using Hotline.Share.Dtos.Settings;
 using Hotline.Share.Enums.FlowEngine;
+using Microsoft.AspNetCore.Mvc;
 using XF.Domain.Authentications;
 
 namespace Hotline.Application.FlowEngine
@@ -84,6 +86,11 @@ namespace Hotline.Application.FlowEngine
         /// </summary>
         Task<NextStepsWithOpinionDto<NextStepOption>> GetNextStepsAsync(string workflowId, string stepId, CancellationToken cancellationToken);
 
+        /// <summary>
+        /// 跨级指派查询下一步可选节点及办理对象参数
+        /// </summary>
+        Task<NextStepsDto<NextStepOption>> GetCrossLevelStepsAsync(GetCrossLevelStepsDto dto, CancellationToken cancellationToken);
+
         /// <summary>
         /// 查询撤回可选节点
         /// </summary>

+ 191 - 141
src/Hotline.Application/FlowEngine/WorkflowApplication.cs

@@ -31,6 +31,8 @@ using Microsoft.Extensions.Logging;
 using System.Text;
 using System.Diagnostics;
 using Hotline.Configurations;
+using Hotline.Share.Dtos.Order.Handle;
+using Microsoft.AspNetCore.Http;
 using Microsoft.Extensions.Options;
 using Newtonsoft.Json;
 using NPOI.SS.Formula.Functions;
@@ -39,79 +41,55 @@ namespace Hotline.Application.FlowEngine;
 
 public class WorkflowApplication : IWorkflowApplication, IScopeDependency
 {
-    private readonly IDefinitionDomainService _definitionDomainService;
     private readonly IWorkflowDomainService _workflowDomainService;
-    private readonly IOrderDomainService _orderDomainService;
-    private readonly IWorkflowRepository _workflowRepository;
-    private IRepository<WorkflowDefinition> _definitionRepository;
     private IRepository<WorkflowStep> _workflowStepRepository;
     private IRepository<WorkflowTrace> _workflowTraceRepository;
     private readonly IRepository<User> _userRepository;
-    private readonly IAccountRepository _accountRepository;
     private readonly ISystemOrganizeRepository _organizeRepository;
     private readonly IRepository<Role> _roleRepository;
     private readonly IWfModuleCacheManager _wfModuleCacheManager;
-    private readonly ISystemDomainService _systemDomainService;
-    private readonly ITimeLimitDomainService _timeLimitDomainService;
-    private readonly IUserDomainService _userDomainService;
-    private readonly IAccountDomainService _accountDomainService;
-    private readonly ISessionContext _sessionContext;
+    private readonly ISessionContextProvider _sessionContextProvider;
     private readonly IMapper _mapper;
     private readonly IFileRepository _fileRepository;
     private readonly ILogger<WorkflowApplication> _logger;
     private readonly ISystemSettingCacheManager _systemSettingCacheManager;
 
     public WorkflowApplication(
-        IDefinitionDomainService definitionDomainService,
         IWorkflowDomainService workflowDomainService,
-        IOrderDomainService orderDomainService,
-        IWorkflowRepository workflowRepository,
-        IRepository<WorkflowDefinition> definitionRepository,
         IRepository<WorkflowStep> workflowStepRepository,
         IRepository<WorkflowTrace> workflowTraceRepository,
         IRepository<User> userRepository,
-        IAccountRepository accountRepository,
         ISystemOrganizeRepository organizeRepository,
         IRepository<Role> roleRepository,
         IWfModuleCacheManager wfModuleCacheManager,
-        ISystemDomainService systemDomainService,
-        ITimeLimitDomainService timeLimitDomainService,
         ISessionContextProvider sessionContextProvider,
         IMapper mapper,
         IFileRepository fileRepository,
         ISystemSettingCacheManager systemSettingCacheManager,
-        ILogger<WorkflowApplication> logger,
-        IOptions<CityBaseConfiguration> cityBaseConfiguration)
+        ILogger<WorkflowApplication> logger)
     {
-        _definitionDomainService = definitionDomainService;
         _workflowDomainService = workflowDomainService;
-        _orderDomainService = orderDomainService;
-        _workflowRepository = workflowRepository;
-        _definitionRepository = definitionRepository;
         _workflowStepRepository = workflowStepRepository;
         _workflowTraceRepository = workflowTraceRepository;
         _userRepository = userRepository;
-        _accountRepository = accountRepository;
         _organizeRepository = organizeRepository;
         _roleRepository = roleRepository;
         _wfModuleCacheManager = wfModuleCacheManager;
-        _systemDomainService = systemDomainService;
-        _timeLimitDomainService = timeLimitDomainService;
+        _sessionContextProvider = sessionContextProvider;
         _mapper = mapper;
         _fileRepository = fileRepository;
         _logger = logger;
         _systemSettingCacheManager = systemSettingCacheManager;
-        _sessionContext = sessionContextProvider.SessionContext;
     }
 
     public async Task<string> StartWorkflowAsync(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 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;
@@ -125,7 +103,7 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
 
         //下一节点是否为动态节点
         var isNextDynamic = startStepDefine.InstanceMode is EInstanceMode.Dynamic &&
-                            !_workflowDomainService.DynamicShouldTerminal(startStepDefine, _sessionContext.OrgLevel);
+                            !_workflowDomainService.DynamicShouldTerminal(startStepDefine, _sessionContextProvider.SessionContext.OrgLevel);
         var firstStepDefine = isNextDynamic
             ? startStepDefine
             : definition.FindStepDefine(dto.NextStepCode);
@@ -151,7 +129,7 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
         }
 
         var workflow = await _workflowDomainService.CreateWorkflowAsync(wfModule, dto.Title,
-            _sessionContext.RequiredUserId, _sessionContext.RequiredOrgId,
+            _sessionContextProvider.SessionContext.RequiredUserId, _sessionContextProvider.SessionContext.RequiredOrgId,
             externalId, cancellationToken);
 
         //var startStepHandles = new List<WorkflowStepHandler>{WorkflowStepHandler.Create(workflow.Id, workflow.ExternalId,
@@ -162,12 +140,12 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
         var startStep = _workflowDomainService.CreateStartStep(workflow, startStepDefine, dto,
             new FlowStepHandler
             {
-                Key = _sessionContext.RequiredUserId,
-                Value = _sessionContext.UserName,
-                UserId = _sessionContext.UserId,
-                Username = _sessionContext.UserName,
-                OrgId = _sessionContext.RequiredOrgId,
-                OrgName = _sessionContext.OrgName,
+                Key = _sessionContextProvider.SessionContext.RequiredUserId,
+                Value = _sessionContextProvider.SessionContext.UserName,
+                UserId = _sessionContextProvider.SessionContext.UserId,
+                Username = _sessionContextProvider.SessionContext.UserName,
+                OrgId = _sessionContextProvider.SessionContext.RequiredOrgId,
+                OrgName = _sessionContextProvider.SessionContext.OrgName,
                 RoleId = defineHandler.Key,
                 RoleName = defineHandler.Value,
             }, expiredTime);
@@ -178,8 +156,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 =
@@ -199,11 +176,11 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
 
         //更新受理人信息
         workflow.UpdateAcceptor(
-            _sessionContext.RequiredUserId,
-            _sessionContext.UserName,
-            _sessionContext.StaffNo,
-            _sessionContext.RequiredOrgId,
-            _sessionContext.OrgName);
+            _sessionContextProvider.SessionContext.RequiredUserId,
+            _sessionContextProvider.SessionContext.UserName,
+            _sessionContextProvider.SessionContext.StaffNo,
+            _sessionContextProvider.SessionContext.RequiredOrgId,
+            _sessionContextProvider.SessionContext.OrgName);
 
         await _workflowDomainService.StartAsync(workflow, startStep, dto, firstStepDefine, isNextDynamic,
             flowAssignInfo, counterSignType, expiredTime, cancellationToken);
@@ -234,7 +211,7 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
         var startStepDefine = definition.FindStartStepDefine();
 
         var workflow = await _workflowDomainService.CreateWorkflowAsync(wfModule, dto.Title,
-            _sessionContext.RequiredUserId, _sessionContext.RequiredOrgId,
+            _sessionContextProvider.SessionContext.RequiredUserId, _sessionContextProvider.SessionContext.RequiredOrgId,
             externalId, cancellationToken);
 
         var defineHandler = startStepDefine.HandlerTypeItems.First();
@@ -243,12 +220,12 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
         var startStep = _workflowDomainService.CreateStartStep(workflow, startStepDefine, dto,
             new FlowStepHandler
             {
-                Key = _sessionContext.RequiredUserId,
-                Value = _sessionContext.UserName,
-                UserId = _sessionContext.UserId,
-                Username = _sessionContext.UserName,
-                OrgId = _sessionContext.RequiredOrgId,
-                OrgName = _sessionContext.OrgName,
+                Key = _sessionContextProvider.SessionContext.RequiredUserId,
+                Value = _sessionContextProvider.SessionContext.UserName,
+                UserId = _sessionContextProvider.SessionContext.UserId,
+                Username = _sessionContextProvider.SessionContext.UserName,
+                OrgId = _sessionContextProvider.SessionContext.RequiredOrgId,
+                OrgName = _sessionContextProvider.SessionContext.OrgName,
                 RoleId = defineHandler.Key,
                 RoleName = defineHandler.Value,
             }, expiredTime,
@@ -271,11 +248,11 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
 
         //更新受理人信息
         workflow.UpdateAcceptor(
-            _sessionContext.RequiredUserId,
-            _sessionContext.UserName,
-            _sessionContext.StaffNo,
-            _sessionContext.RequiredOrgId,
-            _sessionContext.OrgName);
+            _sessionContextProvider.SessionContext.RequiredUserId,
+            _sessionContextProvider.SessionContext.UserName,
+            _sessionContextProvider.SessionContext.StaffNo,
+            _sessionContextProvider.SessionContext.RequiredOrgId,
+            _sessionContextProvider.SessionContext.OrgName);
 
         return workflow.Id;
     }
@@ -306,7 +283,7 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
 
         //下一节点是否为动态节点
         var isNextDynamic = currentStepDefine.InstanceMode is EInstanceMode.Dynamic &&
-                            !_workflowDomainService.DynamicShouldTerminal(currentStepDefine, _sessionContext.OrgLevel);
+                            !_workflowDomainService.DynamicShouldTerminal(currentStepDefine, _sessionContextProvider.SessionContext.OrgLevel);
 
         StepDefine nextStepDefine;
         if (isNextDynamic
@@ -359,10 +336,10 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
         var workflow = await _workflowDomainService.GetWorkflowAsync(dto.WorkflowId, withSteps: true,
             withTraces: true, withCountersigns: true, cancellationToken: cancellationToken);
         return await _workflowDomainService.PreviousAsync(workflow, dto,
-            _sessionContext.RequiredUserId, _sessionContext.UserName,
-            _sessionContext.RequiredOrgId, _sessionContext.OrgName,
-            _sessionContext.OrgAreaCode, _sessionContext.OrgAreaName,
-            _sessionContext.OrgIsCenter, _sessionContext.Roles, cancellationToken);
+            _sessionContextProvider.SessionContext.RequiredUserId, _sessionContextProvider.SessionContext.UserName,
+            _sessionContextProvider.SessionContext.RequiredOrgId, _sessionContextProvider.SessionContext.OrgName,
+            _sessionContextProvider.SessionContext.OrgAreaCode, _sessionContextProvider.SessionContext.OrgAreaName,
+            _sessionContextProvider.SessionContext.OrgIsCenter, _sessionContextProvider.SessionContext.Roles, cancellationToken);
     }
 
     /// <summary>
@@ -498,8 +475,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);
@@ -533,7 +509,7 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
 
         var startStepDefine = definition.FindStartStepDefine();
         if (startStepDefine.InstanceMode is EInstanceMode.Dynamic &&
-            !_workflowDomainService.DynamicShouldTerminal(startStepDefine, _sessionContext.OrgLevel))
+            !_workflowDomainService.DynamicShouldTerminal(startStepDefine, _sessionContextProvider.SessionContext.OrgLevel))
         {
             var settingHandle = _systemSettingCacheManager.GetSetting(SettingConstants.RoleJingBanRen);
             var settingLead = _systemSettingCacheManager.GetSetting(SettingConstants.RoleLingDao);
@@ -565,7 +541,7 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
         var workflow = await _workflowDomainService.GetWorkflowAsync(workflowId, withDefine: true, withSteps: true,
             cancellationToken: cancellationToken);
         var currentStep = _workflowDomainService.FindCurrentStepWaitForHandle(workflow,
-            _sessionContext.RequiredUserId, _sessionContext.RequiredOrgId, _sessionContext.Roles);
+            _sessionContextProvider.SessionContext.RequiredUserId, _sessionContextProvider.SessionContext.RequiredOrgId, _sessionContextProvider.SessionContext.Roles);
         if (currentStep.StepType is EStepType.End)
             throw new UserFriendlyException("结束节点无需办理");
 
@@ -586,6 +562,72 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
         return await GetNextStepsAsync(workflow, currentStep, cancellationToken);
     }
 
+    /// <summary>
+    /// 跨级指派查询下一步可选节点及办理对象参数
+    /// </summary>
+    public async Task<NextStepsDto<NextStepOption>> GetCrossLevelStepsAsync(GetCrossLevelStepsDto dto, CancellationToken cancellationToken)
+    {
+        WorkflowDefinition definition;
+        if (string.IsNullOrEmpty(dto.WorkflowId))
+        {
+            var wfModule = await GetWorkflowModuleAsync(WorkflowModuleConsts.OrderHandle, cancellationToken);
+            definition = wfModule.Definition;
+        }
+        else
+        {
+            var workflow = await _workflowDomainService.GetWorkflowAsync(dto.WorkflowId, withDefine: true, cancellationToken: cancellationToken);
+            definition = workflow.WorkflowDefinition;
+        }
+        if (definition == null)
+            throw new UserFriendlyException("无效模板编码");
+        if (definition.Status is not EDefinitionStatus.Enable)
+            throw new UserFriendlyException("该模板不可用");
+
+        var currentStepDefine = definition.FindStepDefine(dto.StepCode);
+        var nextStepDefines = definition.FindStepDefines(currentStepDefine.NextSteps.Select(d => d.Code))
+            .Where(d => d.StepType == EStepType.Normal && d.BusinessType == EBusinessType.Department)
+            .ToList();
+
+        var stepOptions = new List<NextStepOption>();
+        foreach (var stepDefine in nextStepDefines)
+        {
+            var success = int.TryParse(stepDefine.HandlerTypeItems.First().Key, out var level);
+            int? orgLevel = success ? level : null;
+            var nextStepOption =   new NextStepOption
+            {
+                Key = stepDefine.Code,
+                Value = stepDefine.Name,
+                StepType = stepDefine.StepType,
+                BusinessType = stepDefine.BusinessType,
+                HandlerType = stepDefine.HandlerType,
+                OrgLevel = stepDefine.HandlerType is EHandlerType.OrgLevel ? orgLevel : null,
+            };
+            var orgs = await _organizeRepository.Queryable()
+                .Where(d => d.IsEnable &&
+                d.Level == orgLevel &&
+                dto.OrgIds.Contains(d.ParentId))
+                .ToListAsync(cancellationToken);
+            nextStepOption.Items = orgs.Select(d => new FlowStepHandler
+                {
+                    Key = d.Id,
+                    Value = d.Name,
+                    OrgId = d.Id,
+                    OrgName = d.Name
+                })
+                .ToList();
+            nextStepOption.FlowDirection = _workflowDomainService.GetFlowDirection(dto.BusinessType, stepDefine.BusinessType);
+            stepOptions.Add(nextStepOption);
+        }
+
+        return new NextStepsDto<NextStepOption>
+        {
+            CanReject = false,
+            CanStartCountersign = true,
+            CurrentStepBusinessType = dto.BusinessType,
+            Steps = stepOptions
+        };
+    }
+
     private async Task<NextStepsWithOpinionDto<NextStepOption>> GetNextStepsAsync(Workflow workflow, WorkflowStep currentStep,
         CancellationToken cancellationToken)
     {
@@ -604,15 +646,17 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
         var currentStepDefine = _workflowDomainService.GetStepDefine(workflow.WorkflowDefinition, currentStep.Code);
 
         if (currentStep.InstanceMode is EInstanceMode.Dynamic &&
-            !_workflowDomainService.DynamicShouldTerminal(currentStepDefine, _sessionContext.OrgLevel))
+            !_workflowDomainService.DynamicShouldTerminal(currentStepDefine, _sessionContextProvider.SessionContext.OrgLevel))
         {
             var settingHandle = _systemSettingCacheManager.GetSetting(SettingConstants.RoleJingBanRen);
             var settingLead = _systemSettingCacheManager.GetSetting(SettingConstants.RoleLingDao);
             //动态生成下一步
             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;
         }
 
@@ -798,7 +842,7 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
         nextDto.ReviewResult = EReviewResult.Failed;
         nextDto.NextStepCode = endStepDefine.Code;
         nextDto.NextStepName = endStepDefine.Name;
-        nextDto.FlowDirection = _sessionContext.OrgIsCenter
+        nextDto.FlowDirection = _sessionContextProvider.SessionContext.OrgIsCenter
             ? EFlowDirection.CenterToFile
             : EFlowDirection.OrgToFile;
 
@@ -836,8 +880,6 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
     private async Task<List<NextStepOption>> GetConfigStepsAsync(
         Workflow workflow,
         WorkflowStep currentStep,
-        //EStepType currentStepType,
-        //EBusinessType currentBusinessType,
         List<StepDefine> stepDefines,
         CancellationToken cancellationToken)
     {
@@ -914,7 +956,7 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
             };
         }
 
-        var orgId = _sessionContext.RequiredOrgId;
+        var orgId = _sessionContextProvider.SessionContext.RequiredOrgId;
         var levelOneOrgId = orgId.GetHigherOrgId();
         var isCenter = levelOneOrgId.IsCenter();
         switch (stepDefine.HandlerType)
@@ -998,8 +1040,8 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
                 }
                 else
                 {
-                    var upLevels = levels.Where(d => d <= _sessionContext.OrgLevel).ToList();
-                    var lowLevels = levels.Where(d => d > _sessionContext.OrgLevel).ToList();
+                    var upLevels = levels.Where(d => d <= _sessionContextProvider.SessionContext.OrgLevel).ToList();
+                    var lowLevels = levels.Where(d => d > _sessionContextProvider.SessionContext.OrgLevel).ToList();
                     orgs1 = await query
                         .Where(d => (upLevels.Contains(d.Level) && d.Id.StartsWith(levelOneOrgId)) ||
                                     (lowLevels.Contains(d.Level) && d.Id.Contains(orgId)))
@@ -1007,12 +1049,12 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
                 }
 
                 handlers = orgs1.Select(d => new FlowStepHandler
-                    {
-                        Key = d.Id,
-                        Value = d.Name,
-                        OrgId = d.Id,
-                        OrgName = d.Name
-                    })
+                {
+                    Key = d.Id,
+                    Value = d.Name,
+                    OrgId = d.Id,
+                    OrgName = d.Name
+                })
                     .ToList();
                 break;
             case EHandlerType.OrgType:
@@ -1024,12 +1066,12 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
                     .ToListAsync(cancellationToken);
 
                 handlers = orgs2.Select(d => new FlowStepHandler
-                    {
-                        Key = d.Id,
-                        Value = d.Name,
-                        OrgId = d.Id,
-                        OrgName = d.Name
-                    })
+                {
+                    Key = d.Id,
+                    Value = d.Name,
+                    OrgId = d.Id,
+                    OrgName = d.Name
+                })
                     .ToList();
                 break;
             default:
@@ -1093,20 +1135,20 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
         switch (policy)
         {
             case EDynamicPolicy.OrgUpCenterTop:
-                orgLevel = _sessionContext.OrgLevel - 1;
+                orgLevel = _sessionContextProvider.SessionContext.OrgLevel - 1;
                 if (orgLevel < 0) orgLevel = 0;
                 break;
             case EDynamicPolicy.OrgUp:
-                orgLevel = _sessionContext.OrgLevel - 1;
+                orgLevel = _sessionContextProvider.SessionContext.OrgLevel - 1;
                 if (orgLevel <= 0) orgLevel = 1;
                 break;
             case EDynamicPolicy.OrgDownCenterTop:
-                orgLevel = _sessionContext.OrgIsCenter
+                orgLevel = _sessionContextProvider.SessionContext.OrgIsCenter
                     ? 1
-                    : _sessionContext.OrgLevel + 1;
+                    : _sessionContextProvider.SessionContext.OrgLevel + 1;
                 break;
             case EDynamicPolicy.OrgDown:
-                orgLevel = _sessionContext.OrgLevel + 1;
+                orgLevel = _sessionContextProvider.SessionContext.OrgLevel + 1;
                 break;
             default:
                 throw new ArgumentOutOfRangeException(nameof(policy), policy, null);
@@ -1144,7 +1186,7 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
         switch (policy)
         {
             case EDynamicPolicy.OrgUpCenterTop:
-                orgLevel = _sessionContext.OrgLevel - 1;
+                orgLevel = _sessionContextProvider.SessionContext.OrgLevel - 1;
                 if (orgLevel < 0) orgLevel = 0;
 
                 if (orgLevel == 0)
@@ -1169,7 +1211,7 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
                     businessType = EBusinessType.Department;
 
                     //上级部门Id
-                    upperOrgId = _sessionContext.RequiredOrgId.GetHigherOrgId(orgLevel);
+                    upperOrgId = _sessionContextProvider.SessionContext.RequiredOrgId.GetHigherOrgId(orgLevel);
                     items = await _organizeRepository.Queryable()
                         .Where(d => d.Id == upperOrgId)
                         .Select(d => new FlowStepHandler
@@ -1184,15 +1226,15 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
 
                 break;
             case EDynamicPolicy.OrgUp:
-                businessType = _sessionContext.OrgIsCenter
+                businessType = _sessionContextProvider.SessionContext.OrgIsCenter
                     ? EBusinessType.Send
-                    : _sessionContext.RequiredOrgId.CalcOrgLevel() == 1
+                    : _sessionContextProvider.SessionContext.RequiredOrgId.CalcOrgLevel() == 1
                         ? EBusinessType.Send
                         : EBusinessType.Department;
-                orgLevel = _sessionContext.OrgLevel - 1;
+                orgLevel = _sessionContextProvider.SessionContext.OrgLevel - 1;
                 if (orgLevel <= 0) orgLevel = 1;
                 //上级部门Id
-                upperOrgId = _sessionContext.RequiredOrgId.GetHigherOrgId(orgLevel);
+                upperOrgId = _sessionContextProvider.SessionContext.RequiredOrgId.GetHigherOrgId(orgLevel);
                 items = await _organizeRepository.Queryable()
                     .Where(d => d.Id == upperOrgId)
                     .Select(d => new FlowStepHandler
@@ -1205,7 +1247,7 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
                     .ToListAsync(cancellationToken);
                 break;
             case EDynamicPolicy.OrgUpHandleCenterTop:
-                orgLevel = _sessionContext.OrgLevel - 1;
+                orgLevel = _sessionContextProvider.SessionContext.OrgLevel - 1;
                 if (orgLevel < 0) orgLevel = 0;
 
                 if (orgLevel == 0)
@@ -1230,7 +1272,7 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
                     businessType = EBusinessType.Department;
                     roleName = handleRoleName;
                     //上级部门Id
-                    upperOrgId = _sessionContext.RequiredOrgId.GetHigherOrgId(orgLevel);
+                    upperOrgId = _sessionContextProvider.SessionContext.RequiredOrgId.GetHigherOrgId(orgLevel);
                     items = await _organizeRepository.Queryable()
                         .Where(d => d.Id == upperOrgId)
                         .Select(d => new FlowStepHandler
@@ -1247,16 +1289,16 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
 
                 break;
             case EDynamicPolicy.OrgUpHandle:
-                businessType = _sessionContext.OrgIsCenter
+                businessType = _sessionContextProvider.SessionContext.OrgIsCenter
                     ? EBusinessType.Send
-                    : _sessionContext.RequiredOrgId.CalcOrgLevel() == 1
+                    : _sessionContextProvider.SessionContext.RequiredOrgId.CalcOrgLevel() == 1
                         ? EBusinessType.Send
                         : EBusinessType.Department;
-                orgLevel = _sessionContext.OrgLevel - 1;
+                orgLevel = _sessionContextProvider.SessionContext.OrgLevel - 1;
                 if (orgLevel <= 0) orgLevel = 1;
                 roleName = handleRoleName;
                 //上级部门Id
-                upperOrgId = _sessionContext.RequiredOrgId.GetHigherOrgId(orgLevel);
+                upperOrgId = _sessionContextProvider.SessionContext.RequiredOrgId.GetHigherOrgId(orgLevel);
                 items = await _organizeRepository.Queryable()
                     .Where(d => d.Id == upperOrgId)
                     .Select(d => new FlowStepHandler
@@ -1271,7 +1313,7 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
                     .ToListAsync(cancellationToken);
                 break;
             case EDynamicPolicy.OrgUpLeadCenterTop:
-                orgLevel = _sessionContext.OrgLevel - 1;
+                orgLevel = _sessionContextProvider.SessionContext.OrgLevel - 1;
                 if (orgLevel < 0) orgLevel = 0;
 
                 if (orgLevel == 0)
@@ -1294,24 +1336,27 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
                 else
                 {
                     businessType = EBusinessType.Department;
-                    upperOrgId = _sessionContext.RequiredOrgId.GetHigherOrgId(_sessionContext.OrgLevel);
-                    isLead = _sessionContext.Roles.Any(x => x == leadRoleCode);
+                    upperOrgId = _sessionContextProvider.SessionContext.RequiredOrgId.GetHigherOrgId(_sessionContextProvider.SessionContext.OrgLevel);
+                    isLead = _sessionContextProvider.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 == _sessionContextProvider.SessionContext.RequiredOrgId && x.Roles.Any(r => r.Name == leadRoleCode), cancellationToken);
                         if (isSkip)
                         {
                             roleId = leadRoleCode;
                             roleName = leadRoleName;
                         }
                     }
+
                     if (isLead || !isSkip)
                     {
                         //上级部门Id
-                        upperOrgId = _sessionContext.RequiredOrgId.GetHigherOrgId(orgLevel);
+                        upperOrgId = _sessionContextProvider.SessionContext.RequiredOrgId.GetHigherOrgId(orgLevel);
                         roleId = handleRoleCode;
                         roleName = handleRoleName;
                     }
+
                     items = await _organizeRepository.Queryable()
                         .Where(d => d.Id == upperOrgId)
                         .Select(d => new FlowStepHandler
@@ -1328,31 +1373,34 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
 
                 break;
             case EDynamicPolicy.OrgUpLead:
-                businessType = _sessionContext.OrgIsCenter
+                businessType = _sessionContextProvider.SessionContext.OrgIsCenter
                     ? EBusinessType.Send
-                    : _sessionContext.RequiredOrgId.CalcOrgLevel() == 1
+                    : _sessionContextProvider.SessionContext.RequiredOrgId.CalcOrgLevel() == 1
                         ? EBusinessType.Send
                         : EBusinessType.Department;
-                orgLevel = _sessionContext.OrgLevel - 1;
+                orgLevel = _sessionContextProvider.SessionContext.OrgLevel - 1;
                 if (orgLevel <= 0) orgLevel = 1;
-                upperOrgId = _sessionContext.RequiredOrgId.GetHigherOrgId(_sessionContext.OrgLevel);
-                isLead = _sessionContext.Roles.Any(x => x == leadRoleCode);
+                upperOrgId = _sessionContextProvider.SessionContext.RequiredOrgId.GetHigherOrgId(_sessionContextProvider.SessionContext.OrgLevel);
+                isLead = _sessionContextProvider.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 == _sessionContextProvider.SessionContext.RequiredOrgId && x.Roles.Any(r => r.Name == leadRoleCode), cancellationToken);
                     if (isSkip)
                     {
                         roleId = leadRoleCode;
                         roleName = leadRoleName;
                     }
                 }
+
                 if (isLead || !isSkip)
                 {
                     //上级部门Id
-                    upperOrgId = _sessionContext.RequiredOrgId.GetHigherOrgId(orgLevel);
+                    upperOrgId = _sessionContextProvider.SessionContext.RequiredOrgId.GetHigherOrgId(orgLevel);
                     roleId = handleRoleCode;
                     roleName = handleRoleName;
                 }
+
                 items = await _organizeRepository.Queryable()
                     .Where(d => d.Id == upperOrgId)
                     .Select(d => new FlowStepHandler
@@ -1367,12 +1415,12 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
                     .ToListAsync(cancellationToken);
                 break;
             case EDynamicPolicy.ArriveCenter:
-                businessType = _sessionContext.OrgIsCenter
+                businessType = _sessionContextProvider.SessionContext.OrgIsCenter
                     ? EBusinessType.Send
-                    : _sessionContext.RequiredOrgId.CalcOrgLevel() == 1
+                    : _sessionContextProvider.SessionContext.RequiredOrgId.CalcOrgLevel() == 1
                         ? EBusinessType.Send
                         : EBusinessType.Department;
-                orgLevel = _sessionContext.OrgLevel - 1;
+                orgLevel = _sessionContextProvider.SessionContext.OrgLevel - 1;
                 if (orgLevel <= 0) orgLevel = 1;
                 items = await _organizeRepository.Queryable()
                     .Where(d => d.IsCenter)
@@ -1386,10 +1434,12 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
                     .ToListAsync(cancellationToken);
                 break;
             case EDynamicPolicy.ArriveOneOrg:
-                businessType = _sessionContext.OrgIsCenter ? EBusinessType.Send : EBusinessType.Department;
-                orgLevel = _sessionContext.OrgIsCenter ? 0 : 1;
+                businessType = _sessionContextProvider.SessionContext.OrgIsCenter ? EBusinessType.Send : EBusinessType.Department;
+                orgLevel = _sessionContextProvider.SessionContext.OrgIsCenter ? 0 : 1;
                 //上级部门Id
-                upperOrgId = _sessionContext.OrgIsCenter ? _sessionContext.RequiredOrgId.Substring(0, 3) : _sessionContext.RequiredOrgId.Substring(0, 6);
+                upperOrgId = _sessionContextProvider.SessionContext.OrgIsCenter
+                    ? _sessionContextProvider.SessionContext.RequiredOrgId.Substring(0, 3)
+                    : _sessionContextProvider.SessionContext.RequiredOrgId.Substring(0, 6);
                 items = await _organizeRepository.Queryable()
                     .Where(d => d.Id == upperOrgId)
                     .Select(d => new FlowStepHandler
@@ -1403,7 +1453,7 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
                 break;
             case EDynamicPolicy.OrgDownCenterTop:
                 businessType = EBusinessType.Department;
-                if (_sessionContext.OrgIsCenter)
+                if (_sessionContextProvider.SessionContext.OrgIsCenter)
                 {
                     orgLevel = 1;
                     items = await _organizeRepository.Queryable()
@@ -1419,10 +1469,10 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
                 }
                 else
                 {
-                    orgLevel = _sessionContext.OrgLevel + 1;
+                    orgLevel = _sessionContextProvider.SessionContext.OrgLevel + 1;
                     items = await _organizeRepository.Queryable()
                         .Where(d => !d.IsCenter && d.Level == orgLevel &&
-                                    d.Id.StartsWith(_sessionContext.RequiredOrgId))
+                                    d.Id.StartsWith(_sessionContextProvider.SessionContext.RequiredOrgId))
                         .Select(d => new FlowStepHandler
                         {
                             Key = d.Id,
@@ -1436,9 +1486,9 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
                 break;
             case EDynamicPolicy.OrgDown:
                 businessType = EBusinessType.Department;
-                orgLevel = _sessionContext.OrgLevel + 1;
+                orgLevel = _sessionContextProvider.SessionContext.OrgLevel + 1;
                 items = await _organizeRepository.Queryable()
-                    .Where(d => d.Level == orgLevel && d.Id.StartsWith(_sessionContext.RequiredOrgId))
+                    .Where(d => d.Level == orgLevel && d.Id.StartsWith(_sessionContextProvider.SessionContext.RequiredOrgId))
                     .Select(d => new FlowStepHandler
                     {
                         Key = d.Id,
@@ -1468,7 +1518,6 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
     /// 会签 动态策略
     /// </summary>
     /// <returns></returns>
-
     private async Task<NextStepOption> GetDynamicStepAsync(
         EDynamicPolicyCountersign policy, EStepType stepType,
         EBusinessType currentBusinessType, CancellationToken cancellationToken)
@@ -1481,7 +1530,7 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
         switch (policy)
         {
             case EDynamicPolicyCountersign.OrgUpCenterTop:
-                orgLevel = _sessionContext.OrgLevel - 1;
+                orgLevel = _sessionContextProvider.SessionContext.OrgLevel - 1;
                 if (orgLevel < 0) orgLevel = 0;
 
                 if (orgLevel == 0)
@@ -1506,7 +1555,7 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
                     businessType = EBusinessType.Department;
 
                     //上级部门Id
-                    upperOrgId = _sessionContext.RequiredOrgId.GetHigherOrgId(orgLevel);
+                    upperOrgId = _sessionContextProvider.SessionContext.RequiredOrgId.GetHigherOrgId(orgLevel);
                     items = await _organizeRepository.Queryable()
                         .Where(d => d.Id == upperOrgId)
                         .Select(d => new FlowStepHandler
@@ -1521,15 +1570,15 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
 
                 break;
             case EDynamicPolicyCountersign.OrgUp:
-                businessType = _sessionContext.OrgIsCenter
+                businessType = _sessionContextProvider.SessionContext.OrgIsCenter
                     ? EBusinessType.Send
-                    : _sessionContext.RequiredOrgId.CalcOrgLevel() == 1
+                    : _sessionContextProvider.SessionContext.RequiredOrgId.CalcOrgLevel() == 1
                         ? EBusinessType.Send
                         : EBusinessType.Department;
-                orgLevel = _sessionContext.OrgLevel - 1;
+                orgLevel = _sessionContextProvider.SessionContext.OrgLevel - 1;
                 if (orgLevel <= 0) orgLevel = 1;
                 //上级部门Id
-                upperOrgId = _sessionContext.RequiredOrgId.GetHigherOrgId(orgLevel);
+                upperOrgId = _sessionContextProvider.SessionContext.RequiredOrgId.GetHigherOrgId(orgLevel);
                 items = await _organizeRepository.Queryable()
                     .Where(d => d.Id == upperOrgId)
                     .Select(d => new FlowStepHandler
@@ -1543,7 +1592,7 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
                 break;
             case EDynamicPolicyCountersign.OrgDownCenterTop:
                 businessType = EBusinessType.Department;
-                if (_sessionContext.OrgIsCenter)
+                if (_sessionContextProvider.SessionContext.OrgIsCenter)
                 {
                     orgLevel = 1;
                     items = await _organizeRepository.Queryable()
@@ -1559,10 +1608,10 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
                 }
                 else
                 {
-                    orgLevel = _sessionContext.OrgLevel + 1;
+                    orgLevel = _sessionContextProvider.SessionContext.OrgLevel + 1;
                     items = await _organizeRepository.Queryable()
                         .Where(d => !d.IsCenter && d.Level == orgLevel &&
-                                    d.Id.StartsWith(_sessionContext.RequiredOrgId))
+                                    d.Id.StartsWith(_sessionContextProvider.SessionContext.RequiredOrgId))
                         .Select(d => new FlowStepHandler
                         {
                             Key = d.Id,
@@ -1576,9 +1625,9 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
                 break;
             case EDynamicPolicyCountersign.OrgDown:
                 businessType = EBusinessType.Department;
-                orgLevel = _sessionContext.OrgLevel + 1;
+                orgLevel = _sessionContextProvider.SessionContext.OrgLevel + 1;
                 items = await _organizeRepository.Queryable()
-                    .Where(d => d.Level == orgLevel && d.Id.StartsWith(_sessionContext.RequiredOrgId))
+                    .Where(d => d.Level == orgLevel && d.Id.StartsWith(_sessionContextProvider.SessionContext.RequiredOrgId))
                     .Select(d => new FlowStepHandler
                     {
                         Key = d.Id,
@@ -1603,6 +1652,7 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
             Items = items
         };
     }
+
     /// <summary>
     /// 查询下一节点办理对象类型(user or org)及实际办理对象
     /// </summary>

+ 1 - 0
src/Hotline.Application/Hotline.Application.csproj

@@ -22,6 +22,7 @@
     <ProjectReference Include="..\Hotline.NewRock\Hotline.NewRock.csproj" />
     <ProjectReference Include="..\Hotline.Repository.SqlSugar\Hotline.Repository.SqlSugar.csproj" />
     <ProjectReference Include="..\Hotline.Wex\Hotline.Wex.csproj" />
+    <ProjectReference Include="..\Hotline.XingTang\Hotline.XingTang.csproj" />
     <ProjectReference Include="..\Hotline.YbEnterprise.Sdk\Hotline.YbEnterprise.Sdk.csproj" />
     <ProjectReference Include="..\Hotline\Hotline.csproj" />
     <ProjectReference Include="..\Tr.Sdk\Tr.Sdk.csproj" />

+ 5 - 0
src/Hotline.Application/Mappers/MapperConfigs.cs

@@ -1,4 +1,5 @@
 using Hotline.CallCenter.BlackLists;
+using Hotline.CallCenter.Tels.CallTelDomain;
 using Hotline.Import;
 using Hotline.JudicialManagement;
 using Hotline.Orders;
@@ -44,6 +45,10 @@ namespace Hotline.Application.Mappers
                 .Map(m => m.TelPwd, x => x.Password)
                 .Map(m =>m.Queue, x => x.QueueId);
 
+            config.ForType<QueryTelResponse, TelOutDto>()
+                .Map(m => m.TelPwd, x => x.Password)
+                .Map(m => m.Queue, x => x.QueueId);
+
             config.ForType<ExcelContent, Order>()
                 .Map(d => d.FirstVisitResult, x => x.VisitResult)
                 .IgnoreNullValues(true);

+ 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);
+    }
 }

+ 196 - 79
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;
 using static NPOI.SS.Format.CellNumberFormatter;
 
@@ -81,8 +82,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;
@@ -103,14 +104,14 @@ 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;
     private readonly IOrderTerminateRepository _orderTerminateRepository;
     private readonly IRepository<OrderPublishHistory> _orderPublishHistoryRepository;
 
-    public OrderApplication(
+	public OrderApplication(
         IOrderDomainService orderDomainService,
         IOrderRepository orderRepository,
         IWorkflowDomainService workflowDomainService,
@@ -134,7 +135,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,
@@ -183,7 +184,8 @@ public class OrderApplication : IOrderApplication, IScopeDependency
         _orderObserveRepository = orderObserveRepository;
         _orderTerminateRepository = orderTerminateRepository;
         _orderPublishHistoryRepository = orderPublishHistoryRepository;
-    }
+        _sessionContext = sessionContext;
+	}
 
     /// <summary>
     /// 更新工单办理期满时间(延期调用,其他不调用)
@@ -436,7 +438,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 })
@@ -663,7 +665,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);
@@ -707,10 +709,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))
@@ -719,7 +721,7 @@ public class OrderApplication : IOrderApplication, IScopeDependency
         }
 
         ExpiredTimeWithConfig? expiredTimeConfig = null;
-        if (dto.NextHandlers.Any(d => d.Key == "001171" || d.Key == "001178"))
+        if (dto.Workflow.NextHandlers.Any(d => d.Key == "001171" || d.Key == "001178"))
         {
             var timeResult = await _expireTime.CalcEndTime(DateTime.Now, ETimeType.WorkDay, 45, 80, 50);
             expiredTimeConfig = new ExpiredTimeWithConfig
@@ -735,7 +737,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.Workflow.Opinion,
                 _sessionContextProvider.SessionContext.RequiredUserId, _sessionContextProvider.SessionContext.UserName,
                 canUpdateOrderSender);
             //TODO发送短信即将超期
@@ -744,14 +746,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.Workflow.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.Workflow.Opinion,
                 _sessionContextProvider.SessionContext.RequiredUserId, _sessionContextProvider.SessionContext.UserName,
                 canUpdateOrderSender);
             //TODO发送短信即将超期
@@ -760,7 +762,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.Workflow.FlowDirection is EFlowDirection.CenterToCenter)
         {
             if (_appOptions.Value.IsZiGong == false)
             {
@@ -775,35 +777,24 @@ public class OrderApplication : IOrderApplication, IScopeDependency
                     new PublishAutomaticDelayDto() { OrderId = order.Id });
             }
         }
-        else if (dto.FlowDirection is EFlowDirection.CenterToFile) //中心直接归档,中心意见为实际办理意见
+        else if (dto.Workflow.FlowDirection is EFlowDirection.CenterToFile) //中心直接归档,中心意见为实际办理意见
         {
-            order.CenterToOrgOpinion = dto.Opinion;
+            order.CenterToOrgOpinion = dto.Workflow.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.LeaderSMS.First(m => m.Id == dto.LeaderSMSKey);
+            var dic = _sysDicDataCacheManager.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;
     }
 
@@ -861,7 +852,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);
@@ -1053,6 +1044,7 @@ public class OrderApplication : IOrderApplication, IScopeDependency
             .WhereIF(dto.IsSgin.HasValue && dto.IsSgin == false, d => d.CurrentStepAcceptTime == null)
             .WhereIF(dto.FiledType is FiledType.CenterFiled, d => d.ProcessType == EProcessType.Zhiban)
             .WhereIF(dto.FiledType is FiledType.OrgFiled, d => d.ProcessType == EProcessType.Jiaoban)
+            .WhereIF(!string.IsNullOrEmpty(dto.OrderTagCode),d=>d.OrderTagCode == dto.OrderTagCode)
             .OrderByDescending(d => d.CreationTime);
     }
 
@@ -1347,9 +1339,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")),
@@ -1398,9 +1390,9 @@ public class OrderApplication : IOrderApplication, IScopeDependency
         else
         {
             data = await list.GroupBy(x => new
-                {
-                    x.VisitOrgCode
-                })
+            {
+                x.VisitOrgCode
+            })
                 .Select(x => new VisitAndOrgSatisfactionStatisticsDto()
                 {
                     OrgCode = x.VisitOrgCode,
@@ -1664,10 +1656,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());
             }
@@ -1679,10 +1671,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());
             }
@@ -1836,10 +1828,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,
@@ -1874,10 +1866,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,
@@ -2273,15 +2265,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("工单外部编号不能为空");
@@ -2492,20 +2552,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;
     }
 
@@ -2581,11 +2641,11 @@ public class OrderApplication : IOrderApplication, IScopeDependency
     /// </summary>
     /// <param name="dto"></param>
     /// <returns></returns>
-    public ISugarQueryable<OrderTerminate> OrderTerminateList(OrderTerminateListDto dto) 
+    public ISugarQueryable<OrderTerminate> OrderTerminateList(OrderTerminateListDto dto)
     {
-	    var handler = dto.AuditStatus is 2 or 0 || dto.QueryType is 0;
-		var isAdmin = _orderDomainService.IsCheckAdmin();
-		return _orderTerminateRepository.Queryable(hasHandled: handler, isAdmin: isAdmin)
+        var handler = dto.AuditStatus is 2 or 0 || dto.QueryType is 0;
+        var isAdmin = _orderDomainService.IsCheckAdmin();
+        return _orderTerminateRepository.Queryable(hasHandled: handler, isAdmin: isAdmin)
             .Includes(d => d.Order)
             .WhereIF(!string.IsNullOrEmpty(dto.No), d => d.Order.No!.Contains(dto.No!))
             .WhereIF(!string.IsNullOrEmpty(dto.Title), d => d.Order.Title!.Contains(dto.Title!))
@@ -2593,12 +2653,69 @@ public class OrderApplication : IOrderApplication, IScopeDependency
                 d => d.CreationTime >= dto.ApplyStartTime && d.CreationTime <= dto.ApplyEndTime)
             //.WhereIF(dto.AuditStatus is 1 , d=>d.Status == ETerminateStatus.Approval || d.Status == ETerminateStatus.SendBack )
             //.WhereIF(dto.AuditStatus is 2, d => d.Status == ETerminateStatus.End || dto.Status == ETerminateStatus.Refuse)
-			.WhereIF(dto.QueryType is 1, d => d.CreatorId == _sessionContextProvider.SessionContext.UserId)
+            .WhereIF(dto.QueryType is 1, d => d.CreatorId == _sessionContextProvider.SessionContext.UserId)
             .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
+    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 == _sessionContextProvider.SessionContext.RequiredUserId) || 
+                (step.FlowAssignType == EFlowAssignType.Org && !string.IsNullOrEmpty(step.HandlerOrgId) && 
+                step.HandlerOrgId == _sessionContextProvider.SessionContext.RequiredOrgId) || 
+                (step.FlowAssignType == EFlowAssignType.Role && !string.IsNullOrEmpty(step.RoleId) &&
+                _sessionContextProvider.SessionContext.Roles.Contains(step.RoleId)))).Any() || 
+                (string.IsNullOrEmpty(d.WorkflowId) && 
+                (string.IsNullOrEmpty(d.SignerId) || d.SignerId == _sessionContextProvider.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 == _sessionContextProvider.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>();
+    }
+    #endregion
 }

+ 48 - 41
src/Hotline.Application/Orders/OrderScreenHandler/OrderScreenEndWorkflowHandler.cs

@@ -61,53 +61,60 @@ public class OrderScreenEndWorkflowHandler : INotificationHandler<EndWorkflowNot
 				{
 					screen.Status = EScreenStatus.End;
 					screen.ReplyContent = workflow.ActualOpinion;
-					await _orderScreenRepository.UpdateAsync(screen, cancellationToken);
+					//await _orderScreenRepository.UpdateAsync(screen, cancellationToken);
 					var visitDetail =
 						await _orderVisitedDetailRepository.GetAsync(screen.VisitDetailId, cancellationToken);
 					if (visitDetail != null)
 					{
-						var screenSatisfy = new Kv() { Key = "-1", Value = "视为满意" };
-						visitDetail.OrgProcessingResults = screenSatisfy;
-						//visitDetail.OrgHandledAttitude = screenSatisfy;
-						await _orderVisitedDetailRepository.UpdateAsync(visitDetail, cancellationToken);
-						// 修改主表当前评价结果
-						await _orderVisitRepository.Updateable().SetColumns(v => new OrderVisit() { NowEvaluate = screenSatisfy }).Where(v => v.Id == visitDetail.VisitId).ExecuteCommandAsync(cancellationToken);
-						//获取回访信息
-						var visit = await _orderVisitRepository.Queryable().Includes(x => x.Order)
-							.Includes(x => x.OrderVisitDetails)
-							.Where(x => x.Id == screen.VisitId).FirstAsync(cancellationToken);
-						if (visit != null)
+						if (screen.ScreenType == EOrderScreenType.Seat)
 						{
-							//获取回访明细
-							var visitDe = visit.OrderVisitDetails.First(x => x.Id == screen.VisitDetailId);
-							//推省上
-							await _capPublisher.PublishAsync(Hotline.Share.Mq.EventNames.HotlineOrderScreenApplyed,
-								  new PublishVisitDto()
-								  {
-									  Order = _mapper.Map<OrderDto>(visit.Order),
-									  No = visit.No,
-									  VisitType = visit.VisitType,
-									  VisitName = visit.CreatorName,
-									  VisitTime = visit.VisitTime,
-									  VisitRemark = string.IsNullOrEmpty(visitDe.VisitContent) ? screenSatisfy.Value : visitDe.VisitContent,
-									  AreaCode = visit.Order.AreaCode!,
-									  SubjectResultSatifyCode = visitDe.OrgProcessingResults?.Key,
-									  FirstSatisfactionCode = visit.Order.FirstVisitResultCode!,
-									  ClientGuid = ""
-								  });
-
-							//推门户
-							await _capPublisher.PublishAsync(Hotline.Share.Mq.EventNames.HotlineOrderVisitedWeb, new PublishVisitAllDto()
+							visitDetail.SeatEvaluate = ESeatEvaluate.DefaultSatisfied;
+							await _orderVisitedDetailRepository.UpdateAsync(visitDetail, cancellationToken);
+						}
+						else {
+							var screenSatisfy = new Kv() { Key = "-1", Value = "视为满意" };
+							visitDetail.OrgProcessingResults = screenSatisfy;
+							//visitDetail.OrgHandledAttitude = screenSatisfy;
+							await _orderVisitedDetailRepository.UpdateAsync(visitDetail, cancellationToken);
+							// 修改主表当前评价结果
+							await _orderVisitRepository.Updateable().SetColumns(v => new OrderVisit() { NowEvaluate = screenSatisfy }).Where(v => v.Id == visitDetail.VisitId).ExecuteCommandAsync(cancellationToken);
+							//获取回访信息
+							var visit = await _orderVisitRepository.Queryable().Includes(x => x.Order)
+								.Includes(x => x.OrderVisitDetails)
+								.Where(x => x.Id == screen.VisitId).FirstAsync(cancellationToken);
+							if (visit != null)
 							{
-								Id = visit.Id,
-								Order = _mapper.Map<OrderDto>(visit.Order),
-								OrderVisitDetails = _mapper.Map<List<VisitDetailDto>>(visit.OrderVisitDetails),
-								VisitName = visit.CreatorName,
-								VisitTime = visit.VisitTime,
-								VisitType = visit.VisitType,
-								VisitState = visit.VisitState,
-								PublishTime = visit.PublishTime,
-							}, cancellationToken: cancellationToken);
+								//获取回访明细
+								var visitDe = visit.OrderVisitDetails.First(x => x.Id == screen.VisitDetailId);
+								//推省上
+								await _capPublisher.PublishAsync(Hotline.Share.Mq.EventNames.HotlineOrderScreenApplyed,
+									new PublishVisitDto()
+									{
+										Order = _mapper.Map<OrderDto>(visit.Order),
+										No = visit.No,
+										VisitType = visit.VisitType,
+										VisitName = visit.CreatorName,
+										VisitTime = visit.VisitTime,
+										VisitRemark = string.IsNullOrEmpty(visitDe.VisitContent) ? screenSatisfy.Value : visitDe.VisitContent,
+										AreaCode = visit.Order.AreaCode!,
+										SubjectResultSatifyCode = visitDe.OrgProcessingResults?.Key,
+										FirstSatisfactionCode = visit.Order.FirstVisitResultCode!,
+										ClientGuid = ""
+									});
+
+								//推门户
+								await _capPublisher.PublishAsync(Hotline.Share.Mq.EventNames.HotlineOrderVisitedWeb, new PublishVisitAllDto()
+								{
+									Id = visit.Id,
+									Order = _mapper.Map<OrderDto>(visit.Order),
+									OrderVisitDetails = _mapper.Map<List<VisitDetailDto>>(visit.OrderVisitDetails),
+									VisitName = visit.CreatorName,
+									VisitTime = visit.VisitTime,
+									VisitType = visit.VisitType,
+									VisitState = visit.VisitState,
+									PublishTime = visit.PublishTime,
+								}, cancellationToken: cancellationToken);
+							}
 						}
 					}
 				}

+ 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>

+ 0 - 1
src/Hotline.Application/Subscribers/DatasharingSubscriber.cs

@@ -664,7 +664,6 @@ namespace Hotline.Application.Subscribers
                     orderVisit.VisitTime = dto.VisitTime;
                     orderVisit.VisitType = dto.VisitType;
                     orderVisit.IsCanAiVisit = false;
-                    orderVisit.IsCanHandle = false;
                     var VisitSatisfaction = _sysDicDataCacheManager.GetSysDicDataCache(SysDicTypeConsts.VisitSatisfaction)
                         .Where(x => x.DicDataValue == dto.OrgProcessingResults).Select(m => new Kv { Key = m.DicDataValue, Value = m.DicDataName })
                         .FirstOrDefault();

+ 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;

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

@@ -80,9 +80,9 @@ public class BasicWorkflowDto : EndWorkflowDto
     public string? Remark { get; set; }
 
     /// <summary>
-    /// 是否市州转办
+    /// 话务提醒是否转办
     /// </summary>
-    public bool? Transpond { get; set; }
+    public bool? IsForwarded { get; set; }
 }
 
 public class External

+ 5 - 0
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

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

@@ -21,96 +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 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 Workflow { 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,
 }

+ 42 - 0
src/Hotline.Share/Dtos/Order/Handle/GetCrossLevelStepsDto.cs

@@ -0,0 +1,42 @@
+using Hotline.Share.Enums.FlowEngine;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Hotline.Share.Dtos.Order.Handle
+{
+    public class GetCrossLevelStepsDto
+    {
+        /// <summary>
+        /// 已开启流程的必须传流程id
+        /// </summary>
+        public string? WorkflowId { get; set; }
+
+        /// <summary>
+        /// 流程模板节点编号
+        /// </summary>
+        public string StepCode { get; set; }
+
+        /// <summary>
+        /// 所选部门id
+        /// </summary>
+        public List<string> OrgIds { get; set; }
+
+        /// <summary>
+        /// 模板配置节点类型
+        /// </summary>
+        public EStepType StepType { get; set; }
+
+        /// <summary>
+        /// 模板配置节点业务类型
+        /// </summary>
+        public EBusinessType BusinessType { get; set; }
+
+        /// <summary>
+        /// 办理对象类型
+        /// </summary>
+        public EHandlerType HandlerType { get; set; }
+    }
+}

+ 266 - 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,242 @@ namespace Hotline.Share.Dtos.Order
 	    public string OrderId { get; set; }
     }
 
+
+    public class OrderListOutDto
+    {
+        /// <summary>
+        /// Id
+        /// </summary>
+        public string Id { get; set; }
+
+        /// <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; }
+
+        /// <summary>
+        /// 是否可编辑
+        /// </summary>
+        public bool CanEdit => !string.IsNullOrEmpty(SignerId) &&
+                               (Status is EOrderStatus.WaitForAccept or EOrderStatus.BackToUnAccept or EOrderStatus.SpecialToUnAccept or EOrderStatus.HandOverToUnAccept);
+    }
 }

+ 29 - 0
src/Hotline.Share/Dtos/Order/OrderScreenDto.cs

@@ -2,6 +2,7 @@
 using Hotline.Share.Enums.Order;
 using System;
 using System.Collections.Generic;
+using System.ComponentModel;
 using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
@@ -95,10 +96,23 @@ namespace Hotline.Share.Dtos.Order
 		/// 甄别重提申请次数
 		/// </summary>
 		public int SendBackApplyNum { get; set; }
+
+		/// <summary>
+		/// 甄别类型
+		/// </summary>
+		public EOrderScreenType? ScreenType { get; set; }
 	}
 
 	public class OrderScreenListDto
 	{
+
+		/// <summary>
+		/// 甄别类型
+		/// </summary>
+		public EOrderScreenType? ScreenType { get; set; }
+
+		public string ScreenTypeText => ScreenType.HasValue ? ScreenType.GetDescription() : string.Empty;
+
 		/// <summary>
 		/// 是否可办理
 		/// </summary>
@@ -343,4 +357,19 @@ namespace Hotline.Share.Dtos.Order
 		/// </summary>
 		public DateTime ScreenByEndTime { get; set; }
 	}
+
+	public enum EOrderScreenType 
+	{
+		/// <summary>
+		/// 部门办件
+		/// </summary>
+		[Description("部门办件")]
+		Org = 0,
+
+		/// <summary>
+		/// 中心话务
+		/// </summary>
+		[Description("中心话务")]
+		Seat =1,
+	}
 }

+ 142 - 23
src/Hotline.Share/Dtos/Order/OrderStartFlowDto.cs

@@ -1,40 +1,165 @@
-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 EOrderAssignMode OrderAssignMode { get; set; }
+
+        /// <summary>
+        /// 跨级派单选择的节点
+        /// </summary>
+        public List<CrossStep> CrossSteps { 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 bool IsForwarded { get; set; }
+        public string? TranspondCityValue { get; set; }
+
+        #endregion
     }
 
-    public class ScreenStartFlowDto : StartWorkflowDto<OrderScreenDto>
+    public class CrossStep : BasicWorkflowDto
     {
-
+        /// <summary>
+        /// 办理顺序
+        /// </summary>
+        public int Sort { get; set; }
     }
 
-    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 +172,4 @@ namespace Hotline.Share.Dtos.Order
 
         public NextWorkflowDto NextWorkflow { get; set; }
     }
-
-    public class DelayStartFlowDto : StartWorkflowDto<ApplyDelayDto>
-    {
-
-    }
-
-}
+}

+ 11 - 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>
         /// 归档方式
@@ -90,6 +90,16 @@ namespace Hotline.Share.Dtos.Order
         /// 部门办件态度
         /// </summary>
         public List<string> OrgHandledAttitude { get; set; } = new();
+
+        /// <summary>
+        /// 来源渠道
+        /// </summary>
+        public string Channel { get; set; }
+
+        /// <summary>
+        /// 是否超期
+        /// </summary>
+        public bool? IsOverTime { get; set; }
     }
 
     public record QueryOrderPublishStatisticsAllDto : PagedRequest

+ 47 - 26
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>
@@ -46,23 +52,39 @@ namespace Hotline.Share.Dtos.Order
         /// 工单状态
         /// </summary>
         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>
+		/// 1 中心办理  2 转单件 3 二次派发件
+		/// </summary>
+		public int? QueryType { 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 +95,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 +130,14 @@ 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; }
-
-	}
+    }
 }

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

@@ -112,4 +112,9 @@ public record QueryOrderPublishDto : PagedKeywordRequest
 	/// </summary>
 	public string? Channel { get; set; }
 
+	/// <summary>
+	/// 是否超期
+	/// </summary>
+	public bool? IsOverTime { get; set; }
+
 }

+ 10 - 2
src/Hotline.Share/Dtos/Order/QueryOrderDto.cs

@@ -189,7 +189,12 @@ namespace Hotline.Share.Dtos.Order
         /// 受理情况 true :已签收  false:未签收  空:全部
         /// </summary>
         public bool? IsSgin { get; set; }
-    }
+
+        /// <summary>
+        /// 工单标签Code
+        /// </summary>
+        public string? OrderTagCode { get; set; }
+	}
 
 
 	public enum FiledType
@@ -745,7 +750,10 @@ namespace Hotline.Share.Dtos.Order
         /// </summary>
         public string? OrgNoSatisfiedReason { get; set; }
 
-       
+		/// <summary>
+		/// 甄别类型
+		/// </summary>
+		public EOrderScreenType? ScreenType { get; set; }
 	}
 
     public record UrgeListDto : PagedKeywordRequest

+ 9 - 0
src/Hotline.Share/Dtos/Order/TargetStepInfo.cs

@@ -0,0 +1,9 @@
+using Hotline.Share.Enums.FlowEngine;
+
+namespace Hotline.Share.Dtos.Order;
+
+public class TargetStepInfo
+{
+    public EBusinessType CurrentBusinessType { get; set; }
+    public EBusinessType TargetBusinessType { get; set; }
+}

+ 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>
         /// 查询批次

+ 5 - 0
src/Hotline.Share/Requests/PagedKeywordRequest.cs

@@ -219,6 +219,11 @@ public record PublishedPagedRequest : PagedKeywordRequest
 	/// 发布人名称
 	/// </summary>
 	public string? PublishName { get; set; }
+
+    /// <summary>
+    /// 是否超期
+    /// </summary>
+    public bool? IsOverTime { get; set; }
 }
 
 public record HotspotSubtotalReportPagedRequest : ReportPagedRequest

+ 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

@@ -30,6 +30,20 @@ public static class StringExtensions
         return (TEnum)Enum.Parse(typeof(TEnum), value);
     }
 
+    /// <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);
+    }
+
     public static IList<string> SplitKeywords(this string value)
     {
         var regex = new Regex(@"[ ,,]+");

+ 12 - 0
src/Hotline.XingTang/CallTelClient.cs

@@ -0,0 +1,12 @@
+using Hotline.CallCenter.Tels;
+using Hotline.CallCenter.Tels.CallTelDomain;
+
+namespace Hotline.XingTang;
+
+internal class CallTelClient : ICallTelClient
+{
+    public async Task<List<QueryTelResponse>> QueryTelsAsync(QueryTelRequest request, CancellationToken cancellationToken)
+    {
+        return new List<QueryTelResponse>();
+    }
+}

+ 17 - 0
src/Hotline.XingTang/ServiceCollectionExtensions.cs

@@ -0,0 +1,17 @@
+using Hotline.CallCenter.Tels;
+using Microsoft.Extensions.DependencyInjection;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Hotline.XingTang;
+public static class ServiceCollectionExtensions
+{
+    public static IServiceCollection AddXingTangSDK(this IServiceCollection services)
+    {
+        services.AddScoped<ICallTelClient, CallTelClient>();
+        return services;
+    }
+}

+ 48 - 0
src/Hotline/Authentications/FakeSessionContext.cs

@@ -0,0 +1,48 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using XF.Domain.Authentications;
+
+namespace Hotline.Authentications
+{
+    public class FakeSessionContext : ISessionContext
+    {
+        /// <summary>
+        /// Id of current tenant or null for host
+        /// </summary>
+        public string? UserId { get; init; }
+
+        /// <summary>
+        /// Id of current user or throw Exception for guest
+        /// </summary>
+        /// <exception cref="AuthenticationException"></exception>
+        public string RequiredUserId => UserId ?? throw new ArgumentNullException();
+        public string? UserName { get; init; }
+        public string? Phone { get; init; }
+
+        /// <summary>
+        /// Roles
+        /// </summary>
+        public string[] Roles { get; init; }
+        public string? OrgId { get; init; }
+        public string RequiredOrgId => OrgId ?? throw new ArgumentNullException();
+        public string? OrgName { get; init; }
+        public int OrgLevel { get; init; }
+        public string? OrgAreaCode { get; init; }
+        public bool OrgIsCenter { get; init; }
+
+        /// <summary>
+        /// 部门行政区划名称
+        /// </summary>
+        public string? OrgAreaName { get; init; }
+        public string? AreaId { get; init; }
+        public string? ClientId { get; init; }
+
+        /// <summary>
+        /// 工号
+        /// </summary>
+        public string? StaffNo { get; init; }
+    }
+}

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

@@ -19,5 +19,6 @@ namespace Hotline.Caching.Interfaces
         /// 根据禅道 自贡需求 Id_361, 第一条, 3小条需求;
         /// </summary>
         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]);
     }
 }

+ 17 - 0
src/Hotline/CallCenter/Tels/CallTelDomain/QueryTelRequest.cs

@@ -0,0 +1,17 @@
+using RestSharp;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Hotline.CallCenter.Tels.CallTelDomain;
+public class QueryTelRequest
+{
+    /// <summary>
+    /// 分机号
+    /// </summary>
+    [RequestProperty(Name = "extn")]
+    public string? TelNo { get; set; }
+
+}

+ 23 - 0
src/Hotline/CallCenter/Tels/CallTelDomain/QueryTelResponse.cs

@@ -0,0 +1,23 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Text.Json.Serialization;
+using System.Threading.Tasks;
+
+namespace Hotline.CallCenter.Tels.CallTelDomain;
+public class QueryTelResponse
+{
+    [JsonPropertyName("uuid")]
+    public string Id { get; set; }
+    public string Name { get; set; }
+    [JsonPropertyName("nbr")]
+    public string TelNo { get; set; }
+    public string Description { get; set; }
+
+    public string Password { get; set; }
+
+    [JsonPropertyName("queue_id")]
+    public string QueueId { get; set; }
+
+}

+ 12 - 0
src/Hotline/CallCenter/Tels/ICallTelClient.cs

@@ -0,0 +1,12 @@
+using Hotline.CallCenter.Tels.CallTelDomain;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Hotline.CallCenter.Tels;
+public interface ICallTelClient
+{
+    Task<List<QueryTelResponse>> QueryTelsAsync(QueryTelRequest request, CancellationToken cancellationToken);
+}

+ 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(ISessionContext current, 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>

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

@@ -517,7 +517,7 @@ public partial class Workflow
         string? handleOrgAreaName,
         int handlerOrgLevel)
     {
-        ActualHandleStepAcceptTime = step.AcceptTime;
+        //ActualHandleStepAcceptTime = step.AcceptTime;
         ActualHandleTime = step.HandleTime;
         ActualHandleOrgAreaCode = handleOrgAreaCode;
         ActualHandleOrgAreaName = handleOrgAreaName;
@@ -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;

+ 5 - 5
src/Hotline/FlowEngine/Workflows/WorkflowCountersign.cs

@@ -30,26 +30,26 @@ namespace Hotline.FlowEngine.Workflows
         /// <summary>
         /// 发起人
         /// </summary>
-        public string StarterId { get; set; }
+        public string? StarterId { get; set; }
 
-        public string StarterName { get; set; }
+        public string? StarterName { get; set; }
 
         /// <summary>
         /// 发起人部门code
         /// </summary>
-        public string StarterOrgId { get; set; }
+        public string? StarterOrgId { get; set; }
 
         public string? StarterOrgName { get; set; }
 
         /// <summary>
         /// 发起人部门行政区划代码
         /// </summary>
-        public string StarterOrgAreaCode { get; set; }
+        public string? StarterOrgAreaCode { get; set; }
 
         /// <summary>
         /// 发起人部门行政区划名称
         /// </summary>
-        public string StarterOrgAreaName { get; set; }
+        public string? StarterOrgAreaName { get; set; }
 
         #endregion
 

+ 551 - 72
src/Hotline/FlowEngine/Workflows/WorkflowDomainService.cs

@@ -29,12 +29,14 @@ namespace Hotline.FlowEngine.Workflows
         private readonly IRepository<WorkflowTrace> _workflowTraceRepository;
         private readonly IRepository<WorkflowSupplement> _workflowSupplementRepository;
         private readonly IRepository<WorkflowCountersign> _workflowCountersignRepository;
+        private readonly ISessionContext _sessionContext;
         private readonly IMapper _mapper;
         private readonly Publisher _publisher;
         private readonly ILogger<WorkflowDomainService> _logger;
         private readonly IFileRepository _fileRepository;
         private readonly IRepository<User> _userRepository;
         private readonly ISystemSettingCacheManager _systemSettingCacheManager;
+        private readonly IWfModuleCacheManager _wfModuleCacheManager;
         private readonly ISessionContextProvider _sessionContextProvider;
 
         public WorkflowDomainService(
@@ -44,6 +46,7 @@ namespace Hotline.FlowEngine.Workflows
             IRepository<WorkflowSupplement> workflowSupplementRepository,
             IRepository<WorkflowCountersign> workflowCountersignRepository,
             ISystemSettingCacheManager systemSettingCacheManager,
+            IWfModuleCacheManager wfModuleCacheManager,
             ISessionContextProvider sessionContextProvider,
             IMapper mapper,
             Publisher publisher,
@@ -55,11 +58,13 @@ namespace Hotline.FlowEngine.Workflows
             _workflowTraceRepository = workflowTraceRepository;
             _workflowSupplementRepository = workflowSupplementRepository;
             _workflowCountersignRepository = workflowCountersignRepository;
+            _sessionContext = sessionContextProvider.SessionContext;
             _mapper = mapper;
             _publisher = publisher;
             _logger = logger;
             _fileRepository = fileRepository;
             _systemSettingCacheManager = systemSettingCacheManager;
+            _wfModuleCacheManager = wfModuleCacheManager;
             _sessionContextProvider = sessionContextProvider;
         }
 
@@ -81,8 +86,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,
             };
 
@@ -116,33 +121,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);
 
@@ -153,24 +138,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(ISessionContext current, 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, current.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,
+                    //    current.UserId, current.UserName,
+                    //    current.OrgId, current.OrgName,
+                    //    current.OrgAreaCode, current.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 == current.UserId);
+                //if (exists)
+                //    throw new UserFriendlyException("该用户在当前流程存在未结束会签");
+                //await StartCountersignAsync(current, 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(current.UserId, current.OrgId);
+                        //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, current.OrgAreaCode, current.OrgAreaName, current.OrgLevel);
+            }
+
+            if (workflow.CurrentStepId == currentStep.Id)
+            {
+                workflow.UpdateCurrentStepWhenHandle(currentStep, current.OrgAreaCode, current.OrgAreaName, current.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(current.UserId, current.OrgId);
+
             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,
+                    current.OrgId, expiredTime.HasValue), PublishStrategy.ParallelWhenAll,
+                cancellationToken);
+
+            return nextSteps;
         }
 
         public async Task<Workflow> GetWorkflowAsync(string workflowId,
@@ -253,7 +581,7 @@ namespace Hotline.FlowEngine.Workflows
 
             var unhandlePreviousTrace = workflow.Traces.FirstOrDefault(d =>
                     d.Status is not EWorkflowStepStatus.Handled
-                //&& d.TraceType is EWorkflowTraceType.Previous
+            //&& d.TraceType is EWorkflowTraceType.Previous
             );
             //var previousOpinion = unhandlePreviousTrace?.Opinion ?? null;
 
@@ -373,8 +701,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)
@@ -486,7 +813,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);
 
             ////赋值当前节点的下级办理节点
@@ -1553,7 +1880,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)
@@ -1677,7 +2004,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)
         {
@@ -1694,13 +2021,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
@@ -1719,7 +2046,7 @@ namespace Hotline.FlowEngine.Workflows
                         {
                             //依据会签策略创建会签下一级节点
                             nextSteps = await CreateCountersignStepsAsync(workflow, nextStepDefine, currentStep, dto,
-                                flowAssignInfo.FlowAssignType, expiredTime, isStartCountersign, cancellationToken);
+                                flowAssignType, expiredTime, cancellationToken);
                         }
                     }
                 }
@@ -1735,7 +2062,7 @@ namespace Hotline.FlowEngine.Workflows
                     {
                         //依据会签策略创建会签下一级节点
                         nextSteps = await CreateCountersignStepsAsync(workflow, nextStepDefine, currentStep, dto,
-                            flowAssignInfo.FlowAssignType, expiredTime, isStartCountersign, cancellationToken);
+                            flowAssignType, expiredTime, cancellationToken);
                     }
                 }
             }
@@ -1743,18 +2070,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);
             }
 
@@ -1779,7 +2106,7 @@ namespace Hotline.FlowEngine.Workflows
             StepDefine nextStepDefine,
             WorkflowStep prevStep,
             BasicWorkflowDto dto,
-            FlowAssignInfo flowAssignInfo,
+            EFlowAssignType flowAssignType,
             DateTime? expiredTime,
             CancellationToken cancellationToken)
         {
@@ -1800,7 +2127,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);
         }
@@ -1812,28 +2139,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
@@ -2085,9 +2398,9 @@ namespace Hotline.FlowEngine.Workflows
                 StartStepCode = startStep.Code,
                 StartStepBusiType = startStep.BusinessType,
 
-                StarterId = current.RequiredUserId,
+                StarterId = current.UserId,
                 StarterName = current.UserName ?? string.Empty,
-                StarterOrgId = current.RequiredOrgId,
+                StarterOrgId = current.OrgId,
                 StarterOrgName = current.OrgName,
                 StarterOrgAreaCode = current.OrgAreaCode ?? string.Empty,
                 StarterOrgAreaName = current.OrgAreaName ?? string.Empty,
@@ -2451,7 +2764,7 @@ namespace Hotline.FlowEngine.Workflows
             StepDefine stepDefine,
             WorkflowStep prevStep,
             BasicWorkflowDto dto,
-            FlowAssignInfo flowAssignInfo,
+            EFlowAssignType flowAssignType,
             EWorkflowTraceType traceType,
             DateTime? expiredTime,
             CancellationToken cancellationToken)
@@ -2472,8 +2785,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);
         }
@@ -2512,9 +2825,6 @@ namespace Hotline.FlowEngine.Workflows
 
             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)
@@ -2885,6 +3195,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
     }
 }

+ 7 - 1
src/Hotline/Orders/Order.cs

@@ -1033,7 +1033,13 @@ namespace Hotline.Orders
 		/// </summary>
 		[SugarColumn(ColumnDescription = "退回截至时间")]
 		public DateTime? SendBackAuditEndTime { get; set; }
-    }
+
+		/// <summary>
+		/// 话务提醒是否转办
+		/// </summary>
+		[SugarColumn(ColumnDescription = "话务提醒是否转办")]
+		public bool? IsForwarded { get; set; }
+	}
 
     public partial class Order
     {

+ 7 - 0
src/Hotline/Orders/OrderScreen.cs

@@ -4,6 +4,7 @@ using Hotline.Share.Enums.Order;
 using SqlSugar;
 using System.ComponentModel;
 using Hotline.Share.Dtos.File;
+using Hotline.Share.Dtos.Order;
 using XF.Domain.Repository;
 
 namespace Hotline.Orders
@@ -169,5 +170,11 @@ namespace Hotline.Orders
         /// </summary>
         [SugarColumn(ColumnDescription = "甄别审批时间")]
         public DateTime? NewestAuditTime { get; set; }
+
+        /// <summary>
+        /// 甄别类型
+        /// </summary>
+        [SugarColumn(ColumnDescription = "甄别类型",DefaultValue ="0")]
+		public EOrderScreenType?  ScreenType { get; set; }
 	}
 }

+ 5 - 0
src/Hotline/Permissions/EPermission.cs

@@ -2585,6 +2585,11 @@ namespace Hotline.Permissions
         /// </summary>
         [Display(GroupName ="中心办件统计",Name ="扭转信件统计",Description ="扭转信件统计")]
         VisitJudeStatistics = 110516,
+        /// <summary>
+        /// 数据汇聚情况
+        /// </summary>
+        [Display(GroupName ="中心办件统计",Name ="数据汇聚情况",Description ="数据汇聚情况")]
+        DataConvergeSituation = 110517,
         #endregion
 
         #region 部门办件统计(11,06,00)

+ 3 - 1
src/Hotline/Settings/SystemOrganize.cs

@@ -101,10 +101,12 @@ public static class OrgExtensions
 {
     public static int CalcOrgLevel(this string orgCode)
     {
+        if(string.IsNullOrEmpty(orgCode))
+            throw new UserFriendlyException("非法部门Id");
         //中心算做一级部门
         if (orgCode == OrgSeedData.CenterId) return 1;
         if (orgCode.Length % 3 != 0)
-            throw new UserFriendlyException("非法部门Code");
+            throw new UserFriendlyException("非法部门Id");
         return (orgCode.Length / 3) - 1;
     }
 

+ 25 - 0
src/Tr.Sdk/CallTelClient.cs

@@ -0,0 +1,25 @@
+using Hotline.CallCenter.Tels;
+using Hotline.CallCenter.Tels.CallTelDomain;
+using Mapster;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tr.Sdk;
+public class CallTelClient : ICallTelClient
+{
+    private readonly ITrClient _trClient;
+
+    public CallTelClient(ITrClient trClient)
+    {
+        _trClient = trClient;
+    }
+
+    public async Task<List<QueryTelResponse>> QueryTelsAsync(QueryTelRequest request, CancellationToken cancellationToken)
+    {
+        var result = await _trClient.QueryTelsAsync(request.Adapt<Tels.QueryTelRequest>(), cancellationToken);
+        return result.Adapt<List<QueryTelResponse>>();
+    }
+}

+ 5 - 1
src/Tr.Sdk/Tr.Sdk.csproj

@@ -8,8 +8,12 @@
 
   <ItemGroup>
     <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.1" />
-    <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.0" />
+    <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.2" />
     <PackageReference Include="RestSharp" Version="110.2.0" />
   </ItemGroup>
 
+  <ItemGroup>
+    <ProjectReference Include="..\Hotline\Hotline.csproj" />
+  </ItemGroup>
+
 </Project>

+ 3 - 1
src/Tr.Sdk/TrSdkStartupExtensions.cs

@@ -1,4 +1,5 @@
-using Microsoft.Extensions.DependencyInjection;
+using Hotline.CallCenter.Tels;
+using Microsoft.Extensions.DependencyInjection;
 using System;
 using System.Collections.Generic;
 using System.Linq;
@@ -12,6 +13,7 @@ namespace Tr.Sdk
         public static IServiceCollection AddTrSdk(this IServiceCollection services, string baseUrl, string apiKey, string apiSecret)
         {
             services.AddSingleton<ITrClient, TrClient>(_ => new TrClient(baseUrl, apiKey, apiSecret));
+            services.AddScoped<ICallTelClient, CallTelClient>();
             return services;
         }
     }

+ 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();