Procházet zdrojové kódy

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

Dun.Jason před 2 měsíci
rodič
revize
cc1c551936
23 změnil soubory, kde provedl 326 přidání a 432 odebrání
  1. 117 273
      src/Hotline.Api/Controllers/OrderController.cs
  2. 13 38
      src/Hotline.Api/Controllers/TestController.cs
  3. 1 1
      src/Hotline.Api/Controllers/WorkflowController.cs
  4. 2 37
      src/Hotline.Api/Realtimes/RealtimeService.cs
  5. 1 1
      src/Hotline.Application/FlowEngine/IWorkflowApplication.cs
  6. 2 2
      src/Hotline.Application/FlowEngine/WorkflowApplication.cs
  7. 5 2
      src/Hotline.Application/IndustryClassification/IndustryClassificationApplication.cs
  8. 15 2
      src/Hotline.Application/Jobs/XingTangCallsSyncJob.cs
  9. 4 1
      src/Hotline.Application/Orders/IOrderApplication.cs
  10. 79 3
      src/Hotline.Application/Orders/OrderApplication.cs
  11. 32 5
      src/Hotline.Application/Orders/OrderSecondaryHandlingApplication.cs
  12. 2 2
      src/Hotline.Application/StatisticalReport/OrderReportApplication.cs
  13. 0 18
      src/Hotline.Share/Dtos/Article/NotificationWaitSendDto.cs
  14. 3 0
      src/Hotline.Share/Dtos/Order/OrderPreviousDto.cs
  15. 10 1
      src/Hotline.Share/Dtos/Order/TargetStepInfo.cs
  16. 0 21
      src/Hotline/Article/NotificationWaitSend.cs
  17. 5 0
      src/Hotline/Caching/Interfaces/ISystemSettingCacheManager.cs
  18. 5 0
      src/Hotline/Caching/Services/SystemSettingCacheManager.cs
  19. 2 2
      src/Hotline/FlowEngine/Workflows/StepBasicEntity.cs
  20. 10 15
      src/Hotline/Orders/OrderDomainService.cs
  21. 13 0
      src/Hotline/Orders/OrderSendBackAudit.cs
  22. 0 8
      src/Hotline/Realtimes/IRealtimeService.cs
  23. 5 0
      src/Hotline/Settings/SettingConstants.cs

+ 117 - 273
src/Hotline.Api/Controllers/OrderController.cs

@@ -1,4 +1,5 @@
 using DotNetCore.CAP;
+using FluentValidation;
 using Hotline.Api.Filter;
 using Hotline.Application.CallCenter;
 using Hotline.Application.ExportExcel;
@@ -23,7 +24,6 @@ using Hotline.Orders.Notifications;
 using Hotline.OrderTranspond;
 using Hotline.Push.FWMessage;
 using Hotline.Push.Notifies;
-using Hotline.Repository.SqlSugar.CallCenter;
 using Hotline.Repository.SqlSugar.Extensions;
 using Hotline.Repository.SqlSugar.Ts;
 using Hotline.SeedData;
@@ -32,8 +32,8 @@ using Hotline.Settings.Hotspots;
 using Hotline.Settings.TimeLimitDomain;
 using Hotline.Settings.TimeLimits;
 using Hotline.Share.Dtos;
+using Hotline.Share.Dtos.Article;
 using Hotline.Share.Dtos.CallCenter;
-using Hotline.Share.Dtos.File;
 using Hotline.Share.Dtos.FlowEngine;
 using Hotline.Share.Dtos.FlowEngine.Workflow;
 using Hotline.Share.Dtos.Order;
@@ -44,6 +44,7 @@ using Hotline.Share.Dtos.Order.Publish;
 using Hotline.Share.Dtos.Org;
 using Hotline.Share.Dtos.Settings;
 using Hotline.Share.Dtos.Snapshot;
+using Hotline.Share.Enums.Article;
 using Hotline.Share.Enums.CallCenter;
 using Hotline.Share.Enums.FlowEngine;
 using Hotline.Share.Enums.Order;
@@ -56,6 +57,7 @@ using Hotline.Snapshot;
 using Hotline.Snapshot.Interfaces;
 using Hotline.Tools;
 using Hotline.Users;
+using Hotline.Validators.FlowEngine;
 using Hotline.YbEnterprise.Sdk;
 using Mapster;
 using MapsterMapper;
@@ -67,20 +69,12 @@ using MiniExcelLibs;
 using SqlSugar;
 using System.Text;
 using System.Text.Json;
-using FluentValidation;
-using Hotline.Validators.FlowEngine;
 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 OrderDto = Hotline.Share.Dtos.Order.OrderDto;
-using System.Threading;
-using Hotline.Realtimes;
-using Hotline.Article;
-using Hotline.Share.Dtos.Article;
-using Hotline.Api.Realtimes;
 
 namespace Hotline.Api.Controllers;
 
@@ -162,8 +156,6 @@ public class OrderController : BaseController
     private readonly IRepository<SystemDicData> _sysDicDataRepository;
     private readonly IRepository<SystemOrganize> _systemOrganizeRepository;
     private readonly IRepository<OrderComplement> _orderComplementRepository;
-    private readonly IRealtimeService _realtimeService;
-    private readonly IRepository<NotificationWaitSend> _notificationWaitSendRepository;
 
     public OrderController(
         IOrderDomainService orderDomainService,
@@ -237,9 +229,7 @@ public class OrderController : BaseController
         IOrderSnapshotApplication orderSnapshotApplication,
         IRepository<SystemDicData> sysDicDataRepository,
         IRepository<SystemOrganize> systemOrganizeRepository,
-        IRepository<OrderComplement> orderComplementRepository,
-        IRealtimeService realtimeService,
-        IRepository<NotificationWaitSend> notificationWaitSendRepository)
+        IRepository<OrderComplement> orderComplementRepository)
     {
         _orderDomainService = orderDomainService;
         _orderRepository = orderRepository;
@@ -313,8 +303,6 @@ public class OrderController : BaseController
         _sysDicDataRepository = sysDicDataRepository;
         _systemOrganizeRepository = systemOrganizeRepository;
         _orderComplementRepository = orderComplementRepository;
-        _realtimeService = realtimeService;
-        _notificationWaitSendRepository = notificationWaitSendRepository;
     }
 
     #endregion
@@ -3177,8 +3165,8 @@ public class OrderController : BaseController
             .WhereIF(!string.IsNullOrEmpty(dto.ActualHandleOrgName), d => d.Order.ActualHandleOrgName.Contains(dto.ActualHandleOrgName)) //接办部门(综合查询模糊)
             .WhereIF(!string.IsNullOrEmpty(dto.CurrentStepCode), d => d.Order.CurrentStepCode == dto.CurrentStepCode) //当前办理节点
                .WhereIF(_sessionContext.OrgIsCenter == false, d =>
-               (SqlFunc.IsNullOrEmpty(d.RoleId) && d.OrderId.StartsWith(_sessionContext.RequiredOrgId)) ||
-               (SqlFunc.IsNullOrEmpty(d.RoleId) == false && d.OrderId.StartsWith(_sessionContext.RequiredOrgId)) && _sessionContext.Roles.Contains(d.RoleId)
+               (SqlFunc.IsNullOrEmpty(d.RoleId)==true&& d.OrgId.StartsWith(_sessionContext.RequiredOrgId)) ||
+               (SqlFunc.IsNullOrEmpty(d.RoleId) == false && d.OrgId.StartsWith(_sessionContext.RequiredOrgId) && _sessionContext.Roles.Contains(d.RoleId))
                )
 
             //   .WhereIF(_sessionContext.RequiredOrgId!="001", x => x.OrgId == _sessionContext.OrgId)
@@ -3994,12 +3982,15 @@ public class OrderController : BaseController
         //泸州任务 311 投诉件需限制受理内容的字数等需求
         if (_appOptions.Value.IsLuZhou && dto.AcceptTypeCode == "35")
         {
-            if (dto.Content.Length < 25)
-                throw UserFriendlyException.SameMessage("保存失败,投诉类型受理内容字数需要至少25个字!");
+            if (dto.SourceChannelCode != "S12345" && dto.SourceChannelCode != "SZMHD" && dto.SourceChannelCode != "XCX" && dto.SourceChannelCode != "JCeT")
+            {
+                if (dto.Content.Length < 25)
+                    throw UserFriendlyException.SameMessage("保存失败,投诉类型受理内容字数需要至少25个字!");
 
-            if (dto.Title.Contains("咨询") || dto.Title.Contains("建议") || dto.Title.Contains("求助") || dto.Title.Contains("举报")
-                || dto.Content.Contains("咨询") || dto.Content.Contains("建议") || dto.Content.Contains("求助") || dto.Content.Contains("举报"))
-                throw UserFriendlyException.SameMessage("保存失败!投诉类型标题和受理内容不能出现'咨询、建议、求助、举报'");
+                if (dto.Title.Contains("咨询") || dto.Title.Contains("建议") || dto.Title.Contains("求助") || dto.Title.Contains("举报")
+                    || dto.Content.Contains("咨询") || dto.Content.Contains("建议") || dto.Content.Contains("求助") || dto.Content.Contains("举报"))
+                    throw UserFriendlyException.SameMessage("保存失败!投诉类型标题和受理内容不能出现'咨询、建议、求助、举报'");
+            }
         }
 
         //todo dto validation
