瀏覽代碼

Merge branch 'test' into lib/test

libin 2 月之前
父節點
當前提交
e513b5eba1
共有 57 個文件被更改,包括 1558 次插入437 次删除
  1. 8 5
      src/Hotline.Api/Controllers/ArticleController.cs
  2. 6 6
      src/Hotline.Api/Controllers/Bi/BiOrderController.cs
  3. 3 1
      src/Hotline.Api/Controllers/HomeController.cs
  4. 152 229
      src/Hotline.Api/Controllers/OrderController.cs
  5. 27 0
      src/Hotline.Api/Controllers/Snapshot/BiSnapshotController.cs
  6. 47 25
      src/Hotline.Api/Controllers/Snapshot/RedPackController.cs
  7. 13 38
      src/Hotline.Api/Controllers/TestController.cs
  8. 1 0
      src/Hotline.Api/Controllers/UserController.cs
  9. 1 1
      src/Hotline.Api/Controllers/WorkflowController.cs
  10. 26 35
      src/Hotline.Api/Realtimes/RealtimeService.cs
  11. 1 1
      src/Hotline.Application/FlowEngine/IWorkflowApplication.cs
  12. 2 2
      src/Hotline.Application/FlowEngine/WorkflowApplication.cs
  13. 220 19
      src/Hotline.Application/IndustryClassification/IndustryClassificationApplication.cs
  14. 15 2
      src/Hotline.Application/Jobs/XingTangCallsSyncJob.cs
  15. 4 1
      src/Hotline.Application/Orders/IOrderApplication.cs
  16. 79 3
      src/Hotline.Application/Orders/OrderApplication.cs
  17. 32 5
      src/Hotline.Application/Orders/OrderSecondaryHandlingApplication.cs
  18. 16 0
      src/Hotline.Application/Snapshot/BiSnapshotApplication.cs
  19. 13 0
      src/Hotline.Application/Snapshot/IBiSnapshotApplication.cs
  20. 30 0
      src/Hotline.Application/Snapshot/IRedPackApplication.cs
  21. 190 3
      src/Hotline.Application/Snapshot/RedPackApplication.cs
  22. 4 4
      src/Hotline.Application/Snapshot/SnapshotOrderApplication.cs
  23. 3 0
      src/Hotline.Application/StatisticalReport/CallReport/YiBinCallReportApplication.cs
  24. 2 2
      src/Hotline.Application/StatisticalReport/OrderReportApplication.cs
  25. 24 0
      src/Hotline.Repository.SqlSugar/Snapshot/SpecialRedPackAuditRepository.cs
  26. 4 5
      src/Hotline.Share/Dtos/Article/BulletinDto.cs
  27. 15 0
      src/Hotline.Share/Dtos/CallCenter/BiQueryCallsDto.cs
  28. 3 0
      src/Hotline.Share/Dtos/Order/OrderPreviousDto.cs
  29. 10 1
      src/Hotline.Share/Dtos/Order/TargetStepInfo.cs
  30. 4 0
      src/Hotline.Share/Dtos/Realtime/RingDto.cs
  31. 119 0
      src/Hotline.Share/Dtos/Snapshot/OrderDto.cs
  32. 40 0
      src/Hotline.Share/Dtos/Snapshot/StatisticsDto.cs
  33. 1 1
      src/Hotline/Article/Bulletin.cs
  34. 1 1
      src/Hotline/Article/Circular.cs
  35. 70 5
      src/Hotline/Article/CircularRecordDomainService.cs
  36. 12 2
      src/Hotline/Article/ICircularRecordDomainService.cs
  37. 2 0
      src/Hotline/Caching/Interfaces/IRealtimeCacheManager.cs
  38. 5 0
      src/Hotline/Caching/Interfaces/ISysDicDataCacheManager.cs
  39. 5 0
      src/Hotline/Caching/Interfaces/ISystemSettingCacheManager.cs
  40. 5 0
      src/Hotline/Caching/Services/RealtimeCacheManager.cs
  41. 5 0
      src/Hotline/Caching/Services/SysDicDataCacheManager.cs
  42. 5 0
      src/Hotline/Caching/Services/SystemSettingCacheManager.cs
  43. 2 2
      src/Hotline/FlowEngine/Workflows/StepBasicEntity.cs
  44. 10 15
      src/Hotline/Orders/OrderDomainService.cs
  45. 13 0
      src/Hotline/Orders/OrderSendBackAudit.cs
  46. 1 9
      src/Hotline/Realtimes/IRealtimeService.cs
  47. 13 0
      src/Hotline/SeedData/SystemDicDataSeedData.cs
  48. 15 0
      src/Hotline/Settings/SettingConstants.cs
  49. 5 0
      src/Hotline/Settings/SysDicTypeConsts.cs
  50. 12 0
      src/Hotline/Snapshot/Interfaces/ISpecialRedPackAuditRepository.cs
  51. 137 0
      src/Hotline/Snapshot/SpecialRedPackAudit.cs
  52. 18 0
      src/Hotline/Snapshot/SupplementRecord.cs
  53. 2 2
      src/TianQue.Sdk/TiqnQueService.cs
  54. 1 7
      test/Hotline.Tests/Application/KnowApplicationTest.cs
  55. 1 1
      test/Hotline.Tests/Application/OrderSnapshotApplicationTest.cs
  56. 67 1
      test/Hotline.Tests/Application/RedPackApplicationTest.cs
  57. 36 3
      test/Hotline.Tests/Mock/OrderServiceMock.cs

+ 8 - 5
src/Hotline.Api/Controllers/ArticleController.cs

@@ -183,7 +183,7 @@ namespace Hotline.Api.Controllers
                         .ToListAsync(HttpContext.RequestAborted);
                     if (userlist != null && userlist.Count > 0)
                     {
-                        await _circularRecordDomainService.RecordUserHandle(userlist, true);
+                        await _circularRecordDomainService.RecordUserHandle(userlist, true,circular.Title,circular.Content);
                     }
                 }
                 else
@@ -195,7 +195,7 @@ namespace Hotline.Api.Controllers
                         .ToListAsync(HttpContext.RequestAborted);
                     if (orglist != null && orglist.Count > 0)
                     {
-                        await _circularRecordDomainService.RecordOrgHandle(orglist, true);
+                        await _circularRecordDomainService.RecordOrgHandle(orglist, true,circular.Title,circular.Content);
                     }
                 }
             }
@@ -255,6 +255,7 @@ namespace Hotline.Api.Controllers
             circular.SourceOrgId = dto.SourceOrgId;
             circular.SourceOrgName = dto.SourceOrgName;
             circular.NeedReadNum = dto.CircularReadGroups.Count;
+            circular.LostEfficacyTime = DateTime.Now.AddYears(50);
             await _circularRepository.UpdateAsync(circular, HttpContext.RequestAborted);
             //移除子表重新添加
             await _circularReadGroupRepository.RemoveAsync(x => x.CircularId == dto.Id, false, HttpContext.RequestAborted);
@@ -302,6 +303,7 @@ namespace Hotline.Api.Controllers
             model.CircularState = ECircularState.Draft;
             model.ReadedNum = 0;
             model.NeedReadNum = model.CircularReadGroups.Count;
+            model.LostEfficacyTime = DateTime.Now.AddYears(50);
             var id = await _circularRepository.AddAsync(model, HttpContext.RequestAborted);
             //子表
             var list = dto.CircularReadGroups;
@@ -375,7 +377,7 @@ namespace Hotline.Api.Controllers
                     model.ReadedNum++;
                     await _circularRepository.UpdateAsync(model, HttpContext.RequestAborted);
                     //处理计数
-                    await _circularRecordDomainService.RecordUserHandle(new List<string> { _sessionContext.RequiredUserId }, false, cancellationToken: HttpContext.RequestAborted);
+                    await _circularRecordDomainService.RecordUserHandle(new List<string> { _sessionContext.RequiredUserId }, false,"","", cancellationToken: HttpContext.RequestAborted);
                 }
             }
             else
@@ -394,7 +396,7 @@ namespace Hotline.Api.Controllers
                     model.ReadedNum++;
                     await _circularRepository.UpdateAsync(model, HttpContext.RequestAborted);
                     //处理部门计数
-                    await _circularRecordDomainService.RecordOrgHandle(new List<string> { _sessionContext.RequiredOrgId }, false, cancellationToken: HttpContext.RequestAborted);
+                    await _circularRecordDomainService.RecordOrgHandle(new List<string> { _sessionContext.RequiredOrgId }, false,"","", cancellationToken: HttpContext.RequestAborted);
                 }
             }
 
@@ -542,7 +544,7 @@ namespace Hotline.Api.Controllers
             bulletin.Content = dto.Content;
             bulletin.BulletinTypeId = dto.BulletinTypeId;
             bulletin.BulletinTypeName = dto.BulletinTypeName;
-            bulletin.LoseEfficacyTime = dto.LoseEfficacyTime;
+            bulletin.LoseEfficacyTime = DateTime.Now.AddYears(50);
             bulletin.PushRanges = dto.PushRanges;
             bulletin.DisplayLocation = dto.DisplayLocation;
             bulletin.SourceOrgId = dto.SourceOrgId;
@@ -581,6 +583,7 @@ namespace Hotline.Api.Controllers
             model.BulletinState = Share.Enums.Article.EBulletinState.Draft;
             model.ReadedNum = 0;
             model.IsArrive = false;
+            model.LoseEfficacyTime = DateTime.Now.AddYears(50);
             await _bulletinRepository.AddAsync(model, HttpContext.RequestAborted);
         }
 

+ 6 - 6
src/Hotline.Api/Controllers/Bi/BiOrderController.cs

@@ -6005,20 +6005,20 @@ namespace Hotline.Api.Controllers.Bi
             //中国政府网“我向总理说句话”
             var ZGZFW = new OnlineMassLineReportRes();
             ZGZFW.SourceChannel = "中国政府网“我向总理说句话”";
-            ZGZFW.SumCount = nowList.Where(x => x.IsProvince == true && x.No.StartsWith("ZGZFW")).Count(); //中国政府网“我向总理说句话”
-            ZGZFW.PreviousCount = oldList.Where(x => x.IsProvince == true && x.No.StartsWith("ZGZFW")).Count(); //中国政府网“我向总理说句话”
+            ZGZFW.SumCount = nowList.Where(x => x.IsProvince == true && x.ReceiveProvinceNo.StartsWith("ZGZFW")).Count(); //中国政府网“我向总理说句话”
+            ZGZFW.PreviousCount = oldList.Where(x => x.IsProvince == true && x.ReceiveProvinceNo.StartsWith("ZGZFW")).Count(); //中国政府网“我向总理说句话”
             returnList.Add(ZGZFW);
             //国家政务服务平台“投诉与建议”
             var GJZWFWPT = new OnlineMassLineReportRes();
             GJZWFWPT.SourceChannel = "国家政务服务平台“投诉与建议”";
-            GJZWFWPT.SumCount = nowList.Where(x => x.IsProvince == true && x.No.StartsWith("GJZWFWPT")).Count(); //国家政务服务平台“投诉与建议”
-            GJZWFWPT.PreviousCount = oldList.Where(x => x.IsProvince == true && x.No.StartsWith("GJZWFWPT")).Count(); //国家政务服务平台“投诉与建议”
+            GJZWFWPT.SumCount = nowList.Where(x => x.IsProvince == true && x.ReceiveProvinceNo.StartsWith("GJZWFWPT")).Count(); //国家政务服务平台“投诉与建议”
+            GJZWFWPT.PreviousCount = oldList.Where(x => x.IsProvince == true && x.ReceiveProvinceNo.StartsWith("GJZWFWPT")).Count(); //国家政务服务平台“投诉与建议”
             returnList.Add(GJZWFWPT);
             //省12345政务服务便民热线
             var Province12345 = new OnlineMassLineReportRes();
             Province12345.SourceChannel = "省12345政务服务便民热线";
-            Province12345.SumCount = nowList.Where(x => x.IsProvince == true && x.No.StartsWith("GJZWFWPT")==false && x.No.StartsWith("ZGZFW")==false).Count(); //国家政务服务平台“投诉与建议”
-            Province12345.PreviousCount = oldList.Where(x => x.IsProvince == true && x.No.StartsWith("GJZWFWPT")==false && x.No.StartsWith("ZGZFW")==false).Count(); //国家政务服务平台“投诉与建议”
+            Province12345.SumCount = nowList.Where(x => x.IsProvince == true && x.ReceiveProvinceNo.StartsWith("GJZWFWPT")==false && x.ReceiveProvinceNo.StartsWith("ZGZFW")==false).Count(); //国家政务服务平台“投诉与建议”
+            Province12345.PreviousCount = oldList.Where(x => x.IsProvince == true && x.ReceiveProvinceNo.StartsWith("GJZWFWPT")==false && x.ReceiveProvinceNo.StartsWith("ZGZFW")==false).Count(); //国家政务服务平台“投诉与建议”
             returnList.Add(Province12345);
             //市政府网站“市长与网民”
             var SZYSM = new OnlineMassLineReportRes();

+ 3 - 1
src/Hotline.Api/Controllers/HomeController.cs

@@ -193,7 +193,9 @@ public class HomeController : BaseController
             NationalPlatformWordLimit = int.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.NationalPlatformWordLimit).SettingValue[0]),
             HandleOpinionWordLimit= int.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.HandleOpinionWordLimit).SettingValue[0]),
             CallInOpenType = int.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.CallInOpenType).SettingValue[0]),
-            Snapshot = _systemSettingCacheManager.Snapshot
+            Snapshot = _systemSettingCacheManager.Snapshot,
+            IsTelRest = bool.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.IsTelRest).SettingValue[0]),
+            TelRestNum = int.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.TelRestNum).SettingValue[0])
         };
         return rsp;
     }

+ 152 - 229
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;
@@ -7,6 +8,7 @@ using Hotline.Application.Orders;
 using Hotline.Application.Quality;
 using Hotline.Application.Snapshot;
 using Hotline.Application.Systems;
+using Hotline.Article;
 using Hotline.Authentications;
 using Hotline.Caching.Interfaces;
 using Hotline.CallCenter.Calls;
@@ -23,7 +25,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 +33,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 +45,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 +58,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,16 +70,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;
 
 namespace Hotline.Api.Controllers;
 
