소스 검색

Merge branch 'dev' of http://git.12345lm.cn/Fengwo/hotline into dev

Dun.Jason 10 달 전
부모
커밋
8a76a45fe8
27개의 변경된 파일360개의 추가작업 그리고 181개의 파일을 삭제
  1. 1 2
      src/Hotline.Ai.Jths/AiQualityService.cs
  2. 1 0
      src/Hotline.Api/Controllers/IdentityController.cs
  3. 6 3
      src/Hotline.Api/Controllers/KnowledgeController.cs
  4. 161 71
      src/Hotline.Api/Controllers/OrderController.cs
  5. 1 0
      src/Hotline.Api/Controllers/WebPortalController.cs
  6. 18 0
      src/Hotline.Api/Controllers/WorkflowController.cs
  7. 12 4
      src/Hotline.Api/StartupHelper.cs
  8. 1 1
      src/Hotline.Api/config/appsettings.Development.json
  9. 1 0
      src/Hotline.Application/Bigscreen/DataScreenRefreshService.cs
  10. 1 0
      src/Hotline.Application/Bigscreen/SeatStateDataRefreshService.cs
  11. 1 0
      src/Hotline.Application/CallCenter/Calls/TrApplication.cs
  12. 1 0
      src/Hotline.Application/Handlers/FlowEngine/WorkflowRecallHandler.cs
  13. 27 11
      src/Hotline.Application/Identity/IdentityAppService.cs
  14. 1 0
      src/Hotline.Application/Orders/OrderApplication.cs
  15. 1 0
      src/Hotline.Application/Quality/QualityApplication.cs
  16. 2 2
      src/Hotline.Repository.SqlSugar/Extensions/SqlSugarStartupExtensions.cs
  17. 9 0
      src/Hotline.Share/Dtos/FlowEngine/Workflow/ChangeHandlerDto.cs
  18. 15 0
      src/Hotline.Share/Dtos/Order/Migration/GetOrderMigrationDto.cs
  19. 12 0
      src/Hotline/FlowEngine/Workflows/IWorkflowDomainService.cs
  20. 1 1
      src/Hotline/FlowEngine/Workflows/Workflow.cs
  21. 32 0
      src/Hotline/FlowEngine/Workflows/WorkflowDomainService.cs
  22. 1 1
      src/Hotline/KnowledgeBase/KnowledgeDomainService.cs
  23. 3 2
      src/Hotline/Orders/IOrderDomainService.cs
  24. 38 76
      src/Hotline/Orders/OrderDomainService.cs
  25. 1 0
      src/Hotline/Push/MessageCodeDomainService.cs
  26. 1 0
      src/Hotline/Quality/QualityDomainService.cs
  27. 11 7
      src/Hotline/Settings/SettingConstants.cs

+ 1 - 2
src/Hotline.Ai.Jths/AiQualityService.cs

@@ -27,12 +27,11 @@ namespace Hotline.Ai.Jths
             var fileName = call.RecordingFileName.Split(".");
             var length = fileName.Count() - 1;
             var recordForm = fileName.Any() && fileName.Length > 1 ? fileName[length] : string.Empty;
-            var viteRecordPrefixUrl = new Uri(viteRecordPrefix);
 			List<AiQualityDto> datalist = new List<AiQualityDto>();
             AiQualityDto aiQuality = new AiQualityDto
             {
                 RecordID = model.Id,
-                RecordPath = viteRecordPrefixUrl + call.RecordingAbsolutePath,
+                RecordPath = viteRecordPrefix + call.RecordingAbsolutePath,
                 AgentID = "1001",
                 CallNumber = call.CPN,
                 CallTime = call.CreatedTime.ToString("yyyy-MM-dd HH:mm:ss"),

+ 1 - 0
src/Hotline.Api/Controllers/IdentityController.cs

@@ -7,6 +7,7 @@ using System.Security.Cryptography;
 using System.Text;
 using Hotline.Api.Filter;
 using Hotline.Application.Systems;
+using Hotline.Settings;
 using XC.RSAUtil;
 using XF.Domain.Constants;
 using XF.Domain.Exceptions;

+ 6 - 3
src/Hotline.Api/Controllers/KnowledgeController.cs

@@ -378,7 +378,8 @@ namespace Hotline.Api.Controllers
             var knowledge = await _knowledgeDomainService.KnowledgeInfo(Id, HttpContext.RequestAborted);
             if (knowledge is null)
                 throw UserFriendlyException.SameMessage("知识查询失败!");
-            if (knowledge.Workflow != null) knowledge.IsCanHandle = knowledge.CanHandle(_sessionContext.RequiredUserId, _sessionContext.RequiredOrgId);
+            if (knowledge.Workflow != null) 
+                knowledge.IsCanHandle = knowledge.Workflow.IsCanHandle(_sessionContext.RequiredUserId, _sessionContext.RequiredOrgId, _sessionContext.Roles);
             //转化
             var knowledgeShowInfoDto = _mapper.Map<KnowledgeInfoDto>(knowledge);
 
@@ -693,7 +694,7 @@ namespace Hotline.Api.Controllers
                 .Includes(it => it.Knowledge)
                 .Includes(it => it.User)
                 .Includes(it => it.SystemOrganize)
-                .Includes(it => it.Workflow)
+                .Includes(it => it.Workflow, d=>d.Steps)
                 .Where(it => it.WorkflowId != null)
                 .WhereIF(pagedDto.EKnowledgeApplyType.HasValue, d => d.WorkflowModuleStatus == pagedDto.EKnowledgeApplyType)
                 .WhereIF(pagedDto.EKnowledgeWorkFlowStatus.HasValue, d => d.WorkFlowApplyStatus == pagedDto.EKnowledgeWorkFlowStatus)
@@ -707,7 +708,9 @@ namespace Hotline.Api.Controllers
             foreach (var item in items)
             {
                 if (item.Workflow != null)
-                    item.CanHandle = item.CanHandle(_sessionContext.RequiredUserId, _sessionContext.RequiredOrgId);
+                    //item.CanHandle = item.CanHandle(_sessionContext.RequiredUserId, _sessionContext.RequiredOrgId);
+                    item.CanHandle = item.Workflow.IsCanHandle(
+                        _sessionContext.RequiredUserId, _sessionContext.RequiredOrgId, _sessionContext.Roles);
 
             }
             //处理是否可以办理

+ 161 - 71
src/Hotline.Api/Controllers/OrderController.cs

@@ -29,6 +29,7 @@ using Hotline.Share.Dtos.Enterprise;
 using Hotline.Share.Dtos.FlowEngine;
 using Hotline.Share.Dtos.FlowEngine.Workflow;
 using Hotline.Share.Dtos.Order;
+using Hotline.Share.Dtos.Order.Migration;
 using Hotline.Share.Dtos.Settings;
 using Hotline.Share.Enums.CallCenter;
 using Hotline.Share.Enums.FlowEngine;
@@ -46,6 +47,7 @@ using Microsoft.AspNetCore.Authorization;
 using Microsoft.AspNetCore.Mvc;
 using Microsoft.OpenApi.Writers;
 using MiniExcelLibs;
+using MongoDB.Driver;
 using NPOI.SS.Formula.Functions;
 using NPOI.SS.Util;
 using Org.BouncyCastle.Utilities;
@@ -594,6 +596,46 @@ public class OrderController : BaseController
             .OrderByDescending(x => x.CreationTime).ToListAsync(HttpContext.RequestAborted);
     }
 