@@ -4224,12 +4215,15 @@ public class OrderController : BaseController
         //泸州任务 311 投诉件需限制受理内容的字数等需求
         if (_appOptions.Value.IsLuZhou && dto.AcceptTypeCode == "35")
         {
-            if (dto.Content.Length < 25)
-                throw UserFriendlyException.SameMessage("保存失败,投诉类型受理内容字数需要至少25个字!");
+            if (dto.SourceChannelCode != "S12345" && dto.SourceChannelCode != "SZMHD" && dto.SourceChannelCode != "XCX" && dto.SourceChannelCode != "JCeT")
+            {
+                if (dto.Content.Length < 25)
+                    throw UserFriendlyException.SameMessage("保存失败,投诉类型受理内容字数需要至少25个字!");
 
-            if (dto.Title.Contains("咨询") || dto.Title.Contains("建议") || dto.Title.Contains("求助") || dto.Title.Contains("举报")
-                || dto.Content.Contains("咨询") || dto.Content.Contains("建议") || dto.Content.Contains("求助") || dto.Content.Contains("举报"))
-                throw UserFriendlyException.SameMessage("保存失败!投诉类型标题和受理内容不能出现'咨询、建议、求助、举报'");
+                if (dto.Title.Contains("咨询") || dto.Title.Contains("建议") || dto.Title.Contains("求助") || dto.Title.Contains("举报")
+                    || dto.Content.Contains("咨询") || dto.Content.Contains("建议") || dto.Content.Contains("求助") || dto.Content.Contains("举报"))
+                    throw UserFriendlyException.SameMessage("保存失败!投诉类型标题和受理内容不能出现'咨询、建议、求助、举报'");
+            }
         }
 
         //交通工单校验
@@ -4513,7 +4507,7 @@ public class OrderController : BaseController
           cancellationToken: HttpContext.RequestAborted);
 
         #region 中心归档,自动延期一天
-        if (dto.Workflow.StepType == EStepType.End && dto.Workflow.NextStepCode == "end" && _appOptions.Value.IsYiBin && _sessionContext.OrgIsCenter)
+        if (dto.Workflow.NextStepCode == "end" && _appOptions.Value.IsYiBin && _sessionContext.OrgIsCenter)
         {
             var dOrder = await _orderRepository.GetAsync(workflow.ExternalId, HttpContext.RequestAborted);
             if (dOrder != null && dOrder.ExpiredTime < DateTime.Now)
@@ -5189,7 +5183,7 @@ public class OrderController : BaseController
         var traces = await _workflowTraceRepository.Queryable()
             .Includes(d => d.OrderPublish)
             .Includes(d => d.OrderVisit, x => x.OrderVisitDetails)
-            .Where(d => d.WorkflowId == workflowId)
+            .Where(d => d.WorkflowId == workflowId && (d.HandleMode != EHandleMode.PreviousNoDisplay || d.HandleMode == null))
             .OrderBy(d => d.CreationTime)
             .ToTreeAsync(d => d.Traces, d => d.ParentId, null);
 
@@ -5506,54 +5500,25 @@ public class OrderController : BaseController
         var oneSendBack = bool.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.OneOrgSendBack)?.SettingValue[0]);
         var twoSendBack = bool.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.TwoOrgSendBack)?.SettingValue[0]);
         //var workflow = await _workflowDomainService.GetWorkflowAsync(dto.WorkflowId, withSteps: true, cancellationToken: HttpContext.RequestAborted);
-        var order = await _orderRepository
-            .Queryable()
-            .Includes(d => d.Workflow)
-            .FirstAsync(d => d.Id == dto.OrderId);
+        var order = await _orderRepository.Queryable().Includes(d => d.Workflow).FirstAsync(d => d.Id == dto.OrderId);
         dto.ExpiredTime = order.ExpiredTime;
-        var (currentStep, prevStep, isOrgToCenter, isSecondToFirstOrgLevel) = await _workflowApplication.GetPreviousInformationAsync(
-            dto.WorkflowId, _sessionContext.RequiredUserId, _sessionContext.RequiredOrgId, _sessionContext.Roles, HttpContext.RequestAborted);
         var audit = new OrderSendBackAudit
         {
             OrderId = dto.OrderId,
             State = ESendBackAuditState.Apply,
             Content = dto.Opinion,
             SendBackData = dto,
-            ApplyOrgId = currentStep.AcceptorOrgId,
-            ApplyOrgName = currentStep!.AcceptorOrgName,
-            SendBackOrgId = prevStep.HandlerOrgId, //prevStep.AcceptorOrgId,
-            SendBackOrgName = prevStep.HandlerOrgName, //prevStep!.AcceptorOrgName,
-            SendBackStepName = prevStep.Name,
-            WorkflowStepSendBackCrTime = currentStep.CreationTime,
             WorkflowOrgId = _sessionContext.RequiredOrgId,
             WorkflowUserId = _sessionContext.RequiredUserId,
             WorkflowRoleIds = _sessionContext.Roles.ToList(),
             Status = order.Status,
-            TraceId = currentStep.Id,
-            OrderExpiredTime = order.ExpiredTime
+            OrderExpiredTime = order.ExpiredTime,
+            AssignStepId = dto.AssignStepId,
+            IsAssign = !string.IsNullOrEmpty(dto.AssignStepId),
         };
         audit.InitId();
         if (dto.Files.Any())
             audit.FileJson = await _fileRepository.AddFileAsync(dto.Files, audit.Id, "", HttpContext.RequestAborted);
-        // if (_appOptions.Value.IsZiGong && prevStep.BusinessType == EBusinessType.Send)
-        // {
-        //     // 平均派单
-        //     var averageSendOrder = bool.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.AverageSendOrder).SettingValue[0]);
-        //     if (averageSendOrder)
-        //     {
-        //         var handler = await _orderDomainService.AverageOrder(HttpContext.RequestAborted);
-        //         dto.Handler = handler;
-        //     }
-        // }
-
-        //todo 需整理各个地市退回业务的需求(指派类型及指派对象)
-        //if (workflow.FlowType == EFlowType.Handle) //该逻辑需放在退回操作前依据业务判断
-        //{
-        //    prevStep.FlowAssignType = prevStep.BusinessType is EBusinessType.Seat ? EFlowAssignType.Role :
-        //        prevStep.BusinessType is EBusinessType.Send ? EFlowAssignType.User : EFlowAssignType.Org;
-        //}
-
-
         if (oneSendBack || twoSendBack)
         {
             var sendBack = await _orderSendBackAuditRepository.Queryable()
@@ -5565,13 +5530,16 @@ public class OrderController : BaseController
                 .AnyAsync();
             if (specialAny) throw UserFriendlyException.SameMessage("工单已存在待审批特提信息!");
             if (order.Workflow.IsInCountersign) throw UserFriendlyException.SameMessage("工单会签中,无法进行退回!");
+
+            var (currentStep, prevStep, steps, isOrgToCenter, isSecondToFirstOrgLevel) = await _workflowApplication.GetPreviousInformationAsync(
+               dto.WorkflowId, _sessionContext.RequiredUserId, _sessionContext.RequiredOrgId, _sessionContext.Roles, HttpContext.RequestAborted);
+
             if (oneSendBack && isOrgToCenter && _appOptions.Value.IsZiGong)
             {
                 if (order.SendBackAuditEndTime.HasValue && order.SendBackAuditEndTime.Value < DateTime.Now)
-                    throw UserFriendlyException.SameMessage("工单截至退回时间【" + order.SendBackAuditEndTime.Value.ToString("yyyy-MM-dd HH:mm:ss") +
-                                                            "】,无法进行退回!");
-                var sendBackAgain = await _orderSendBackAuditRepository.Queryable()
-                    .Where(x => x.OrderId == dto.OrderId && x.IsReturnAgain == false).AnyAsync();
+                    throw UserFriendlyException.SameMessage("工单截至退回时间【" + order.SendBackAuditEndTime.Value.ToString("yyyy-MM-dd HH:mm:ss") + "】,无法进行退回!");
+
+                var sendBackAgain = await _orderSendBackAuditRepository.Queryable().Where(x => x.OrderId == dto.OrderId && x.IsReturnAgain == false).AnyAsync();
                 if (sendBackAgain)
                     throw UserFriendlyException.SameMessage("工单已不允许退回!");
             }
@@ -5580,52 +5548,29 @@ public class OrderController : BaseController
             {
                 await _orderRepository.Updateable().SetColumns(o => new Orders.Order() { Status = EOrderStatus.SendBackAudit })
                     .Where(o => o.Id == order.Id).ExecuteCommandAsync(HttpContext.RequestAborted);
+                audit.ApplyOrgId = currentStep.AcceptorOrgId;
+                audit.ApplyOrgName = currentStep!.AcceptorOrgName;
+                ///判断是否指定
+
+                var assignStep = new WorkflowStep();
+                if (audit.IsAssign.Value)
+                {
+                    assignStep = steps.FirstOrDefault(x => x.Id == dto.AssignStepId);
+                }
+                audit.SendBackOrgId = audit.IsAssign.Value ? assignStep.HandlerOrgId : prevStep.HandlerOrgId;
+                audit.SendBackOrgName = audit.IsAssign.Value ? assignStep.HandlerOrgName : prevStep.HandlerOrgName;
+                audit.SendBackStepName = audit.IsAssign.Value ? assignStep.Name : prevStep.Name;
+
+                audit.WorkflowStepSendBackCrTime = currentStep.CreationTime;
+                audit.TraceId = currentStep.Id;
+                await _orderSendBackAuditRepository.AddAsync(audit, HttpContext.RequestAborted);
             }
             else
             {
                 audit.State = ESendBackAuditState.End;
                 audit.AuditUser = "默认通过";
                 audit.AuditTime = DateTime.Now;
-                // if (prevStep.BusinessType == EBusinessType.Send && dto.Handler != null)
-                // {
-                //     await _orderRepository.Updateable().SetColumns(o => new Orders.Order()
-                //             { CenterToOrgHandlerId = dto.Handler.UserId, CenterToOrgHandlerName = dto.Handler.Username })
-                //         .Where(o => o.Id == order.Id).ExecuteCommandAsync(HttpContext.RequestAborted);
-                // }
-
-                var (workflow, currentStep1, prevDefine, prevStep1, newStep, flowDirection) =
-                    await _workflowApplication.PreviousAsync(dto,
-                        async (workflow1, currentStep1, prevStepDefine, prevStep1, newStep) =>
-                        {
-                            var stepAssignInfo =
-                                await _orderApplication.GetOrderPreviousAssignInfoAsync(workflow1, prevStepDefine, prevStep1,
-                                    HttpContext.RequestAborted);
-                            if (stepAssignInfo is null) return;
-                            var validator = new StepAssignInfoValidator();
-                            await validator.ValidateAndThrowAsync(stepAssignInfo);
-                            newStep.Assign(stepAssignInfo);
-                        },
-                        HttpContext.RequestAborted);
-
-                //记录退回后最新的流程节点
-                if (newStep != null && !string.IsNullOrEmpty(newStep.Id))
-                    audit.NewCurrentStepId = newStep.Id;
-
-                if (prevStep.BusinessType == EBusinessType.Send)
-                {
-                    await _orderRepository.Updateable().SetColumns(o => new Order()
-                    { CenterToOrgHandlerId = newStep.HandlerId, CenterToOrgHandlerName = newStep.HandlerName })
-                        .Where(o => o.Id == order.Id).ExecuteCommandAsync(HttpContext.RequestAborted);
-                }
-
-                var processType = flowDirection == EFlowDirection.OrgToCenter || flowDirection == EFlowDirection.CenterToCenter
-                    ? EProcessType.Zhiban
-                    : EProcessType.Jiaoban;
-                if (currentStep.AcceptorOrgId != OrgSeedData.CenterId && prevStep.BusinessType == EBusinessType.Send)
-                    order.SendBackNum = order.SendBackNum.HasValue ? order.SendBackNum.Value + 1 : 1;
-                await _orderRepository.Updateable().SetColumns(o => new Orders.Order() { ProcessType = processType, SendBackNum = order.SendBackNum })
-                    .Where(o => o.Id == order.Id).ExecuteCommandAsync(HttpContext.RequestAborted);
-                //发送短信TODO
+                await _orderApplication.OrderPrevious(audit, order, HttpContext.RequestAborted);
             }
         }
         else