@@ -158,6 +157,7 @@ public class OrderController : BaseController
     private readonly IRepository<SystemDicData> _sysDicDataRepository;
     private readonly IRepository<SystemOrganize> _systemOrganizeRepository;
     private readonly IRepository<OrderComplement> _orderComplementRepository;
+    private readonly ICircularRecordDomainService _circularRecordDomainService;
 
     public OrderController(
         IOrderDomainService orderDomainService,
@@ -231,7 +231,8 @@ public class OrderController : BaseController
         IOrderSnapshotApplication orderSnapshotApplication,
         IRepository<SystemDicData> sysDicDataRepository,
         IRepository<SystemOrganize> systemOrganizeRepository,
-        IRepository<OrderComplement> orderComplementRepository)
+        IRepository<OrderComplement> orderComplementRepository,
+        ICircularRecordDomainService circularRecordDomainService)
     {
         _orderDomainService = orderDomainService;
         _orderRepository = orderRepository;
@@ -305,6 +306,7 @@ public class OrderController : BaseController
         _sysDicDataRepository = sysDicDataRepository;
         _systemOrganizeRepository = systemOrganizeRepository;
         _orderComplementRepository = orderComplementRepository;
+        _circularRecordDomainService = circularRecordDomainService;
     }
 
     #endregion
@@ -3167,8 +3169,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)
@@ -3984,12 +3986,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
@@ -4214,12 +4219,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("保存失败!投诉类型标题和受理内容不能出现'咨询、建议、求助、举报'");
+            }
         }
 
         //交通工单校验
@@ -4503,7 +4511,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)
@@ -5179,7 +5187,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);
 
@@ -5496,54 +5504,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()
@@ -5555,13 +5534,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("工单已不允许退回!");
             }
@@ -5570,52 +5552,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
@@ -5623,49 +5582,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);
-            // }
+            await _orderApplication.OrderPrevious(audit, order, 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 _orderSendBackAuditRepository.AddAsync(audit, HttpContext.RequestAborted);
     }
 
     /// <summary>
@@ -5696,7 +5615,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);
 
@@ -5709,60 +5628,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>
@@ -5794,7 +5670,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)
@@ -5806,57 +5682,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);
+
         }
     }
 
@@ -5955,8 +5794,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
         {
@@ -8951,7 +8807,7 @@ public class OrderController : BaseController
 
     #region 添加补充
     /// <summary>
-    /// 添加补充
+    /// 添加补充 _notificationWaitSendRepository
     /// </summary>
     /// <param name="dto"></param>
     /// <returns></returns>
@@ -8978,7 +8834,74 @@ public class OrderController : BaseController
             IsProComplement = false
         };
 
-        await _orderComplementRepository.AddAsync(complement, HttpContext.RequestAborted);
+        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)
+            {
+                //获取办理指定类型
+                var workflowStepHandler = work.GetWorkflowStepHandler();
+                if (workflowStepHandler != null)
+                {
+
+                    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(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(d => new CircularReadGroupDto
+                            {
+                                UserId = d.Id,
+                                UserName = d.Name
+                            }).ToListAsync();
+                        users.AddRange(userlist);
+                    }
+                    else if (!string.IsNullOrEmpty(workflowStepHandler.OrgId))//指定部门
+                    {
+                        users.Add(new CircularReadGroupDto()
+                        {
+                            OrgId = workflowStepHandler.OrgId,
+                            OrgName = workflowStepHandler.OrgName
+                        });
+                        circularDto.CircularType = ECircularType.Org;
+                    }
+
+                    if (users != null && users.Count > 0)
+                    {
+                        circularDto.CircularReadGroups = users;
+                        //调用推送消息通用接口
+                        await _circularRecordDomainService.AddCircularMessage(circularDto, HttpContext.RequestAborted);
+                    }
+                }
+            }
+
+            #endregion
+        }
     }
+
     #endregion
 }

+ 27 - 0
src/Hotline.Api/Controllers/Snapshot/BiSnapshotController.cs

@@ -0,0 +1,27 @@
+using Hotline.Application.Snapshot;
+using Hotline.Share.Dtos.Snapshot;
+using Microsoft.AspNetCore.Mvc;
+
+namespace Hotline.Api.Controllers.Snapshot;
+
+/// <summary>
+/// 随手拍统计
+/// </summary>
+public class BiSnapshotController : BaseController
+{
+    private readonly IBiSnapshotApplication _biSnapshotApplication;
+
+    public BiSnapshotController(IBiSnapshotApplication biSnapshotApplication)
+    {
+        _biSnapshotApplication = biSnapshotApplication;
+    }
+
+    /// <summary>
+    /// 随手拍统计
+    /// </summary>
+    /// <returns></returns>
+    [HttpGet("statistics")]
+    public async Task<SnapshotStatisticsOutDto> GetSnapshotStatisticsAsync([FromBody] SnapshotStatisticsInDto dto)
+        =>  await _biSnapshotApplication.GetSnapshotStatisticsAsync(dto, HttpContext.RequestAborted);
+
+}

+ 47 - 25
src/Hotline.Api/Controllers/Snapshot/RedPackController.cs

@@ -9,6 +9,9 @@ using Hotline.Caching.Interfaces;
 using XF.Utility.EnumExtensions;
 using Hotline.Share.Enums.Snapshot;
 using Quartz.Impl.Triggers;
+using Hotline.Orders;
+using XF.Domain.Exceptions;
+using Mapster;
 
 namespace Hotline.Api.Controllers.Snapshot;
 
@@ -19,16 +22,48 @@ public class RedPackController : BaseController
     private readonly ISystemDicDataCacheManager _sysDic;
     private readonly ISnapshotSMSTemplateRepository _snapshotSMSTemplateRepository;
     private readonly IOrderSnapshotRepository _orderSnapshotRepository;
+    private readonly IOrderRepository _orderRepository;
 
-    public RedPackController(IRedPackApplication redPackApplication, IIndustryRepository industryRepository, ISystemDicDataCacheManager sysDic, ISnapshotSMSTemplateRepository snapshotSMSTemplateRepository, IOrderSnapshotRepository orderSnapshotRepository)
+    public RedPackController(IRedPackApplication redPackApplication, IIndustryRepository industryRepository, ISystemDicDataCacheManager sysDic, ISnapshotSMSTemplateRepository snapshotSMSTemplateRepository, IOrderSnapshotRepository orderSnapshotRepository, IOrderRepository orderRepository)
     {
         _redPackApplication = redPackApplication;
         _industryRepository = industryRepository;
         _sysDic = sysDic;
         _snapshotSMSTemplateRepository = snapshotSMSTemplateRepository;
         _orderSnapshotRepository = orderSnapshotRepository;
+        _orderRepository = orderRepository;
     }
 
+    #region 特殊红包
+    /// <summary>
+    /// 特殊红包审批集合
+    /// </summary>
+    /// <param name="dto"></param>
+    /// <returns></returns>
+    [HttpGet("special")]
+    public async Task<PagedDto<SnapshotOrderAuditItemsOutDto>> GetRedPackSpecialItems([FromQuery] SnapshotOrderAuditItemsInDto dto)
+        => (await _redPackApplication.GetRedPackSpecialAuditItems(dto).ToPagedListAsync(dto)).ToPaged();
+
+    /// <summary>
+    /// 特殊红包审核通过或拒绝
+    /// </summary>
+    /// <param name="dto"></param>
+    /// <returns></returns>
+    [HttpPut("audit/special")]
+    public async Task UpdateRedPackSpecialAuditAsync([FromBody] UpdateRedPackSpecialAuditInDto dto)
+        => await _redPackApplication.AuditRedPackSpecialAuditAsync(dto);
+
+    /// <summary>
+    /// 特殊红包审核详情信息(OrderId)
+    /// </summary>
+    /// <returns></returns>
+    [HttpGet("audit/special/{id}")]
+    public async Task<SnapshotOrderAuditSpecialDetailOutDto> GetRedPackSpecialAuditBaseDataAsync(string id)
+    {
+        return await _redPackApplication.GetRedPackSpecialAuditBaseDataAsync(id);
+    }
+    #endregion
+
     #region 红包审核
     /// <summary>
     /// 获取市民红包审批列表
@@ -110,31 +145,9 @@ public class RedPackController : BaseController
     /// </summary>
     /// <returns></returns>
     [HttpGet("record/basedata/{id}")]
-    public async Task<Dictionary<string, object>> UpdateRedPackRecordBaseDataAsync(string id)
+    public async Task<GetRedPackRecordBaseDataOutDto> UpdateRedPackRecordBaseDataAsync(string id)
     {
-        var snapshot = await _orderSnapshotRepository.Queryable()
-            .Where(m => m.Id == id)
-            .Select(m => new
-            {
-                m.Id,
-                m.AwardBankCardNo,
-                m.AwardOpenBank,
-                m.AwardName,
-                m.AwardAmount
-            })
-            .FirstAsync();
-        var dic = new Dictionary<string, object>()
-        {
-            { "replenishType", _sysDic.SnapshotReplenishType }
-        };
-        if (snapshot != null)
-        {
-            dic.Add("bankCardNo", snapshot.AwardBankCardNo);
-            dic.Add("openBank", snapshot.AwardOpenBank);
-            dic.Add("name", snapshot.AwardName);
-            dic.Add("amount", snapshot.AwardAmount);
-        }
-        return dic;
+        return await _redPackApplication.GetRedPackRecordBaseDataAsync(id); ;
     }
 
     /// <summary>
@@ -146,6 +159,15 @@ public class RedPackController : BaseController
     public async Task UpdateRedPackRecordAsync([FromBody] UpdateRedPackRecordInDto dto)
         => await _redPackApplication.UpdateRedPackRecordAsync(dto);
 
+    /// <summary>
+    /// 添加补充发放信息(特殊红包)
+    /// </summary>
+    /// <param name="dto"></param>
+    /// <returns></returns>
+    [HttpPut("record/special")]
+    public async Task UpdateRedPackSpecialRecordAsync([FromBody] UpdateRedPackRecordInDto dto)
+        => await _redPackApplication.UpdateRedPackSpecialRecordAsync(dto);
+
     /// <summary>
     /// 补充发放集合基础信息
     /// </summary>

+ 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 - 0
src/Hotline.Api/Controllers/UserController.cs