+    /// <summary>
+    /// 查询发布平移待办理人
+    /// </summary>
+    /// <param name="id"></param>
+    /// <returns></returns>
+    /// <exception cref="UserFriendlyException"></exception>
+    [HttpGet("published/migration/{id}")]
+    public async Task<GetOrderMigrationDto> PublishMigration(string id)
+    {
+        var steps = await _workflowStepRepository.Queryable()
+            .Where(d => d.ExternalId == id && d.Status != EWorkflowStepStatus.Handled)
+            .ToListAsync(HttpContext.RequestAborted);
+
+        if (!steps.Any())
+            throw new UserFriendlyException("未查询到待办理节点");
+        if (steps.Count > 1)
+            throw new UserFriendlyException("多个待办理节点暂不支持平移");
+        var step = steps.First();
+        if (step.BusinessType is not EBusinessType.Center and EBusinessType.Send)
+            throw new UserFriendlyException("非中心节点暂不支持平移");
+
+        var setting = _systemSettingCacheManager.GetSetting(SettingConstants.RolePaiDan);
+        var roles = setting?.SettingValue.ToList();
+        var users = await _userRepository.Queryable()
+            .Includes(d => d.Organization)
+            .Where(d => d.Roles.Any(x => roles.Contains(x.Name)))
+            .ToListAsync(HttpContext.RequestAborted);
+        return new GetOrderMigrationDto
+        {
+            StepId = step.Id,
+            Handlers = users.Select(d => new FlowStepHandler
+            {
+                UserId = d.Id,
+                Username = d.Name,
+                OrgId = d.OrgId,
+                OrgName = d.Organization.Name
+            }).ToList()
+        };
+    }
+
     #endregion
 
     #region 工单回访
@@ -671,7 +713,7 @@ public class OrderController : BaseController
         var dissatisfiedReason = _sysDicDataCacheManager.GetSysDicDataCache(SysDicTypeConsts.DissatisfiedReason);
         var visitManner = _sysDicDataCacheManager.GetSysDicDataCache(SysDicTypeConsts.VisitManner).Where(x => x.DicDataValue != "-1");
         //var callRecord = await _trCallRecordRepository.GetAsync(x => x.CallAccept == orderVisit.CallId); //由CallAccept改为OtherAccept
-        var callRecord = await _trCallRecordRepository.GetAsync(x => x.OtherAccept == orderVisit.CallId && string.IsNullOrEmpty(x.OtherAccept)==false,HttpContext.RequestAborted);
+        var callRecord = await _trCallRecordRepository.GetAsync(x => x.OtherAccept == orderVisit.CallId && string.IsNullOrEmpty(x.OtherAccept) == false, HttpContext.RequestAborted);
         var recordingFileUrl = "";
         var recordingBaseAddress = "";
         var recordingAbsolutePath = "";
@@ -789,7 +831,7 @@ public class OrderController : BaseController
                 _mapper.Map(detaildto, visit.OrderVisitDetails[i]);
             }
         }
-       
+
         await _orderVisitRepository.UpdateAsync(visit, HttpContext.RequestAborted);
         await _orderVisitedDetailRepository.UpdateRangeAsync(visit.OrderVisitDetails, HttpContext.RequestAborted);
         await _orderRepository.UpdateAsync(visit.Order, HttpContext.RequestAborted);