@@ -5633,49 +5578,9 @@ public class OrderController : BaseController
             audit.State = ESendBackAuditState.End;
             audit.AuditUser = "默认通过";
             audit.AuditTime = DateTime.Now;
-            // if (prevStep.BusinessType == EBusinessType.Send && dto.Handler != null)
-            // {
-            //     await _orderRepository.Updateable().SetColumns(o => new Orders.Order()
-            //             { CenterToOrgHandlerId = dto.Handler.UserId, CenterToOrgHandlerName = dto.Handler.Username })
-            //         .Where(o => o.Id == order.Id).ExecuteCommandAsync(HttpContext.RequestAborted);
-            // }
-
-            var (workflow, currentStep1, prevDefine, prevStep1, newStep, flowDirection) =
-                await _workflowApplication.PreviousAsync(dto,
-                    async (workflow1, currentStep1, prevStepDefine, prevStep1, newStep) =>
-                    {
-                        var stepAssignInfo =
-                            await _orderApplication.GetOrderPreviousAssignInfoAsync(workflow1, prevStepDefine, prevStep1, HttpContext.RequestAborted);
-                        if (stepAssignInfo is null) return;
-                        var validator = new StepAssignInfoValidator();
-                        await validator.ValidateAndThrowAsync(stepAssignInfo);
-                        newStep.Assign(stepAssignInfo);
-                    },
-                    HttpContext.RequestAborted);
-
-            //记录退回后最新的流程节点
-            if (newStep != null && !string.IsNullOrEmpty(newStep.Id))
-                audit.NewCurrentStepId = newStep.Id;
-
-            if (prevStep.BusinessType == EBusinessType.Send)
-            {
-                await _orderRepository.Updateable().SetColumns(o => new Order()
-                { CenterToOrgHandlerId = newStep.HandlerId, CenterToOrgHandlerName = newStep.HandlerName })
-                    .Where(o => o.Id == order.Id).ExecuteCommandAsync(HttpContext.RequestAborted);
-            }
-
-            var processType = flowDirection == EFlowDirection.OrgToCenter || flowDirection == EFlowDirection.CenterToCenter
-                ? EProcessType.Zhiban
-                : EProcessType.Jiaoban;
-            if (currentStep.AcceptorOrgId != OrgSeedData.CenterId && prevStep.BusinessType == EBusinessType.Send)
-                order.SendBackNum = order.SendBackNum.HasValue ? order.SendBackNum.Value + 1 : 1;
-            await _orderRepository.Updateable().SetColumns(o => new Orders.Order() { ProcessType = processType, SendBackNum = order.SendBackNum })
-                .Where(o => o.Id == order.Id).ExecuteCommandAsync(HttpContext.RequestAborted);
+            await _orderApplication.OrderPrevious(audit, order, HttpContext.RequestAborted);
 
-            //发送短信TODO
         }
-
-        await _orderSendBackAuditRepository.AddAsync(audit, HttpContext.RequestAborted);
     }
 
     /// <summary>
@@ -5706,7 +5611,7 @@ public class OrderController : BaseController
             var order = await _orderRepository.Queryable().Includes(d => d.Workflow).FirstAsync(d => d.Id == sendBack.OrderId);
             if (_appOptions.Value.IsZiGong)
             {
-                var (currentStep, prevStep, isOrgToCenter, isSecondToFirstOrgLevel) = await _workflowApplication.GetPreviousInformationAsync(
+                var (currentStep, prevStep, steps, isOrgToCenter, isSecondToFirstOrgLevel) = await _workflowApplication.GetPreviousInformationAsync(
                     order.WorkflowId, sendBack.WorkflowUserId, sendBack.WorkflowOrgId, sendBack.WorkflowRoleIds.ToArray(),
                     HttpContext.RequestAborted);
 
@@ -5719,60 +5624,17 @@ public class OrderController : BaseController
                         var handler = await _orderDomainService.AverageOrder(HttpContext.RequestAborted);
                         sendBack.SendBackData.Handler = handler;
                     }
-
-                    await _orderRepository.Updateable().SetColumns(o => new Orders.Order()
-                    {
-                        CenterToOrgHandlerId = sendBack.SendBackData.Handler.UserId,
-                        CenterToOrgHandlerName = sendBack.SendBackData.Handler.Username
-                    })
-                        .Where(o => o.Id == sendBack.Id).ExecuteCommandAsync(HttpContext.RequestAborted);
                 }
             }
-
-            //string applicantId, string applicantOrgId, string[] applicantRoleIds,
-            //    ISessionContext current, CancellationToken cancellationToken);
             sendBack.SendBackData.ExpiredTime = order.ExpiredTime;
-            //var result = await _workflowApplication.PreviousAsync(sendBack.SendBackData, sendBack.WorkflowUserId,
-            //    sendBack.WorkflowOrgId, sendBack.WorkflowRoleIds.ToArray(),
-            //    cancellationToken: HttpContext.RequestAborted);
-
-            var (workflow, currentStep1, prevDefine, prevStep1, newStep, flowDirection) =
-                await _workflowApplication.PreviousAsync(sendBack.SendBackData,
-                    sendBack.WorkflowUserId, sendBack.WorkflowOrgId, sendBack.WorkflowRoleIds.ToArray(),
-                    async (workflow1, currentStep1, prevStepDefine, prevStep1, newStep) =>
-                    {
-                        var stepAssignInfo =
-                            await _orderApplication.GetOrderPreviousAssignInfoAsync(workflow1, prevStepDefine,
-                                prevStep1, HttpContext.RequestAborted);
-                        if (stepAssignInfo is null) return;
-                        var validator = new StepAssignInfoValidator();
-                        await validator.ValidateAndThrowAsync(stepAssignInfo);
-                        newStep.Assign(stepAssignInfo);
-                    },
-                    HttpContext.RequestAborted);
-
-            //var flowDirection = await _workflowApplication.PreviousAsync(sendBack.SendBackData, sendBack.WorkflowUserId, HttpContext.RequestAborted);
-            var processType = flowDirection == EFlowDirection.OrgToCenter ||
-                              flowDirection == EFlowDirection.CenterToCenter
-                ? EProcessType.Zhiban
-                : EProcessType.Jiaoban;
-            if (sendBack.ApplyOrgId != OrgSeedData.CenterId && sendBack.SendBackOrgId == OrgSeedData.CenterId)
-                order.SendBackNum = order.SendBackNum.HasValue ? order.SendBackNum.Value + 1 : 1;
-            await _orderRepository.Updateable().SetColumns(o => new Orders.Order() { ProcessType = processType, SendBackNum = order.SendBackNum })
-                .Where(o => o.Id == sendBack.OrderId).ExecuteCommandAsync(HttpContext.RequestAborted);
-
-            //记录退回后最新的流程节点
-            if (newStep != null && !string.IsNullOrEmpty(newStep.Id))
-                sendBack.NewCurrentStepId = newStep.Id;
-            //发送短信TODO
+            await _orderApplication.OrderPrevious(sendBack, order, HttpContext.RequestAborted);
         }
         else
         {
             await _orderRepository.Updateable().SetColumns(o => new Orders.Order() { Status = sendBack.Status.Value })
                 .Where(o => o.Id == sendBack.OrderId).ExecuteCommandAsync(HttpContext.RequestAborted);
+            await _orderSendBackAuditRepository.UpdateAsync(sendBack, HttpContext.RequestAborted);
         }