@@ -161,6 +161,7 @@ public class UserController : BaseController
     public async Task<PagedDto<UserDto>> QueryPaged([FromQuery] UserPagedDto dto)
     {
         var query =  _userApplication.QueryPaged(dto);
+        Console.WriteLine(query.ToSqlString());
         var (total, items) = await query.ToPagedListAsync(dto, HttpContext.RequestAborted);
 
         //var (total, items) = await _userRepository.Queryable(includeDeleted: true)

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

+ 26 - 35
src/Hotline.Api/Realtimes/RealtimeService.cs

@@ -5,7 +5,6 @@ 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;
@@ -122,16 +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 Task OrderComplementAsync(string userId, string msg, CancellationToken cancellationToken) =>
-        SendToUserAsync(userId, RealtimeMethods.OrderComplementRecord, msg, cancellationToken);
-
     #region 系统信息通知
 
     /// <summary>
@@ -140,16 +129,16 @@ public class RealtimeService : IRealtimeService, IScopeDependency
     /// <param name="id"></param>
     /// <param name="cancellationToken"></param>
     /// <returns></returns>
-    public async Task CircularRecoordAsync(string id, CancellationToken cancellationToken)
+    public async Task CircularRecoordAsync(string id,string title,string sendContent, CancellationToken cancellationToken)
     {
         //获取所有需要推送的
         var record = await _circularRecordRepository.Queryable().Where(x => x.Id == id).FirstAsync();
 
         if (record.CircularType == Share.Enums.Article.ECircularType.Person)
         {
-            //个人
+            //个人数量
             await SendToUserAsync(record.UserId, RealtimeMethods.CircularRecord,
-                new CircularRecoordDto() { CircularType = Share.Enums.Article.ECircularType.Person, Count = record.RecordCount },
+                new CircularRecoordDto() { CircularType = Share.Enums.Article.ECircularType.Person, Count = record.RecordCount,SendContent = sendContent,Title= title },
                 cancellationToken);
         }
         else
@@ -160,10 +149,12 @@ public class RealtimeService : IRealtimeService, IScopeDependency
             {
                 try
                 {
+                    //数量
                     await SendToUserAsync(user.Id, RealtimeMethods.CircularRecord,
                         new CircularRecoordDto()
-                            { CircularType = Share.Enums.Article.ECircularType.Org, Count = record.RecordCount },
+                        { CircularType = Share.Enums.Article.ECircularType.Org, Count = record.RecordCount,SendContent = sendContent,Title = title },
                         cancellationToken);
+
                 }
                 catch
                 {
@@ -176,37 +167,37 @@ public class RealtimeService : IRealtimeService, IScopeDependency
 
     #region 大屏.数据展示
 
-    public Task BsDataShowChanged1Async(object obj, CancellationToken cancellationToken) => 
+    public Task BsDataShowChanged1Async(object obj, CancellationToken cancellationToken) =>
         SendToGroupAsync(RealtimeGroupNames.BigScreenDataShow, RealtimeMethods.BsDataShowArea1, obj, cancellationToken);
-    public Task BsDataShowChanged2Async(object obj, CancellationToken cancellationToken) => 
+    public Task BsDataShowChanged2Async(object obj, CancellationToken cancellationToken) =>
         SendToGroupAsync(RealtimeGroupNames.BigScreenDataShow, RealtimeMethods.BsDataShowArea2, obj, cancellationToken);
-    public Task BsDataShowChanged3Async(object obj, CancellationToken cancellationToken) => 
+    public Task BsDataShowChanged3Async(object obj, CancellationToken cancellationToken) =>
         SendToGroupAsync(RealtimeGroupNames.BigScreenDataShow, RealtimeMethods.BsDataShowArea3, obj, cancellationToken);
-    public Task BsDataShowChanged4Async(object obj, CancellationToken cancellationToken) => 
+    public Task BsDataShowChanged4Async(object obj, CancellationToken cancellationToken) =>
         SendToGroupAsync(RealtimeGroupNames.BigScreenDataShow, RealtimeMethods.BsDataShowArea4, obj, cancellationToken);
-    public Task BsDataShowChanged5Async(object obj, CancellationToken cancellationToken) => 
+    public Task BsDataShowChanged5Async(object obj, CancellationToken cancellationToken) =>
         SendToGroupAsync(RealtimeGroupNames.BigScreenDataShow, RealtimeMethods.BsDataShowArea5, obj, cancellationToken);
-    public Task BsDataShowChanged6Async(object obj, CancellationToken cancellationToken) => 
+    public Task BsDataShowChanged6Async(object obj, CancellationToken cancellationToken) =>
         SendToGroupAsync(RealtimeGroupNames.BigScreenDataShow, RealtimeMethods.BsDataShowArea6, obj, cancellationToken);
-    public Task BsDataShowChanged7Async(object obj, CancellationToken cancellationToken) => 
+    public Task BsDataShowChanged7Async(object obj, CancellationToken cancellationToken) =>
         SendToGroupAsync(RealtimeGroupNames.BigScreenDataShow, RealtimeMethods.BsDataShowArea7, obj, cancellationToken);
-    public Task BsDataShowChanged8Async(object obj, CancellationToken cancellationToken) => 
+    public Task BsDataShowChanged8Async(object obj, CancellationToken cancellationToken) =>
         SendToGroupAsync(RealtimeGroupNames.BigScreenDataShow, RealtimeMethods.BsDataShowArea8, obj, cancellationToken);
 
-	#endregion
+    #endregion
 
-	#region 大屏坐席数据
-	public Task BsSeatStateDataShowChangedAsync1(object obj, CancellationToken cancellationToken) =>
-		SendToGroupAsync(RealtimeGroupNames.BigScreenSeatState, RealtimeMethods.BsSeatStateDataShowArea1, obj, cancellationToken);
+    #region 大屏坐席数据
+    public Task BsSeatStateDataShowChangedAsync1(object obj, CancellationToken cancellationToken) =>
+        SendToGroupAsync(RealtimeGroupNames.BigScreenSeatState, RealtimeMethods.BsSeatStateDataShowArea1, obj, cancellationToken);
 
-	public Task BsSeatStateDataShowChangedAsync2(object obj, CancellationToken cancellationToken) =>
-		SendToGroupAsync(RealtimeGroupNames.BigScreenSeatState, RealtimeMethods.BsSeatStateDataShowArea2, obj, cancellationToken);
+    public Task BsSeatStateDataShowChangedAsync2(object obj, CancellationToken cancellationToken) =>
+        SendToGroupAsync(RealtimeGroupNames.BigScreenSeatState, RealtimeMethods.BsSeatStateDataShowArea2, obj, cancellationToken);
 
-	public Task BsSeatStateDataShowChangedAsync3(object obj, CancellationToken cancellationToken) =>
-		SendToGroupAsync(RealtimeGroupNames.BigScreenSeatState, RealtimeMethods.BsSeatStateDataShowArea3, obj, cancellationToken);
+    public Task BsSeatStateDataShowChangedAsync3(object obj, CancellationToken cancellationToken) =>
+        SendToGroupAsync(RealtimeGroupNames.BigScreenSeatState, RealtimeMethods.BsSeatStateDataShowArea3, obj, cancellationToken);
 
-	public Task BsSeatStateDataShowChangedAsync4(object obj, CancellationToken cancellationToken) =>
-		SendToGroupAsync(RealtimeGroupNames.BigScreenSeatState, RealtimeMethods.BsSeatStateDataShowArea4, obj, cancellationToken);
+    public Task BsSeatStateDataShowChangedAsync4(object obj, CancellationToken cancellationToken) =>
+        SendToGroupAsync(RealtimeGroupNames.BigScreenSeatState, RealtimeMethods.BsSeatStateDataShowArea4, obj, cancellationToken);
     #endregion
 
     #region 数据大屏
@@ -227,7 +218,7 @@ public class RealtimeService : IRealtimeService, IScopeDependency
     /// <param name="cancellationToken"></param>
     /// <returns></returns>
     public Task OrderHandlingDetailAsync(object obj, CancellationToken cancellationToken) =>
-        SendToGroupAsync(RealtimeGroupNames.BigDataScreen, RealtimeMethods.OrderHandlingDetail,obj, cancellationToken);
+        SendToGroupAsync(RealtimeGroupNames.BigDataScreen, RealtimeMethods.OrderHandlingDetail, obj, cancellationToken);
 
     /// <summary>
     /// 推送二次办理中工单概览
@@ -292,6 +283,6 @@ public class RealtimeService : IRealtimeService, IScopeDependency
 
     #endregion
 
-   
+
 
 }

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

+ 220 - 19
src/Hotline.Application/IndustryClassification/IndustryClassificationApplication.cs

@@ -3,6 +3,7 @@ using Hotline.IndustryClassification;
 using Hotline.Orders;
 using Hotline.Settings;
 using Hotline.Share.Dtos.IndustryClassification;
+using Hotline.Share.Enums.FlowEngine;
 using Hotline.Share.Enums.Order;
 using Hotline.Share.Requests;
 using MapsterMapper;
@@ -18,16 +19,19 @@ namespace Hotline.Application.IndustryClassification
         private readonly ISystemOrganizeRepository _organizeRepository;
         private readonly IIndustryClassificationRepositpry _industrialManagementRepository;
         private readonly ISessionContext _sessionContext;
+        private readonly IOrderRepository _orderRepository;
 
         public IndustryClassificationApplication(IMapper mapper,
             ISystemOrganizeRepository organizeRepository,
             IIndustryClassificationRepositpry industrialManagementRepository,
-            ISessionContext sessionContext)
+            ISessionContext sessionContext,
+            IOrderRepository orderRepository)
         {
             _mapper = mapper;
             _organizeRepository = organizeRepository;
             _industrialManagementRepository = industrialManagementRepository;
             _sessionContext = sessionContext;
+            _orderRepository = orderRepository;
         }
 
         /// <summary>
@@ -56,22 +60,86 @@ namespace Hotline.Application.IndustryClassification
         /// </summary>
         /// <param name="dto"></param>
         /// <returns></returns>
-        public ISugarQueryable<IndustrialManagementOrderDto> GetIndustrialManagementOrderList(IndustrialManagementRequestDto dto)
+        public  ISugarQueryable<IndustrialManagementOrderDto> GetIndustrialManagementOrderList(IndustrialManagementRequestDto dto)
         {
             string industrialId = "";
             if (!_sessionContext.OrgIsCenter)
             {
                 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()
+            var queryorder = _industrialManagementRepository.Queryable()
+             .InnerJoin<IndustryAssociations>((i, io) => i.Id == io.IndustrialManagementId)
+             .InnerJoin<Order>((i, io, o) => io.OrganizeId == o.ActualHandleOrgCode)
+             // .InnerJoin<Order>((i, io, s, o) => s.ExternalId == o.Id)
+             .Where((i, io, o) => o.Status >= EOrderStatus.Filed)
+             .WhereIF(!string.IsNullOrEmpty(industrialId), (i, io, o) => io.IndustrialManagementId == industrialId)
+             .OrderBy((i, io, o) => i.Id)
+             .GroupBy((i, io, o) => new
+             {
+                 o.Id,
+                 o.Status,
+                 IndustrialId = i.Id,
+                 i.IndustrialName,
+                 o.Title,
+                 o.No,
+                 o.AcceptType,
+                 o.AcceptTypeCode,
+                 o.HotspotId,
+                 o.HotspotName,
+                 o.HotspotSpliceName,
+                 o.HotspotExternal,
+                 o.CreationTime,
+                 o.StartTime,
+                 o.OrgLevelOneCode,
+                 o.OrgLevelOneName,
+                 o.ActualHandleOrgName,
+                 o.ActualHandleOrgCode,
+                 o.CounterSignType,
+                 o.FiledTime,
+                 o.ExpiredTime,
+                 o.NearlyExpiredTime,
+                 o.NearlyExpiredTimeOne
+             })
+             .Select((i, io, o) => new IndustrialManagementOrderDto
+             {
+
+                 Id = o.Id,
+                 Status = o.Status,
+                 IndustrialId = i.Id,
+                 IndustrialName = i.IndustrialName,
+                 Title = o.Title,
+                 No = o.No,
+                 AcceptType = o.AcceptType,
+                 AcceptTypeCode = o.AcceptTypeCode,
+                 HotspotId = o.HotspotId,
+                 HotspotName = o.HotspotName,
+                 HotspotSpliceName = o.HotspotSpliceName,
+                 HotspotExternal = o.HotspotExternal,
+                 CreationTime = o.CreationTime,
+                 StartTime = o.StartTime,
+                 OrgLevelOneCode = o.OrgLevelOneCode,
+                 OrgLevelOneName = o.OrgLevelOneName,
+                 ActualHandleOrgName = o.ActualHandleOrgName,
+                 ActualHandleOrgCode = o.ActualHandleOrgCode,
+                 CounterSignType = o.CounterSignType,
+                 FiledTime = o.FiledTime,
+                 ExpiredTime = o.ExpiredTime,
+                 NearlyExpiredTime = o.NearlyExpiredTime,
+                 NearlyExpiredTimeOne = o.NearlyExpiredTimeOne
+             }).MergeTable()
+               ;
+            var queryhuiqian = _industrialManagementRepository.Queryable()
                 .InnerJoin<IndustryAssociations>((i, io) => i.Id == io.IndustrialManagementId)
-                .InnerJoin<WorkflowStep>((i, io, s) => io.OrganizeId == s.HandlerOrgId)
+                .InnerJoin<WorkflowTrace>((i, io, s) => io.OrganizeId == s.HandlerOrgId)
                 .InnerJoin<Order>((i, io, s, o) => s.ExternalId == o.Id)
-                .Where((i, io, s, o) => o.Status >= EOrderStatus.Filed)
+                .Where((i, io, s, o) => o.Status >= EOrderStatus.Filed && s.CountersignPosition == ECountersignPosition.Direct)
                 .WhereIF(!string.IsNullOrEmpty(industrialId), (i, io, s, o) => io.IndustrialManagementId == industrialId)
                 .OrderBy((i, io, s, o) => i.Id)
                 .GroupBy((i, io, s, o) => new
@@ -127,20 +195,153 @@ namespace Hotline.Application.IndustryClassification
                     NearlyExpiredTime = o.NearlyExpiredTime,
                     NearlyExpiredTimeOne = o.NearlyExpiredTimeOne
                 }).MergeTable()
-                .WhereIF(!string.IsNullOrEmpty(dto.IndustrialName), d => d.IndustrialName.Contains(dto.IndustrialName))
-                .WhereIF(!string.IsNullOrEmpty(dto.Title), d => d.Title.Contains(dto.Title))
-                .WhereIF(!string.IsNullOrEmpty(dto.No), d => d.No.Contains(dto.No))
-                .WhereIF(!string.IsNullOrEmpty(dto.AcceptTypeCode), d => d.AcceptTypeCode == dto.AcceptTypeCode)
-                  .WhereIF(dto.StartTime.HasValue, d => d.CreationTime >= dto.StartTime) //受理时间开始
-                .WhereIF(dto.EndTime.HasValue, d => d.CreationTime <= dto.EndTime) //受理时间结束
-                .WhereIF(!string.IsNullOrEmpty(dto.HotspotSpliceName), d => d.HotspotSpliceName.Contains(dto.HotspotSpliceName))
-                .WhereIF(!string.IsNullOrEmpty(dto.OrgLevelOneName), d => d.OrgLevelOneName.Contains(dto.OrgLevelOneName))
-                .WhereIF(!string.IsNullOrEmpty(dto.ActualHandleOrgName), d => d.ActualHandleOrgName.Contains(dto.ActualHandleOrgName))
-                .OrderByIF(string.IsNullOrEmpty(dto.SortField), d => d.CreationTime, OrderByType.Desc) //默认排序时间为创建时间
-                .OrderByIF(dto is { SortField: "creationTime", SortRule: 0 }, d => d.CreationTime, OrderByType.Asc) //工单创建时间
-                .OrderByIF(dto is { SortField: "creationTime", SortRule: 1 }, d => d.CreationTime, OrderByType.Desc) //工单创建时间降序
                 ;
+
+            var query = _orderRepository.UnionAll(queryorder, queryhuiqian)
+                       .OrderBy(d => d.Id)
+                       .GroupBy(d => new
+                       {
+                           d.Id,
+                           d.Status,
+                           d.IndustrialId,
+                           d.IndustrialName,
+                           d.Title,
+                           d.No,
+                           d.AcceptType,
+                           d.AcceptTypeCode,
+                           d.HotspotId,
+                           d.HotspotName,
+                           d.HotspotSpliceName,
+                           d.HotspotExternal,
+                           d.CreationTime,
+                           d.StartTime,
+                           d.OrgLevelOneCode,
+                           d.OrgLevelOneName,
+                           d.ActualHandleOrgName,
+                           d.ActualHandleOrgCode,
+                           d.CounterSignType,
+                           d.FiledTime,
+                           d.ExpiredTime,
+                           d.NearlyExpiredTime,
+                           d.NearlyExpiredTimeOne
+                       })
+                       .Select(d => new IndustrialManagementOrderDto
+                       {
+
+                           Id = d.Id,
+                           Status = d.Status,
+                           IndustrialId = d.IndustrialId,
+                           IndustrialName = d.IndustrialName,
+                           Title = d.Title,
+                           No = d.No,
+                           AcceptType = d.AcceptType,
+                           AcceptTypeCode = d.AcceptTypeCode,
+                           HotspotId = d.HotspotId,
+                           HotspotName = d.HotspotName,
+                           HotspotSpliceName = d.HotspotSpliceName,
+                           HotspotExternal = d.HotspotExternal,
+                           CreationTime = d.CreationTime,
+                           StartTime = d.StartTime,
+                           OrgLevelOneCode = d.OrgLevelOneCode,
+                           OrgLevelOneName = d.OrgLevelOneName,
+                           ActualHandleOrgName = d.ActualHandleOrgName,
+                           ActualHandleOrgCode = d.ActualHandleOrgCode,
+                           CounterSignType = d.CounterSignType,
+                           FiledTime = d.FiledTime,
+                           ExpiredTime = d.ExpiredTime,
+                           NearlyExpiredTime = d.NearlyExpiredTime,
+                           NearlyExpiredTimeOne = d.NearlyExpiredTimeOne
+                       }).MergeTable()
+                       .WhereIF(!string.IsNullOrEmpty(dto.IndustrialName), d => d.IndustrialName.Contains(dto.IndustrialName))
+                       .WhereIF(!string.IsNullOrEmpty(dto.Title), d => d.Title.Contains(dto.Title))
+                       .WhereIF(!string.IsNullOrEmpty(dto.No), d => d.No.Contains(dto.No))
+                       .WhereIF(!string.IsNullOrEmpty(dto.AcceptTypeCode), d => d.AcceptTypeCode == dto.AcceptTypeCode)
+                         .WhereIF(dto.StartTime.HasValue, d => d.CreationTime >= dto.StartTime) //受理时间开始
+                       .WhereIF(dto.EndTime.HasValue, d => d.CreationTime <= dto.EndTime) //受理时间结束
+                       .WhereIF(!string.IsNullOrEmpty(dto.HotspotSpliceName), d => d.HotspotSpliceName.Contains(dto.HotspotSpliceName))
+                       .WhereIF(!string.IsNullOrEmpty(dto.OrgLevelOneName), d => d.OrgLevelOneName.Contains(dto.OrgLevelOneName))
+                       .WhereIF(!string.IsNullOrEmpty(dto.ActualHandleOrgName), d => d.ActualHandleOrgName.Contains(dto.ActualHandleOrgName))
+                       .OrderByIF(string.IsNullOrEmpty(dto.SortField), d => d.CreationTime, OrderByType.Desc) //默认排序时间为创建时间
+                       .OrderByIF(dto is { SortField: "creationTime", SortRule: 0 }, d => d.CreationTime, OrderByType.Asc) //工单创建时间
+                       .OrderByIF(dto is { SortField: "creationTime", SortRule: 1 }, d => d.CreationTime, OrderByType.Desc) //工单创建时间降序
+                       ;
+
             return query;
+
+            //var query = _industrialManagementRepository.Queryable()
+            //    .InnerJoin<IndustryAssociations>((i, io) => i.Id == io.IndustrialManagementId)
+            //    .InnerJoin<WorkflowStep>((i, io, s) => io.OrganizeId == s.HandlerOrgId)
+            //    .InnerJoin<Order>((i, io, s, o) => s.ExternalId == o.Id)
+            //    .Where((i, io, s, o) => o.Status >= EOrderStatus.Filed)
+            //    .WhereIF(!string.IsNullOrEmpty(industrialId), (i, io, s, o) => io.IndustrialManagementId == industrialId)
+            //    .OrderBy((i, io, s, o) => i.Id)
+            //    .GroupBy((i, io, s, o) => new
+            //    {
+            //        o.Id,
+            //        o.Status,
+            //        IndustrialId = i.Id,
+            //        i.IndustrialName,
+            //        o.Title,
+            //        o.No,
+            //        o.AcceptType,
+            //        o.AcceptTypeCode,
+            //        o.HotspotId,
+            //        o.HotspotName,
+            //        o.HotspotSpliceName,
+            //        o.HotspotExternal,
+            //        o.CreationTime,
+            //        o.StartTime,
+            //        o.OrgLevelOneCode,
+            //        o.OrgLevelOneName,
+            //        o.ActualHandleOrgName,
+            //        o.ActualHandleOrgCode,
+            //        o.CounterSignType,
+            //        o.FiledTime,
+            //        o.ExpiredTime,
+            //        o.NearlyExpiredTime,
+            //        o.NearlyExpiredTimeOne
+            //    })
+            //    .Select((i, io, s, o) => new IndustrialManagementOrderDto
+            //    {
+
+            //        Id = o.Id,
+            //        Status = o.Status,
+            //        IndustrialId = i.Id,
+            //        IndustrialName = i.IndustrialName,
+            //        Title = o.Title,
+            //        No = o.No,
+            //        AcceptType = o.AcceptType,
+            //        AcceptTypeCode = o.AcceptTypeCode,
+            //        HotspotId = o.HotspotId,
+            //        HotspotName = o.HotspotName,
+            //        HotspotSpliceName = o.HotspotSpliceName,
+            //        HotspotExternal = o.HotspotExternal,
+            //        CreationTime = o.CreationTime,
+            //        StartTime = o.StartTime,
+            //        OrgLevelOneCode = o.OrgLevelOneCode,
+            //        OrgLevelOneName = o.OrgLevelOneName,
+            //        ActualHandleOrgName = o.ActualHandleOrgName,
+            //        ActualHandleOrgCode = o.ActualHandleOrgCode,
+            //        CounterSignType = o.CounterSignType,
+            //        FiledTime = o.FiledTime,
+            //        ExpiredTime = o.ExpiredTime,
+            //        NearlyExpiredTime = o.NearlyExpiredTime,
+            //        NearlyExpiredTimeOne = o.NearlyExpiredTimeOne
+            //    }).MergeTable()
+            //    .WhereIF(!string.IsNullOrEmpty(dto.IndustrialName), d => d.IndustrialName.Contains(dto.IndustrialName))
+            //    .WhereIF(!string.IsNullOrEmpty(dto.Title), d => d.Title.Contains(dto.Title))
+            //    .WhereIF(!string.IsNullOrEmpty(dto.No), d => d.No.Contains(dto.No))
+            //    .WhereIF(!string.IsNullOrEmpty(dto.AcceptTypeCode), d => d.AcceptTypeCode == dto.AcceptTypeCode)
+            //      .WhereIF(dto.StartTime.HasValue, d => d.CreationTime >= dto.StartTime) //受理时间开始
+            //    .WhereIF(dto.EndTime.HasValue, d => d.CreationTime <= dto.EndTime) //受理时间结束
+            //    .WhereIF(!string.IsNullOrEmpty(dto.HotspotSpliceName), d => d.HotspotSpliceName.Contains(dto.HotspotSpliceName))
+            //    .WhereIF(!string.IsNullOrEmpty(dto.OrgLevelOneName), d => d.OrgLevelOneName.Contains(dto.OrgLevelOneName))
+            //    .WhereIF(!string.IsNullOrEmpty(dto.ActualHandleOrgName), d => d.ActualHandleOrgName.Contains(dto.ActualHandleOrgName))
+            //    .OrderByIF(string.IsNullOrEmpty(dto.SortField), d => d.CreationTime, OrderByType.Desc) //默认排序时间为创建时间
+            //    .OrderByIF(dto is { SortField: "creationTime", SortRule: 0 }, d => d.CreationTime, OrderByType.Asc) //工单创建时间
+            //    .OrderByIF(dto is { SortField: "creationTime", SortRule: 1 }, d => d.CreationTime, OrderByType.Desc) //工单创建时间降序
+            //    ;
+
         }
     }
 }

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