@@ -1461,10 +1503,12 @@ public class OrderController : BaseController
     {
         var model = await _orderDelayRepository.Queryable()
             .Includes(d => d.Order)
-            .Includes(d => d.Workflow)
+            .Includes(d => d.Workflow, x => x.Steps)
             .FirstAsync(d => d.Id == id);
         var rspModel = _mapper.Map<OrderDelayDto>(model);
-        rspModel.IsCanHandle = model.CanHandle(_sessionContext.RequiredUserId, _sessionContext.RequiredOrgId);
+        //rspModel.IsCanHandle = model.CanHandle(_sessionContext.RequiredUserId, _sessionContext.RequiredOrgId);
+        rspModel.IsCanHandle = model.Workflow?.IsCanHandle(
+            _sessionContext.RequiredUserId, _sessionContext.RequiredOrgId, _sessionContext.Roles) ?? false;
         rspModel.Handle = false;
         if (!string.IsNullOrEmpty(rspModel.WorkflowId))
         {
@@ -1802,11 +1846,13 @@ public class OrderController : BaseController
     {
         var model = await _orderScreenRepository.Queryable(canView: false)
             .Includes(x => x.Order)
-            .Includes(x => x.Workflow)
+            .Includes(x => x.Workflow, d => d.Steps)
             .Includes(x => x.Visit, d => d.Order)
             .FirstAsync(x => x.Id == id);
         var rspModel = _mapper.Map<OrderScreenListDto>(model);
-        rspModel.IsCanHandle = model.CanHandle(_sessionContext.RequiredUserId, _sessionContext.RequiredOrgId);
+        //rspModel.IsCanHandle = model.CanHandle(_sessionContext.RequiredUserId, _sessionContext.RequiredOrgId);
+        rspModel.IsCanHandle = model.Workflow?.IsCanHandle(
+            _sessionContext.RequiredUserId, _sessionContext.RequiredOrgId, _sessionContext.Roles) ?? false;
         rspModel.Handle = false;
         if (!string.IsNullOrEmpty(rspModel.WorkflowId))
         {
@@ -2462,7 +2508,7 @@ public class OrderController : BaseController
 
         //工单ID跟通话记录相关联
         //var callRecord = await _trCallRecordRepository.GetAsync(p => p.CallAccept == order.CallId, HttpContext.RequestAborted);//由CallAccept改为OtherAccept
-        var callRecord = await _trCallRecordRepository.GetAsync(p => p.OtherAccept== order.CallId && string.IsNullOrEmpty(p.OtherAccept) ==false, HttpContext.RequestAborted);
+        var callRecord = await _trCallRecordRepository.GetAsync(p => p.OtherAccept == order.CallId && string.IsNullOrEmpty(p.OtherAccept) == false, HttpContext.RequestAborted);
         if (callRecord != null && string.IsNullOrEmpty(callRecord.ExternalId))
         {
             callRecord.ExternalId = order.Id;
@@ -3103,7 +3149,7 @@ public class OrderController : BaseController
             .WhereIF(dto.StartTime.HasValue, d => d.CreationTime >= dto.StartTime)
             .WhereIF(dto.EndTime.HasValue, d => d.CreationTime <= dto.EndTime)
             .Where(d => d.Source < ESource.MLSQ || d.Source > ESource.WZSC)
-            .Where(d => d.Status != EOrderStatus.BackToProvince &&  d.Status < EOrderStatus.Filed)
+            .Where(d => d.Status != EOrderStatus.BackToProvince && d.Status < EOrderStatus.Filed)
             //.Where(d => SqlFunc.Subqueryable<OrderSpecial>().Where(os => os.OrderId == d.Id).NotAny())
             .Where(d => d.OrderSpecials.Any() == false || d.OrderSpecials.Any(s => s.State == 0) == false)
             .OrderByDescending(d => d.StartTime)
@@ -3113,7 +3159,7 @@ public class OrderController : BaseController
 
         dto.IsHandled = false;
         isHandled = dto.IsHandled.HasValue && dto.IsHandled.Value;
-		EOrderStatus[] handleStatuses = EnumExts.GetFields<EOrderStatus>().Select(d => (EOrderStatus)d.Key).ToArray();
+        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
@@ -3136,7 +3182,7 @@ public class OrderController : BaseController
             .OrderByIF(dto.IsHandled == false, d => d.CreationTime, OrderByType.Desc)
             .ToPagedListAsync(dto, HttpContext.RequestAborted);
 
-		var page2 = new PagedDto<OrderDto>(total2, _mapper.Map<IReadOnlyList<OrderDto>>(items2));
+        var page2 = new PagedDto<OrderDto>(total2, _mapper.Map<IReadOnlyList<OrderDto>>(items2));
 
         return new { Waited = page1, Sign = page2 };
     }
@@ -4840,7 +4886,7 @@ public class OrderController : BaseController
                     try
                     {
 
-                        if(item.Source!= "麻辣社区"|| item.Source!= "人民网" || item.Source!= "省长信箱" || item.Source!= "问政四川")
+                        if (item.Source != "麻辣社区" || item.Source != "人民网" || item.Source != "省长信箱" || item.Source != "问政四川")
                         //if (item.Source < ESource.MLSQ || item.Source > ESource.WZSC)
                         {
                             errorCount++;
@@ -4866,78 +4912,78 @@ public class OrderController : BaseController
                                 break;
                         }
                         var order = await _orderRepository.GetAsync(x => x.ExternalId == item.ExternalId && x.Source == (ESource)SourceCode, HttpContext.RequestAborted);
-                        
-                            order = _mapper.Map<Orders.Order>(item);
-                            #region 处理数据开始
-                            order.Source = (ESource)SourceCode; //来源
-
-                            //处理热点
-                            //处理一级热点
-                            string hotspotId = "";
-                            string hotspotName = "";
-                            string hotspotFullName = "";
-                            var hotspotOne = await _hotspotTypeRepository.Queryable().FirstAsync(x => x.HotSpotName == item.HotspotNameOne, HttpContext.RequestAborted);
-                            if (hotspotOne!=null)
+
+                        order = _mapper.Map<Orders.Order>(item);
+                        #region 处理数据开始
+                        order.Source = (ESource)SourceCode; //来源
+
+                        //处理热点
+                        //处理一级热点
+                        string hotspotId = "";
+                        string hotspotName = "";
+                        string hotspotFullName = "";
+                        var hotspotOne = await _hotspotTypeRepository.Queryable().FirstAsync(x => x.HotSpotName == item.HotspotNameOne, HttpContext.RequestAborted);
+                        if (hotspotOne != null)
+                        {
+                            hotspotId = hotspotOne.Id;
+                            hotspotName = hotspotOne.HotSpotName;
+                            hotspotFullName = hotspotOne.HotSpotFullName;
+                            var hotspotTwo = await _hotspotTypeRepository.Queryable().FirstAsync(x => x.HotSpotName == item.HotspotNameTwo && x.ParentId == hotspotId, HttpContext.RequestAborted);
+                            if (hotspotTwo != null)
                             {
-                                hotspotId = hotspotOne.Id;
-                                hotspotName = hotspotOne.HotSpotName;
-                                hotspotFullName = hotspotOne.HotSpotFullName;
-                                var hotspotTwo = await _hotspotTypeRepository.Queryable().FirstAsync(x => x.HotSpotName == item.HotspotNameTwo && x.ParentId == hotspotId,HttpContext.RequestAborted);
-                                if (hotspotTwo!=null)
+                                hotspotId = hotspotTwo.Id;
+                                hotspotName = hotspotTwo.HotSpotName;
+                                hotspotFullName = hotspotTwo.HotSpotFullName;
+                                var hotspotThree = await _hotspotTypeRepository.Queryable().FirstAsync(x => x.HotSpotName == item.HotspotNameThree && x.ParentId == hotspotId, HttpContext.RequestAborted);
+                                if (hotspotThree != null)
                                 {
-                                    hotspotId = hotspotTwo.Id;
-                                    hotspotName = hotspotTwo.HotSpotName;
-                                    hotspotFullName = hotspotTwo.HotSpotFullName;
-                                    var hotspotThree = await _hotspotTypeRepository.Queryable().FirstAsync(x => x.HotSpotName == item.HotspotNameThree && x.ParentId == hotspotId, HttpContext.RequestAborted);
-                                    if (hotspotThree!=null)
+                                    hotspotId = hotspotThree.Id;
+                                    hotspotName = hotspotThree.HotSpotName;
+                                    hotspotFullName = hotspotThree.HotSpotFullName;
+                                    var hotspotFour = await _hotspotTypeRepository.Queryable().FirstAsync(x => x.HotSpotName == item.HotspotNameFour && x.ParentId == hotspotId, HttpContext.RequestAborted);
+                                    if (hotspotFour != null)
                                     {
-                                        hotspotId = hotspotThree.Id;
-                                        hotspotName = hotspotThree.HotSpotName;
-                                        hotspotFullName = hotspotThree.HotSpotFullName;
-                                        var hotspotFour = await _hotspotTypeRepository.Queryable().FirstAsync(x => x.HotSpotName == item.HotspotNameFour && x.ParentId == hotspotId, HttpContext.RequestAborted);
-                                        if (hotspotFour!=null)
+                                        hotspotId = hotspotFour.Id;
+                                        hotspotName = hotspotFour.HotSpotName;
+                                        hotspotFullName = hotspotFour.HotSpotFullName;
+                                        var hotspotFive = await _hotspotTypeRepository.Queryable().FirstAsync(x => x.HotSpotName == item.HotspotNameFive && x.ParentId == hotspotId, HttpContext.RequestAborted);
+                                        if (hotspotFive != null)
                                         {
-                                            hotspotId = hotspotFour.Id;
-                                            hotspotName = hotspotFour.HotSpotName;
-                                            hotspotFullName = hotspotFour.HotSpotFullName;
-                                            var hotspotFive = await _hotspotTypeRepository.Queryable().FirstAsync(x => x.HotSpotName == item.HotspotNameFive && x.ParentId == hotspotId, HttpContext.RequestAborted);
-                                            if (hotspotFive!=null)
-                                            {
-                                                hotspotId = hotspotFive.Id;
-                                                hotspotName = hotspotFive.HotSpotName;
-                                                hotspotFullName = hotspotFive.HotSpotFullName;
-                                            }
+                                            hotspotId = hotspotFive.Id;
+                                            hotspotName = hotspotFive.HotSpotName;
+                                            hotspotFullName = hotspotFive.HotSpotFullName;
                                         }
                                     }
                                 }
                             }
-                            order.HotspotId = hotspotId;
-                            order.HotspotName = hotspotName;
-                            order.HotspotSpliceName = hotspotFullName;
+                        }
+                        order.HotspotId = hotspotId;
+                        order.HotspotName = hotspotName;
+                        order.HotspotSpliceName = hotspotFullName;
 
-                            //处理部门
-                            var orgOne = await _organizeRepository.Queryable().FirstAsync(x => x.Name == item.OrgLevelOneName, HttpContext.RequestAborted);
-                            if (orgOne!=null)
-                            {
-                                order.OrgLevelOneCode = orgOne.Id;
-                                order.OrgLevelOneName = orgOne.Name;
-                                var orgTwo = await _organizeRepository.Queryable().FirstAsync(x => x.Name == item.OrgLevelTwoName && x.ParentId == order.OrgLevelOneCode, HttpContext.RequestAborted);
-                                if (orgTwo!=null)
-                                {
-                                    order.OrgLevelTwoCode = orgTwo.Id;
-                                    order.OrgLevelTwoName = orgTwo.Name;
-                                }
-                            }
-                            //承办部门
-                            var ActualHandleOrg = await _organizeRepository.Queryable().FirstAsync(x => x.Name == item.ActualHandleOrgName, HttpContext.RequestAborted);
-                            if (ActualHandleOrg!=null)
+                        //处理部门
+                        var orgOne = await _organizeRepository.Queryable().FirstAsync(x => x.Name == item.OrgLevelOneName, HttpContext.RequestAborted);
+                        if (orgOne != null)
+                        {
+                            order.OrgLevelOneCode = orgOne.Id;
+                            order.OrgLevelOneName = orgOne.Name;
+                            var orgTwo = await _organizeRepository.Queryable().FirstAsync(x => x.Name == item.OrgLevelTwoName && x.ParentId == order.OrgLevelOneCode, HttpContext.RequestAborted);
+                            if (orgTwo != null)
                             {
-                                order.ActualHandleOrgCode = ActualHandleOrg.Id;
+                                order.OrgLevelTwoCode = orgTwo.Id;
+                                order.OrgLevelTwoName = orgTwo.Name;
                             }
+                        }
+                        //承办部门
+                        var ActualHandleOrg = await _organizeRepository.Queryable().FirstAsync(x => x.Name == item.ActualHandleOrgName, HttpContext.RequestAborted);
+                        if (ActualHandleOrg != null)
+                        {
+                            order.ActualHandleOrgCode = ActualHandleOrg.Id;
+                        }
 
-                            //处理地址
-                            order.Address = $"{order.Province}{order.City}{order.County}{order.Town}";
-                            order.FullAddress = $"{order.Address}{order.Street}";
+                        //处理地址
+                        order.Address = $"{order.Province}{order.City}{order.County}{order.Town}";
+                        order.FullAddress = $"{order.Address}{order.Street}";
 
                         #endregion
                         if (order is null)
@@ -5386,4 +5432,48 @@ public class OrderController : BaseController
         return _mapper.Map<IReadOnlyList<OrderModifyingRecordsDto>>(list);
     }
     #endregion
+
+    #region 工单平移
+
+    /// <summary>
+    /// 查询工单平移待办理人
+    /// </summary>
+    /// <returns></returns>
+    [HttpGet("migration/{orderId}")]
+    public async Task<GetOrderMigrationDto> Migration(string orderId)
+    {
+        var steps = await _workflowStepRepository.Queryable()
+            .Where(d => d.ExternalId == orderId && d.Status != EWorkflowStepStatus.Handled)
+            .ToListAsync(HttpContext.RequestAborted);
+
+        if (!steps.Any())
+            throw new UserFriendlyException("未查询到待办理节点");
+        if (steps.Count > 1)
+            throw new UserFriendlyException("多个待办理节点暂不支持平移");
+        var step = steps.First();
+        if (step.BusinessType is not EBusinessType.Center and EBusinessType.Send)
+            throw new UserFriendlyException("非中心节点暂不支持平移");
+
+        var setting = step.BusinessType is EBusinessType.Center
+            ? _systemSettingCacheManager.GetSetting(SettingConstants.RoleZuoXi)
+            : _systemSettingCacheManager.GetSetting(SettingConstants.RolePaiDan);
+        var roles = setting?.SettingValue.ToList();
+        var users = await _userRepository.Queryable()
+            .Includes(d => d.Organization)
+            .Where(d => d.Roles.Any(x => roles.Contains(x.Name)))
+            .ToListAsync(HttpContext.RequestAborted);
+        return new GetOrderMigrationDto
+        {
+            StepId = step.Id,
+            Handlers = users.Select(d => new FlowStepHandler
+            {
+                UserId = d.Id,
+                Username = d.Name,
+                OrgId = d.OrgId,
+                OrgName = d.Organization.Name
+            }).ToList()
+        };
+    }
+
+    #endregion
 }

+ 1 - 0
src/Hotline.Api/Controllers/WebPortalController.cs

@@ -26,6 +26,7 @@ using Hotline.Share.Dtos.DataSharing.PusherHotlineDto;
 using Hotline.Share.Dtos.Order;
 using Org.BouncyCastle.Ocsp;
 using System.Threading;
+using Hotline.Settings;
 
 namespace Hotline.Api.Controllers
 {

+ 18 - 0
src/Hotline.Api/Controllers/WorkflowController.cs

@@ -45,6 +45,7 @@ public class WorkflowController : BaseController
     private readonly IRepository<Role> _roleRepository;
     private readonly ISystemDomainService _systemDomainService;
     private readonly IWfModuleDomainService _wfModuleDomainService;
+    private readonly IRepository<WorkflowStep> _workflowStepRepository;
     private readonly IRepository<WorkflowModule> _wfModuleRepository;
     private readonly IRepository<WorkflowTrace> _workflowTraceRepository;
     private readonly IRepository<WorkflowCountersign> _workflowCountersignRepository;
@@ -66,6 +67,7 @@ public class WorkflowController : BaseController
         IRepository<Role> roleRepository,
         ISystemDomainService systemDomainService,
         IWfModuleDomainService wfModuleDomainService,
+        IRepository<WorkflowStep> workflowStepRepository,
         IRepository<WorkflowModule> wfModuleRepository,
         IRepository<WorkflowTrace> workflowTraceRepository,
         IRepository<WorkflowCountersign> workflowCountersignRepository,
@@ -87,6 +89,7 @@ public class WorkflowController : BaseController
         _roleRepository = roleRepository;
         _systemDomainService = systemDomainService;
         _wfModuleDomainService = wfModuleDomainService;
+        _workflowStepRepository = workflowStepRepository;
         _wfModuleRepository = wfModuleRepository;
         _sessionContext = sessionContext;
         _mapper = mapper;
@@ -650,6 +653,21 @@ public class WorkflowController : BaseController
         };
     }
 
+    /// <summary>
+    /// 改变某节点办理人
+    /// </summary>
+    [HttpPost("change-handler")]
+    public async Task ChangeHandler([FromBody] ChangeHandlerDto dto)
+    {
+        var step = await _workflowStepRepository.GetAsync(dto.StepId, HttpContext.RequestAborted);
+        if (step is null)
+            throw new UserFriendlyException("无效节点编号");
+        await _workflowDomainService.ChangeHandlerBatchAsync(new List<(string userId, string username, string orgId, string orgName, ICollection<WorkflowStep> steps)>
+            {
+                new(dto.Handler.UserId,dto.Handler.Username,dto.Handler.OrgId,dto.Handler.OrgName, new List<WorkflowStep>{step})
+            }, HttpContext.RequestAborted);
+    }
+
     #endregion
 
 }

+ 12 - 4
src/Hotline.Api/StartupHelper.cs

@@ -18,6 +18,7 @@ using XF.Domain.Entities;
 using XF.Domain.Exceptions;
 using XF.Domain.Options;
 using XF.Domain.Repository;
+using XF.EasyCaching;
 
 namespace Hotline.Api
 {
@@ -222,12 +223,19 @@ namespace Hotline.Api
         /// <returns></returns>
         public static IServiceCollection RegisterSignalR(this IServiceCollection services, ConfigurationManager configuration)
         {
-            var connectionString = configuration.GetConnectionString("Redis");
-            if (string.IsNullOrEmpty(connectionString))
-                throw new UserFriendlyException("未配置signalR的redis连接");
-            services.AddSignalR().AddStackExchangeRedis(connectionString, options =>
+            //var connectionString = configuration.GetConnectionString("Redis");
+            //if (string.IsNullOrEmpty(connectionString))
+            //    throw new UserFriendlyException("未配置signalR的redis连接");
+            //services.AddSignalR().AddStackExchangeRedis(connectionString, options =>
+            //{
+            //    options.Configuration.ChannelPrefix = "Hotline:signalr:";
+            //});
+            var cacheConfig = configuration.GetRequiredSection("Cache").Get<CacheOptions>();
+            services.AddSignalR().AddStackExchangeRedis($"{cacheConfig.Host}:{cacheConfig.Port}", options =>
             {
                 options.Configuration.ChannelPrefix = "Hotline:signalr:";
+                options.Configuration.Password = cacheConfig.Password;
+                options.Configuration.DefaultDatabase = cacheConfig.Database;
             });
 
             return services;

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

@@ -225,7 +225,7 @@
   },
   //智能质检
   "AiQuality": {
-    "Url": "http://118.121.201.140:19072/"
+    "Url": "http://118.122.73.80:19072/"
   },
   //企业服务
   "Enterprise": {

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

@@ -12,6 +12,7 @@ using System.Collections.Generic;
 using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
+using Hotline.Settings;
 using XF.Domain.Constants;
 
 namespace Hotline.Application.Bigscreen

+ 1 - 0
src/Hotline.Application/Bigscreen/SeatStateDataRefreshService.cs

@@ -1,5 +1,6 @@
 using Hotline.Caching.Interfaces;
 using Hotline.Realtimes;
+using Hotline.Settings;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.Hosting;
 using Microsoft.Extensions.Logging;

+ 1 - 0
src/Hotline.Application/CallCenter/Calls/TrApplication.cs

@@ -1,6 +1,7 @@
 using Hotline.Caching.Interfaces;
 using Hotline.CallCenter.Calls;
 using Hotline.CallCenter.Tels;
+using Hotline.Settings;
 using Hotline.Share.Dtos.CallCenter;
 using Hotline.Share.Dtos.TrCallCenter;
 using Hotline.Share.Enums.CallCenter;

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

@@ -4,6 +4,7 @@ using Hotline.FlowEngine.Notifications;
 using Hotline.FlowEngine.WorkflowModules;
 using Hotline.FlowEngine.Workflows;
 using Hotline.Orders;
+using Hotline.Settings;
 using Hotline.Settings.TimeLimits;
 using Hotline.Share.Dtos.Order;
 using Hotline.Share.Enums.FlowEngine;

+ 27 - 11
src/Hotline.Application/Identity/IdentityAppService.cs

@@ -1,18 +1,23 @@
 using System.Security.Claims;
+using Hotline.Caching.Interfaces;
 using Hotline.Identity;
 using Hotline.Identity.Accounts;
 using Hotline.Orders;
 using Hotline.Push;
 using Hotline.Schedulings;
 using Hotline.Settings;
+using Hotline.Share.Dtos.FlowEngine;
 using Hotline.Share.Dtos.Identity;
+using Hotline.Share.Enums.FlowEngine;
 using Hotline.Share.Enums.Identity;
 using Hotline.Users;
 using IdentityModel;
+using Microsoft.AspNetCore.Http;
 using Microsoft.AspNetCore.Identity;
 using Microsoft.Extensions.Options;
 using XF.Domain.Authentications;
 using XF.Domain.Cache;
+using XF.Domain.Constants;
 using XF.Domain.Dependency;
 using XF.Domain.Exceptions;
 using XF.Domain.Options;
@@ -31,8 +36,9 @@ public class IdentityAppService : IIdentityAppService, IScopeDependency
     private readonly IMessageCodeDomainService _messageCodeDomainService;
     private readonly IRepository<Scheduling> _schedulingRepository;
     private readonly IOrderDomainService _orderDomainService;
+    private readonly ISystemSettingCacheManager _systemSettingCacheManager;
 
-    public IdentityAppService(
+	public IdentityAppService(
         IAccountRepository accountRepository,
         IAccountDomainService accountDomainService,
         IRepository<User> userRepository,
@@ -41,7 +47,8 @@ public class IdentityAppService : IIdentityAppService, IScopeDependency
         ITypedCache<AudienceTicket> cacheAudience,
         IMessageCodeDomainService messageCodeDomainService,
         IRepository<Scheduling> schedulingRepository,
-         IOrderDomainService orderDomainService)
+        IOrderDomainService orderDomainService,
+        ISystemSettingCacheManager systemSettingCacheManager)
     {
         _accountRepository = accountRepository;
         _accountDomainService = accountDomainService;
@@ -52,7 +59,9 @@ public class IdentityAppService : IIdentityAppService, IScopeDependency
         _messageCodeDomainService = messageCodeDomainService;
         _schedulingRepository = schedulingRepository;
         _orderDomainService = orderDomainService;
-    }
+        _systemSettingCacheManager = systemSettingCacheManager;
+
+	}
 
     public async Task<string> LoginAsync(LoginDto dto, CancellationToken cancellationToken)
     {
@@ -99,9 +108,12 @@ public class IdentityAppService : IIdentityAppService, IScopeDependency
             .FirstAsync(d => d.Id == account.Id);
         if (user == null)
             throw UserFriendlyException.SameMessage("未查询到用户数据");
-        //平均派单
-        await AverageOrderScheduling(account.Id, cancellationToken);
-
+		//平均派单
+		var averageSendOrder = bool.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.AverageSendOrder).SettingValue[0]);
+		if (averageSendOrder)
+		{
+			await AverageOrderScheduling(account.Id, cancellationToken);
+		}
         var jwtOptions = _identityOptionsAccessor.Value.Jwt;
         var claims = new List<Claim>
         {
@@ -141,18 +153,22 @@ public class IdentityAppService : IIdentityAppService, IScopeDependency
         try
         {
             DateTime time = DateTime.Parse(DateTime.Now.ToString("yyyy-MM-dd"));
-
+            
             //&& x.AtWork!.Value != true
             //根据当前时间获取排班信息
-            var scheduling = await _schedulingRepository.Queryable().Includes(x => x.SchedulingUser)
-                .Where(x => x.SchedulingTime == time && x.WorkingTime <= DateTime.Now.TimeOfDay && x.OffDutyTime >= DateTime.Now.TimeOfDay && (x.AtWork == true || x.AtWork == null) && x.SchedulingUser.UserId == id)
+            var scheduling = await _schedulingRepository.Queryable()
+                .Includes(x => x.SchedulingUser)
+                .Where(x => x.SchedulingTime == time &&
+                            x.WorkingTime <= DateTime.Now.TimeOfDay && 
+                            x.OffDutyTime >= DateTime.Now.TimeOfDay && 
+                            (x.AtWork == true || x.AtWork == null) && 
+                            x.SchedulingUser.UserId == id)
                 .OrderBy(x => x.SendOrderNum).FirstAsync(cancellationToken);
             if (scheduling != null)
             {
                 scheduling.AtWork = true;
-                await _schedulingRepository.UpdateAsync(scheduling, cancellationToken);
                 //执行登录平均派单
-                await _orderDomainService.LogAverageOrder(id, cancellationToken);
+                await _orderDomainService.LogAverageOrder(id, scheduling, cancellationToken);
             }
         }
         catch

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

@@ -26,6 +26,7 @@ using Novacode;
 using SqlSugar;
 using System.Data;
 using System.Dynamic;
+using Hotline.Settings;
 using XF.Domain.Authentications;
 using XF.Domain.Constants;
 using XF.Domain.Dependency;

+ 1 - 0
src/Hotline.Application/Quality/QualityApplication.cs

@@ -3,6 +3,7 @@ using Hotline.Caching.Interfaces;
 using Hotline.CallCenter.Calls;
 using Hotline.Orders;
 using Hotline.Quality;
+using Hotline.Settings;
 using Hotline.Share.Dtos.Quality;
 using Hotline.Share.Enums.Quality;
 using MapsterMapper;

+ 2 - 2
src/Hotline.Repository.SqlSugar/Extensions/SqlSugarStartupExtensions.cs

@@ -207,8 +207,8 @@ namespace Hotline.Repository.SqlSugar.Extensions
             /***写AOP等方法***/
             db.Aop.OnLogExecuting = (sql, pars) =>
             {
-                //Log.Information("Sql: {0}", sql);
-                //Log.Information("SqlParameters: {0}", string.Join(',', pars.Select(d => d.Value)));
+                Log.Information("Sql: {0}", sql);
+                Log.Information("SqlParameters: {0}", string.Join(',', pars.Select(d => d.Value)));
             };
             db.Aop.OnError = (exp) =>//SQL报错
             {

+ 9 - 0
src/Hotline.Share/Dtos/FlowEngine/Workflow/ChangeHandlerDto.cs

@@ -0,0 +1,9 @@
+using Hotline.Share.Dtos.FlowEngine;
+
+namespace Hotline.Share.Dtos.FlowEngine.Workflow;
+
+public class ChangeHandlerDto
+{
+    public string StepId { get; set; }
+    public FlowStepHandler Handler { get; set; }
+}

+ 15 - 0
src/Hotline.Share/Dtos/Order/Migration/GetOrderMigrationDto.cs

@@ -0,0 +1,15 @@
+using Hotline.Share.Dtos.FlowEngine;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Hotline.Share.Dtos.Order.Migration
+{
+    public class GetOrderMigrationDto
+    {
+        public string StepId { get; set; }
+        public IReadOnlyList<FlowStepHandler> Handlers { get; set; }
+    }
+}

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

@@ -214,6 +214,11 @@ namespace Hotline.FlowEngine.Workflows
         /// </summary>
         Task<IReadOnlyList<string>> GetUnhandleStepIdsFromSendPoolAsync(string sendPoolId, CancellationToken cancellationToken);
 
+        /// <summary>
+        /// 查询归属某用户的所有流程节点
+        /// </summary>
+        Task<List<WorkflowStep>> GetStepsBelongsToAsync(string userId, CancellationToken cancellationToken);
+
         /// <summary>
         /// 批量改派工单至指定用户
         /// </summary>
@@ -221,6 +226,13 @@ namespace Hotline.FlowEngine.Workflows
             IReadOnlyList<(string userId, string username, string orgId, string orgName, IReadOnlyList<string> stepIds)> handlers,
             CancellationToken cancellationToken);
 
+        /// <summary>
+        /// 批量修改工单办理对象
+        /// </summary>
+        Task ChangeHandlerBatchAsync(
+            IReadOnlyList<(string userId, string username, string orgId, string orgName, ICollection<WorkflowStep> steps)> handlers,
+            CancellationToken cancellationToken);
+
         /// <summary>
         /// 查询工单办理中的一级部门
         /// </summary>

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

@@ -722,7 +722,7 @@ public partial class Workflow
     /// <returns></returns>
     public bool IsCanHandle(string userId, string orgId, string[] roleIds)
     {
-        if (!Steps.Any())
+        if (Steps is null || !Steps.Any())
             throw new UserFriendlyException("未查询节点信息");
         return Status is EWorkflowStatus.Runnable &&
                //(HandlerUsers.Any(d => d.Key == userId) || HandlerOrgs.Any(d => d.Key == orgCode));

+ 32 - 0
src/Hotline/FlowEngine/Workflows/WorkflowDomainService.cs

@@ -647,6 +647,17 @@ namespace Hotline.FlowEngine.Workflows
                 .ToListAsync(cancellationToken);
         }
 
+        /// <summary>
+        /// 查询归属某用户的所有流程节点
+        /// </summary>
+        public async Task<List<WorkflowStep>> GetStepsBelongsToAsync(string userId, CancellationToken cancellationToken)
+        {
+            return await _workflowStepRepository.Queryable()
+                .Where(d => d.HandlerId == userId)
+                .OrderBy(d=>d.CreationTime)
+                .ToListAsync(cancellationToken);
+        }
+
         /// <summary>
         /// 批量改变办理对象
         /// </summary>
@@ -705,6 +716,27 @@ namespace Hotline.FlowEngine.Workflows
             return steps.Select(d => d.WorkflowId).ToList();
         }
 
+        /// <summary>
+        /// 批量修改工单办理对象
+        /// </summary>
+        public async Task ChangeHandlerBatchAsync(
+            IReadOnlyList<(string userId, string username, string orgId, string orgName, ICollection<WorkflowStep> steps)> handlers,
+            CancellationToken cancellationToken)
+        {
+            foreach (var handler in handlers)
+            {
+                foreach (var step in handler.steps)
+                {
+                    step.FlowAssignType = EFlowAssignType.User;
+                    step.Assign(handler.userId, handler.username, 
+                        handler.orgId, handler.orgName);
+                }
+            }
+
+            await _workflowStepRepository.UpdateRangeAsync(handlers.SelectMany(d => d.steps).ToList(),
+                cancellationToken);
+        }
+
         /// <summary>
         /// 查询工单办理中的一级部门
         /// </summary>

+ 1 - 1
src/Hotline/KnowledgeBase/KnowledgeDomainService.cs

@@ -48,7 +48,7 @@ namespace Hotline.KnowledgeBase
         public async Task<Knowledge> KnowledgeInfo(string Id, CancellationToken cancellationToken)
         {
             var know = await _knowledgeRepository.Queryable(false, false, false)
-                .Includes(x => x.Workflow)
+                .Includes(x => x.Workflow, d => d.Steps)
                 .Includes(x=>x.SourceOrganize)
                 .Includes(x=>x.KnowledgeType).FirstAsync(p => p.Id == Id);
             if (know is null)

+ 3 - 2
src/Hotline/Orders/IOrderDomainService.cs

@@ -1,4 +1,5 @@
-using Hotline.Share.Dtos;
+using Hotline.Schedulings;
+using Hotline.Share.Dtos;
 using Hotline.Share.Dtos.FlowEngine;
 using Hotline.Share.Dtos.Order;
 
@@ -68,7 +69,7 @@ namespace Hotline.Orders
         /// </summary>
         /// <param name="userId"></param>
         /// <returns></returns>
-        Task LogAverageOrder(string userId, CancellationToken cancellationToken);
+        Task LogAverageOrder(string userId, Scheduling scheduling, CancellationToken cancellationToken);
 
         /// <summary>
         /// 触发平均派单

+ 38 - 76
src/Hotline/Orders/OrderDomainService.cs

@@ -17,6 +17,7 @@ using Hotline.Users;
 using Hotline.Share.Dtos;
 using Hotline.Settings.Hotspots;
 using Hotline.Share.Dtos.FlowEngine;
+using Microsoft.AspNetCore.Http;
 
 namespace Hotline.Orders;
 
@@ -190,7 +191,6 @@ public class OrderDomainService : IOrderDomainService, IScopeDependency
             .Where(x => x.SchedulingTime == time && x.WorkingTime <= DateTime.Now.TimeOfDay && x.OffDutyTime >= DateTime.Now.TimeOfDay && x.AtWork == true)
             .OrderBy(x => x.SendOrderNum).FirstAsync(cancellationToken);
         if (scheduling is null)
-            //return new List<Kv> { new(OrderDefaults.SourceChannel.SendPoolId, "待派单池") };
             return new FlowStepHandler
             {
                 Key = OrderDefaults.SourceChannel.SendPoolId,
@@ -198,13 +198,8 @@ public class OrderDomainService : IOrderDomainService, IScopeDependency
                 UserId = OrderDefaults.SourceChannel.SendPoolId,
                 Username = "待派单池",
             };
-
-        //var user = await _userRepository.GetAsync(x => x.Id == scheduling.SchedulingUser.UserId, cancellationToken);
-        //if (user is null)
-        //    throw new UserFriendlyException("无效用户编号");
         scheduling.SendOrderNum++;
         await _schedulingRepository.UpdateAsync(scheduling, cancellationToken);
-        //return new List<Kv> { new(user.Id, user.Name) };
         var user = scheduling.SchedulingUser;
         return new FlowStepHandler
         {
@@ -222,45 +217,36 @@ public class OrderDomainService : IOrderDomainService, IScopeDependency
     /// </summary>
     /// <param name="userId"></param>
     /// <returns></returns>
-    public async Task LogAverageOrder(string userId, CancellationToken cancellationToken)
+    public async Task LogAverageOrder(string userId, Scheduling scheduling, CancellationToken cancellationToken)
     {
         //1.获取默认派单员所属的工单
         //2.获取今天上班的人员
         //3.给当前这个用户平均派单
-        var steps = await _workflowDomainService.GetUnhandleStepIdsFromSendPoolAsync(OrderDefaults.SourceChannel.SendPoolId, cancellationToken);
-        var stepsList = steps.ToList();
+
+        var steps = await _workflowDomainService.GetStepsBelongsToAsync(OrderDefaults.SourceChannel.SendPoolId,
+            cancellationToken);
 
         var user = await _userRepository.Queryable()
             .Includes(d => d.Organization)
             .FirstAsync(d => d.Id == userId, cancellationToken);
-        DateTime time = DateTime.Parse(DateTime.Now.ToString("yyyy-MM-dd"));
+        var time = DateTime.Parse(DateTime.Now.ToString("yyyy-MM-dd"));
         var schedulings = await _schedulingRepository.Queryable().Includes(x => x.SchedulingUser)
             .Where(x => x.SchedulingTime == time && x.WorkingTime <= DateTime.Now.TimeOfDay && x.OffDutyTime >= DateTime.Now.TimeOfDay).CountAsync(cancellationToken);
-
         if (schedulings > 0)
         {
-            List<string> stepIds = new List<string>();
-            var sendNum = stepsList.Count() / schedulings;
-            for (int i = 0; i < sendNum; i++)
-            {
-                stepIds.Add(stepsList[0]);
-                stepsList.Remove(stepsList[0]);
-            }
-            List<(string, string, string, string, IReadOnlyList<string> stepIds)> handlers = new();
-            ; handlers.Add(new ValueTuple<string, string, string, string, IReadOnlyList<string>>(user.Id, user.Name, user.OrgId, user.Organization.Name, stepIds));
-            var workflowIds = await _workflowDomainService.ChangeHandlerRangeAsync(OrderDefaults.SourceChannel.SendPoolId, handlers, cancellationToken);
-            var orders = await _orderRepository.Queryable().Includes(d => d.Workflow).Where(d => workflowIds.Contains(d.WorkflowId))
-                .ToListAsync(cancellationToken);
-            foreach (var order in orders)
+            var sendNum = steps.Count / schedulings;
+            if (sendNum <= 0) return;
+            var sendSteps = steps.Take(sendNum).ToList();
+            await _workflowDomainService.ChangeHandlerBatchAsync(new List<(string userId, string username, string orgId, string orgName, ICollection<WorkflowStep> steps)>
             {
-                _mapper.Map(order.Workflow, order);
-            }
-            var scheduling = await _schedulingRepository.Queryable().Includes(x => x.SchedulingUser)
-                .Where(x => x.SchedulingTime == time && x.WorkingTime <= DateTime.Now.TimeOfDay && x.OffDutyTime >= DateTime.Now.TimeOfDay && x.SchedulingUser.UserId == userId).FirstAsync(cancellationToken);
+                new(user.Id, user.Name, user.OrgId, user.Organization.Name, sendSteps)
+            }, cancellationToken);
             scheduling.SendOrderNum += sendNum;
-            await _schedulingRepository.UpdateAsync(scheduling, cancellationToken);
-            await _orderRepository.UpdateRangeAsync(orders, cancellationToken);
+            await _schedulingRepository.Updateable()
+                .SetColumns(s => new Scheduling() { SendOrderNum = scheduling.SendOrderNum, AtWork = scheduling.AtWork } )
+                .Where(s=> s.Id == scheduling.Id).ExecuteCommandAsync(cancellationToken);
         }
+
     }
 
     /// <summary>
@@ -279,48 +265,30 @@ public class OrderDomainService : IOrderDomainService, IScopeDependency
 
         if (schedulings.Any())
         {
-            var steps = await _workflowDomainService.GetUnhandleStepIdsFromSendPoolAsync(OrderDefaults.SourceChannel.SendPoolId, cancellationToken);
-            var stepsList = steps.ToList();
-
-            var sendNum = steps.Count() / schedulings.Count();
-            List<(string userId, string username, string orgId, string orgName, IReadOnlyList<string> stepIds)> handlers = new();
-            if (sendNum > 0)
-            {
-
-                foreach (var scheduling in schedulings)
-                {
-                    List<string> stepIds = new List<string>();
-                    for (int i = 0; i < sendNum; i++)
-                    {
-                        stepIds.Add(stepsList[0]);
-                        stepsList.Remove(stepsList[0]);
-                    }
-                    handlers.Add(new ValueTuple<string, string, string, string, IReadOnlyList<string>>(scheduling.SchedulingUser.UserId, scheduling.SchedulingUser.UserName, scheduling.SchedulingUser.OrgId, scheduling.SchedulingUser.OrgIdName, stepIds));
-
-                }
-            }
-            sendNum = steps.Count() % schedulings.Count();
-            if (sendNum > 0)
+            var steps = await _workflowDomainService.GetStepsBelongsToAsync(OrderDefaults.SourceChannel.SendPoolId,
+                cancellationToken);
+            if (steps.Any())
             {
-                List<string> stepIds = new List<string>();
-                for (int i = 0; i < sendNum; i++)
+                List<(string userId, string username, string orgId, string orgName, ICollection<WorkflowStep> steps)> handlers = new();
+                var avg = steps.Count / schedulings.Count;
+                var remaining = steps.Count % schedulings.Count;
+                for (var i = 0; i < schedulings.Count; i++)
                 {
-                    stepIds.Add(stepsList[0]);
-                    stepsList.Remove(stepsList[0]);
-                }
-                handlers.Add(new ValueTuple<string, string, string, string, IReadOnlyList<string>>(schedulings[0].SchedulingUser.UserId, schedulings[0].SchedulingUser.UserName, schedulings[0].SchedulingUser.OrgId, schedulings[0].SchedulingUser.OrgIdName, stepIds));
-            }
-            if (handlers.Any())
-            {
-                var workflowIds = await _workflowDomainService.ChangeHandlerRangeAsync(OrderDefaults.SourceChannel.SendPoolId, handlers, cancellationToken);
-                var orders = await _orderRepository.Queryable().Includes(d => d.Workflow).Where(d => workflowIds.Contains(d.WorkflowId))
-                    .ToListAsync(cancellationToken);
-                foreach (var order in orders)
-                {
-                    _mapper.Map(order.Workflow, order);
-                }
-
-                await _orderRepository.UpdateRangeAsync(orders, cancellationToken);
+                    var scheduling = schedulings[i];
+                    var size = avg + (i < remaining ? 1 : 0);
+                    handlers.Add(new(
+                        scheduling.SchedulingUser.UserId,
+                        scheduling.SchedulingUser.UserName,
+                        scheduling.SchedulingUser.OrgId,
+                        scheduling.SchedulingUser.OrgIdName,
+                        steps.Take(size).ToList()));
+                    scheduling.SendOrderNum += size;
+					await _schedulingRepository.Updateable()
+	                    .SetColumns(s => new Scheduling() { SendOrderNum = scheduling.SendOrderNum })
+	                    .Where(s => s.Id == scheduling.Id).ExecuteCommandAsync(cancellationToken);
+				}
+                if (handlers.Any())
+                    await _workflowDomainService.ChangeHandlerBatchAsync(handlers, cancellationToken);
             }
         }
     }
@@ -403,12 +371,6 @@ public class OrderDomainService : IOrderDomainService, IScopeDependency
             valid.Result = "标题或受理内容出现限制词,请检查!";
         return valid;
     }