-
-        await _orderSendBackAuditRepository.UpdateAsync(sendBack, HttpContext.RequestAborted);
     }
 
     /// <summary>
@@ -5804,7 +5666,7 @@ public class OrderController : BaseController
                 var order = await _orderRepository.Queryable().Includes(d => d.Workflow).FirstAsync(d => d.Id == sendBack.OrderId);
                 if (_appOptions.Value.IsZiGong)
                 {
-                    var (currentStep, prevStep, isOrgToCenter, isSecondToFirstOrgLevel) = await _workflowApplication.GetPreviousInformationAsync(
+                    var (currentStep, prevStep, steps, isOrgToCenter, isSecondToFirstOrgLevel) = await _workflowApplication.GetPreviousInformationAsync(
                         order.WorkflowId, sendBack.WorkflowUserId, sendBack.WorkflowOrgId, sendBack.WorkflowRoleIds.ToArray(),
                         HttpContext.RequestAborted);
                     if (prevStep.BusinessType == EBusinessType.Send)
@@ -5816,57 +5678,20 @@ public class OrderController : BaseController
                             var handler = await _orderDomainService.AverageOrder(HttpContext.RequestAborted);
                             sendBack.SendBackData.Handler = handler;
                         }
-
-                        await _orderRepository.Updateable().SetColumns(o => new Orders.Order()
-                        {
-                            CenterToOrgHandlerId = sendBack.SendBackData.Handler.UserId,
-                            CenterToOrgHandlerName = sendBack.SendBackData.Handler.Username
-                        })
-                            .Where(o => o.Id == sendBack.Id).ExecuteCommandAsync(HttpContext.RequestAborted);
                     }
                 }
 
                 sendBack.SendBackData.ExpiredTime = order.ExpiredTime;
-                //var result = await _workflowApplication.PreviousAsync(sendBack.SendBackData,
-                //    sendBack.WorkflowUserId, sendBack.WorkflowOrgId, sendBack.WorkflowRoleIds.ToArray(),
-                //    cancellationToken: HttpContext.RequestAborted);
-                //var flowDirection = await _workflowApplication.PreviousAsync(sendBack.SendBackData, sendBack.WorkflowUserId, HttpContext.RequestAborted);
-
-                var (workflow, currentStep1, prevDefine, prevStep1, newStep, flowDirection) =
-                    await _workflowApplication.PreviousAsync(sendBack.SendBackData,
-                        sendBack.WorkflowUserId, sendBack.WorkflowOrgId, sendBack.WorkflowRoleIds.ToArray(),
-                        async (workflow1, currentStep1, prevStepDefine, prevStep1, newStep) =>
-                        {
-                            var stepAssignInfo =
-                                await _orderApplication.GetOrderPreviousAssignInfoAsync(workflow1, prevStepDefine,
-                                    prevStep1, HttpContext.RequestAborted);
-                            if (stepAssignInfo is null) return;
-                            var validator = new StepAssignInfoValidator();
-                            await validator.ValidateAndThrowAsync(stepAssignInfo);
-                            newStep.Assign(stepAssignInfo);
-                        },
-                        HttpContext.RequestAborted);
-
-                var processType = flowDirection == EFlowDirection.OrgToCenter || flowDirection == EFlowDirection.CenterToCenter
-                    ? EProcessType.Zhiban
-                    : EProcessType.Jiaoban;
-                if (sendBack.ApplyOrgId != OrgSeedData.CenterId && sendBack.SendBackOrgId == OrgSeedData.CenterId)
-                    order.SendBackNum = order.SendBackNum.HasValue ? order.SendBackNum.Value + 1 : 1;
-                await _orderRepository.Updateable().SetColumns(o => new Orders.Order() { ProcessType = processType, SendBackNum = order.SendBackNum })
-                    .Where(o => o.Id == sendBack.OrderId).ExecuteCommandAsync(HttpContext.RequestAborted);
-
-                //记录退回后最新的流程节点
-                if (newStep != null && !string.IsNullOrEmpty(newStep.Id))
-                    sendBack.NewCurrentStepId = newStep.Id;
-                //发送短信TODO
+                await _orderApplication.OrderPrevious(sendBack, order, HttpContext.RequestAborted);
             }
             else
             {
                 await _orderRepository.Updateable().SetColumns(o => new Orders.Order() { Status = sendBack.Status.Value })
                     .Where(o => o.Id == sendBack.OrderId).ExecuteCommandAsync(HttpContext.RequestAborted);
+                await _orderSendBackAuditRepository.UpdateAsync(sendBack, HttpContext.RequestAborted);
             }
 
-            await _orderSendBackAuditRepository.UpdateAsync(sendBack, HttpContext.RequestAborted);
+
         }
     }
 
@@ -5965,8 +5790,25 @@ public class OrderController : BaseController
         if (string.IsNullOrEmpty(order.WorkflowId))
             throw UserFriendlyException.SameMessage("该工单未开启流程");
 
-        var (currentStep, prevStep, isOrgToCenter, isSecondToFirstOrgLevel) = await _workflowApplication.GetPreviousInformationAsync(
+        var (currentStep, prevStep, steps, isOrgToCenter, isSecondToFirstOrgLevel) = await _workflowApplication.GetPreviousInformationAsync(
             order.WorkflowId, _sessionContext.RequiredUserId, _sessionContext.RequiredOrgId, _sessionContext.Roles, HttpContext.RequestAborted);
+        ///查询上一节点是否是领导节点
+        ///ture 流程排除领导节点 排除当前节点 prevStep.BusinessType == EBusinessType.DepartmentLeader
+        if (_appOptions.Value.IsYiBin && prevStep.Name.Contains("领导"))
+        {
+            var step = steps.Where(x => !x.Name.Contains("领导") && x.Code != currentStep.Code).OrderByDescending(x => x.CreationTime).FirstOrDefault();
+            var resStep = new List<WorkflowStep>
+            {
+                step,
+                prevStep
+            };
+            return new TargetStepInfo
+            {
+                CurrentBusinessType = currentStep.BusinessType,
+                TargetBusinessType = prevStep.BusinessType,
+                Steps = _mapper.Map<List<TargetStep>>(resStep)
+            };
+        }
 
         return new TargetStepInfo
         {
@@ -8991,6 +8833,7 @@ public class OrderController : BaseController
         var id = await _orderComplementRepository.AddAsync(complement, HttpContext.RequestAborted);
         if (!string.IsNullOrEmpty(id))
         {
+            #region 处理推送消息
             //获取当前办理节点数据
             var work = await _workflowStepRepository.GetAsync(p => p.Id == data.ActualHandleStepId, HttpContext.RequestAborted);
             if (work != null)
@@ -8999,60 +8842,61 @@ public class OrderController : BaseController
                 var workflowStepHandler = work.GetWorkflowStepHandler();
                 if (workflowStepHandler != null)
                 {
-                    List<string> users = [];
+
+                    AddCircularDto circularDto = new AddCircularDto()
+                    {
+                        Title = "工单补充",
+                        Content = "工单" + data.No + "有补充内容,请注意查收!",
+                        CircularTypeId = "6",
+                        CircularTypeName = "工单补充",
+                        IsMustRead = true,
+                        SourceOrgId = _sessionContext.RequiredOrgId,
+                        SourceOrgName = _sessionContext.OrgName,
+                        CircularType = ECircularType.Person
+                    };
+
+                    List<CircularReadGroupDto> users = [];
                     if (!string.IsNullOrEmpty(workflowStepHandler.UserId)) //指定用户
                     {
-                        users.Add(workflowStepHandler.UserId);
+                        users.Add(new CircularReadGroupDto()
+                        {
+                            UserId = workflowStepHandler.UserId,
+                            UserName = workflowStepHandler.Username
+                        });
                     }
                     else if (!string.IsNullOrEmpty(workflowStepHandler.RoleId))//指定角色
                     {
                         //查询指定角色下面所有的用户
                         var userlist = await _userRepository.Queryable().Where(x =>
-                            x.OrgId == workflowStepHandler.OrgId && x.Roles.Any(d => workflowStepHandler.RoleId.Contains(d.Id))).Select(p => p.Id).ToListAsync();
-                        if (userlist != null && userlist.Count > 0)
-                            users.AddRange(userlist);
+                            x.OrgId == workflowStepHandler.OrgId && x.Roles.Any(d => workflowStepHandler.RoleId.Contains(d.Id)))
+                            .Select(d => new CircularReadGroupDto
+                            {
+                                UserId = d.Id,
+                                UserName = d.Name
+                            }).ToListAsync();
+                        users.AddRange(userlist);
                     }
                     else if (!string.IsNullOrEmpty(workflowStepHandler.OrgId))//指定部门
                     {
-                        //添加成功以后查询需要发送的人员信息
-                        var acceptSmsRoleIds = _systemSettingCacheManager.GetSetting(SettingConstants.AcceptSmsRoleIds)?.SettingValue;
-
-                        //查询指定部门下面经办人的信息
-                        var userlist = await _userRepository.Queryable().Where(x =>
-                            x.OrgId == workflowStepHandler.OrgId && x.Roles.Any(d => acceptSmsRoleIds.Contains(d.Id))).Select(p => p.Id).ToListAsync();
-                        if (userlist != null && userlist.Count > 0)
-                            users.AddRange(userlist);
+                        users.Add(new CircularReadGroupDto()
+                        {
+                            OrgId = workflowStepHandler.OrgId,
+                            OrgName = workflowStepHandler.OrgName
+                        });
+                        circularDto.CircularType = ECircularType.Org;
                     }
+
                     if (users != null && users.Count > 0)
                     {
-                        var msg = @"工单" + data.No + "有补充内容,请注意查收!";
-                        foreach (var item in users)
-                        {
-                            //发送消息
-                            await _realtimeService.OrderComplementAsync(item, msg, HttpContext.RequestAborted);
-                        }
+                        circularDto.CircularReadGroups = users;
+                        //调用推送消息通用接口
                     }
                 }
             }
-        }
-    }
 