+ 16 - 0
src/Hotline.Application/Snapshot/BiSnapshotApplication.cs

@@ -0,0 +1,16 @@
+using Hotline.Share.Dtos.Snapshot;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using XF.Domain.Dependency;
+
+namespace Hotline.Application.Snapshot;
+public class BiSnapshotApplication : IBiSnapshotApplication, IScopeDependency
+{
+    public Task<SnapshotStatisticsOutDto> GetSnapshotStatisticsAsync(SnapshotStatisticsInDto dto, CancellationToken token)
+    {
+        throw new NotImplementedException();
+    }
+}

+ 13 - 0
src/Hotline.Application/Snapshot/IBiSnapshotApplication.cs

@@ -0,0 +1,13 @@
+using Hotline.Share.Dtos.Snapshot;
+using Microsoft.AspNetCore.Http;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Hotline.Application.Snapshot;
+public interface IBiSnapshotApplication
+{
+    Task<SnapshotStatisticsOutDto> GetSnapshotStatisticsAsync(SnapshotStatisticsInDto dto, CancellationToken token);
+}

+ 30 - 0
src/Hotline.Application/Snapshot/IRedPackApplication.cs

@@ -23,6 +23,7 @@ public interface IRedPackApplication
     /// <param name="dto"></param>
     /// <returns></returns>
     Task AuditRedPackGuiderAuditAsync(UpdateRedPackGuiderAuditInDto dto);
+    Task AuditRedPackSpecialAuditAsync(UpdateRedPackSpecialAuditInDto dto);
 
     /// <summary>
     /// 获取特提参数
@@ -65,6 +66,14 @@ public interface IRedPackApplication
     /// <returns></returns>
     ISugarQueryable<SnapshotOrderGuiderAuditItemsOutDto> GetRedPackGuiderAuditItems(SnapshotOrderGuiderAuditItemsInDto dto);
 
+    /// <summary>
+    /// 获取补充发放记录基础数据
+    /// </summary>
+    /// <param name="v"></param>
+    /// <param name="orderId"></param>
+    /// <returns></returns>
+    Task<GetRedPackRecordBaseDataOutDto> GetRedPackRecordBaseDataAsync(string orderId);
+
     /// <summary>
     /// 红包发放明细
     /// </summary>
@@ -86,6 +95,20 @@ public interface IRedPackApplication
     ISugarQueryable<SnapshotRedPackRecordItemsOutDto> GetRedPackRecordItems(SnapshotRedPackRecordItemsInDto dto);
     ISugarQueryable<SnapshotRedPackRecordSupplementItemsOutDto> GetRedPackRecordSupplementItems(SnapshotRedPackRecordSupplementItemsInDto dto);
 
+    /// <summary>
+    /// 特殊红包审核详情信息
+    /// </summary>
+    /// <param name="id"></param>
+    /// <returns></returns>
+    Task<SnapshotOrderAuditSpecialDetailOutDto> GetRedPackSpecialAuditBaseDataAsync(string id);
+
+    /// <summary>
+    /// 特殊红包审批集合
+    /// </summary>
+    /// <param name="dto"></param>
+    /// <returns></returns>
+    ISugarQueryable<SnapshotOrderAuditItemsOutDto> GetRedPackSpecialAuditItems(SnapshotOrderAuditItemsInDto dto);
+
     /// <summary>
     /// 审核添加备注
     /// </summary>
@@ -99,4 +122,11 @@ public interface IRedPackApplication
     /// <param name="dto"></param>
     /// <returns></returns>
     Task UpdateRedPackRecordAsync(UpdateRedPackRecordInDto dto);
+
+    /// <summary>
+    /// 添加补充发放信息(特殊红包)
+    /// </summary>
+    /// <param name="dto"></param>
+    /// <returns></returns>
+    Task UpdateRedPackSpecialRecordAsync(UpdateRedPackRecordInDto dto);
 }

+ 190 - 3
src/Hotline.Application/Snapshot/RedPackApplication.cs

@@ -1,4 +1,6 @@
-using Hotline.Orders;
+using DocumentFormat.OpenXml.Office2010.Excel;
+using Hotline.Caching.Interfaces;
+using Hotline.Orders;
 using Hotline.Push.FWMessage;
 using Hotline.Share.Attributes;
 using Hotline.Share.Dtos;
@@ -21,6 +23,7 @@ using XF.Domain.Dependency;
 using XF.Domain.Exceptions;
 using XF.Domain.Repository;
 using XF.Utility.EnumExtensions;
+using static NPOI.SS.Format.CellNumberFormatter;
 
 namespace Hotline.Application.Snapshot;
 public class RedPackApplication : IRedPackApplication, IScopeDependency
@@ -29,6 +32,11 @@ public class RedPackApplication : IRedPackApplication, IScopeDependency
     private readonly IOrderRepository _orderRepository;
     private readonly ISnapshotSMSTemplateRepository _snapshotSMSTemplateRepository;
     private readonly IIndustryRepository _industryRepository;
+    private readonly ISpecialRedPackAuditRepository _specialRedPackAuditRepository;
+
+    /// <summary>
+    /// 市民红包审核仓储
+    /// </summary>
     private readonly IRedPackAuditRepository _redPackAuditRepository;
     private readonly IRedPackRecordRepository _redPackRecordRepository;
     private readonly IRepository<OrderSpecial> _orderSpecialRepository;
@@ -38,8 +46,9 @@ public class RedPackApplication : IRedPackApplication, IScopeDependency
     private readonly ISupplementRecordRepository _supplementRecordRepository;
     private readonly IPushDomainService _pushDomainService;
     private readonly ILogger<RedPackApplication> _logger;
+    private readonly ISystemDicDataCacheManager _systemDic;
 
-    public RedPackApplication(IOrderSnapshotRepository orderSnapshotRepository, ISnapshotSMSTemplateRepository snapshotSMSTemplateRepository, IOrderRepository orderRepository, IIndustryRepository industryRepository, IRedPackAuditRepository redPackAuditRepository, IRedPackRecordRepository redPackRecordRepository, IRepository<OrderSpecial> orderSpecialRepository, ISessionContext sessionContext, IRedPackGuiderAuditRepository redPackGuiderAuditRepository, IThirdAccountRepository thirdAccountRepository, ISupplementRecordRepository supplementRecordRepository, IPushDomainService pushDomainService, ILogger<RedPackApplication> logger)
+    public RedPackApplication(IOrderSnapshotRepository orderSnapshotRepository, ISnapshotSMSTemplateRepository snapshotSMSTemplateRepository, IOrderRepository orderRepository, IIndustryRepository industryRepository, IRedPackAuditRepository redPackAuditRepository, IRedPackRecordRepository redPackRecordRepository, IRepository<OrderSpecial> orderSpecialRepository, ISessionContext sessionContext, IRedPackGuiderAuditRepository redPackGuiderAuditRepository, IThirdAccountRepository thirdAccountRepository, ISupplementRecordRepository supplementRecordRepository, IPushDomainService pushDomainService, ILogger<RedPackApplication> logger, ISpecialRedPackAuditRepository specialRedPackAuditRepository, ISystemDicDataCacheManager systemDic)
     {
         _orderSnapshotRepository = orderSnapshotRepository;
         _snapshotSMSTemplateRepository = snapshotSMSTemplateRepository;
@@ -54,6 +63,8 @@ public class RedPackApplication : IRedPackApplication, IScopeDependency
         _supplementRecordRepository = supplementRecordRepository;
         _pushDomainService = pushDomainService;
         _logger = logger;
+        _specialRedPackAuditRepository = specialRedPackAuditRepository;
+        _systemDic = systemDic;
     }
 
     /// <summary>