-    #endregion
-
-    #region SchedulingSendOrder
-
-
-
     #endregion
 
     #region private

+ 1 - 0
src/Hotline/Push/MessageCodeDomainService.cs

@@ -2,6 +2,7 @@
 using Hotline.Caching.Interfaces;
 using Hotline.Identity.Accounts;
 using Hotline.Push.Notifies;
+using Hotline.Settings;
 using Hotline.Share.Dtos.Push;
 using Hotline.Share.Enums.Push;
 using Hotline.Users;

+ 1 - 0
src/Hotline/Quality/QualityDomainService.cs

@@ -2,6 +2,7 @@
 using Hotline.Caching.Interfaces;
 using Hotline.CallCenter.Calls;
 using Hotline.Orders;
+using Hotline.Settings;
 using Hotline.Share.Dtos.Quality;
 using Hotline.Share.Enums.Quality;
 using MapsterMapper;

+ 11 - 7
src/XF.Domain/Constants/SettingConstants.cs → src/Hotline/Settings/SettingConstants.cs

@@ -1,10 +1,4 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace XF.Domain.Constants
+namespace Hotline.Settings
 {
     public class SettingConstants
     {
@@ -329,5 +323,15 @@ namespace XF.Domain.Constants
         /// 录音文件前缀地址
         /// </summary>
 		public const string ViteRecordPrefix = "ViteRecordPrefix";
+
+        /// <summary>
+        /// 派单员角色
+        /// </summary>
+		public const string RolePaiDan = "RolePaiDan";
+
+        /// <summary>
+        /// 坐席角色
+        /// </summary>
+		public const string RoleZuoXi = "RoleZuoXi";
 	}
 }