-    /// <summary>
-    /// 根据userid查询补充消息
-    /// </summary>
-    /// <param name="userId"></param>
-    /// <returns></returns>
-    [HttpGet("get_notification_wait_send/{userId}")]
-    public async Task<List<NotificationWaitSendDto>> GetNotificationWaitSend(string userId)
-    {
-        var data = await _notificationWaitSendRepository.Queryable()
-                .Where(p => p.UserId == userId && p.Method == RealtimeMethods.OrderComplementRecord&&p.State=="0")
-                .ToListAsync();
-        if (data != null && data.Count > 0)
-        {
-            await _notificationWaitSendRepository.RemoveRangeAsync(data, HttpContext.RequestAborted);
+            #endregion
         }
-        return _mapper.Map<List<NotificationWaitSendDto>>(data);
     }
+
     #endregion
 }

+ 13 - 38
src/Hotline.Api/Controllers/TestController.cs

@@ -1,7 +1,5 @@
-using DocumentFormat.OpenXml.Drawing.Charts;
-using DocumentFormat.OpenXml.Office2016.Drawing.ChartDrawing;
-using DotNetCore.CAP;
-using Hotline.Ai.Visit;
+using DotNetCore.CAP;
+using Hotline.Ai.Quality;
 using Hotline.Application.CallCenter;
 using Hotline.Application.ExportExcel;
 using Hotline.Application.FlowEngine;
@@ -9,7 +7,6 @@ using Hotline.Application.JudicialManagement;
 using Hotline.Application.Orders;
 using Hotline.Application.Quality;
 using Hotline.Application.StatisticalReport;
-using Hotline.Authentications;
 using Hotline.Caching.Interfaces;
 using Hotline.CallCenter.BlackLists;
 using Hotline.CallCenter.Calls;
@@ -18,9 +15,6 @@ using Hotline.CallCenter.Ivrs;
 using Hotline.CallCenter.Tels;
 using Hotline.Configurations;
 using Hotline.ContingencyManagement;
-using Hotline.FlowEngine;
-using Hotline.FlowEngine.Definitions;
-using Hotline.FlowEngine.Notifications;
 using Hotline.FlowEngine.WorkflowModules;
 using Hotline.FlowEngine.Workflows;
 using Hotline.Import;
@@ -28,62 +22,43 @@ using Hotline.JudicialManagement;
 using Hotline.Orders;
 using Hotline.Realtimes;
 using Hotline.Repository.SqlSugar;
-using Hotline.Repository.SqlSugar.CallCenter;
 using Hotline.Repository.SqlSugar.Ts;
 using Hotline.Settings;
 using Hotline.Settings.Hotspots;
 using Hotline.Settings.TimeLimitDomain;
 using Hotline.Settings.TimeLimits;
 using Hotline.Share.Dtos;
-using Hotline.Share.Dtos.FlowEngine;
 using Hotline.Share.Dtos.FlowEngine.Workflow;
+using Hotline.Share.Dtos.Home;
 using Hotline.Share.Dtos.Order;
 using Hotline.Share.Dtos.Realtime;
+using Hotline.Share.Dtos.Settings;
 using Hotline.Share.Dtos.Snapshot;
 using Hotline.Share.Dtos.TrCallCenter;
 using Hotline.Share.Enums.CallCenter;
 using Hotline.Share.Enums.FlowEngine;
 using Hotline.Share.Enums.JudicialManagement;
 using Hotline.Share.Enums.Order;
-using Hotline.Share.Enums.Quality;
 using Hotline.Share.Mq;
 using Hotline.Users;
 using Mapster;
 using MapsterMapper;
 using MediatR;
 using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.Builder.Extensions;
 using Microsoft.AspNetCore.Mvc;
 using Microsoft.Extensions.Options;
 using MiniExcelLibs;
-using Newtonsoft.Json;
-using NPOI.POIFS.Crypt.Dsig;
+using NETCore.Encrypt;
 using SqlSugar;
-using StackExchange.Redis;
-using System.Threading;
 using XC.RSAUtil;
 using XF.Domain.Authentications;
 using XF.Domain.Cache;
-using XF.Domain.Exceptions;
 using XF.Domain.Filters;
 using XF.Domain.Locks;
 using XF.Domain.Queues;
 using XF.Domain.Repository;
-using static System.Runtime.InteropServices.JavaScript.JSType;
 using Order = Hotline.Orders.Order;
-using Hotline.Share.Dtos.Settings;
 using OrderDto = Hotline.Share.Dtos.Order.OrderDto;
-using Hotline.Share.Dtos.Home;
-using Google.Protobuf.WellKnownTypes;
-using Microsoft.AspNetCore.DataProtection;
-using Hotline.Share.Tools;
-using NETCore.Encrypt;
-using Hotline.Ai.Quality;
-using Hotline.EventBus;
-using Hotline.JudicialManagement.Notifies;
-using Hotline.Snapshot.Notifications;
-using XF.Domain.Entities;
-using Hotline.Repository.SqlSugar.Orders;
 
 namespace Hotline.Api.Controllers;
 
@@ -279,15 +254,15 @@ ICallApplication callApplication,
     /// <returns></returns>
     [HttpGet("phonenumber_test")]
     [AllowAnonymous]
-    public async Task GetPhoneNumberTest()
+    public async Task<ThirdPhoneOutDto> GetPhoneNumberTest()
     {
-        await _realtimeService.OrderComplementAsync("08db98a6-29f9-4812-8ccc-c81bc1ab2d12","测试推送消息", HttpContext.RequestAborted);
-        //var inDto = new ThirdTokenDto
-        //{
-        //    AppId = _systemSettingCacheManager.WxOpenAppId,
-        //    Secret = _systemSettingCacheManager.WxOpenAppSecret
-        //};
-        //return await _thirdIdentiyService.GetPhoneNumberAsync(inDto);
+
+        var inDto = new ThirdTokenDto
+        {
+            AppId = _systemSettingCacheManager.WxOpenAppId,
+            Secret = _systemSettingCacheManager.WxOpenAppSecret
+        };
+        return await _thirdIdentiyService.GetPhoneNumberAsync(inDto);
     }
 
     /// <summary>

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

@@ -443,7 +443,7 @@ public class WorkflowController : BaseController
     {
         var workflow = await _workflowDomainService.GetWorkflowAsync(workflowId, withTracesTree: true,
             cancellationToken: HttpContext.RequestAborted);
-        workflow.Traces = workflow.Traces.Where(d => d.TraceStyle == ETraceStyle.Flow).ToList();
+        workflow.Traces = workflow.Traces.Where(d => d.TraceStyle == ETraceStyle.Flow).Where(d => d.HandleMode != EHandleMode.PreviousNoDisplay).ToList();
 
         var workflowDto = _mapper.Map<WorkflowDto>(workflow);
         if (workflowDto.Traces.Any())

+ 2 - 37
src/Hotline.Api/Realtimes/RealtimeService.cs

@@ -1,12 +1,10 @@
-using DocumentFormat.OpenXml.Spreadsheet;
-using Hotline.Article;
+using Hotline.Article;
 using Hotline.Caching.Interfaces;
 using Hotline.Caching.Services;
 using Hotline.Realtimes;
 using Hotline.Share.Dtos.Realtime;
 using Hotline.Users;
 using Microsoft.AspNetCore.SignalR;
-using XF.Domain.Cache;
 using XF.Domain.Dependency;
 using XF.Domain.Exceptions;
 using XF.Domain.Repository;
@@ -19,20 +17,17 @@ public class RealtimeService : IRealtimeService, IScopeDependency
     private readonly IRealtimeCacheManager _realtimeCacheManager;
     private readonly IRepository<CircularRecord> _circularRecordRepository;
     private readonly IRepository<User> _userRepository;
-    private readonly IRepository<NotificationWaitSend> _notificationWaitSendRepository;
 
     public RealtimeService(
         IHubContext<HotlineHub> hubContext,
         IRealtimeCacheManager realtimeCacheManager,
         IRepository<CircularRecord> circularRecordRepository,
-        IRepository<User> userRepository,
-        IRepository<NotificationWaitSend> notificationWaitSendRepository)
+        IRepository<User> userRepository)
     {
         _hubContext = hubContext;
         _realtimeCacheManager = realtimeCacheManager;
         _circularRecordRepository = circularRecordRepository;
         _userRepository = userRepository;
-        _notificationWaitSendRepository = notificationWaitSendRepository;
     }
 
     #region 通讯通知