@@ -70,8 +81,13 @@ public class RedPackApplication : IRedPackApplication, IScopeDependency
         if (redPackAudit.Status != ERedPackAuditStatus.Pending) throw UserFriendlyException.SameMessage("已审核, 不可重复审核");
         redPackAudit.SMSTemplateId = dto.SMSTemplateId;
         redPackAudit.Status = status;
-        redPackAudit.Remark = dto.Opinion;
+        redPackAudit.AuditRemark = dto.Opinion;
         redPackAudit.IsSendSMS = dto.IsSendSms;
+        redPackAudit.AuditId = _sessionContext.UserId;
+        redPackAudit.AuditName = _sessionContext.UserName;
+        redPackAudit.AuditTime = DateTime.Now;
+        redPackAudit.AuditOrgId = _sessionContext.OrgId;
+        redPackAudit.AuditOrgName = _sessionContext.OrgName;
         var order = await _orderRepository.Queryable()
             .Where(m => m.Id == redPackAudit.OrderId)
             .Select(m => new { m.Id, m.No, m.FromName, m.FromPhone })
@@ -232,6 +248,67 @@ public class RedPackApplication : IRedPackApplication, IScopeDependency
         return outDto;
     }
 
+    /// <summary>
+    /// 特殊红包审批列表
+    /// </summary>
+    /// <param name="dto"></param>
+    /// <returns></returns>
+    [ExportExcel("特殊红包审批")]
+    public ISugarQueryable<SnapshotOrderAuditItemsOutDto> GetRedPackSpecialAuditItems(SnapshotOrderAuditItemsInDto dto)
+    {
+        var beginTime = new DateTime(2024, 8, 1);
+        var endTime = new DateTime(2024, 9, 30, 23, 59, 59);
+        var query = _redPackAuditRepository.Queryable()
+            .LeftJoin<OrderSnapshot>((redPackAudit, snapshot) => redPackAudit.OrderId == snapshot.Id)
+            .LeftJoin<Order>((redPackAudit, snapshot, order) => redPackAudit.OrderId == order.Id)
+            .LeftJoin<SpecialRedPackAudit>((redPackAudit, snapshot, order, special) => redPackAudit.OrderId == special.OrderId)
+            .Where((redPackAudit, snapshot, order, special) => (snapshot.IndustryName == "安全隐患" && order.OrderTagCode!.Contains("DHZY")) || (order.CreationTime >=beginTime && order.CreationTime <= endTime && redPackAudit.ApprovedAmount == 20 && order.AcceptTypeCode != "30" && snapshot.IndustryName != "电气焊作业申报"))
+            .WhereIF(dto.Status == 0, (redPackAudit, snapshot, order, special) => special.Id == null)
+            .WhereIF(dto.Status == 1, (redPackAudit, snapshot, order, special) => special.Status == ERedPackAuditStatus.Agree)
+            .WhereIF(dto.Status == 2, (redPackAudit, snapshot, order, special) => special.Status == ERedPackAuditStatus.Refuse)
+            .OrderByDescending((redPackAudit, snapshot, order, special) => redPackAudit.CreationTime)
+                        .Select((redPackAudit, snapshot,order) => new SnapshotOrderAuditItemsOutDto
+                        {
+                            Id = redPackAudit.Id,
+                            RedPackAuditId = redPackAudit.Id,
+                            OrderId = order.Id,
+                            No = order.No,
+                            Title = order.Title,
+                            IndustryName = snapshot.IndustryName,
+                            IndustryId = snapshot.IndustryId,
+                            SourceChannel = order.SourceChannel,
+                            SourceChannelCode = order.SourceChannelCode,
+                            Status = order.Status,
+                            IsDanger = snapshot.IsDanger,
+                            FromPhone = order.FromPhone,
+                            FromName = order.FromName,
+                            AuditTime = redPackAudit.AuditTime,
+                            ApprovedAmount = redPackAudit.ApprovedAmount,
+                            AcutalAmount = redPackAudit.AcutalAmount,
+                            IsIssued = redPackAudit.IsIssued,
+                            //RecordRemark = record.Remark,
+                            County = order.County,
+                            // IsRectify = s.IsRepetition
+                            IsDeal = snapshot.IsDeal,
+                            NetworkENumber = snapshot.NetworkENumber,
+                            IsTruth = snapshot.IsTruth,
+                            IsTruthDepartment = snapshot.IsTruthDepartment,
+                            IsRepetition = snapshot.IsRepetition,
+                            CreationTime = order.CreationTime,
+                            OrgLevelOneCode = order.OrgLevelOneCode,
+                            OrgLevelOneName = order.OrgLevelOneName,
+                            AuditId = redPackAudit.AuditId,
+                            AuditName = redPackAudit.AuditName,
+                            AuditOrgId = redPackAudit.AuditOrgId,
+                            AuditOrgName = redPackAudit.AuditOrgName,
+                            AuditRemark = redPackAudit.AuditRemark,
+                            //BankCardNo = record.BankCardNo,
+                            //OpenBank = record.OpenBank,
+                            AuditStatus = redPackAudit.Status,
+                        });
+        return query;
+    }
+
     /// <summary>
     /// 红包审核集合
     /// </summary>
@@ -438,10 +515,50 @@ public class RedPackApplication : IRedPackApplication, IScopeDependency
         var record = await _redPackRecordRepository.Queryable()
             .Where(m => m.RedPackAuditId == dto.RedPackAuditId)
             .FirstAsync() ?? throw UserFriendlyException.SameMessage("审核记录不存在");
+        record.DistributionState = EReadPackSendStatus.Successful;
+        record.PickupStatus = ERedPackPickupStatus.Received;
         var audit = await _redPackAuditRepository.GetAsync(dto.RedPackAuditId) ?? throw UserFriendlyException.SameMessage("审核记录不存在");
 
         dto.Adapt(audit);
         await _redPackAuditRepository.UpdateAsync(audit);
+        await _redPackRecordRepository.UpdateAsync(record);
+
+        var entity = dto.Adapt<SupplementRecord>();
+        var industryName = await _orderSnapshotRepository.Queryable()
+            .Where(m => m.Id == record.OrderId)
+            .Select(m => new { m.IndustryName, m.IndustryId })
+            .FirstAsync();
+        entity.OrderId = record.OrderId;
+        entity.RedPackRecordId = record.Id;
+        entity.RedPackAuditId = audit.Id;
+        entity.No = record.No;
+        entity.IndustryName = industryName.IndustryName;
+        entity.IndustryId = industryName.IndustryId;
+        await _supplementRecordRepository.AddAsync(entity);
+
+        if (dto.IsSendSMS)
+        {
+            // TODO: 发送短信
+        }
+    }
+
+    /// <summary>
+    /// 添加补充发放信息(特殊红包)
+    /// </summary>
+    /// <param name="dto"></param>
+    /// <returns></returns>
+    public async Task UpdateRedPackSpecialRecordAsync(UpdateRedPackRecordInDto dto)
+    {
+        var record = await _redPackRecordRepository.Queryable()
+            .Where(m => m.RedPackAuditId == dto.RedPackAuditId)
+            .FirstAsync() ?? throw UserFriendlyException.SameMessage("审核记录不存在");
+        record.DistributionState = EReadPackSendStatus.Successful;
+        record.PickupStatus = ERedPackPickupStatus.Received;
+        var audit = await _specialRedPackAuditRepository.GetAsync(dto.RedPackAuditId) ?? throw UserFriendlyException.SameMessage("审核记录不存在");
+
+        dto.Adapt(audit);
+        await _specialRedPackAuditRepository.UpdateAsync(audit);
+        await _redPackRecordRepository.UpdateAsync(record);
 
         var entity = dto.Adapt<SupplementRecord>();
         var industryName = await _orderSnapshotRepository.Queryable()
@@ -462,6 +579,7 @@ public class RedPackApplication : IRedPackApplication, IScopeDependency
         }
     }
 
+
     #region 红包发放记录
 
     /// <summary>
@@ -577,6 +695,75 @@ public class RedPackApplication : IRedPackApplication, IScopeDependency
         return query;
     }
 
+    public async Task AuditRedPackSpecialAuditAsync(UpdateRedPackSpecialAuditInDto dto)
+    {
+        var audit = await _redPackAuditRepository.GetAsync(dto.RedPackAuditId) ?? throw UserFriendlyException.SameMessage("审核记录不存在");
+        if (_specialRedPackAuditRepository.ExistByOrderId(audit.OrderId)) throw UserFriendlyException.SameMessage("已审核, 不可重复审核");
+        var specialAudit = audit.Adapt<SpecialRedPackAudit>();
+        if (dto.AuditTypeId.NotNullOrEmpty())
+        { 
+            var specialType = _systemDic.SnapshotRedPackSpecialType.FirstOrDefault(m => m.Id == dto.AuditTypeId);
+            if (specialType != null)
+            {
+                specialAudit.AuditType = specialType.DicDataName;
+                specialAudit.AuditTypeCode = specialType.DicDataValue;
+            }
+        }
+        specialAudit.Status = dto.Status;
+        specialAudit.ApprovedAmount = dto.ApprovedAmount;
+        specialAudit.AuditId = _sessionContext.UserId;
+        specialAudit.AuditName = _sessionContext.UserName;
+        specialAudit.AuditOrgId = _sessionContext.OrgId;
+        specialAudit.AuditOrgName = _sessionContext.OrgName;
+        await _specialRedPackAuditRepository.AddAsync(specialAudit);
+    }
+
+    public async Task<SnapshotOrderAuditSpecialDetailOutDto> GetRedPackSpecialAuditBaseDataAsync(string id)
+    {
+        var outDto = new SnapshotOrderAuditSpecialDetailOutDto();
+        var order = await _orderRepository.GetAsync(id) ?? throw new UserFriendlyException("工单不存在");
+        outDto.Order = order.Adapt<SnapshotOrderAuditOrderDetailOutDto>();
+        outDto.AuditType = _systemDic.SnapshotRedPackSpecialType;
+        outDto.AuditComBox = EnumExts.GetDescriptions<ERedPackAuditStatus>().Where(m => m.Key != 0).ToList();
+        var audit = await _redPackAuditRepository.GetAsync(m => m.OrderId == id);
+        outDto.AuditDetail = audit.Adapt<SnapshotOrderAuditSpecialAuditDetailOutDto>();
+        return outDto;
+    }
+
+    public async Task<GetRedPackRecordBaseDataOutDto> GetRedPackRecordBaseDataAsync(string orderId)
+    {
+        var snapshot = await _orderSnapshotRepository.Queryable()
+            .Where(m => m.Id == orderId)
+            .Select(m => new
+            {
+                m.Id,
+                m.AwardBankCardNo,
+                m.AwardOpenBank,
+                m.AwardName,
+                m.AwardAmount
+            })
+            .FirstAsync();
+        var outDto = new GetRedPackRecordBaseDataOutDto();
+        if (snapshot != null)
+        {
+            outDto.BankCardNo = snapshot.AwardBankCardNo;
+            outDto.OpenBank = snapshot.AwardOpenBank;
+            outDto.Name = snapshot.AwardName;
+            outDto.Amount = snapshot.AwardAmount;
+        }
+        var audit = await _specialRedPackAuditRepository.Queryable().Where(m => m.OrderId == orderId).FirstAsync() 
+            ?? throw new UserFriendlyException("审核记录不存在");
+        if (audit != null)
+        {
+            outDto.AuditType = audit.AuditType;
+            outDto.AuditTypeCode = audit.AuditTypeCode;
+            outDto.RedPackAuditId = audit.Id;
+        }
+        outDto.OrderId = orderId;
+        outDto.ReplenishType = _systemDic.SnapshotReplenishType;
+        return outDto;
+    }
+
 
     #endregion
 }

+ 4 - 4
src/Hotline.Application/Snapshot/SnapshotOrderApplication.cs

@@ -278,15 +278,15 @@ public class SnapshotOrderApplication : IOrderSnapshotApplication, IScopeDepende
         {
             throw UserFriendlyException.SameMessage("随手拍工单标记不能为空");
         }
-        snapshot.Labels = labels;
-        snapshot.LabelName = string.Join('|', labels.Select(m => m.Value));
+        snapshot!.Labels = labels;
+        snapshot.LabelName = string.Join('|', labels!.Select(m => m.Value));
         //if (labels.Any(m => m.Key == "yzg"))
         //    snapshot.IsRectifyDepartment = true;
         //if (labels.Any(m => m.Key == "wzg"))
         //    snapshot.IsRectifyDepartment = false;
-        if (labels.Any(m => m.Key == "ss"))
+        if (labels!.Any(m => m.Key == "ss"))
             snapshot.IsTruthDepartment = true;
-        if (labels.Any(m => m.Key == "bss"))
+        if (labels!.Any(m => m.Key == "bss"))
             snapshot.IsTruthDepartment = false;
         snapshot.LabelTime = DateTime.Now;
         snapshot.LabelUserName = _sessionContext.UserName;

+ 3 - 0
src/Hotline.Application/StatisticalReport/CallReport/YiBinCallReportApplication.cs

@@ -339,6 +339,8 @@ public class YiBinCallReportApplication : CallReportApplicationBase, ICallReport
         return  _trCallRecordRepository.Queryable()
             .WhereIF(dto.RingStartTime.HasValue, x => x.BeginRingTime >= dto.RingStartTime)
             .WhereIF(dto.RingEndTime.HasValue, x => x.BeginRingTime <= dto.RingEndTime)
+            .WhereIF(dto.EndRingStartTime.HasValue,x=>x.EndRingTimg >= dto.EndRingStartTime)
+            .WhereIF(dto.EndRingEndTime.HasValue,x=>x.EndRingTimg <= dto.EndRingEndTime)
             .WhereIF(!string.IsNullOrEmpty(dto.EmpId), x => x.UserId == dto.EmpId)
             .WhereIF(dto.AnsweredStartTime.HasValue, x => x.AnsweredTime >= dto.AnsweredStartTime)
             .WhereIF(dto.AnsweredEndTime.HasValue, x => x.AnsweredTime <= dto.AnsweredEndTime)
@@ -363,6 +365,7 @@ public class YiBinCallReportApplication : CallReportApplicationBase, ICallReport
                 TelNo = x.TelNo,
                 AnsweredTime = x.AnsweredTime,
                 RingTimeBegin  = x.BeginRingTime,
+                RingTimeEnd = x.EndRingTimg,
                 SeatName = x.UserName
             });
     }

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