@@ -126,36 +121,6 @@ public class RealtimeService : IRealtimeService, IScopeDependency
 
     #endregion
 
-    /// <summary>
-    /// 工单补充消息通知 
-    /// </summary>
-    /// <param name="userId"></param>
-    /// <param name="msg"></param>
-    /// <param name="cancellationToken"></param>
-    /// <returns></returns>
-    public async Task OrderComplementAsync(string userId, string msg, CancellationToken cancellationToken)
-    {
-        var connection = await _realtimeCacheManager.GetConnectionOtherAsync(userId, cancellationToken);
-        if (connection == null)
-        {
-            //如果没有建立链接,写入数据库
-            NotificationWaitSend notificationWait = new NotificationWaitSend()
-            {
-                UserId = userId,
-                Msg = msg,
-                Method = RealtimeMethods.OrderComplementRecord,
-                State = "0"
-            };
-            await _notificationWaitSendRepository.AddAsync(notificationWait, cancellationToken);
-        }
-        else
-        {
-            //如果建立了链接直接发消息
-            await _hubContext.Clients.Client(connection.ConnectionId).SendAsync(RealtimeMethods.OrderComplementRecord, msg, cancellationToken);
-        }
-    }
-
-
     #region 系统信息通知
 
     /// <summary>

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

@@ -115,7 +115,7 @@ namespace Hotline.Application.FlowEngine
         /// <summary>
         /// 检查退回节点信息
         /// </summary>
-        Task<(WorkflowStep currentStep, WorkflowStep prevStep, bool isOrgToCenter, bool isSecondToFirstOrgLevel)>
+        Task<(WorkflowStep currentStep, WorkflowStep prevStep,List<WorkflowStep> steps, bool isOrgToCenter, bool isSecondToFirstOrgLevel)>
             GetPreviousInformationAsync(string workflowId, string operatorId, string operatorOrgId, string[] roles,
                 CancellationToken cancellationToken);
 

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

@@ -1867,7 +1867,7 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
     /// <summary>
     /// 检查退回节点信息
     /// </summary>
-    public async Task<(WorkflowStep currentStep, WorkflowStep prevStep, bool isOrgToCenter, bool isSecondToFirstOrgLevel)>
+    public async Task<(WorkflowStep currentStep, WorkflowStep prevStep,List<WorkflowStep> steps, bool isOrgToCenter, bool isSecondToFirstOrgLevel)>
         GetPreviousInformationAsync(string workflowId, string operatorId, string operatorOrgId, string[] roles,
             CancellationToken cancellationToken)
     {
@@ -1881,7 +1881,7 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
                                       prevStep.HandlerType is EHandlerType.OrgLevel &&
                                       prevStep.Handlers.First().Key.CheckIfOrgLevelIs(1) &&
                                       !prevStep.Handlers.First().Key.IsCenter();
-        return (currentStep, prevStep, isOrgToCenter, isSecondToFirstOrgLevel);
+        return (currentStep, prevStep, workflow.Steps, isOrgToCenter, isSecondToFirstOrgLevel);
     }
 
     /// <summary>

+ 5 - 2
src/Hotline.Application/IndustryClassification/IndustryClassificationApplication.cs

@@ -63,8 +63,11 @@ namespace Hotline.Application.IndustryClassification
             {
                 var industrial = _industrialManagementRepository.Queryable()
                       .Includes(d => d.Orgs)
-                      .FirstAsync(d => d.Orgs.Any(p => p.Id == _sessionContext.RequiredOrgId)).GetAwaiter();
-                industrialId = industrial.GetResult().Id;
+                      .FirstAsync(d => d.Orgs.Any(p => p.Id == _sessionContext.RequiredOrgId)).GetAwaiter().GetResult();
+                if (industrial is not null)
+                    industrialId = industrial.Id;
+                else
+                    industrialId = "-1";
             }
 
             var query = _industrialManagementRepository.Queryable()

+ 15 - 2
src/Hotline.Application/Jobs/XingTangCallsSyncJob.cs

@@ -130,7 +130,8 @@ namespace Hotline.Application.Jobs
                     if (calls.Any())
                     {
                         //推省上
-                        await _capPublisher.PublishAsync(EventNames.HotlineCallAdd, calls.Adapt<List<CallNativeDto>>());
+                        //await _capPublisher.PublishAsync(EventNames.HotlineCallAdd, calls.Adapt<List<CallNativeDto>>());
+                        await PublishMessageAsync(calls);
                     }
                     _logger.LogInformation($"旧方法同步通话记录成功,数量:{calls.Count}");
                 }
@@ -176,7 +177,8 @@ namespace Hotline.Application.Jobs
                     if (calls.Any())
                     {
                         //推省上
-                        await _capPublisher.PublishAsync(EventNames.HotlineCallAdd, calls.Adapt<List<CallNativeDto>>());
+                        await PublishMessageAsync(calls);
+                        // await _capPublisher.PublishAsync(EventNames.HotlineCallAdd, calls.Adapt<List<CallNativeDto>>());
                     }
                     _logger.LogInformation($"新方法同步通话记录成功,数量:{calls.Count}");
                 }
@@ -195,6 +197,17 @@ namespace Hotline.Application.Jobs
             }
         }
 
+        private async Task PublishMessageAsync(List<CallNative> calls)
+        {
+            if (_systemSettingCacheManager.IsNoPushCallNativeOutNoFile)
+            {
+                var removeCalls = calls.Where(m => m.Direction == ECallDirection.Out && m.AudioFile.IsNullOrEmpty()).ToList();
+                calls = calls.Except(removeCalls).ToList();
+            }
+            //推省上
+            await _capPublisher.PublishAsync(EventNames.HotlineCallAdd, calls.Adapt<List<CallNativeDto>>());
+        }
+
         private async Task<string> GetCallIdAsync(string callNo, CancellationToken cancellation)
         {
             if (string.IsNullOrEmpty(callNo)) return string.Empty;

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

@@ -454,5 +454,8 @@ namespace Hotline.Application.Orders
         /// </summary>
         /// <returns></returns>
         ISugarQueryable<OrderDelay> WaitDelayList(DelayListDto dto);
-    }
+
+        Task OrderPrevious(OrderSendBackAudit sendBack, Order order, CancellationToken cancellationToken);
+
+	}
 }

+ 79 - 3
src/Hotline.Application/Orders/OrderApplication.cs

@@ -59,6 +59,7 @@ using WordInfo = PanGu.WordInfo;
 using Hotline.Repository.SqlSugar.Orders;
 using Quartz.Simpl;
 using J2N.Text;
+using Hotline.Application.FlowEngine;
 
 namespace Hotline.Application.Orders;
 
@@ -108,8 +109,9 @@ public class OrderApplication : IOrderApplication, IScopeDependency
     private readonly IRepository<OrderTsDetails> _orderTsDetailsRepository;
     private readonly IRepository<KnowledgeQuote> _knowledgeQuoteRepository;
     private readonly IRepository<OrderSpecial> _orderSpecialRepository;
+	private readonly IWorkflowApplication _workflowApplication;
 
-    public OrderApplication(
+	public OrderApplication(
         IOrderDomainService orderDomainService,
         IOrderRepository orderRepository,
         IWorkflowDomainService workflowDomainService,
@@ -152,7 +154,8 @@ public class OrderApplication : IOrderApplication, IScopeDependency
         IOrderDelayRepository orderDelayRepository,
         IRepository<OrderTsDetails> orderTsDetailsRepository,
         IRepository<KnowledgeQuote> knowledgeQuoteRepository,
-        IRepository<OrderSpecial> orderSpecialRepository)
+        IRepository<OrderSpecial> orderSpecialRepository,
+		IWorkflowApplication workflowApplication)
     {
         _orderDomainService = orderDomainService;
         _workflowDomainService = workflowDomainService;
@@ -197,7 +200,9 @@ public class OrderApplication : IOrderApplication, IScopeDependency
         _orderTsDetailsRepository = orderTsDetailsRepository;
         _knowledgeQuoteRepository = knowledgeQuoteRepository;
         _orderSpecialRepository = orderSpecialRepository;
-    }
+        _workflowApplication = workflowApplication;
+
+	}
 
     /// <summary>
     /// 更新工单办理期满时间(延期调用,其他不调用)
@@ -5145,4 +5150,75 @@ public class OrderApplication : IOrderApplication, IScopeDependency
 
         return query;
     }
+
+    #region 退回重写
+
+	public async Task OrderPrevious(OrderSendBackAudit sendBack,Order order, CancellationToken cancellationToken) {
+
+		var (workflow, currentStep, prevDefine, prevStep, newStep, flowDirection) =
+			   await _workflowApplication.PreviousAsync(sendBack.SendBackData,
+				   async (workflow1, currentStep1, prevStepDefine, prevStep1, newStep) =>
+				   {
+					   var stepAssignInfo =
+						   await GetOrderPreviousAssignInfoAsync(workflow1, prevStepDefine, prevStep1, cancellationToken);
+					   if (stepAssignInfo is null) return;
+					   var validator = new StepAssignInfoValidator();
+					   await validator.ValidateAndThrowAsync(stepAssignInfo);
+					   newStep.Assign(stepAssignInfo);
+					   if (sendBack.AssignStepId != prevStep1.Id) prevStep1.HandleMode = EHandleMode.PreviousNoDisplay;
+				   },
+				   cancellationToken);
+
+		if (sendBack.IsAssign.Value)
+        {
+			while (sendBack.AssignStepId != prevStep.Id)
+			{
+                (workflow, currentStep, prevDefine, prevStep, newStep, flowDirection) =
+               await _workflowApplication.PreviousAsync(sendBack.SendBackData,
+                   async (workflow1, currentStep1, prevStepDefine, prevStep1, newStep) =>
+                   {
+                       var stepAssignInfo =
+                           await GetOrderPreviousAssignInfoAsync(workflow1, prevStepDefine, prevStep1, cancellationToken);
+                       if (stepAssignInfo is null) return;
+                       var validator = new StepAssignInfoValidator();
+                       await validator.ValidateAndThrowAsync(stepAssignInfo);
+                       newStep.Assign(stepAssignInfo);
+                       if (sendBack.AssignStepId != prevStep.Id) prevStep1.HandleMode = EHandleMode.PreviousNoDisplay;
+                   },
+                   cancellationToken);
+			}
+		}
+
+        sendBack.ApplyOrgId = currentStep.AcceptorOrgId;
+        sendBack.ApplyOrgName = currentStep!.AcceptorOrgName;
+        sendBack.SendBackOrgId = prevStep.HandlerOrgId;
+        sendBack.SendBackOrgName = prevStep.HandlerOrgName;
+        sendBack.SendBackStepName = prevStep.Name;
+        sendBack.WorkflowStepSendBackCrTime = currentStep.CreationTime;
+        sendBack.TraceId = currentStep.Id;
+		//记录退回后最新的流程节点
+		if (newStep != null && !string.IsNullOrEmpty(newStep.Id))
+			sendBack.NewCurrentStepId = newStep.Id;
+
+		var processType = flowDirection == EFlowDirection.OrgToCenter || flowDirection == EFlowDirection.CenterToCenter
+			? EProcessType.Zhiban
+			: EProcessType.Jiaoban;
+		if (currentStep.AcceptorOrgId != OrgSeedData.CenterId && prevStep.BusinessType == EBusinessType.Send)
+			order.SendBackNum = order.SendBackNum.HasValue ? order.SendBackNum.Value + 1 : 1;
+
+		if (prevStep.BusinessType == EBusinessType.Send)
+        {
+            await _orderRepository.Updateable().SetColumns(o => new Order()
+            { CenterToOrgHandlerId = newStep.HandlerId, CenterToOrgHandlerName = newStep.HandlerName, ProcessType = processType, SendBackNum = order.SendBackNum })
+                .Where(o => o.Id == order.Id).ExecuteCommandAsync(cancellationToken);
+        }
+        else {
+			await _orderRepository.Updateable().SetColumns(o => new Order() { ProcessType = processType, SendBackNum = order.SendBackNum })
+			.Where(o => o.Id == order.Id).ExecuteCommandAsync(cancellationToken);
+		}
+	
+
+		await _orderSendBackAuditRepository.AddAsync(sendBack, cancellationToken);
+	}
+	#endregion
 }

+ 32 - 5
src/Hotline.Application/Orders/OrderSecondaryHandlingApplication.cs

@@ -29,6 +29,8 @@ using Hotline.Settings.TimeLimitDomain;
 using Microsoft.Extensions.Options;
 using DocumentFormat.OpenXml.Office2010.Excel;
 using Hotline.Share.Dtos.FlowEngine.Workflow;
+using Hotline.Validators.FlowEngine;
+using FluentValidation;
 
 namespace Hotline.Application.Orders
 {
@@ -51,6 +53,7 @@ namespace Hotline.Application.Orders
         private readonly IOrderDomainService _orderDomainService;
         private readonly IOrderScreenRepository _orderScreenRepository;
         private readonly IOptionsSnapshot<AppConfiguration> _appOptions;
+		private readonly IOrderApplication _orderApplication;
 
 		public OrderSecondaryHandlingApplication(
             IMapper mapper,
@@ -69,7 +72,8 @@ namespace Hotline.Application.Orders
             IOrderDomainService orderDomainService,
             IOrderScreenRepository orderScreenRepository,
             IOptionsSnapshot<AppConfiguration> appOptions,
-			ICalcExpireTime expireTime)
+			ICalcExpireTime expireTime,
+			IOrderApplication orderApplication)
         {
             _mapper = mapper;
             _orderSecondaryHandlingRepository = orderSecondaryHandlingRepository;
@@ -88,6 +92,7 @@ namespace Hotline.Application.Orders
             _expireTime = expireTime;
             _orderScreenRepository = orderScreenRepository;
             _appOptions = appOptions;
+			_orderApplication = orderApplication;
 		}
 
         /// <summary>
@@ -189,7 +194,14 @@ namespace Hotline.Application.Orders
                 var expiredTime = await _expireTime.CalcEndTime(DateTime.Now,DateTime.Now, order.AcceptTypeCode);
                 var processType = step.FlowDirection == EFlowDirection.OrgToCenter || step.FlowDirection == EFlowDirection.CenterToCenter ? EProcessType.Zhiban : EProcessType.Jiaoban;
                 var isOrderFiled = order.Status >= EOrderStatus.Filed;
-                await _orderRepository.Updateable().SetColumns(o => new Order() { ExpiredTime = expiredTime.ExpiredTime, NearlyExpiredTime = expiredTime.NearlyExpiredTime, NearlyExpiredTimeOne = expiredTime.NearlyExpiredTimeOne, ReTransactNum = reTransactNum, ProcessType = processType, Status = EOrderStatus.Handling })
+                order.ExpiredTime = expiredTime.ExpiredTime;
+				order.NearlyExpiredTime = expiredTime.NearlyExpiredTime;
+				order.NearlyExpiredTimeOne = expiredTime.NearlyExpiredTimeOne;
+				order.ReTransactNum = reTransactNum;
+				order.ProcessType = processType;
+				order.Status = EOrderStatus.Special;
+
+				await _orderRepository.Updateable().SetColumns(o => new Order() { ExpiredTime = expiredTime.ExpiredTime, NearlyExpiredTime = expiredTime.NearlyExpiredTime, NearlyExpiredTimeOne = expiredTime.NearlyExpiredTimeOne, ReTransactNum = reTransactNum, ProcessType = processType, Status = EOrderStatus.Handling })
                     .Where(o => o.Id == order.Id).ExecuteCommandAsync(cancellationToken);
                 var orderDto = _mapper.Map<OrderDto>(order);
                 await _capPublisher.PublishAsync(Hotline.Share.Mq.EventNames.HotlineOrderExpiredTimeUpdate, orderDto, cancellationToken: cancellationToken);
@@ -220,9 +232,24 @@ namespace Hotline.Application.Orders
 					}
                 }
 
-                // var reverseFlowStepAssignInfo = new ReverseFlowStepAssignInfo(EReverseFlowStepCreationPolicy.OriginStep);
-                await _workflowDomainService.RecallAsync(recall, recall.NextHandlers.FirstOrDefault(), EWorkflowTraceType.SecondHandle, 
-                    expiredTime.ExpiredTime, isOrderFiled,  EHandleMode.SecondaryHandle, cancellationToken: cancellationToken);
+				var (workflow, targetStepDefine, currentStep, targetStep, newStep, isOrgToCenter) = await _workflowDomainService.RecallAsync(recall,
+						recall.NextHandlers.FirstOrDefault(), EWorkflowTraceType.SecondHandle,
+						order.ExpiredTime, order.Status >= EOrderStatus.Filed, EHandleMode.SecondaryHandle,
+						async (workflow, currentStep, targetStepDefine, targetStep, targetStepNew) =>
+						{
+							var basicWorkflowDto = _mapper.Map<BasicWorkflowDto>(dto);
+							var stepAssignInfo = await _orderApplication.GetOrderRecallAssignInfoAsync(workflow, targetStepDefine, targetStep,
+								basicWorkflowDto, cancellationToken);
+							if (stepAssignInfo is null) return;
+							var validator = new StepAssignInfoValidator();
+							await validator.ValidateAndThrowAsync(stepAssignInfo, cancellationToken);
+
+							targetStepNew.Assign(stepAssignInfo);
+						},
+						cancellationToken);
+                _mapper.Map(workflow, order);
+                order.FileEmpty();
+                await _orderRepository.UpdateAsync(order, false, cancellationToken);
 
                 visit.VisitState = EVisitState.None;
                 await _orderVisitRepository.UpdateAsync(visit, cancellationToken);