+ 24 - 0
src/Hotline.Repository.SqlSugar/Snapshot/SpecialRedPackAuditRepository.cs

@@ -0,0 +1,24 @@
+using Hotline.Repository.SqlSugar.DataPermissions;
+using Hotline.Snapshot;
+using Hotline.Snapshot.Interfaces;
+using MediatR;
+using SqlSugar;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using XF.Domain.Dependency;
+
+namespace Hotline.Repository.SqlSugar.Snapshot;
+public class SpecialRedPackAuditRepository : BaseRepository<SpecialRedPackAudit>, ISpecialRedPackAuditRepository, IScopeDependency
+{
+    public SpecialRedPackAuditRepository(ISugarUnitOfWork<HotlineDbContext> uow, IDataPermissionFilterBuilder dataPermissionFilterBuilder, IServiceProvider serviceProvider) : base(uow, dataPermissionFilterBuilder, serviceProvider)
+    {
+    }
+
+    public bool ExistByOrderId(string orderId)
+    {
+        return Queryable().Where(m => m.OrderId == orderId).Any();
+    }
+}

+ 4 - 5
src/Hotline.Share/Dtos/Article/BulletinDto.cs

@@ -250,8 +250,7 @@ namespace Hotline.Share.Dtos.Article
 
         public bool? IsTimeOut { get; set; }
     }
-
-
+   
     public record AddCircularDto
     {
         /// <summary>
@@ -282,7 +281,7 @@ namespace Hotline.Share.Dtos.Article
         /// <summary>
         /// 失效时间
         /// </summary>
-        public DateTime LostEfficacyTime { get; set; }
+        public DateTime? LostEfficacyTime { get; set; }
 
         /// <summary>
         /// 来源单位ID
@@ -328,7 +327,7 @@ namespace Hotline.Share.Dtos.Article
         /// <summary>
         /// 失效时间
         /// </summary>
-        public DateTime LoseEfficacyTime { get; set; }
+        public DateTime? LoseEfficacyTime { get; set; }
 
         /// <summary>
         /// 发布范围  EPushRange
@@ -376,7 +375,7 @@ namespace Hotline.Share.Dtos.Article
         /// <summary>
         /// 失效时间
         /// </summary>
-        public DateTime LoseEfficacyTime { get; set; }
+        public DateTime? LoseEfficacyTime { get; set; }
 
         /// <summary>
         /// 发布范围  EPushRange

+ 15 - 0
src/Hotline.Share/Dtos/CallCenter/BiQueryCallsDto.cs

@@ -645,6 +645,16 @@ public record QuerySeatMonthCallDetailRequest:PagedRequest
     /// </summary>
     public DateTime? RingEndTime { get;set; }
 
+    /// <summary>
+    /// 振铃结束时间开始
+    /// </summary>
+    public DateTime? EndRingStartTime { get; set; }
+
+    /// <summary>
+    /// 振铃结束时间结束
+    /// </summary>
+    public DateTime? EndRingEndTime { get; set; }
+
     /// <summary>
     /// 点数进入明细类型(1:呼入接通总量 2:有效接通量 3:接通秒挂量 4:超时接通量 5:按时接通量 6:呼入未接通总量 7:未接通秒挂量 8:超时未接通量)
     /// </summary>
@@ -668,6 +678,11 @@ public class QuerySeatMonthCallDetailResp
     /// </summary>
     public DateTime? RingTimeBegin { get; set; }
 
+    /// <summary>
+    /// 振铃结束时间
+    /// </summary>
+    public DateTime? RingTimeEnd { get; set; }
+
     /// <summary>
     /// 接通时间
     /// </summary>

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

+ 4 - 0
src/Hotline.Share/Dtos/Realtime/RingDto.cs

@@ -31,4 +31,8 @@ public record CircularRecoordDto
     public int Count { get; set; }
 
     public ECircularType CircularType { get; set; }
+
+    public string? SendContent { get; set; }
+
+    public string? Title { get; set; }
 }

+ 119 - 0
src/Hotline.Share/Dtos/Snapshot/OrderDto.cs

@@ -702,6 +702,58 @@ public class GetRedPackAuditSMSTemplateInDto
     public ESnapshotSMSStatus Status { get; set; }
 }
 
+public class SnapshotOrderAuditSpecialDetailOutDto
+{ 
+    /// <summary>
+    /// 工单信息
+    /// </summary>
+    public SnapshotOrderAuditOrderDetailOutDto Order { get; set; }
+
+    /// <summary>
+    /// 审核信息
+    /// </summary>
+    public SnapshotOrderAuditSpecialAuditDetailOutDto AuditDetail { get; set; }
+
+    /// <summary>
+    /// 审核状态
+    /// </summary>
+    public IEnumerable<KeyValuePair<int, string>> AuditComBox { get; set; }
+
+    /// <summary>
+    /// 审核类型
+    /// </summary>
+    public IEnumerable<SystemDicDataOutDto> AuditType { get; set; }
+}
+
+public class SnapshotOrderAuditSpecialAuditDetailOutDto
+{
+    /// <summary>
+    /// 审批人
+    /// </summary>
+    public string? AuditId { get; set; }
+
+    /// <summary>
+    /// 审批人
+    /// </summary>
+    public string? AuditName { get; set; }
+
+    /// <summary>
+    /// 审批部门
+    /// </summary>
+    public string? AuditOrgId { get; set; }
+
+    /// <summary>
+    /// 审批部门名称
+    /// </summary>
+    public string? AuditOrgName { get; set; }
+
+    /// <summary>
+    /// 审批意见
+    /// </summary>
+    public string? AuditRemark { get; set; }
+
+}
+
 public class SnapshotOrderAuditDetailOutDto
 {
     /// <summary>
@@ -860,6 +912,36 @@ public class UpdateRedPackGuiderAuditInDto
     public string Opinion { get; set; }
 }
 
+public class UpdateRedPackSpecialAuditInDto
+{
+    /// <summary>
+    /// 审核Id
+    /// </summary>
+    [Required]
+    public string RedPackAuditId { get; set; }
+
+    /// <summary>
+    /// 审核状态
+    /// </summary>
+    public ERedPackAuditStatus Status { get; set; }
+
+    /// <summary>
+    /// 审批类型
+    /// </summary>
+    public string? AuditTypeId { get; set; }
+
+    /// <summary>
+    /// 审核意见
+    /// </summary>
+    public string Opinion { get; set; }
+
+    /// <summary>
+    /// 审批金额
+    /// </summary>
+    public double? ApprovedAmount { get; set; }
+
+}
+
 public class UpdateRedPackAuditInDto
 {
     /// <summary>
@@ -889,6 +971,43 @@ public class UpdateRedPackAuditInDto
     public bool IsSendSms { get; set; }
 }
 
+public class GetRedPackRecordBaseDataOutDto
+{
+    /// <summary>
+    /// 金额
+    /// </summary>
+    public double? Amount { get; set; }
+
+    /// <summary>
+    /// 名字
+    /// </summary>
+    public string? Name { get; set; }
+
+    /// <summary>
+    /// 开户银行
+    /// </summary>
+    public string? OpenBank { get; set; }
+
+    /// <summary>
+    /// 银行卡号
+    /// </summary>
+    public string? BankCardNo { get; set; }
+
+    /// <summary>
+    /// 补充奖励类型
+    /// </summary>
+    public string? AuditType { get; set; }
+
+    /// <summary>
+    /// 补充奖励类型
+    /// </summary>
+    public string? AuditTypeCode { get; set; }
+    public string OrderId { get; set; }
+
+    public IReadOnlyCollection<SystemDicDataOutDto> ReplenishType { get; set; }
+
+    public string? RedPackAuditId { get; set; }
+}
 public class GetAuditBackBaseDataOutDto
 {
     /// <summary>

+ 40 - 0
src/Hotline.Share/Dtos/Snapshot/StatisticsDto.cs

@@ -0,0 +1,40 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Hotline.Share.Dtos.Snapshot;
+
+/// <summary>
+/// 随手拍统计输出Dto
+/// </summary>
+public class SnapshotStatisticsOutDto
+{ 
+    /// <summary>
+    /// 随手拍来件数
+    /// </summary>
+    public int SuiShouPaiLaiJianShu { get; set; }
+}
+
+/// <summary>
+/// 随手拍统计输入Dto
+/// </summary>
+public class SnapshotStatisticsInDto
+{
+    /// <summary>
+    /// 开始时间
+    /// </summary>
+    [Required]
+    public DateTime StartTime { get; set; }
+
+    /// <summary>
+    /// 结束时间
+    /// </summary>
+    [Required]
+    public DateTime EndTime { get; set; }
+}
+public class StatisticsDto
+{
+}

+ 1 - 1
src/Hotline/Article/Bulletin.cs

@@ -33,7 +33,7 @@ namespace Hotline.Article
         /// <summary>
         /// 失效时间
         /// </summary>
-        public DateTime LoseEfficacyTime { get; set; }
+        public DateTime? LoseEfficacyTime { get; set; }
 
         /// <summary>
         /// 公告状态

+ 1 - 1
src/Hotline/Article/Circular.cs

@@ -50,7 +50,7 @@ namespace Hotline.Article
         /// <summary>
         /// 失效时间
         /// </summary>
-        public DateTime LostEfficacyTime { get; set; }
+        public DateTime? LostEfficacyTime { get; set; }
 
         /// <summary>
         /// 通知状态

+ 70 - 5
src/Hotline/Article/CircularRecordDomainService.cs

@@ -1,4 +1,9 @@
 using Hotline.Realtimes;
+using Hotline.Share.Dtos.Article;
+using Hotline.Share.Enums.Article;
+using MapsterMapper;
+using Microsoft.AspNetCore.Http;
+using XF.Domain.Authentications;
 using XF.Domain.Dependency;
 using XF.Domain.Repository;
 
@@ -8,12 +13,21 @@ namespace Hotline.Article
     {
         private readonly IRepository<CircularRecord> _circularRecordRepository;
         private readonly IRealtimeService _realtimeService;
-        public CircularRecordDomainService(IRepository<CircularRecord> circularRecordRepository, IRealtimeService realtimeService)
+        private readonly IMapper _mapper;
+        private readonly IRepository<Circular> _circularRepository;
+        private readonly IRepository<CircularReadGroup> _circularReadGroupRepository;
+        private readonly ISessionContext _sessionContext;
+
+        public CircularRecordDomainService(IRepository<CircularRecord> circularRecordRepository, IRealtimeService realtimeService, IMapper mapper, IRepository<Circular> circularRepository, IRepository<CircularReadGroup> circularReadGroupRepository, ISessionContext sessionContext)
         {
             _circularRecordRepository = circularRecordRepository;
             _realtimeService = realtimeService;
+            _mapper = mapper;
+            _circularRepository = circularRepository;
+            _circularReadGroupRepository = circularReadGroupRepository;
+            _sessionContext = sessionContext;
         }
-        public async Task RecordUserHandle(List<string> userIds, bool isAdd,int count = 1, CancellationToken cancellationToken= default)
+        public async Task RecordUserHandle(List<string> userIds, bool isAdd,string title,string sendContent,int count = 1, CancellationToken cancellationToken= default)
         {
             foreach (var item in userIds)
             {
@@ -51,14 +65,14 @@ namespace Hotline.Article
                 }
                 try
                 {
-                    await _realtimeService.CircularRecoordAsync(userRecord.Id, cancellationToken);
+                    await _realtimeService.CircularRecoordAsync(userRecord.Id,title,sendContent, cancellationToken);
                 }
                 catch{}
             }
             
         }
 
-        public async Task RecordOrgHandle(List<string> orgIds, bool isAdd,int count =1, CancellationToken cancellationToken = default)
+        public async Task RecordOrgHandle(List<string> orgIds, bool isAdd,string title, string sendContent, int count =1, CancellationToken cancellationToken = default)
         {
             foreach (var item in orgIds)
             {
@@ -99,10 +113,61 @@ namespace Hotline.Article
                 }
                 try
                 {
-                    await _realtimeService.CircularRecoordAsync(orgRecord.Id, cancellationToken);
+                    await _realtimeService.CircularRecoordAsync(orgRecord.Id, title, sendContent, cancellationToken);
                 }
                 catch{}
             }
         }
+
+        public async Task AddCircularMessage(AddCircularDto dto, CancellationToken cancellationToken = default)
+        {
+            var model = _mapper.Map<Circular>(dto);
+            model.CommitTime = DateTime.Now;
+            model.ExaminTime = DateTime.Now;
+            model.ExaminManId = _sessionContext.UserId;
+            model.CircularState = ECircularState.ReviewPass;
+            model.ExaminOpinion = "内部消息,系统默认通过";
+            model.ReadedNum = 0;
+            model.NeedReadNum = model.CircularReadGroups.Count;
+            model.LostEfficacyTime = DateTime.Now.AddYears(50);
+            var id = await _circularRepository.AddAsync(model,cancellationToken);
+            //子表
+            var list = dto.CircularReadGroups;
+            var readGroupList = new List<CircularReadGroup>();
+            foreach (var item in list)
+            {
+                var readGroup = _mapper.Map<CircularReadGroup>(item);
+                readGroup.CircularId = id;
+                readGroup.IsRead = false;
+                readGroupList.Add(readGroup);
+            }
+            await _circularReadGroupRepository.AddRangeAsync(readGroupList);
+
+            //更新阅读数量
+            if (model.CircularType == Share.Enums.Article.ECircularType.Person)
+            {
+                //个人
+                var userlist = await _circularReadGroupRepository.Queryable()
+                    .Where(x => x.CircularId == model.Id)
+                .Select(x => x.UserId)
+                    .ToListAsync();
+                if (userlist != null && userlist.Count > 0)
+                {
+                    await this.RecordUserHandle(userlist, true,model.Title,model.Content);
+                }
+            }
+            else
+            {
+                //部门
+                var orglist = await _circularReadGroupRepository.Queryable()
+                    .Where(x => x.CircularId == model.Id)
+                .Select(x => x.OrgId)
+                    .ToListAsync();
+                if (orglist != null && orglist.Count > 0)
+                {
+                    await this.RecordOrgHandle(orglist, true,model.Title,model.Content);
+                }
+            }
+        }
     }
 }

+ 12 - 2
src/Hotline/Article/ICircularRecordDomainService.cs

@@ -1,10 +1,20 @@
 
+using Hotline.Share.Dtos.Article;
+
 namespace Hotline.Article
 {
     public interface ICircularRecordDomainService
     {
-        Task RecordUserHandle(List<string> userIds,bool isAdd,int count = 1,CancellationToken cancellationToken = default);
+        Task RecordUserHandle(List<string> userIds,bool isAdd,string title, string sendContent, int count = 1,CancellationToken cancellationToken = default);
+
+        Task RecordOrgHandle(List<string> orgIds, bool isAdd,string title, string sendContent, int count = 1, CancellationToken cancellationToken= default);
 
-        Task RecordOrgHandle(List<string> orgIds, bool isAdd, int count = 1, CancellationToken cancellationToken= default);
+        /// <summary>
+        /// 内部通知(无视审核)
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <param name="cancellationToken"></param>
+        /// <returns></returns>
+        Task AddCircularMessage(AddCircularDto dto, CancellationToken cancellationToken = default);
     }
 }

+ 2 - 0
src/Hotline/Caching/Interfaces/IRealtimeCacheManager.cs

@@ -12,5 +12,7 @@ namespace Hotline.Caching.Interfaces
         Task<RealtimeConnection> GetConnectionAsync(string userId, CancellationToken cancellationToken);
 
         Task<bool> IsInGroupAsync(string useId, string groupName, CancellationToken cancellationToken);
+
+        Task<RealtimeConnection> GetConnectionOtherAsync(string userId, CancellationToken cancellationToken);
     }
 }

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

@@ -77,5 +77,10 @@ namespace Hotline.Caching.Interfaces
         /// 兴唐动作状态映射
         /// </summary>
         IReadOnlyCollection<SystemDicDataOutDto> XingTangOperationMap { get; }
+
+        /// <summary>
+        /// 随手拍特殊红包审核类型
+        /// </summary>
+        IReadOnlyCollection<SystemDicDataOutDto> SnapshotRedPackSpecialType { get; }
     }
 }

+ 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/RealtimeCacheManager.cs

@@ -28,4 +28,9 @@ public class RealtimeCacheManager : IRealtimeCacheManager, IScopeDependency
         var connection = await GetConnectionAsync(useId, cancellationToken);
         return connection.IsInGroup(groupName);
     }
+
+    public async Task<RealtimeConnection> GetConnectionOtherAsync(string userId, CancellationToken cancellationToken)
+    {
+        return await _cacheConnection.GetAsync(userId, cancellationToken);
+    }
 }

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

@@ -137,6 +137,11 @@ namespace Hotline.Caching.Services
         /// </summary>
         public IReadOnlyCollection<SystemDicDataOutDto> VisitSatisfaction => GetOrAdd(SysDicTypeConsts.VisitSatisfaction);
 
+        /// <summary>
+        /// 随手拍特殊红包审核类型
+        /// </summary>
+        public IReadOnlyCollection<SystemDicDataOutDto> SnapshotRedPackSpecialType => GetOrAdd(SysDicTypeConsts.SnapshotRedPackSpecialType);
+
         /// <summary>
         /// 兴唐动作状态映射
         /// </summary>

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

+ 1 - 9
src/Hotline/Realtimes/IRealtimeService.cs

@@ -24,7 +24,7 @@ namespace Hotline.Realtimes
 
         #region 系统消息通知
 
-        Task CircularRecoordAsync(string id, CancellationToken cancellationToken);
+        Task CircularRecoordAsync(string id,string title,string sendContent, CancellationToken cancellationToken);
 
         #endregion
 
@@ -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);
     }
 }

+ 13 - 0
src/Hotline/SeedData/SystemDicDataSeedData.cs

@@ -29,6 +29,15 @@ public class SystemDicDataSeedData : ISeedData<SystemDicData>
                 new() { Id = "08dd5190-3fbb-4156-864e-9ebbe46b498e", DicDataValue = "30", DicDataName = "400", Sort = 7},
                 ];
         }
+        if (dicTypeCode == SysDicTypeConsts.SnapshotRedPackSpecialType)
+        {
+            return [
+                new() {  Id = "08dd4bf7-b7c6-471b-8ee5-deb5a1c4776a", DicDataValue = "dqh", DicDataName = "电气焊类", Sort = 1},
+                new() {  Id = "08dd4bf7-b7ab-4729-80b2-b2c5db5d221c", DicDataValue = "qp", DicDataName = "气瓶类", Sort = 2},
+                new() {  Id = "08dd4bf7-b352-4b52-8a96-78f73197c22e", DicDataValue = "dde", DicDataName = "电动车类", Sort = 3},
+                new() {  Id = "08dd4bf7-b349-48ea-8f54-fb8b29070fa3", DicDataValue = "xftd", DicDataName = "消防通道", Sort = 4},
+                ];
+        }
         if (dicTypeCode == SysDicTypeConsts.SnapshotOrderLabel)
         {
             return [
@@ -208,6 +217,10 @@ public class SystemDicDataSeedData : ISeedData<SystemDicData>
         {
             dicType = ["08dbefe5-1cc5-4069-8d69-33afd978f68d", "兴唐动作状态映射"];
         }
+        if (dicTypeCode == SysDicTypeConsts.SnapshotRedPackSpecialType)
+        {
+            dicType = ["08dd4bf7-b7cd-471b-8729-73b3ceaae8f5", "随手拍特殊红包审批类型"];
+        }
 
         return new SystemDicType
         {

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

@@ -713,5 +713,20 @@ namespace Hotline.Settings
         /// 兴唐恒信 - 分机状态变更接收
         /// </summary>
         public const string XthxVerificationStatusAdd = "XthxVerificationStatusAdd";
+
+        /// <summary>
+        /// 是否启用小休计时
+        /// </summary>
+        public const string IsTelRest = "IsTelRest";
+
+        /// <summary>
+        /// 小休计时秒数
+        /// </summary>
+        public const string TelRestNum = "TelRestNum";
+
+        /// <summary>
+        /// 是否不推送呼出的无文件的通话记录
+        /// </summary>
+        public const string IsNoPushCallNativeOutNoFile = "IsNoPushCallNativeOutNoFile";
     }
 }

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

@@ -311,4 +311,9 @@ public class SysDicTypeConsts
     /// 兴唐动作状态映射
     /// </summary>
     public static string XingTangOperationMap = "XingTangOperationMap";
+
+    /// <summary>
+    /// 随手拍特殊红包审核类型
+    /// </summary>
+    public static string SnapshotRedPackSpecialType = "SnapshotRedPackSpecialType";
 }

+ 12 - 0
src/Hotline/Snapshot/Interfaces/ISpecialRedPackAuditRepository.cs

@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using XF.Domain.Repository;
+
+namespace Hotline.Snapshot.Interfaces;
+public interface ISpecialRedPackAuditRepository : IRepository<SpecialRedPackAudit>
+{
+    bool ExistByOrderId(string orderId);
+}

+ 137 - 0
src/Hotline/Snapshot/SpecialRedPackAudit.cs

@@ -0,0 +1,137 @@
+using SqlSugar;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using XF.Domain.Repository;
+using Hotline.Orders;
+using Hotline.Share.Enums.Snapshot;
+
+namespace Hotline.Snapshot;
+
+/// <summary>
+/// 特殊红包审核
+/// 在特殊红包审核界面审核通过/不通过后生成数据
+/// </summary>
+[Description("特殊市民红包审核")]
+public class SpecialRedPackAudit : CreationSoftDeleteEntity
+{
+    /// <summary>
+    /// 关联工单编号
+    /// <inheritdoc cref="Order"/>表的Id字段
+    /// </summary>    
+    [SugarColumn(ColumnDescription = "关联工单编号")]
+    public string OrderId { get; set; }
+
+    /// <summary>
+    /// 领取手机号码
+    /// </summary>
+    [SugarColumn(ColumnDescription = "领取红包手机号码")]
+    public string? PhoneNumber { get; set; }
+
+    /// <summary>
+    /// 审核状态
+    /// </summary>
+    [SugarColumn(ColumnDescription = "审核状态")]
+    public ERedPackAuditStatus Status { get; set; }
+
+    /// <summary>
+    /// 审批时间
+    /// </summary>
+    [SugarColumn(ColumnDescription = "审批时间")]
+    public DateTime? AuditTime { get; set; }
+
+    /// <summary>
+    /// 配置金额
+    /// </summary>
+    [SugarColumn(ColumnDescription = "配置金额")]
+    public double ShouldAmount { get; set; }
+
+    /// <summary>
+    /// 审批金额
+    /// </summary>
+    [SugarColumn(ColumnDescription = "审批金额")]
+    public double? ApprovedAmount { get; set; }
+
+    /// <summary>
+    /// 实发金额
+    /// </summary>
+    [SugarColumn(ColumnDescription = "实发金额")]
+    public double? AcutalAmount { get; set; }
+
+    /// <summary>
+    /// 市民奖励发放结果
+    /// </summary>
+    [SugarColumn(ColumnDescription = "市民奖励发放结果")]
+    public bool IsSend { get; set; }
+
+    /// <summary>
+    /// 市民奖励发放备注
+    /// </summary>
+    [SugarColumn(ColumnDescription = "市民奖励发放备注")]
+    public string? SendRemarks { get; set; }
+
+    /// <summary>
+    /// 操作人
+    /// </summary>
+    [SugarColumn(ColumnDescription = "操作人")]
+    public string? AuditId { get; set; }
+
+    /// <summary>
+    /// 操作人
+    /// </summary>
+    [SugarColumn(ColumnDescription = "操作人")]
+    public string? AuditName { get; set; }
+
+    /// <summary>
+    /// 审批部门
+    /// </summary>
+    [SugarColumn(ColumnDescription = "审批部门")]
+    public string? AuditOrgId { get; set; }
+
+    /// <summary>
+    /// 审批部门名称
+    /// </summary>
+    [SugarColumn(ColumnDescription = "审批部门名称")]
+    public string? AuditOrgName { get; set; }
+
+    /// <summary>
+    /// 审批意见
+    /// </summary>
+    [SugarColumn(ColumnDescription = "审批意见")]
+    public string? AuditRemark { get; set; }
+
+    public string? Remark { get; set; }
+
+    /// <summary>
+    /// 是否已发放
+    /// </summary>
+    [SugarColumn(ColumnDescription = "是否已发放")]
+    public bool? IsIssued { get; set; }
+
+    /// <summary>
+    /// 短信Id
+    /// </summary>
+    [SugarColumn(ColumnDescription = "短信Id")]
+    public string? SMSTemplateId { get; set; }
+
+    /// <summary>
+    /// 是否发送短信
+    /// </summary>
+    [SugarColumn(ColumnDescription = "是否发送短信")]
+    public bool? IsSendSMS { get; set; }
+
+    /// <summary>
+    /// 特殊红包审核,审批类型
+    /// </summary>
+    [SugarColumn(ColumnDescription = "特殊红包审核,审批类型")]
+    public string? AuditType { get; set; }
+
+    /// <summary>
+    /// 特殊红包审核,审批类型
+    /// </summary>
+    [SugarColumn(ColumnDescription = "特殊红包审核,审批类型")]
+    public string? AuditTypeCode { get; set; }
+}

+ 18 - 0
src/Hotline/Snapshot/SupplementRecord.cs

@@ -93,9 +93,27 @@ public class SupplementRecord : FullStateEntity
     [SugarColumn(ColumnDescription = "开户行")]
     public string? OpenBank { get; set; }
 
+    /// <summary>
+    /// 补充发放人姓名
+    /// </summary>
+    [SugarColumn(ColumnDescription = "补充发放人姓名")]
+    public string? Name { get; set; }
+
     /// <summary>
     /// 是否发送短信
     /// </summary>
     [SugarColumn(ColumnDescription = "是否发送短信")]
     public bool IsSendSMS { get; set; }
+
+    /// <summary>
+    /// 特殊红包审核,审批类型
+    /// </summary>
+    [SugarColumn(ColumnDescription = "特殊红包审核,审批类型")]
+    public string? AuditType { get; set; }
+
+    /// <summary>
+    /// 特殊红包审核,审批类型
+    /// </summary>
+    [SugarColumn(ColumnDescription = "特殊红包审核,审批类型")]
+    public string? AuditTypeCode { get; set; }
 }

+ 2 - 2
src/TianQue.Sdk/TiqnQueService.cs

@@ -98,8 +98,8 @@ public class TiqnQueService : IGuiderSystemService, IScopeDependency
             Message = result.Msg,
             Result = new GuiderSystemOutDto
             {
-                GuiderSystemId = result.Data.AppealNumber,
-                JsonData = result.Data.ToJson()
+                GuiderSystemId = result.Data?.AppealNumber,
+                JsonData = result.Data?.ToJson()
             }
         };
     }

+ 1 - 7
test/Hotline.Tests/Application/KnowApplicationTest.cs

@@ -33,13 +33,7 @@ public class KnowApplicationTest : TestBase
     private readonly IRepository<KnowledgeWord> _knowledgeWordRepository;
     private readonly IRepository<KnowledgeHotWord> _knowledgeHotWordRepository;
 