+ 2 - 2
src/Hotline.Application/StatisticalReport/OrderReportApplication.cs

@@ -2773,7 +2773,7 @@ namespace Hotline.Application.StatisticalReport
                    SourceChannel = p.Order.SourceChannel,
                    SourceChannelCode = p.Order.SourceChannelCode,
                    SpecialCreationTime = p.CreationTime,
-                   Name = SqlFunc.Subqueryable<WorkflowStep>().Where(s => s.Id == p.NewCurrentStepId).Select(s => s.HandlerName)
+                   Name = SqlFunc.Subqueryable<WorkflowTrace>().Where(s => s.Id == p.NewCurrentStepId).Select(s => s.HandlerName)
                })
            //将结果合并成一个表
            .MergeTable()
@@ -2842,7 +2842,7 @@ namespace Hotline.Application.StatisticalReport
                     SourceChannel = p.Order.SourceChannel,
                     SourceChannelCode = p.Order.SourceChannelCode,
                     SpecialCreationTime = p.CreationTime,
-                    Name = SqlFunc.Subqueryable<WorkflowStep>().Where(s => s.Id == p.NewCurrentStepId).Select(s => s.HandlerName)
+                    Name = SqlFunc.Subqueryable<WorkflowTrace>().Where(s => s.Id == p.NewCurrentStepId).Select(s => s.HandlerName)
                 })
             //将结果合并成一个表
             .MergeTable()

+ 0 - 18
src/Hotline.Share/Dtos/Article/NotificationWaitSendDto.cs

@@ -1,18 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace Hotline.Share.Dtos.Article
-{
-    public class NotificationWaitSendDto
-    {
-        public string Id { get; set; }
-
-        public string Msg { get; set; }
-
-        public string? Method { get; set; }
-
-    }
-}

+ 3 - 0
src/Hotline.Share/Dtos/Order/OrderPreviousDto.cs

@@ -7,4 +7,7 @@ public class OrderPreviousDto : PreviousWorkflowDto
 {
     public string OrderId { get; set; }
 
+    public string? AssignStepId { get; set; }
+
+
 }

+ 10 - 1
src/Hotline.Share/Dtos/Order/TargetStepInfo.cs

@@ -1,4 +1,6 @@
-using Hotline.Share.Enums.FlowEngine;
+using Hotline.Share.Dtos.FlowEngine;
+using Hotline.Share.Dtos.FlowEngine.Workflow;
+using Hotline.Share.Enums.FlowEngine;
 
 namespace Hotline.Share.Dtos.Order;
 
@@ -6,4 +8,11 @@ public class TargetStepInfo
 {
     public EBusinessType CurrentBusinessType { get; set; }
     public EBusinessType TargetBusinessType { get; set; }
+
+    public List<TargetStep> Steps { get; set; }
+}
+public class TargetStep {
+	public string Id { get; set; }
+	public string Name { get; set; }
+
 }

+ 0 - 21
src/Hotline/Article/NotificationWaitSend.cs

@@ -1,21 +0,0 @@
-using XF.Domain.Repository;
-
-namespace Hotline.Article
-{
-    /// <summary>
-    /// 消息待推送
-    /// </summary>
-    public class NotificationWaitSend : CreationEntity
-    {
-        public string UserId { get; set; }
-
-        public string Msg { get; set; }
-
-        public string? Method { get; set; }
-
-        /// <summary>
-        /// 0:待发,1:已发
-        /// </summary>
-        public string? State { get; set; }
-    }
-}

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

@@ -105,5 +105,10 @@ namespace Hotline.Caching.Interfaces
         /// 是否开启自动填写办理意见至汇总节点
         /// </summary>
         bool IsAutoFillSummaryOpinion { get; }
+
+        /// <summary>
+        /// 是否不推送呼出的无文件的通话记录
+        /// </summary>
+        bool IsNoPushCallNativeOutNoFile { get; }
     }
 }

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

@@ -204,5 +204,10 @@ namespace Hotline.Caching.Services
         /// 是否开启自动填写办理意见至汇总节点
         /// </summary>
         public bool IsAutoFillSummaryOpinion => GetOrDefault(null, SettingConstants.IsAutoFillSummaryOpinion, "是否开启自动填写办理意见至汇总节点", false, "是否开启自动填写办理意见至汇总节点");
+
+        /// <summary>
+        /// 是否不推送呼出的无文件的通话记录(true: 不推送, false: 推送)
+        /// </summary>
+        public bool IsNoPushCallNativeOutNoFile => GetOrDefault("08dd23d1-5c5b-4262-8c99-8c83710723ea", SettingConstants.IsNoPushCallNativeOutNoFile, "是否不推送呼出的无文件的通话记录(true: 不推送, false: 推送)", true, "是否不推送呼出的无文件的通话记录(true: 不推送, false: 推送)");
     }
 }

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

@@ -601,8 +601,8 @@ public abstract class StepBasicEntity : CreationEntity
                     FlowAssignType = FlowAssignType.Value,
                     Key = HandlerOrgId,
                     Value = HandlerOrgName,
-                    UserId = HandlerId,
-                    Username = HandlerName,
+                    //UserId = HandlerId,
+                    //Username = HandlerName,
                     OrgId = HandlerOrgId,
                     OrgName = HandlerOrgName,
                     RoleId = RoleId,

+ 10 - 15
src/Hotline/Orders/OrderDomainService.cs

@@ -604,21 +604,16 @@ public class OrderDomainService : IOrderDomainService, IScopeDependency
         if (schedulings > 0)
         {
             var sendNum = steps.Count / schedulings;
-            scheduling.SendOrderNum += sendNum;
-            if (!scheduling.LoginSendOrderNum.HasValue)
-            {
-                scheduling.LoginSendOrderNum = scheduling.LoginSendOrderNum.HasValue && scheduling.LoginSendOrderNum > sendNum ? scheduling.LoginSendOrderNum : sendNum;
-                await _schedulingRepository.Updateable()
-                    .SetColumns(s => new Scheduling() { LoginSendOrderNum = scheduling.LoginSendOrderNum })
-                    .Where(s => s.SchedulingTime == scheduling.SchedulingTime).ExecuteCommandAsync(cancellationToken);
-            }
-            sendNum = scheduling.LoginSendOrderNum.Value;
-            await _schedulingRepository.Updateable()
-                .SetColumns(s => new Scheduling() { SendOrderNum = scheduling.SendOrderNum, AtWork = scheduling.AtWork })
-                .Where(s => s.Id == scheduling.Id).ExecuteCommandAsync(cancellationToken);
-
-            if (sendNum <= 0) return;
-            var sendSteps = steps.Take(sendNum).ToList();
+			if (!scheduling.LoginSendOrderNum.HasValue)
+			{
+				scheduling.LoginSendOrderNum = scheduling.LoginSendOrderNum.HasValue && scheduling.LoginSendOrderNum > sendNum ? scheduling.LoginSendOrderNum : sendNum;
+			}
+			sendNum = scheduling.LoginSendOrderNum.Value;
+			if (sendNum <= 0) return;
+			var sendSteps = steps.Take(sendNum).ToList();
+			await _schedulingRepository.Updateable()
+				.SetColumns(s => new Scheduling() { AtWork = scheduling.AtWork, LoginSendOrderNum = scheduling.LoginSendOrderNum })
+				.Where(s => s.Id == scheduling.Id).ExecuteCommandAsync(cancellationToken);
             await _orderRepository.Updateable().SetColumns(o => new Order()
             {
                 CenterToOrgHandlerId = user.OrgId,

+ 13 - 0
src/Hotline/Orders/OrderSendBackAudit.cs

@@ -162,5 +162,18 @@ namespace Hotline.Orders
 		[SugarColumn(ColumnDescription = "工单超期时间")]
 		public DateTime? OrderExpiredTime { get; set; }
 
+
+		/// <summary>
+		/// 指定节点Id
+		/// </summary>
+		[SugarColumn(ColumnDescription = "指定节点Id")]
+		public string? AssignStepId { get; set; }
+
+		/// <summary>
+		/// 是否指定节点
+		/// </summary>
+		[SugarColumn(ColumnDescription = "是否指定节点")]
+		public bool? IsAssign { get; set; }
+
 	}
 }

+ 0 - 8
src/Hotline/Realtimes/IRealtimeService.cs

@@ -86,13 +86,5 @@ namespace Hotline.Realtimes
 
         #endregion
 
-        /// <summary>
-        /// 工单补充消息通知
-        /// </summary>
-        /// <param name="userId"></param>
-        /// <param name="msg"></param>
-        /// <param name="cancellationToken"></param>
-        /// <returns></returns>
-        Task OrderComplementAsync(string userId, string msg, CancellationToken cancellationToken);
     }
 }

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

@@ -723,5 +723,10 @@ namespace Hotline.Settings
         /// 小休计时秒数
         /// </summary>
         public const string TelRestNum = "TelRestNum";
+
+        /// <summary>
+        /// 是否不推送呼出的无文件的通话记录
+        /// </summary>
+        public const string IsNoPushCallNativeOutNoFile = "IsNoPushCallNativeOutNoFile";
     }
 }