-    public KnowApplicationTest(IAccountRepository accountRepository, IRepository<Role> roleRepository,
-        UserController userController, IServiceScopeFactory scopeFactory,
-        IRepository<User> userRepository, IHttpContextAccessor httpContextAccessor,
-        IKnowApplication knowApplication, IMediator mediator, IRepository<KnowledgeBase.Knowledge> knowledgeRepository,
-        IKnowledgeDomainService knowledgeDomainService, IRepository<KnowledgeWord> knowledgeWordRepository,
-        IRepository<KnowledgeHotWord> knowledgeHotWordRepository, IThirdIdentiyService thirdService, IThirdAccountRepository thirdAccount, ITypedCache<SystemSetting> cacheSettingData)
-        : base(accountRepository, roleRepository, userController, scopeFactory, userRepository, httpContextAccessor, thirdService, thirdAccount, cacheSettingData)
+    public KnowApplicationTest(IAccountRepository accountRepository, IRepository<Role> roleRepository, UserController userController, IServiceScopeFactory scopeFactory, IRepository<User> userRepository, IHttpContextAccessor httpContextAccessor, IKnowApplication knowApplication, IMediator mediator, IRepository<KnowledgeBase.Knowledge> knowledgeRepository, IKnowledgeDomainService knowledgeDomainService, IRepository<KnowledgeWord> knowledgeWordRepository, IRepository<KnowledgeHotWord> knowledgeHotWordRepository, IThirdIdentiyService thirdService, IThirdAccountRepository thirdAccount, ITypedCache<SystemSetting> cacheSettingData) : base(accountRepository, roleRepository, userController, scopeFactory, userRepository, httpContextAccessor, thirdService, thirdAccount, cacheSettingData)
     {
         _knowApplication = knowApplication;
         _mediator = mediator;

+ 1 - 1
test/Hotline.Tests/Application/OrderSnapshotApplicationTest.cs

@@ -69,7 +69,7 @@ public class OrderSnapshotApplicationTest : TestBase
             .办理到网格员(SetZuoXi)
             .StepHandle(async order =>
             {
-                Thread.Sleep(20 * 1000);
+                Thread.Sleep(5 * 1000);
                 var orderEntity = await _orderRepository.GetAsync(order.Id);
                 orderEntity.ActualHandleStepName.ShouldBe("工单标记");
             }

+ 67 - 1
test/Hotline.Tests/Application/RedPackApplicationTest.cs

@@ -1,11 +1,15 @@
 using Hotline.Api.Controllers;
 using Hotline.Application.Snapshot;
+using Hotline.Caching.Interfaces;
 using Hotline.Identity.Accounts;
 using Hotline.Identity.Roles;
+using Hotline.Repository.SqlSugar.Snapshot;
 using Hotline.Settings;
+using Hotline.Share.Dtos;
 using Hotline.Share.Dtos.Snapshot;
 using Hotline.Share.Enums.Snapshot;
 using Hotline.Snapshot.Interfaces;
+using Hotline.Tests.Mock;
 using Hotline.Users;
 using Microsoft.AspNetCore.Http;
 using Microsoft.Extensions.DependencyInjection;
@@ -24,11 +28,73 @@ public class RedPackApplicationTest : TestBase
 {
     private readonly IRedPackApplication _redPackApplication;
     private readonly IRedPackRecordRepository _redPackRecordRepository;
+    private readonly OrderServiceMock _orderServiceMock;
+    private readonly ISystemDicDataCacheManager _systemDicDataCacheManager;
+    private readonly IIndustryRepository _industryRepository;
+    private readonly ISpecialRedPackAuditRepository _specialRedPackAuditRepository;
 
-    public RedPackApplicationTest(IAccountRepository accountRepository, IRepository<Role> roleRepository, UserController userController, IServiceScopeFactory scopeFactory, IRepository<User> userRepository, IHttpContextAccessor httpContextAccessor, IThirdIdentiyService thirdIdentiyService, IThirdAccountRepository thirdAccountRepository, IRedPackApplication redPackApplication, IRedPackRecordRepository redPackRecordRepository, ITypedCache<SystemSetting> cacheSettingData) : base(accountRepository, roleRepository, userController, scopeFactory, userRepository, httpContextAccessor, thirdIdentiyService, thirdAccountRepository, cacheSettingData)
+    public RedPackApplicationTest(IAccountRepository accountRepository, IRepository<Role> roleRepository, UserController userController, IServiceScopeFactory scopeFactory, IRepository<User> userRepository, IHttpContextAccessor httpContextAccessor, IThirdIdentiyService thirdIdentiyService, IThirdAccountRepository thirdAccountRepository, IRedPackApplication redPackApplication, IRedPackRecordRepository redPackRecordRepository, ITypedCache<SystemSetting> cacheSettingData, OrderServiceMock orderServiceMock, ISystemDicDataCacheManager systemDicDataCacheManager, IIndustryRepository industryRepository, ISpecialRedPackAuditRepository specialRedPackAuditRepository) : base(accountRepository, roleRepository, userController, scopeFactory, userRepository, httpContextAccessor, thirdIdentiyService, thirdAccountRepository, cacheSettingData)
     {
         _redPackApplication = redPackApplication;
         _redPackRecordRepository = redPackRecordRepository;
+        _orderServiceMock = orderServiceMock;
+        _systemDicDataCacheManager = systemDicDataCacheManager;
+        _industryRepository = industryRepository;
+        _specialRedPackAuditRepository = specialRedPackAuditRepository;
+    }
+
+    /// <summary>
+    /// 获取特殊红包集合
+    /// </summary>
+    /// <returns></returns>
+    [Fact]
+    public async Task GetRedPackSpecialAuditItems_Test()
+    {
+        var dhzy = _systemDicDataCacheManager.OrderTag.Where(m => m.DicDataName == "动火作业").FirstOrDefault();
+        dhzy.ShouldNotBeNull("SysDicTypeConsts.OrderTag 缺少动作火作业基础信息");
+        SetZuoXi();
+        SetSettingCache(SettingConstants.OvertimeBack, "0.00027778");
+        var industryItems = await _industryRepository.Queryable().Where(m => m.Name == "安全隐患").Select(d => new { d.Id, d.Name, }).ToListAsync();
+        var industry = industryItems.First();
+        var order = _orderServiceMock.CreateOrder(industryId: industry.Id, industryName: industry.Name, tags: [dhzy.Id])
+            .办理到网格员()
+            .StepHandle(async order => Thread.Sleep(5 * 1000))
+            .办理到派单员(Set班长)
+            .办理到一级部门(SetPaiDanYuan)
+            .办理到归档(Set一级部门)
+            .发布工单(SetZuoXi, _systemDicDataCacheManager.SnapshotOrderLabel.Where(m => m.DicDataValue == "bss").Select(m => new Kv(m.DicDataName, m.DicDataName)).ToList())
+            .部门审核市民红包(Set应急管理局)
+            .StepHandle(async order => 
+            {
+                var items = await _redPackApplication.GetRedPackSpecialAuditItems(new SnapshotOrderAuditItemsInDto(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 0)).ToListAsync();
+                items.Where(m => m.No == order.No).Count().ShouldBe(1);
+                items = await _redPackApplication.GetRedPackSpecialAuditItems(new SnapshotOrderAuditItemsInDto(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, -1)).ToListAsync();
+                items.Where(m => m.No == order.No).Count().ShouldBe(1);
+                items = await _redPackApplication.GetRedPackSpecialAuditItems(new SnapshotOrderAuditItemsInDto(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 1)).ToListAsync();
+                items.Where(m => m.No == order.No).Count().ShouldBe(0, "审批同意不应该存在数据");
+                items = await _redPackApplication.GetRedPackSpecialAuditItems(new SnapshotOrderAuditItemsInDto(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 2)).ToListAsync();
+                items.Where(m => m.No == order.No).Count().ShouldBe(0);
+            })
+            .部门审核特殊红包(Set应急管理局, 29)
+            .StepHandle(async order =>
+            {
+                var special = await _specialRedPackAuditRepository.Queryable().Where(m => m.OrderId == order.Id).FirstAsync();
+                special.ApprovedAmount.ShouldBe(29);
+                special.Status.ShouldBe(ERedPackAuditStatus.Agree);
+                special.AuditRemark.ShouldBe("红包审核通过");
+                var redPackAudit = await _redPackApplication.GetRedPackSpecialAuditBaseDataAsync(order.Id);
+                redPackAudit.AuditDetail.AuditId.ShouldNotBeNull();
+                redPackAudit.AuditDetail.AuditName.ShouldNotBeNull();
+                redPackAudit.AuditDetail.AuditOrgId.ShouldNotBeNull();
+                redPackAudit.AuditDetail.AuditOrgName.ShouldNotBeNull();
+                redPackAudit.AuditDetail.AuditRemark.ShouldNotBeNull("审核意见不能为空");
+                redPackAudit.AuditDetail.AuditRemark.ShouldBe("红包审核通过");
+
+                var items = await _redPackApplication.GetRedPackSpecialAuditItems(new SnapshotOrderAuditItemsInDto(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 1)).ToListAsync();
+                items.Count.ShouldNotBe(0, $"工单No: {order.No}");
+            })
+            .GetCreateResult();
+
     }
 
     /// <summary>

+ 36 - 3
test/Hotline.Tests/Mock/OrderServiceMock.cs

@@ -26,6 +26,7 @@ using Hotline.Share.Dtos;
 using Hotline.FlowEngine.Notifications;
 using Hotline.Api.Controllers.Bi;
 using XF.Domain.Exceptions;
+using System;
 
 namespace Hotline.Tests.Mock;
 public class OrderServiceMock
@@ -41,8 +42,9 @@ public class OrderServiceMock
     public readonly ISessionContext _sessionContext;
     public readonly IOrderServiceStartWorkflow _orderServiceStartWorkflow;
     public readonly RedPackController _redPackController;
+    private readonly ISystemDicDataCacheManager _systemDicDataCacheManager;
 
-    public OrderServiceMock(OrderController orderController, IRepository<Order> orderRepository, IOrderVisitRepository orderVisitRepository, SnapshotController snapshotController, ISessionContext sessionContext, IOrderServiceStartWorkflow orderServiceStartWorkflow, ISystemSettingCacheManager systemSettingCacheManager, RedPackController redPackController)
+    public OrderServiceMock(OrderController orderController, IRepository<Order> orderRepository, IOrderVisitRepository orderVisitRepository, SnapshotController snapshotController, ISessionContext sessionContext, IOrderServiceStartWorkflow orderServiceStartWorkflow, ISystemSettingCacheManager systemSettingCacheManager, RedPackController redPackController, ISystemDicDataCacheManager systemDicDataCacheManager)
     {
         _orderController = orderController;
         _orderController.ControllerContext = new ControllerContext
@@ -62,9 +64,10 @@ public class OrderServiceMock
         _systemSettingCacheManager = systemSettingCacheManager;
         _redPackController = redPackController;
         _redPackController.ControllerContext = new ControllerContext
-        { 
+        {
             HttpContext = new DefaultHttpContext(),
         };
+        _systemDicDataCacheManager = systemDicDataCacheManager;
     }
 
     public IOrderServiceStartWorkflow CreateSnapshotOrder(Action action)
@@ -100,7 +103,7 @@ public class OrderServiceMock
         return _orderServiceStartWorkflow;
     }
 
-    public IOrderServiceStartWorkflow CreateOrder(string callId = "", string industryId = "", string industryName = "")
+    public IOrderServiceStartWorkflow CreateOrder(string callId = "", string industryId = "", string industryName = "", List<string> tags = null)
     {
         var json = "{\"sourceChannel\":\"因特网\",\"sourceChannelCode\":\"YTW\",\"transferPhone\":null,\"fromPhone\":null,\"acceptorName\":\"单元测试\",\"acceptorStaffNo\":\"\",\"fromName\":\"1233333333\",\"fromGender\":1,\"identityType\":1,\"licenceType\":null,\"licenceTypeCode\":null,\"licenceNo\":null,\"ageRange\":null,\"ageRangeCode\":null,\"contact\":\"12333333333\",\"isSecret\":false,\"acceptSms\":false,\"no\":null,\"title\":\"\",\"hotspotId\":\"1912\",\"eventCategoryId\":null,\"incidentTime\":null,\"incidentPurpose\":null,\"areaCode\":\"519800\",\"city\":\"省内\",\"street\":null,\"isRepeat\":\"false\",\"pushType\":null,\"pushTypeCode\":null,\"content\":\"单元测试内容\",\"duplicateIds\":[],\"duplicateTitle\":null,\"callAddress\":null,\"repeatableEventDetails\":[],\"orderExtension\":null,\"transpond\":false,\"isEnforcementOrder\":false,\"focusOnEventsArr\":[],\"focusOnEvents\":null,\"isFormalistWorkOrder\":false,\"isSensitiveWorkOrders\":false,\"isUrgent\":false,\"isThreePartyConference\":false,\"is24HoursComplete\":false,\"company\":null,\"orderPushTypes\":[],\"acceptType\":\"咨询\",\"acceptTypeCode\":\"10\",\"files\":[],\"hotspotSpliceName\":\"互联互通-转接乐山市12345\",\"hotspotName\":\"转接乐山市12345\",\"hotspotCode\":\"1912\",\"hotspotExternal\":\"19\",\"county\":\"\",\"town\":\"\"}";
         AddOrderDto = json.FromJson<AddOrderDto>();
@@ -113,11 +116,20 @@ public class OrderServiceMock
         AddOrderDto.IndustryId = industryId;
         AddOrderDto.IndustryName = industryName;
         AddOrderDto.Title = "单元测试" + DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss");
+        if (tags.NotNullOrEmpty())
+            AddOrderDto.Tags = tags;
         CreateOrderOutDto = _orderController.Add(AddOrderDto).GetAwaiter().GetResult().ToJson().FromJson<CreateOrderOutDto>();
         _orderServiceStartWorkflow.orderServiceMock = this;
         return _orderServiceStartWorkflow;
     }
 
+    public OrderServiceMock UpdateOrder()
+    {
+        UpdateOrderDto order = _orderController.Get(CreateOrderOutDto.Id).GetAwaiter().GetResult();
+        _orderController.Update(order).GetAwaiter().GetResult();
+        return this;
+    }
+
     public CreateOrderOutDto GetCreateResult()
     {
         return CreateOrderOutDto;
@@ -473,4 +485,25 @@ public class OrderServiceMock
         return this;
     }
 
+    public OrderServiceMock 部门审核特殊红包(Action action, double? approvedAmount = 0)
+    {
+        action?.Invoke();
+        var baseData = _redPackController.GetRedPackSpecialItems(new SnapshotOrderAuditItemsInDto(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 0)).GetAwaiter().GetResult();
+        var item = baseData.Items.FirstOrDefault(m => m.No == CreateOrderOutDto.No);
+        var basedata = _systemDicDataCacheManager.SnapshotRedPackSpecialType.First();
+        if (item == null)
+            throw UserFriendlyException.SameMessage("获取特殊审核红包信息错误, 不存在被测试的对象.");
+        var inDto = new UpdateRedPackSpecialAuditInDto
+        {
+            AuditTypeId = basedata.Id,
+            ApprovedAmount = approvedAmount,
+            RedPackAuditId = item.RedPackAuditId,
+            Status = ERedPackAuditStatus.Agree,
+            Opinion = "红包审核通过",
+        };
+
+        _redPackController.UpdateRedPackSpecialAuditAsync(inDto).GetAwaiter().GetResult();
+        return this;
+
+    }
 }