Bladeren bron

解决冲突

Dun.Jason 4 maanden geleden
bovenliggende
commit
37d1d17345
100 gewijzigde bestanden met toevoegingen van 4306 en 1240 verwijderingen
  1. 12 6
      Hotline.sln
  2. 3 2
      NuGet.Config
  3. 6 3
      src/Hotline.Api/Controllers/Bi/BiCallController.cs
  4. 29 22
      src/Hotline.Api/Controllers/Bi/BiOrderController.cs
  5. 1 1
      src/Hotline.Api/Controllers/CommonPController.cs
  6. 8 2
      src/Hotline.Api/Controllers/ExportWordController.cs
  7. 2 0
      src/Hotline.Api/Controllers/HomeController.cs
  8. 12 0
      src/Hotline.Api/Controllers/IdentityController.cs
  9. 45 21
      src/Hotline.Api/Controllers/JudicialManagementOrdersController.cs
  10. 103 9
      src/Hotline.Api/Controllers/KnowledgeCommonController.cs
  11. 99 73
      src/Hotline.Api/Controllers/KnowledgeController.cs
  12. 323 176
      src/Hotline.Api/Controllers/OrderController.cs
  13. 1 1
      src/Hotline.Api/Controllers/OrderTerminateController.cs
  14. 8 4
      src/Hotline.Api/Controllers/OrgController.cs
  15. 187 0
      src/Hotline.Api/Controllers/PushProvinceController.cs
  16. 29 0
      src/Hotline.Api/Controllers/Snapshot/IndustryController.cs
  17. 135 0
      src/Hotline.Api/Controllers/Snapshot/SnapshotController.cs
  18. 0 26
      src/Hotline.Api/Controllers/SnapshotController.cs
  19. 22 8
      src/Hotline.Api/Controllers/SysController.cs
  20. 280 3
      src/Hotline.Api/Controllers/TestController.cs
  21. 1 0
      src/Hotline.Api/Controllers/UserController.cs
  22. 114 4
      src/Hotline.Api/Controllers/WebPortalController.cs
  23. 52 71
      src/Hotline.Api/Controllers/WorkflowController.cs
  24. 3 3
      src/Hotline.Api/Filter/LogFilterAttribute.cs
  25. 2 0
      src/Hotline.Api/Hotline.Api.csproj
  26. 18 5
      src/Hotline.Api/StartupExtensions.cs
  27. 2 2
      src/Hotline.Api/config/appsettings.Development.json
  28. 4 4
      src/Hotline.Application.Contracts/Validators/FlowEngine/BasicWorkflowDtoValidator.cs
  29. 4 4
      src/Hotline.Application.Contracts/Validators/FlowEngine/NextWorkflowDtoValidator.cs
  30. 18 0
      src/Hotline.Application.Contracts/Validators/Snapshot/IndustryValidator.cs
  31. 10 2
      src/Hotline.Application.Tests/Application/DefaultCallApplicationTest.cs
  32. 9 3
      src/Hotline.Application.Tests/Application/KnowApplicationTest.cs
  33. 94 0
      src/Hotline.Application.Tests/Application/SnapshotApplicationMockTest.cs
  34. 229 0
      src/Hotline.Application.Tests/Application/SnapshotApplicationTest.cs
  35. 28 4
      src/Hotline.Application.Tests/Application/SystemSettingCacheManagerTest.cs
  36. 2 0
      src/Hotline.Application.Tests/Application/XingTangCallsSyncJobTest.cs
  37. 20 6
      src/Hotline.Application.Tests/Application/ZiGongCallReportApplicationTest.cs
  38. 0 78
      src/Hotline.Application.Tests/Controller/DefaultHttpContextAccessor.cs
  39. 130 0
      src/Hotline.Application.Tests/Controller/DefaultSessionContext.cs
  40. 1 1
      src/Hotline.Application.Tests/Controller/KnowledgeControllerTest.cs
  41. 28 3
      src/Hotline.Application.Tests/Controller/OrderControllerTest.cs
  42. 38 0
      src/Hotline.Application.Tests/Controller/SnapshotControllerTest.cs
  43. 4 1
      src/Hotline.Application.Tests/Domain/OrderVisitDomainServiceTest.cs
  44. 30 0
      src/Hotline.Application.Tests/Dto/CapDto.cs
  45. 17 0
      src/Hotline.Application.Tests/Dto/PublishedEntity.cs
  46. 4 0
      src/Hotline.Application.Tests/Hotline.Application.Tests.csproj
  47. 6 1
      src/Hotline.Application.Tests/Mock/OrderServiceMock.cs
  48. 23 0
      src/Hotline.Application.Tests/Mock/ThirdTestService.cs
  49. 10 0
      src/Hotline.Application.Tests/Repository/FWMQRepositoryTest.cs
  50. 11 0
      src/Hotline.Application.Tests/SqlSuger/CapDbContext.cs
  51. 33 0
      src/Hotline.Application.Tests/SqlSuger/CapDbExtensions.cs
  52. 27 5
      src/Hotline.Application.Tests/Startup.cs
  53. 37 6
      src/Hotline.Application.Tests/TestBase.cs
  54. 12 2
      src/Hotline.Application.Tests/appsettings.Development.json
  55. 25 25
      src/Hotline.Application/CallCenter/DefaultCallApplication.cs
  56. 17 5
      src/Hotline.Application/FlowEngine/IWorkflowApplication.cs
  57. 181 123
      src/Hotline.Application/FlowEngine/WorkflowApplication.cs
  58. 11 0
      src/Hotline.Application/Handlers/FlowEngine/WorkflowEndHandler.cs
  59. 20 9
      src/Hotline.Application/Handlers/FlowEngine/WorkflowStartHandler.cs
  60. 1 0
      src/Hotline.Application/Hotline.Application.csproj
  61. 10 0
      src/Hotline.Application/Identity/IIdentityAppService.cs
  62. 76 15
      src/Hotline.Application/Identity/IdentityAppService.cs
  63. 46 6
      src/Hotline.Application/Knowledge/KnowApplication.cs
  64. 8 1
      src/Hotline.Application/Mappers/MapperConfigs.cs
  65. 22 0
      src/Hotline.Application/Mappers/SnapshotMapperConfigs.cs
  66. 1 1
      src/Hotline.Application/Mappers/WebPortalMapperConfigs.cs
  67. 4 1
      src/Hotline.Application/Orders/IOrderApplication.cs
  68. 107 6
      src/Hotline.Application/Orders/OrderApplication.cs
  69. 1 0
      src/Hotline.Application/Orders/OrderScreenHandler/OrderScreenEndWorkflowHandler.cs
  70. 4 2
      src/Hotline.Application/Orders/OrderSecondaryHandlingApplication.cs
  71. 1 1
      src/Hotline.Application/Orders/OrderSendBackAuditApplication.cs
  72. 26 0
      src/Hotline.Application/Snapshot/DefaultSnapshotApplication.cs
  73. 16 0
      src/Hotline.Application/Snapshot/IIndustryApplication.cs
  74. 67 0
      src/Hotline.Application/Snapshot/ISnapshotApplication.cs
  75. 43 0
      src/Hotline.Application/Snapshot/IndustryApplication.cs
  76. 280 0
      src/Hotline.Application/Snapshot/SnapshotApplicationBase.cs
  77. 25 0
      src/Hotline.Application/Snapshot/ZiGongSnapshotApplication.cs
  78. 5 2
      src/Hotline.Application/StatisticalReport/CallReport/CallReportApplicationBase.cs
  79. 1 1
      src/Hotline.Application/StatisticalReport/CallReport/LuZhouCallReportApplication.cs
  80. 2 1
      src/Hotline.Application/StatisticalReport/CallReport/YiBinCallReportApplication.cs
  81. 1 1
      src/Hotline.Application/StatisticalReport/CallReport/ZiGongCallReportApplication.cs
  82. 0 0
      src/Hotline.Application/StatisticalReport/OrderReportApplication.cs
  83. 75 52
      src/Hotline.Application/Subscribers/DatasharingSubscriber.cs
  84. 8 1
      src/Hotline.Application/Subscribers/InternalCapSubscriber.cs
  85. 19 19
      src/Hotline.Repository.SqlSugar/CallCenter/CallNativeRepository.cs
  86. 14 0
      src/Hotline.Repository.SqlSugar/Extensions/SqlSugarStartupExtensions.cs
  87. 5 0
      src/Hotline.Repository.SqlSugar/File/FileRepository.cs
  88. 51 35
      src/Hotline.Repository.SqlSugar/Orders/OrderRepository.cs
  89. 18 0
      src/Hotline.Repository.SqlSugar/Snapshot/IndustryRepository.cs
  90. 29 0
      src/Hotline.Repository.SqlSugar/Snapshot/ThirdAccountRepository.cs
  91. 33 0
      src/Hotline.Share/Dtos/Article/BulletinDto.cs
  92. 124 0
      src/Hotline.Share/Dtos/GetCaseReultSendModel.cs
  93. 50 0
      src/Hotline.Share/Dtos/JudicialManagement/JudicialManagementAddOrderDto.cs
  94. 15 8
      src/Hotline.Share/Dtos/Knowledge/KnowledgeDto.cs
  95. 59 13
      src/Hotline.Share/Dtos/Knowledge/KnowledgePagedDto.cs
  96. 110 2
      src/Hotline.Share/Dtos/Knowledge/KnowledgeTypeDto.cs
  97. 9 4
      src/Hotline.Share/Dtos/Order/OrderBiDto.cs
  98. 6 4
      src/Hotline.Share/Dtos/Order/OrderDto.cs
  99. 345 340
      src/Hotline.Share/Dtos/Order/OrderScreenDto.cs
  100. 9 0
      src/Hotline.Share/Dtos/Order/OrderSpecialDto.cs

+ 12 - 6
Hotline.sln

@@ -53,10 +53,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "XingTang.Sdk", "src\XingTan
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Hotline.XingTang", "src\Hotline.XingTang\Hotline.XingTang.csproj", "{9F99C272-5BC2-452C-9D97-BC756AF04669}"
 EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Hotline.Application.Tests", "src\Hotline.Application.Tests\Hotline.Application.Tests.csproj", "{801A8807-F95E-428B-B8C3-3F9244B9E080}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Hotline.Application.Tests", "src\Hotline.Application.Tests\Hotline.Application.Tests.csproj", "{21E8510A-9D78-44B6-AC9C-2A9D4023853D}"
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Hotline.Logger", "src\Hotline.Logger\Hotline.Logger.csproj", "{37784861-ABC0-41F4-87B4-2E08A89A2C42}"
 EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Hotline.WeChat", "src\Hotline.WeChat\Hotline.WeChat.csproj", "{75215667-65AF-4B7B-85E7-3140239B30CC}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -139,14 +141,18 @@ Global
 		{9F99C272-5BC2-452C-9D97-BC756AF04669}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{9F99C272-5BC2-452C-9D97-BC756AF04669}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{9F99C272-5BC2-452C-9D97-BC756AF04669}.Release|Any CPU.Build.0 = Release|Any CPU
-		{801A8807-F95E-428B-B8C3-3F9244B9E080}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{801A8807-F95E-428B-B8C3-3F9244B9E080}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{801A8807-F95E-428B-B8C3-3F9244B9E080}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{801A8807-F95E-428B-B8C3-3F9244B9E080}.Release|Any CPU.Build.0 = Release|Any CPU
+		{21E8510A-9D78-44B6-AC9C-2A9D4023853D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{21E8510A-9D78-44B6-AC9C-2A9D4023853D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{21E8510A-9D78-44B6-AC9C-2A9D4023853D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{21E8510A-9D78-44B6-AC9C-2A9D4023853D}.Release|Any CPU.Build.0 = Release|Any CPU
 		{37784861-ABC0-41F4-87B4-2E08A89A2C42}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{37784861-ABC0-41F4-87B4-2E08A89A2C42}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{37784861-ABC0-41F4-87B4-2E08A89A2C42}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{37784861-ABC0-41F4-87B4-2E08A89A2C42}.Release|Any CPU.Build.0 = Release|Any CPU
+		{75215667-65AF-4B7B-85E7-3140239B30CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{75215667-65AF-4B7B-85E7-3140239B30CC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{75215667-65AF-4B7B-85E7-3140239B30CC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{75215667-65AF-4B7B-85E7-3140239B30CC}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
@@ -175,8 +181,8 @@ Global
 		{C3F289D5-C50B-46DB-852C-9543EF9B0355} = {D041C554-B78E-4AAF-B597-E309DC8EEF4F}
 		{CF2A8B80-FF4E-4291-B383-D735BB629F32} = {D041C554-B78E-4AAF-B597-E309DC8EEF4F}
 		{9F99C272-5BC2-452C-9D97-BC756AF04669} = {D041C554-B78E-4AAF-B597-E309DC8EEF4F}
-		{801A8807-F95E-428B-B8C3-3F9244B9E080} = {08D63205-1445-430F-A4AB-EF1744E3AC11}
 		{37784861-ABC0-41F4-87B4-2E08A89A2C42} = {D041C554-B78E-4AAF-B597-E309DC8EEF4F}
+		{75215667-65AF-4B7B-85E7-3140239B30CC} = {D041C554-B78E-4AAF-B597-E309DC8EEF4F}
 	EndGlobalSection
 	GlobalSection(ExtensibilityGlobals) = postSolution
 		SolutionGuid = {4B8EA790-BD13-4422-8D63-D6DBB77B823F}

+ 3 - 2
NuGet.Config

@@ -1,7 +1,8 @@
 <?xml version="1.0" encoding="utf-8"?>
 <configuration>
   <packageSources>
-    <add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
-    <add key="fengwo.org" value="http://110.188.24.182:5555/v3/index.json" />
+    <clear />
+    <add key="nuget.org" value="https://api.nuget.org/v3/index.json"  protocolVersion="3"/>
+    <add key="fengwo.org" value="http://110.188.24.182:5555/v3/index.json" allowInsecureConnections="true"/>
   </packageSources>
 </configuration>

+ 6 - 3
src/Hotline.Api/Controllers/Bi/BiCallController.cs

@@ -20,6 +20,8 @@ using System.Data;
 using System.Linq.Dynamic.Core;
 using XF.Domain.Repository;
 using Hotline.Settings;
+using XF.Utility.EnumExtensions;
+using Hotline.Share.Enums.CallCenter;
 
 namespace Hotline.Api.Controllers.Bi;
 
@@ -167,9 +169,10 @@ public class BiCallController : BaseController
     [HttpGet("query_calls_statistics_detail/base-data")]
     public Dictionary<string, dynamic> QueryCallsStatisticsDetailBaseData()
     {
-        return _baseDataApplication
-            .EndBy()
-            .Build();
+        return new Dictionary<string, dynamic>
+        {
+            { "endBy", EnumExts.GetDescriptions<EEndBy>() }
+        };
     }
 
     /// <summary>

+ 29 - 22
src/Hotline.Api/Controllers/Bi/BiOrderController.cs

@@ -1270,14 +1270,14 @@ namespace Hotline.Api.Controllers.Bi
             var citySumModel = new VisitAndOrgSatisfactionStatisticsDto()
             {
                 OrgName = "市直合计",
-                TotalSumCount = data.Where(x => x.OrgType == EOrgType.City).Sum(x => x.TotalSumCount),
-                VerySatisfiedCount = data.Where(x => x.OrgType == EOrgType.City).Sum(x => x.VerySatisfiedCount),
-                SatisfiedCount = data.Where(x => x.OrgType == EOrgType.City).Sum(x => x.SatisfiedCount),
-                RegardedAsSatisfiedCount = data.Where(x => x.OrgType == EOrgType.City).Sum(x => x.RegardedAsSatisfiedCount),
-                DefaultSatisfiedCount = data.Where(x => x.OrgType == EOrgType.City).Sum(x => x.DefaultSatisfiedCount),
-                NoSatisfiedCount = data.Where(x => x.OrgType == EOrgType.City).Sum(x => x.NoSatisfiedCount),
-                NoEvaluateCount = data.Where(x => x.OrgType == EOrgType.City).Sum(x => x.NoEvaluateCount),
-                NoPutThroughCount = data.Where(x => x.OrgType == EOrgType.City).Sum(x => x.NoPutThroughCount),
+                TotalSumCount = data.Where(x => x.OrgType == EOrgType.City && x.OrgCode!="001").Sum(x => x.TotalSumCount),
+                VerySatisfiedCount = data.Where(x => x.OrgType == EOrgType.City && x.OrgCode != "001").Sum(x => x.VerySatisfiedCount),
+                SatisfiedCount = data.Where(x => x.OrgType == EOrgType.City && x.OrgCode != "001").Sum(x => x.SatisfiedCount),
+                RegardedAsSatisfiedCount = data.Where(x => x.OrgType == EOrgType.City && x.OrgCode != "001").Sum(x => x.RegardedAsSatisfiedCount),
+                DefaultSatisfiedCount = data.Where(x => x.OrgType == EOrgType.City && x.OrgCode != "001").Sum(x => x.DefaultSatisfiedCount),
+                NoSatisfiedCount = data.Where(x => x.OrgType == EOrgType.City && x.OrgCode != "001").Sum(x => x.NoSatisfiedCount),
+                NoEvaluateCount = data.Where(x => x.OrgType == EOrgType.City && x.OrgCode != "001").Sum(x => x.NoEvaluateCount),
+                NoPutThroughCount = data.Where(x => x.OrgType == EOrgType.City && x.OrgCode!="001").Sum(x => x.NoPutThroughCount),
             };
 
             var sumModel = new VisitAndOrgSatisfactionStatisticsDto()
@@ -1321,14 +1321,14 @@ namespace Hotline.Api.Controllers.Bi
             var citySumModel = new VisitAndOrgSatisfactionStatisticsDto()
             {
                 OrgName = "市直合计",
-                TotalSumCount = data.Where(x => x.OrgType == EOrgType.City).Sum(x => x.TotalSumCount),
-                VerySatisfiedCount = data.Where(x => x.OrgType == EOrgType.City).Sum(x => x.VerySatisfiedCount),
-                SatisfiedCount = data.Where(x => x.OrgType == EOrgType.City).Sum(x => x.SatisfiedCount),
-                RegardedAsSatisfiedCount = data.Where(x => x.OrgType == EOrgType.City).Sum(x => x.RegardedAsSatisfiedCount),
-                DefaultSatisfiedCount = data.Where(x => x.OrgType == EOrgType.City).Sum(x => x.DefaultSatisfiedCount),
-                NoSatisfiedCount = data.Where(x => x.OrgType == EOrgType.City).Sum(x => x.NoSatisfiedCount),
-                NoEvaluateCount = data.Where(x => x.OrgType == EOrgType.City).Sum(x => x.NoEvaluateCount),
-                NoPutThroughCount = data.Where(x => x.OrgType == EOrgType.City).Sum(x => x.NoPutThroughCount),
+                TotalSumCount = data.Where(x => x.OrgType == EOrgType.City && x.OrgCode != "001").Sum(x => x.TotalSumCount),
+                VerySatisfiedCount = data.Where(x => x.OrgType == EOrgType.City && x.OrgCode != "001").Sum(x => x.VerySatisfiedCount),
+                SatisfiedCount = data.Where(x => x.OrgType == EOrgType.City && x.OrgCode != "001").Sum(x => x.SatisfiedCount),
+                RegardedAsSatisfiedCount = data.Where(x => x.OrgType == EOrgType.City && x.OrgCode != "001").Sum(x => x.RegardedAsSatisfiedCount),
+                DefaultSatisfiedCount = data.Where(x => x.OrgType == EOrgType.City && x.OrgCode != "001").Sum(x => x.DefaultSatisfiedCount),
+                NoSatisfiedCount = data.Where(x => x.OrgType == EOrgType.City && x.OrgCode != "001").Sum(x => x.NoSatisfiedCount),
+                NoEvaluateCount = data.Where(x => x.OrgType == EOrgType.City && x.OrgCode != "001").Sum(x => x.NoEvaluateCount),
+                NoPutThroughCount = data.Where(x => x.OrgType == EOrgType.City && x.OrgCode != "001").Sum(x => x.NoPutThroughCount),
             };
             var sumModel = new VisitAndOrgSatisfactionStatisticsDto()
             {
@@ -3542,13 +3542,20 @@ namespace Hotline.Api.Controllers.Bi
         /// </summary>
         /// <returns></returns>
         [HttpGet("org-visitdetail-list-basedata")]
-        public Dictionary<string, dynamic> OrgVisitDetailListBaseData()
+        public async Task<Dictionary<string, dynamic>> OrgVisitDetailListBaseData()
         {
-            return _baseDataApplication
-                .VisitSatisfaction()
-                .OrgsOptions(_sessionContext)
-                .AttitudeType()
-                .Build();
+            IReadOnlyList<SystemOrganize> items;
+            if (_sessionContext.OrgIsCenter)
+                items = await _systemOrganizeRepository.GetOrgJson();
+            else
+                items = await _systemOrganizeRepository.GetOrgJsonForUser(_sessionContext.RequiredOrgId); ;
+
+            return new Dictionary<string, dynamic>
+            {
+                { "visitSatisfaction", _systemDicDataCacheManager.GetVisitSatisfaction() },
+                { "orgsOptions", items },
+                { "attitudeType", EnumExts.GetDescriptions<EAttitudeType>() }
+            };
         }
 
         /// <summary>

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

@@ -95,7 +95,7 @@ namespace Hotline.Api.Controllers
 		[Obsolete("请调用sys/area/tree")]
 		public async Task<List<SystemArea>> GetAearTree()
 		{
-			return await _systemAreaDomainService.GetAreaTree();
+			return await _systemAreaDomainService.GetAreaTree(_appOptions.Value.IsZiGong ? 6 : 0);
 		}
 
 		#endregion

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

@@ -1,6 +1,8 @@
 using Hotline.Application.ExportWord;
+using Hotline.Caching.Interfaces;
 using Hotline.Configurations;
 using Hotline.Orders;
+using Hotline.Settings;
 using Hotline.Share.Dtos.OrderExportWord;
 using Hotline.Share.Enums.Order;
 using MapsterMapper;
@@ -19,6 +21,7 @@ namespace Hotline.Api.Controllers
         private readonly IRepository<OrderVisitDetail> _orderVisitedDetailRepository;
         private readonly ILogger<ExportWordController> _logger;
         private readonly IOptionsSnapshot<AppConfiguration> _appOptions;
+        private readonly ISystemSettingCacheManager _systemSettingCacheManager;
 
         public ExportWordController(IOrderRepository orderRepository,
             IWordHelperService wordHelperService,
@@ -26,7 +29,8 @@ namespace Hotline.Api.Controllers
            IRepository<OrderVisit> orderVisitRepository,
            IRepository<OrderVisitDetail> orderVisitedDetailRepository,
            ILogger<ExportWordController> logger,
-            IOptionsSnapshot<AppConfiguration> appOptions)
+            IOptionsSnapshot<AppConfiguration> appOptions,
+            ISystemSettingCacheManager systemSettingCacheManager)
         {
             _orderRepository = orderRepository;
             _wordHelperService = wordHelperService;
@@ -35,6 +39,7 @@ namespace Hotline.Api.Controllers
             _orderVisitedDetailRepository = orderVisitedDetailRepository;
             _logger = logger;
             _appOptions = appOptions;
+            _systemSettingCacheManager = systemSettingCacheManager;
         }
 
         /// <summary>
@@ -47,6 +52,7 @@ namespace Hotline.Api.Controllers
             var streams = new Dictionary<string, Stream>();
             var path = $"{Directory.GetCurrentDirectory()}/Template/AssignmentForm.doc";
             int num = 1;
+            var isTheAssignmentFormConfidential = bool.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.IsTheAssignmentFormConfidential).SettingValue[0]);
             foreach (var item in Ids)
             {
                 var order = await _orderRepository.GetAsync(item, HttpContext.RequestAborted);
@@ -54,7 +60,7 @@ namespace Hotline.Api.Controllers
                     continue;
 
                 //处理保密数据
-                if (_appOptions.Value.IsZiGong && order.IsSecret)
+                if (isTheAssignmentFormConfidential && order.IsSecret)
                 {
                     var maskString = "***";
                     order.Contact = maskString;

+ 2 - 0
src/Hotline.Api/Controllers/HomeController.cs

@@ -191,6 +191,8 @@ public class HomeController : BaseController
             OldHotlineOrderState = _systemSettingCacheManager.GetSetting(SettingConstants.OldHotlineOrderState).SettingValue[0],
             FileExt = _systemSettingCacheManager.GetSetting(SettingConstants.FileExt).SettingValue[0],
             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]),
         };
         return rsp;
     }

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

@@ -13,6 +13,8 @@ using Microsoft.Extensions.Options;
 using XC.RSAUtil;
 using XF.Domain.Constants;
 using XF.Domain.Exceptions;
+using Hotline.Share.Dtos.Snapshot;
+using Swashbuckle.AspNetCore.Annotations;
 
 namespace Hotline.Api.Controllers;
 
@@ -89,6 +91,16 @@ jxrWXHbT1FB6DqkdOnBbQqS1Azqz5HxLlSyEK3F60e3SgB5iZsDZ
 		return res ;
     }
 
+    /// <summary>
+    /// 获取微信openId
+    /// </summary>
+    /// <param name="dto">微信小程序 Code</param>
+    /// <returns>登录成功, 用户是新用户, 需要调用获取用户手机号码 third/phone 接口</returns>
+    [AllowAnonymous]
+    [HttpPost("third/token")]
+    public async Task<TokenOutDto> GetThirdTokenAsync([FromBody] ThirdTokenInDto dto)
+        => await _identityAppService.GetThredTokenAsync(dto);
+
     [AllowAnonymous]
     [ApiExplorerSettings(IgnoreApi = true)]
     [HttpPost("token")]

+ 45 - 21
src/Hotline.Api/Controllers/JudicialManagementOrdersController.cs

@@ -3,6 +3,7 @@ using Hotline.Application.JudicialManagement;
 using Hotline.Caching.Interfaces;
 using Hotline.File;
 using Hotline.JudicialManagement;
+using Hotline.Orders;
 using Hotline.Repository.SqlSugar.Extensions;
 using Hotline.Settings;
 using Hotline.Settings.TimeLimits;
@@ -35,6 +36,7 @@ namespace Hotline.Api.Controllers
         private readonly IJudicialManagementOrdersService _judicialManagementOrdersService;
         private readonly IRepository<EnforcementOrdersHandler> _enforcementOrdersHandlerRepository;
         private readonly IEnforcementApplication _enforcementApplication;
+        private readonly IRepository<Order> _orderRepository;
 
         /// <summary>
         /// 
@@ -51,6 +53,7 @@ namespace Hotline.Api.Controllers
         /// <param name="judicialManagementOrdersService"></param>
         /// <param name="enforcementOrdersHandlerRepository"></param>
         /// <param name="enforcementApplication"></param>
+        /// <param name="orderRepository"></param>
         public JudicialManagementOrdersController(
          IRepository<JudicialComplaintsEventType> judicialComplaintsEventTypeRepository,
          IRepository<EnforcementHotspot> enforcementHotspotRepository,
@@ -63,7 +66,8 @@ namespace Hotline.Api.Controllers
           IFileRepository fileRepository,
           IJudicialManagementOrdersService judicialManagementOrdersService,
            IRepository<EnforcementOrdersHandler> enforcementOrdersHandlerRepository,
-           IEnforcementApplication enforcementApplication
+           IEnforcementApplication enforcementApplication,
+            IRepository<Order> orderRepository
          )
         {
             _judicialComplaintsEventTypeRepository = judicialComplaintsEventTypeRepository;
@@ -78,6 +82,7 @@ namespace Hotline.Api.Controllers
             _judicialManagementOrdersService = judicialManagementOrdersService;
             _enforcementOrdersHandlerRepository = enforcementOrdersHandlerRepository;
             _enforcementApplication = enforcementApplication;
+            _orderRepository = orderRepository;
         }
 
         /// <summary>
@@ -134,8 +139,8 @@ namespace Hotline.Api.Controllers
             if (dto.Files.Any())
                 order.FileJson = await _fileRepository.AddFileAsync(dto.Files, order.Id, "", HttpContext.RequestAborted);
             else
-	            order.FileJson = new List<Share.Dtos.File.FileJson>();
-			await _judicialManagementOrdersRepository.UpdateAsync(order, HttpContext.RequestAborted);
+                order.FileJson = new List<Share.Dtos.File.FileJson>();
+            await _judicialManagementOrdersRepository.UpdateAsync(order, HttpContext.RequestAborted);
 
             //处理执法部门
             if (dto.EnforcementOrdersHandler != null && dto.EnforcementOrdersHandler.Any())
@@ -193,6 +198,25 @@ namespace Hotline.Api.Controllers
                 return new();
             var dto = _mapper.Map<JudicialManagementOrderDto>(order);
 
+            //如果是12345创建的工单,查询办理信息
+            if (dto.OrderSoure == EOrderSoure.Hotline)
+            {
+                var data = await _orderRepository.GetAsync(p => p.No == order.No, HttpContext.RequestAborted);
+                if (data != null)
+                {
+                    dto.CenterToOrgTime = data.CenterToOrgTime;
+                    dto.ActualHandleOrgName = data.ActualHandleOrgName;
+                    dto.ActualHandleTime = data.ActualHandleTime;
+                    dto.TimeLimit = data.TimeLimit;
+                    dto.TimeLimitUnit = data.TimeLimitUnit;
+                    dto.TimeLimitCount = data.TimeLimitCount;
+                    dto.ExpiredTime = data.ExpiredTime;
+                    dto.IsResolved = data.IsResolved;
+                    dto.FileOpinion = data.FileOpinion;
+                    dto.CenterOpinion = data.CenterToOrgOpinion;
+                }
+            }
+
             if (dto.FileJson != null && dto.FileJson.Any())
             {
                 var ids = order.FileJson.Select(x => x.Id).ToList();
@@ -481,22 +505,22 @@ namespace Hotline.Api.Controllers
             else
                 areCodeText = AreaCode;
 
-             var items = await _judicialComplaintsEventTypeRepository.Queryable()
-             .LeftJoin<JudicialManagementOrders>((x, o) => o.EventTypeSpliceName != null &&
-             (x.EventTypeName == o.EventTypeSpliceName || o.EventTypeSpliceName.Contains(x.EventTypeName)))
-            .Where((x, o) => o.CreationTime >= StartTime && o.CreationTime <= EndTime && o.IsItCounted == true)
-            .WhereIF(!string.IsNullOrEmpty(areCodeText), (x, o) => o.AreaCode.StartsWith(areCodeText))
-             .Where((x, o) => x.ParentId == Id)
-             .GroupBy((x, o) => new { x.Id, x.EventTypeName })
-             .Select((x, o) => new
-             {
-                 Id = x.Id,
-                 Name = x.EventTypeName,
-                 Num = SqlFunc.AggregateSum(SqlFunc.IIF(o.Id != null, 1, 0)),
-                 Sublevel = SqlFunc.AggregateSum(SqlFunc.IIF(x.EventTypeName != o.EventTypeName, 1, 0)) > 0,
-             })
-             .MergeTable()
-             .ToListAsync();
+            var items = await _judicialComplaintsEventTypeRepository.Queryable()
+            .LeftJoin<JudicialManagementOrders>((x, o) => o.EventTypeSpliceName != null &&
+            (x.EventTypeName == o.EventTypeSpliceName || o.EventTypeSpliceName.Contains(x.EventTypeName)))
+           .Where((x, o) => o.CreationTime >= StartTime && o.CreationTime <= EndTime && o.IsItCounted == true)
+           .WhereIF(!string.IsNullOrEmpty(areCodeText), (x, o) => o.AreaCode.StartsWith(areCodeText))
+            .Where((x, o) => x.ParentId == Id)
+            .GroupBy((x, o) => new { x.Id, x.EventTypeName })
+            .Select((x, o) => new
+            {
+                Id = x.Id,
+                Name = x.EventTypeName,
+                Num = SqlFunc.AggregateSum(SqlFunc.IIF(o.Id != null, 1, 0)),
+                Sublevel = SqlFunc.AggregateSum(SqlFunc.IIF(x.EventTypeName != o.EventTypeName, 1, 0)) > 0,
+            })
+            .MergeTable()
+            .ToListAsync();
 
             var data = await _judicialManagementOrdersRepository.Queryable()
                 .Where(d => d.CreationTime >= StartTime && d.CreationTime <= EndTime)
@@ -675,11 +699,11 @@ namespace Hotline.Api.Controllers
         [HttpPost("regional_classification_statistics_export")]
         public async Task<FileStreamResult> GetRegionalClassificationStatisticsExportAsync([FromBody] ExportExcelDto<QueryRegionalClassificationStatisticsDto> dto)
         {
-            var list= await _enforcementApplication.GetRegionalClassificationStatisticsAsync(dto.QueryDto.StartTime, dto.QueryDto.EndTime).ToListAsync();
+            var list = await _enforcementApplication.GetRegionalClassificationStatisticsAsync(dto.QueryDto.StartTime, dto.QueryDto.EndTime).ToListAsync();
 
             list.Add(new RegionalClassificationStatisticsDto()
             {
-                AreaCode="",
+                AreaCode = "",
                 AreaName = "总计",
                 OrderCountNum = list.Sum(x => x.OrderCountNum),
                 TheClueIsTrue = list.Sum(x => x.TheClueIsTrue),

+ 103 - 9
src/Hotline.Api/Controllers/KnowledgeCommonController.cs

@@ -1,8 +1,11 @@
 using Hotline.KnowledgeBase;
 using Hotline.Permissions;
+using Hotline.Settings;
+using Hotline.Settings.Hotspots;
 using Hotline.Share.Dtos.Knowledge;
 using MapsterMapper;
 using Microsoft.AspNetCore.Mvc;
+using SqlSugar;
 using XF.Domain.Authentications;
 using XF.Domain.Exceptions;
 using XF.Domain.Repository;
@@ -21,6 +24,8 @@ namespace Hotline.Api.Controllers
         private readonly IRepository<KnowledgeStandard> _knowledgeStandardRepository;
         private readonly IMapper _mapper;
         private readonly ISessionContext _sessionContext;
+		private readonly ISystemOrganizeRepository _systemOrganizeRepository;
+		private readonly IRepository<Hotspot> _hotspotTypeRepository;
 
 		/// <summary>
 		/// 
@@ -31,14 +36,15 @@ namespace Hotline.Api.Controllers
 		/// <param name="mapper"></param>
 		public KnowledgeCommonController(IRepository<KnowledgeType> knowledgeTypeRepository, IKnowledgeTypeDomainService knowledgeTypeDomainService,
             IRepository<KnowledgeStandard> knowledgeStandardRepository, ISessionContext sessionContext,
-			IMapper mapper)
+			IMapper mapper, ISystemOrganizeRepository systemOrganizeRepository, IRepository<Hotspot> hotspotTypeRepository)
         {
             _knowledgeTypeRepository = knowledgeTypeRepository;
             _knowledgeTypeDomainService = knowledgeTypeDomainService;
             _knowledgeStandardRepository = knowledgeStandardRepository;
             _mapper = mapper;
             _sessionContext = sessionContext;
-
+            _systemOrganizeRepository = systemOrganizeRepository;
+            _hotspotTypeRepository = hotspotTypeRepository;
 		}
         #endregion
 
@@ -137,19 +143,107 @@ namespace Hotline.Api.Controllers
         /// <param name="IsEnable">不传查询所有</param>
         /// <returns></returns>
         [HttpGet("treelist")]
-        public async Task<List<KnowledgeType>> GetTreeList(bool? IsEnable)
+        public async Task<List<KnowledgeTypeDto>> GetTreeList(bool? IsEnable,string? Attribution)
+        {
+            return await _knowledgeTypeRepository.Queryable().WhereIF(IsEnable.HasValue, x =>x.IsEnable == IsEnable)
+				 //.Where(x => x.KnowledgeTypeOrgs.Any(to => to.OrgId == _sessionContext.RequiredOrgId) || x.KnowledgeTypeOrgs.Any() == false)
+				 .Where(x=> SqlFunc.Subqueryable<KnowledgeTypeOrg>().Where(to=>to.TypeId == x.Id && to.OrgId == _sessionContext.RequiredOrgId).Any()
+				 || SqlFunc.Subqueryable<KnowledgeTypeOrg>().Where(to => to.TypeId == x.Id).NotAny())
+				 .Select(x=> new KnowledgeTypeDto()
+				 {
+                     Id = x.Id.SelectAll(),
+                     KnowledgeNum = SqlFunc.Subqueryable<KnowledgeRelationType>().LeftJoin<Knowledge>((kr,k)=> kr.KnowledgeId == k.Id)
+	                     .Where((kr, k)=>kr.KnowledgeTypeSpliceName.StartsWith(x.SpliceName))
+	                     .WhereIF(!string.IsNullOrEmpty(Attribution),(kr,k)=>k.Attribution == Attribution).DistinctCount(kr=>kr.KnowledgeId)
+
+				 })
+				 .OrderBy(x => x.Sort).ToTreeAsync(it => it.children, it => it.ParentId, null,it=> it.Id);
+        }
+
+        /// <summary>
+        /// 知识部门
+        /// </summary>
+        /// <param name="IsEnable">不传查询所有</param>
+        /// <returns></returns>
+        [HttpGet("treelist/org")]
+        public async Task<List<KnowledgeOrgDto>> GetTreeList(string? Attribution)
+        {
+
+	        //await Db.Queryable<SystemOrganize>()
+		       // .Where(it => it.Id.StartsWith(orgCode))
+		       // .OrderBy(d => d.Id)
+		       // .ToTreeAsync(it => it.Children, it => it.ParentId, orgCode.Length > 6 ? orgCode.Substring(0, orgCode.Length - 3) : null);
+	        var orgCode = _sessionContext.RequiredOrgId;
+			var query=  _systemOrganizeRepository.Queryable().WhereIF(!_sessionContext.OrgIsCenter, it => it.Id.StartsWith(_sessionContext.RequiredOrgId))
+					.Select(it=> new KnowledgeOrgDto()
+					{
+						Id = it.Id.SelectAll(),
+						KnowledgeNum = SqlFunc.Subqueryable<Knowledge>()
+							.Where(k => k.CreatorOrgId.StartsWith(it.Id))
+							.WhereIF(!string.IsNullOrEmpty(Attribution),  k => k.Attribution == Attribution).DistinctCount(k => k.Id)
+
+					}).OrderBy(d => d.Id);
+
+			if (_sessionContext.OrgIsCenter)
+			{
+				return await query.ToTreeAsync(x => x.Children, it => it.ParentId, null);
+			}
+            return await query.ToTreeAsync(it => it.Children, it => it.ParentId, orgCode.Length > 6 ? orgCode.Substring(0, orgCode.Length - 3) : null);
+
+        }
+
+        /// <summary>
+        /// 查询子项
+        /// </summary>
+        [HttpGet("treelist/hotspot")]
+        public async Task<IReadOnlyList<KnowledgeHotSpotDto>> GetChildren([FromQuery] string? id, string? Attribution)
         {
-            return await _knowledgeTypeRepository.Queryable().WhereIF(IsEnable.HasValue, p => p.IsEnable == IsEnable)
-				 .Where(x => x.KnowledgeTypeOrgs.Any(to => to.OrgId == _sessionContext.RequiredOrgId) || x.KnowledgeTypeOrgs.Any() == false).OrderBy(p => p.Sort).ToTreeAsync(it => it.children, it => it.ParentId, null,it=> it.Id);
+	        var list = await _hotspotTypeRepository.Queryable()
+		        .WhereIF(!string.IsNullOrEmpty(id), x => x.ParentId == id)
+		        .WhereIF(string.IsNullOrEmpty(id), x => x.ParentId == null || x.ParentId == "")
+		        .OrderBy(d => d.HotSpotName)
+		        .Select(x => new KnowledgeHotSpotDto
+				{
+					Id = x.Id.SelectAll(),
+					HasChild = SqlFunc.Subqueryable<Hotspot>().Where(d => d.ParentId == x.Id).NotAny(),
+					KnowledgeNum = SqlFunc.Subqueryable<Knowledge>().LeftJoin<Hotspot>((k,h)=>k.HotspotId == h.Id)
+						.Where((k,h) =>h.HotSpotFullName.StartsWith(x.HotSpotFullName))
+						.WhereIF(!string.IsNullOrEmpty(Attribution), (k,h) => k.Attribution == Attribution).DistinctCount(k => k.Id)
+				}).ToListAsync();
+
+	        return list;
         }
 
+
         /// <summary>
-        /// 知识分类- 只获取一级
+        /// 名称检索热点树形
         /// </summary>
-        /// <param name="ParentId">不传默认查询第一级</param>
-        /// <param name="IsEnable">不传查询所有,包含已经禁用的</param>
+        /// <param name="name"></param>
         /// <returns></returns>
-        [HttpGet("typelist")]
+        [HttpGet("treelist/children-hasname")]
+        public async Task<IReadOnlyList<KnowledgeHotSpotDto>> GetChildrenHasName([FromQuery] string Name, string? Attribution)
+        {
+	        var arr = _hotspotTypeRepository.Queryable()
+		        .WhereIF(!string.IsNullOrEmpty(Name), x => x.HotSpotFullName.Contains(Name)).Select(x => x.Id).ToList().Cast<object>().ToArray();
+	        //.ToTreeAsync(x => x.Children, it => it.ParentId, null);
+	        return await _hotspotTypeRepository.Queryable().Select(x => new KnowledgeHotSpotDto
+			{
+				Id = x.Id.SelectAll(),
+				HasChild = SqlFunc.Subqueryable<Hotspot>().Where(d => d.ParentId == x.Id).NotAny(),
+				KnowledgeNum = SqlFunc.Subqueryable<Knowledge>().LeftJoin<Hotspot>((k, h) => k.HotspotId == h.Id)
+					.Where((k, h) => h.HotSpotFullName.StartsWith(x.HotSpotFullName))
+					.WhereIF(!string.IsNullOrEmpty(Attribution), (k, h) => k.Attribution == Attribution).DistinctCount(k => k.Id)
+			}).ToTreeAsync(x => x.Children, it => it.ParentId, "", arr);
+        }
+
+
+		/// <summary>
+		/// 知识分类- 只获取一级
+		/// </summary>
+		/// <param name="ParentId">不传默认查询第一级</param>
+		/// <param name="IsEnable">不传查询所有,包含已经禁用的</param>
+		/// <returns></returns>
+		[HttpGet("typelist")]
         public async Task<List<KnowledgeType>> GetTypeList(string? ParentId, bool? IsEnable)
         {
             return await _knowledgeTypeRepository

+ 99 - 73
src/Hotline.Api/Controllers/KnowledgeController.cs

@@ -67,6 +67,7 @@ namespace Hotline.Api.Controllers
         private readonly IKnowApplication _knowApplication;
         private readonly IMediator _mediator;
         private readonly IWorkflowApplication _workflowApplication;
+        private readonly IWorkflowDomainService _workflowDomainService;
         private readonly IKnowledgeWorkFlowRepository _knowledgeWorkFlowRepository;
         private readonly IRepository<User> _userRepository;
         private readonly IRepository<KnowledgeType> _knowledgeTypeRepository;
@@ -88,7 +89,7 @@ namespace Hotline.Api.Controllers
         private readonly IOptionsSnapshot<AppConfiguration> _appOptions;
 
 
-		public KnowledgeController(
+        public KnowledgeController(
            IKnowledgeRepository knowledgeRepository,
            ISessionContext sessionContext,
            IKnowledgeDomainService knowledgeDomainService,
@@ -96,6 +97,7 @@ namespace Hotline.Api.Controllers
            IKnowApplication knowApplication,
            IMediator mediator,
            IWorkflowApplication workflowApplication,
+           IWorkflowDomainService workflowDomainService,
            IKnowledgeWorkFlowRepository knowledgeWorkFlowRepository,
            IRepository<User> userRepository,
            IRepository<KnowledgeType> knowledgeTypeRepository,
@@ -120,7 +122,7 @@ namespace Hotline.Api.Controllers
             IRepository<KnowledgeWord> knowledgeWordRepository,
             IRepository<KnowledgeHotWord> knowledgeWordHotRepository,
            IOptionsSnapshot<AppConfiguration> appOptions,
-			IRepository<KnowledgeHotWord> knowledgeHotWordRepository)
+            IRepository<KnowledgeHotWord> knowledgeHotWordRepository)
         {
             _knowledgeRepository = knowledgeRepository;
             _sessionContext = sessionContext;
@@ -129,6 +131,7 @@ namespace Hotline.Api.Controllers
             _knowApplication = knowApplication;
             _mediator = mediator;
             _workflowApplication = workflowApplication;
+            _workflowDomainService = workflowDomainService;
             _knowledgeWorkFlowRepository = knowledgeWorkFlowRepository;
             _userRepository = userRepository;
             _knowledgeTypeRepository = knowledgeTypeRepository;
@@ -153,7 +156,7 @@ namespace Hotline.Api.Controllers
             _knowledgeWordRepository = knowledgeWordRepository;
             _knowledgeHotWordRepository = knowledgeHotWordRepository;
             _appOptions = appOptions;
-		}
+        }
 
         #endregion
 
@@ -219,7 +222,7 @@ namespace Hotline.Api.Controllers
             var know = await _knowledgeRepository.GetAsync(dto.Data.Id, HttpContext.RequestAborted);
             if (know != null && know.Status == EKnowledgeStatus.OnShelf)
             {
-                if (_sessionContext.OrgIsCenter ||  !_appOptions.Value.IsYiBin)
+                if (_sessionContext.OrgIsCenter || !_appOptions.Value.IsYiBin)
                 {
                     know.Status = EKnowledgeStatus.OffShelf;
                     know.OnShelfTime = null;
@@ -232,16 +235,17 @@ namespace Hotline.Api.Controllers
                     //推省上
                     await _capPublisher.PublishAsync(EventNames.HotlineKnowledgeRemove, pushKnowledge, cancellationToken: HttpContext.RequestAborted);
                 }
-                else {
-	                know.Status = EKnowledgeStatus.OffShelfAudit;
-	                await _knowledgeRepository.UpdateAsync(know, HttpContext.RequestAborted);
+                else
+                {
+                    know.Status = EKnowledgeStatus.OffShelfAudit;
+                    await _knowledgeRepository.UpdateAsync(know, HttpContext.RequestAborted);
 
-					var startDto = _mapper.Map<StartWorkflowDto>(dto.Workflow);
-					startDto.DefinitionModuleCode = WorkflowModuleConsts.KnowledgeOffshelf;
+                    var startDto = _mapper.Map<StartWorkflowDto>(dto.Workflow);
+                    startDto.DefinitionModuleCode = WorkflowModuleConsts.KnowledgeOffshelf;
                     startDto.Opinion = dto.Data.Opinion;
-					startDto.Title = "知识库下架";
-					await StartFlow(know.Id, WorkflowModuleConsts.KnowledgeOffshelf, EKnowledgeApplyType.Offshelf, startDto);
-				}
+                    startDto.Title = "知识库下架";
+                    await StartFlow(know.Id, WorkflowModuleConsts.KnowledgeOffshelf, EKnowledgeApplyType.Offshelf, startDto);
+                }
             }
             else
                 throw UserFriendlyException.SameMessage("知识下架失败");
@@ -308,24 +312,24 @@ namespace Hotline.Api.Controllers
             knowledge.HotspotId = dto.Data.HotspotId;
             knowledge.HotspotExternal = dto.Data.HotspotExternal;
 
-			if (dto.Data.Files.Any())
+            if (dto.Data.Files.Any())
                 knowledge.FileJson = await _fileRepository.AddFileAsync(dto.Data.Files, knowledge.Id, "", HttpContext.RequestAborted);
             else
                 knowledge.FileJson = new List<Share.Dtos.File.FileJson>();
             if (dto.Workflow != null) knowledge.Renewaln = knowledge.Status != EKnowledgeStatus.Drafts;
             if (_appOptions.Value.IsYiBin)
             {
-				//临时处理 修改后提交修改基本信息
-				if (!_sessionContext.OrgIsCenter)
-				{
-					knowledge.Attribution = "部门知识库";
-				}
-				knowledge.CreatorId = _sessionContext.UserId;
-				knowledge.CreatorName = _sessionContext.UserName;
-				knowledge.CreatorOrgId = _sessionContext.OrgId;
-				knowledge.CreatorOrgName = _sessionContext.OrgName;
-				knowledge.CreatorOrgLevel = _sessionContext.OrgLevel;
-			}
+                //临时处理 修改后提交修改基本信息
+                if (!_sessionContext.OrgIsCenter)
+                {
+                    knowledge.Attribution = "部门知识库";
+                }
+                knowledge.CreatorId = _sessionContext.UserId;
+                knowledge.CreatorName = _sessionContext.UserName;
+                knowledge.CreatorOrgId = _sessionContext.OrgId;
+                knowledge.CreatorOrgName = _sessionContext.OrgName;
+                knowledge.CreatorOrgLevel = _sessionContext.OrgLevel;
+            }
 
             await _knowledgeRepository.UpdateNullAsync(knowledge, HttpContext.RequestAborted);
             if (dto.Data.KnowledgeType.Any())
@@ -403,7 +407,8 @@ namespace Hotline.Api.Controllers
                     nextWorkflowDto.NextStepCode = next.Steps.First().Key;
                     nextWorkflowDto.NextStepName = next.Steps.First().Value;
                     if (dto.IsPass)
-                        await _workflowApplication.NextAsync(nextWorkflowDto, cancellationToken: HttpContext.RequestAborted);
+                        await _workflowDomainService.NextAsync(_sessionContext, nextWorkflowDto,
+                            cancellationToken: HttpContext.RequestAborted);
                     else
                     {
                         var reject = nextWorkflowDto.Adapt<RejectDto>();
@@ -455,7 +460,7 @@ namespace Hotline.Api.Controllers
                 var startDto = _mapper.Map<StartWorkflowDto>(dto.Workflow);
                 startDto.DefinitionModuleCode = WorkflowModuleConsts.KnowledgeDelete;
                 startDto.Title = "知识库删除";
-				//await _workflowApplication.StartWorkflowAsync(startDto, _sessionContext, knowledge.Id, cancellationToken: HttpContext.RequestAborted);
+                //await _workflowApplication.StartWorkflowAsync(startDto, _sessionContext, knowledge.Id, cancellationToken: HttpContext.RequestAborted);
                 await StartFlow(dto.Data.Id, WorkflowModuleConsts.KnowledgeDelete, EKnowledgeApplyType.Delete, startDto);
             }
         }
@@ -579,12 +584,12 @@ namespace Hotline.Api.Controllers
 
             if (knowledgeShowInfoDto.FileJson != null && knowledgeShowInfoDto.FileJson.Any())
             {
-	            var ids = knowledgeShowInfoDto.FileJson.Select(x => x.Id).ToList();
-	            knowledgeShowInfoDto.Files = await _fileRepository.GetFilesAsync(ids, HttpContext.RequestAborted);
+                var ids = knowledgeShowInfoDto.FileJson.Select(x => x.Id).ToList();
+                knowledgeShowInfoDto.Files = await _fileRepository.GetFilesAsync(ids, HttpContext.RequestAborted);
             }
 
-			//var files = await _fileRepository.Queryable().Where(x => x.Key == knowledge.Id).ToListAsync();
-   //         if (files.Any()) knowledgeShowInfoDto.Files = _mapper.Map<List<FileDto>>(files);
+            //var files = await _fileRepository.Queryable().Where(x => x.Key == knowledge.Id).ToListAsync();
+            //         if (files.Any()) knowledgeShowInfoDto.Files = _mapper.Map<List<FileDto>>(files);
 
             if (IsAddPv == true)
                 _mediator.Publish(new GetKnowledgeInfoNotify(knowledge));
@@ -674,37 +679,48 @@ namespace Hotline.Api.Controllers
         [HttpGet("knowledge-status-data")]
         public Dictionary<string, dynamic> KnowledgeStatus()
         {
-            var tabNames = new List<KeyValuePair<string, string>>
+            var tabNames = new List<KeyValuePair<int, string>>
             {
-                new KeyValuePair<string, string>("3", "已上架"),
-                new KeyValuePair<string, string>("4", "已下架"),
-                new KeyValuePair<string, string>("1", "审核中"),
-                new KeyValuePair<string, string>("8", "草稿"),
-                new KeyValuePair<string, string>("", "全部")
-			};
-
-            var tabNewDraftsNames = new List<KeyValuePair<string, string>>
+                new KeyValuePair<int, string>(3, "已上架"),
+                new KeyValuePair<int, string>(4, "已下架"),
+                new KeyValuePair<int, string>(1, "审核中"),
+                new KeyValuePair<int, string>(8, "草稿"),
+                new KeyValuePair<int, string>(-1, "全部")
+            };
+
+            var tabNewDraftsNames = new List<KeyValuePair<int, string>>
             {
-	            new KeyValuePair<string, string>("", "全部"),
-				new KeyValuePair<string, string>("0", "待提交"),
-	            new KeyValuePair<string, string>("5", "审核不通过"),
+                new KeyValuePair<int, string>(-1, "全部"),
+                new KeyValuePair<int, string>(0, "待提交"),
+                new KeyValuePair<int, string>(5, "审核不通过"),
             };
 
             var tabAuditingNames = new List<KeyValuePair<string, string>>
             {
-	            new KeyValuePair<string, string>("", "全部"),
-				new KeyValuePair<string, string>("KnowledgeAdd", "新增审核"),
-	            new KeyValuePair<string, string>("KnowledgeUpdate", "修改审核"),
-	            new KeyValuePair<string, string>("KnowledgeDelete", "删除审核"),
-	            new KeyValuePair<string, string>("KnowledgeOffshelf", "下架审核"),
-			};
+                new KeyValuePair<string, string>("", "全部"),
+                new KeyValuePair<string, string>("KnowledgeAdd", "新增审核"),
+                new KeyValuePair<string, string>("KnowledgeUpdate", "修改审核"),
+                new KeyValuePair<string, string>("KnowledgeDelete", "删除审核"),
+                new KeyValuePair<string, string>("KnowledgeOffshelf", "下架审核"),
+            };
 
-			return _baseDataApplication
-                .FileType(EFileType.excel | EFileType.pdf)
-                .Add("tabNames", tabNames)
-                .Add("tabNewDraftsNames", tabNewDraftsNames)
-                .Add("tabAuditingNames", tabAuditingNames)
-				.Build();
+            //return _baseDataApplication
+            //    .FileType(EFileType.excel | EFileType.pdf)
+            //    .Build();
+            var ignoreFileType = EFileType.excel | EFileType.pdf;
+            var items = EnumExts.GetDescriptions<EFileType>();
+            var filteredDictionary = items
+                 .Where(kvp => (ignoreFileType & (EFileType)kvp.Key) == 0)
+                 .ToDictionary(kvp => kvp.Key, kvp => kvp.Value)
+                 .ToList();
+
+            return new Dictionary<string, dynamic>
+            {
+                { "fileType", filteredDictionary},
+                { "tabNames", tabNames },
+                { "tabNewDraftsNames", tabNewDraftsNames },
+                { "tabAuditingNames", tabAuditingNames }
+            };
         }
 
         /// <summary>
@@ -760,9 +776,14 @@ namespace Hotline.Api.Controllers
         [HttpGet("knowretrieval/base_data")]
         public Dictionary<string, dynamic> GetKnowretrievalBaseData()
         {
-            return _baseDataApplication
-                .KnowledgeRetrievalType(new[]{3, 4})
-                .Build();
+            //return _baseDataApplication
+            //    .KnowledgeRetrievalType(new[] { 3, 4 })
+            //    .Build();
+            var ignoreFileType = new[] { 3, 4 };
+            return new Dictionary<string, dynamic>
+            {
+                { "knowledgeRetrievalType", EnumExts.GetDescriptions<EKnowledgeRetrievalType>().Where(m => !ignoreFileType.Contains(m.Key)).ToList() }
+            };
         }
 
         /// <summary>
@@ -994,19 +1015,19 @@ namespace Hotline.Api.Controllers
         [HttpGet("offshelf-flow-start")]
         public async Task<NextStepsDto> GetOffshelfFlowStartOptionsAsync()
         {
-	        return await _workflowApplication.GetStartStepsAsync(WorkflowModuleConsts.KnowledgeOffshelf,
-		        HttpContext.RequestAborted);
+            return await _workflowApplication.GetStartStepsAsync(WorkflowModuleConsts.KnowledgeOffshelf,
+                HttpContext.RequestAborted);
         }
 
-		/// <summary>
-		/// 开始流程
-		/// </summary>
-		/// <param name="id">知识ID</param>
-		/// <param name="moduleCode">知识模板编号</param>
-		/// <param name="eKnowledgeApplyType">申请类型</param>
-		/// <param name="dto">流程开启参数</param>
-		/// <returns></returns>
-		private async Task<string> StartFlow(string id, string moduleCode, EKnowledgeApplyType eKnowledgeApplyType, StartWorkflowDto dto)
+        /// <summary>
+        /// 开始流程
+        /// </summary>
+        /// <param name="id">知识ID</param>
+        /// <param name="moduleCode">知识模板编号</param>
+        /// <param name="eKnowledgeApplyType">申请类型</param>
+        /// <param name="dto">流程开启参数</param>
+        /// <returns></returns>
+        private async Task<string> StartFlow(string id, string moduleCode, EKnowledgeApplyType eKnowledgeApplyType, StartWorkflowDto dto)
         {
             var knowledge = await _knowledgeRepository.GetAsync(id, HttpContext.RequestAborted);
             if (knowledge == null)
@@ -1022,7 +1043,7 @@ namespace Hotline.Api.Controllers
             await _knowledgeDomainService.AddWorkFlowAsync(id, eKnowledgeApplyType, HttpContext.RequestAborted);
 
             dto.DefinitionModuleCode = moduleCode;
-			//dto.Title = knowledge.Title;
+            //dto.Title = knowledge.Title;
             return await _workflowApplication.StartWorkflowAsync(dto, id, cancellationToken: HttpContext.RequestAborted);
         }
         #endregion
@@ -1196,8 +1217,9 @@ namespace Hotline.Api.Controllers
                 var type = await _knowledgeTypeRepository.GetAsync(x => x.Id == dto.KnowledgeTypeId);
                 typeSpliceName = type?.SpliceName;
             }
-            var (total, items) = await _knowledgeCorrectionRepository.Queryable()
+            var (total, items) = await _knowledgeCorrectionRepository.Queryable(includeDeleted: true)
                 .Includes(x => x.Knowledge)
+                .Where(m => m.IsDeleted == false && m.Knowledge.IsDeleted == false)
                 //.WhereIF(!string.IsNullOrEmpty(dto.KnowledgeTypeId), x => x.Knowledge.KnowledgeTypeId == dto.KnowledgeTypeId!)
                 .WhereIF(!string.IsNullOrEmpty(dto.CreatorName), x => x.CreatorName == dto.CreatorName!)
                  //.WhereIF(!string.IsNullOrEmpty(typeSpliceName), x => SqlFunc.JsonLike(x.Knowledge.KnowledgeType, typeSpliceName))
@@ -1595,9 +1617,13 @@ namespace Hotline.Api.Controllers
         [HttpGet("hotword/basedata")]
         public Dictionary<string, dynamic> AddKnowledgeHotWordBaseDataAsync()
         {
-            return _baseDataApplication
-                .KnowledgeHotWordType()
-                .Build();
+            //return _baseDataApplication
+            //    .KnowledgeHotWordType()
+            //    .Build();
+            return new Dictionary<string, dynamic>
+            {
+                { "knowledgeHotWordType", EnumExts.GetDescriptions<EKnowledgeHotWordType>() }
+            };
         }
 
         /// <summary>

+ 323 - 176
src/Hotline.Api/Controllers/OrderController.cs

@@ -65,17 +65,9 @@ using XF.Domain.Repository;
 using XF.Utility.EnumExtensions;
 using Hotline.Application.Contracts.Validators.FlowEngine;
 using Hotline.Authentications;
-using Microsoft.AspNetCore.Components;
-using Quartz.Simpl;
-using static Lucene.Net.Util.Fst.Util;
-using DocumentFormat.OpenXml.Spreadsheet;
-using System.Threading;
-using Hotline.Caching.Services;
-using Hotline.CallCenter.Calls;
-using Hotline.CallCenter.BlackLists;
-using Hotline.Share.Mq;
 using Hotline.Share.Dtos.CallCenter;
-using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using Hotline.Share.Dtos.File;
+using NPOI.SS.Formula.Functions;
 
 namespace Hotline.Api.Controllers;
 
@@ -288,12 +280,37 @@ public class OrderController : BaseController
 
     #region 工单发布
 
+    /// <summary>
+    /// 发布代办(集合)
+    /// </summary>
+    /// <param name="dto"></param>
+    /// <returns></returns>
+    [HttpGet("publish/items")]
+    public async Task<IList<PublishDto>> GetPublishOrderListAsync([FromQuery] QueryOrderPublishDto dto)
+    { 
+        var query = _orderApplication.GetPublishOrderList(dto);
+        return (await query.ToFixedListAsync(dto.QueryIndex, 200, HttpContext.RequestAborted))
+            .Adapt<List<PublishDto>>();
+    }
+
+    /// <summary>
+    /// 发布代办
+    /// </summary>
+    /// <param name="dto"></param>
+    /// <returns></returns>
+    [HttpGet("publish/count")]
+    public async Task<int> GetPublishOrderCount([FromQuery] QueryOrderPublishDto dto)
+    {
+        return await _orderApplication.GetPublishOrderList(dto).CountAsync(HttpContext.RequestAborted);
+    }
+
     /// <summary>
     /// 查询(工单发布)
     /// </summary>
     /// <param name="dto"></param>
     /// <returns></returns>
     [HttpGet("publish")]
+    [Obsolete("前端不再调用,择机删除")]
     public async Task<PagedDto<PublishDto>> PublishOrderList([FromQuery] QueryOrderPublishDto dto)
     {
         var (total, items) = await _orderRepository.Queryable()
@@ -314,8 +331,8 @@ public class OrderController : BaseController
             .WhereIF(!string.IsNullOrEmpty(dto.FromPhone), d => d.FromPhone == dto.FromPhone) //来电号码
                                                                                               //.WhereIF(!string.IsNullOrEmpty(dto.PubMan),
                                                                                               //    d => d.AcceptorName.Contains(dto.PubMan!) || d.AcceptorStaffNo.Contains(dto.PubMan!))
-            //.WhereIF(dto.PubRange == EPublicState.Pub, d => d.OrderPublish.PublishState)
-            //.WhereIF(dto.PubRange == EPublicState.NoPub, d => !d.OrderPublish.PublishState)
+                                                                                              //.WhereIF(dto.PubRange == EPublicState.Pub, d => d.OrderPublish.PublishState)
+                                                                                              //.WhereIF(dto.PubRange == EPublicState.NoPub, d => !d.OrderPublish.PublishState)
             .WhereIF(dto.IsProvinceOrder.HasValue && dto.IsProvinceOrder == true, d => d.Source == ESource.ProvinceStraight)
             .WhereIF(dto.IsProvinceOrder.HasValue && dto.IsProvinceOrder == false, d => d.Source != ESource.ProvinceStraight)
             .WhereIF(dto.FiledType != null && dto.FiledType == FiledType.CenterFiled, d => d.ProcessType == EProcessType.Zhiban)
@@ -399,7 +416,7 @@ public class OrderController : BaseController
                 .OrderByDescending(m => m.CreationTime).FirstAsync(HttpContext.RequestAborted);
             if (publishedDeleted != null && _sessionContext.RequiredUserId != publishedDeleted.CreatorId)
             {
-                throw UserFriendlyException.SameMessage($"工单被取消发布过, 之前的发布人是 [{publishedDeleted.CreatorName}], 您不能发布;");
+                throw UserFriendlyException.SameMessage($"工单被取消发布过, 之前的发布人是 [{publishedDeleted.CreatorName}], 您不能发布;");
             }
         }
 
@@ -593,7 +610,7 @@ public class OrderController : BaseController
                 .Select(x => new Kv { Key = x.DicDataValue, Value = x.DicDataName }).ToList()
         };
         var (idName, idNames) = await _workflowDomainService.GetHandleOrgsAsync(order.WorkflowId, HttpContext.RequestAborted);
-        res.ActualHandleOrgName = new Kv() {  Key=order.ActualHandleOrgCode, Value= order.ActualHandleOrgName };
+        res.ActualHandleOrgName = new Kv() { Key = order.ActualHandleOrgCode, Value = order.ActualHandleOrgName };
         res.idNames = order.CounterSignType == null
             ? null
             : idNames.Where(d => d.Key != idName.Key).ToList();
@@ -606,6 +623,7 @@ public class OrderController : BaseController
     /// <param name="dto"></param>
     /// <returns></returns>
     [HttpGet("published")]
+    [Obsolete("前端不再调用,择机删除")]
     public async Task<PagedDto<PublishedDto>> PublishedOrderList([FromQuery] PublishedPagedRequest dto)
     {
         var (total, items) = await _orderPublishRepository.Queryable()
@@ -638,6 +656,30 @@ public class OrderController : BaseController
         return new PagedDto<PublishedDto>(total, _mapper.Map<IReadOnlyList<PublishedDto>>(items));
     }
 
+    /// <summary>
+    /// 发布列表(集合)
+    /// </summary>
+    /// <param name="dto"></param>
+    /// <returns></returns>
+    [HttpGet("published/items")]
+    public async Task<IList<PublishedDto>> GetPublishedOrderListAsync([FromQuery] PublishedPagedRequest dto)
+    {
+        var query = _orderApplication.GetPublishedOrder(dto);
+        return (await query.ToFixedListAsync(dto.QueryIndex, 200, HttpContext.RequestAborted))
+            .Adapt<List<PublishedDto>>();
+    }
+
+    /// <summary>
+    /// 发布列表(总条数)
+    /// </summary>
+    /// <param name="dto"></param>
+    /// <returns></returns>
+    [HttpGet("published/count")]
+    public async Task<int> GetPublishedOrderCountAsync([FromQuery] PublishedPagedRequest dto)
+    {
+        return await _orderApplication.GetPublishedOrder(dto).CountAsync();
+    }
+
     /// <summary>
     /// 发布详情
     /// </summary>
@@ -741,7 +783,8 @@ public class OrderController : BaseController
         var users = await _userRepository.Queryable()
             .Includes(d => d.Organization)
             .Includes(d => d.Roles)
-            .Where(d => d.Roles.Any(x => roles.Contains(x.Name)))
+            .WhereIF(!_appOptions.Value.IsZiGong, d => d.Roles.Any(x => roles.Contains(x.Name)))
+            .WhereIF(_appOptions.Value.IsZiGong, d => d.OrgId == OrgSeedData.CenterId)
             .ToListAsync(HttpContext.RequestAborted);
         return users.Select(d => new OrderMigrationHandler
         {
@@ -865,9 +908,10 @@ public class OrderController : BaseController
                      (d.Order.ExpiredTime > d.Order.ActualHandleTime && d.Order.Status >= EOrderStatus.Filed)) //否 超期
             .WhereIF(dto.StartTime.HasValue, d => d.VisitTime >= dto.StartTime)
             .WhereIF(dto.EndTime.HasValue, d => d.VisitTime <= dto.EndTime)
-            .WhereIF(dto.StartPublishTime.HasValue && dto.EndPublishTime.HasValue,d=> d.PublishTime>= dto.StartPublishTime && d.PublishTime <= dto.EndPublishTime) // 发布时间
-            .WhereIF(!string.IsNullOrEmpty(dto.ActualHandleOrgName),d=>d.Order.ActualHandleOrgName == dto.ActualHandleOrgName) //接办部门
-            .OrderByDescending(x => x.PublishTime)
+            .WhereIF(dto.StartPublishTime.HasValue && dto.EndPublishTime.HasValue, d => d.PublishTime >= dto.StartPublishTime && d.PublishTime <= dto.EndPublishTime) // 发布时间
+            .WhereIF(!string.IsNullOrEmpty(dto.ActualHandleOrgName), d => d.Order.ActualHandleOrgName == dto.ActualHandleOrgName) //接办部门
+            .WhereIF(!string.IsNullOrEmpty(dto.OrgLevelOneName), d => d.Order.OrgLevelOneName.Contains(dto.OrgLevelOneName)) //一级部门
+			.OrderByDescending(x => x.PublishTime)
             .WhereIF(dto.Channel.NotNullOrEmpty(), d => d.Order.SourceChannelCode == dto.Channel)
             .OrderByDescending(d => d.PublishTime)
             .ToPagedListAsync(dto.PageIndex, dto.PageSize, HttpContext.RequestAborted);
@@ -887,16 +931,16 @@ public class OrderController : BaseController
     /// </summary>
     /// <returns></returns>
     [HttpGet("visit/basedata")]
-    public Dictionary<string, dynamic> VisitBaseData()
-        => _baseDataApplication
-            .VisitType()
-            .VoiceEvaluate()
-            .SeatEvaluate()
-            .VisitSatisfaction()
-            .VisitManner()
-            .VisitStateQuery()
-            .SourceChannel()
-            .Build();
+    public Dictionary<string, dynamic> VisitBaseData() => new Dictionary<string, dynamic>
+        {
+            { "seatEvaluate", EnumExts.GetDescriptions<ESeatEvaluate>() },
+            { "visitType", EnumExts.GetDescriptions<EVisitType>() },
+            { "voiceEvaluate", EnumExts.GetDescriptions<EVoiceEvaluate>() },
+            { "visitSatisfaction", _sysDicDataCacheManager.GetVisitSatisfaction().Where(m => m.DicDataValue != "-1").ToList() },
+            { "visitMananer", _sysDicDataCacheManager.VisitMananer.Where(x => x.DicDataValue != "-1").ToList() },
+            { "visitStateQuery", EnumExts.GetDescriptions<EVisitStateQuery>() },
+            { "sourceChannel", _sysDicDataCacheManager.SourceChannel }
+        };
 
     /// <summary>
     /// 回访详情
@@ -1162,12 +1206,21 @@ public class OrderController : BaseController
     /// <returns></returns>
     [HttpGet("visit/batch-basedata")]
     public Dictionary<string, dynamic> VisitBatchBaseDat()
-        => _baseDataApplication
-            .SeatEvaluate()
-            .DissatisfiedReason()
-            .VisitSatisfaction()
-            .VisitManner()
-            .Build();
+    {
+        //return _baseDataApplication
+        //    .SeatEvaluate()
+        //    .DissatisfiedReason()
+        //    .VisitSatisfaction()
+        //    .VisitManner()
+        //    .Build();
+        return new Dictionary<string, dynamic>
+        {
+            { "seatEvaluate", EnumExts.GetDescriptions<ESeatEvaluate>() },
+            { "dissatisfiedReason", _sysDicDataCacheManager.GetSysDicDataCache(SysDicTypeConsts.DissatisfiedReason) },
+            { "visitSatisfaction", _sysDicDataCacheManager.GetVisitSatisfaction() },
+            { "visitManner", _sysDicDataCacheManager.VisitMananer }
+        };
+    }
 
     /// <summary>
     /// 扭转列表
@@ -1286,7 +1339,8 @@ public class OrderController : BaseController
         var users = await _userRepository.Queryable()
             .Includes(d => d.Organization)
             .Includes(d => d.Roles)
-            .Where(d => d.Roles.Any(x => roles.Contains(x.Name)))
+            .WhereIF(!_appOptions.Value.IsZiGong, d => d.Roles.Any(x => roles.Contains(x.Name)))
+            .WhereIF(_appOptions.Value.IsZiGong, d => d.OrgId == OrgSeedData.CenterId)
             .ToListAsync(HttpContext.RequestAborted);
         return users.Select(d => new OrderMigrationHandler
         {
@@ -1311,60 +1365,66 @@ public class OrderController : BaseController
             .ExecuteCommandAsync(HttpContext.RequestAborted);
     }
 
-	/// <summary>
-	/// 部门满意度明细
-	/// </summary>
-	/// <param name="dto"></param>
-	/// <returns></returns>
-	[HttpGet("org-visitdetail-list")]
-	public async Task<PagedDto<OrgVisitDetailListResp>> OrgVisitDetailList([FromQuery] OrgVisitDetailListReq dto)
-	{
-		var query = _orderRepository.OrgVisitDetailFiltrationList(dto);
-		var (total, items) = await query.ToPagedListAsync(dto.PageIndex, dto.PageSize, HttpContext.RequestAborted);
+    /// <summary>
+    /// 部门满意度明细
+    /// </summary>
+    /// <param name="dto"></param>
+    /// <returns></returns>
+    [HttpGet("org-visitdetail-list")]
+    public async Task<PagedDto<OrgVisitDetailListResp>> OrgVisitDetailList([FromQuery] OrgVisitDetailListReq dto)
+    {
+        //特殊需求:管理员账号使用该查询条件查询的数据,与富顺的账号查询的数据一致;
+        var isAdmin = _orderDomainService.IsCheckAdmin();
+        var orgId = isAdmin ? "001059" : _sessionContext.RequiredOrgId;
+        var query = _orderRepository.OrgVisitDetailFiltrationList(dto, orgId);
+        var (total, items) = await query.ToPagedListAsync(dto.PageIndex, dto.PageSize, HttpContext.RequestAborted);
 
-		return new PagedDto<OrgVisitDetailListResp>(total, _mapper.Map<IReadOnlyList<OrgVisitDetailListResp>>(items));
-	}
+        return new PagedDto<OrgVisitDetailListResp>(total, _mapper.Map<IReadOnlyList<OrgVisitDetailListResp>>(items));
+    }
 
-	/// <summary>
-	/// 部门满意度明细导出
-	/// </summary>
-	/// <param name="dto"></param>
-	/// <returns></returns>
-	[HttpPost("org-visitdetail-list-export")]
-	public async Task<FileStreamResult> OrgVisitDetailListExport([FromBody] ExportExcelDto<OrgVisitDetailListReq> dto)
-	{
-		var query = _orderRepository.OrgVisitDetailFiltrationList(dto.QueryDto);
+    /// <summary>
+    /// 部门满意度明细导出
+    /// </summary>
+    /// <param name="dto"></param>
+    /// <returns></returns>
+    [HttpPost("org-visitdetail-list-export")]
+    public async Task<FileStreamResult> OrgVisitDetailListExport([FromBody] ExportExcelDto<OrgVisitDetailListReq> dto)
+    {
+        //特殊需求:管理员账号使用该查询条件查询的数据,与富顺的账号查询的数据一致;
+        var isAdmin = _orderDomainService.IsCheckAdmin();
+        var orgId = isAdmin ? "001059" : _sessionContext.RequiredOrgId;
+        var query = _orderRepository.OrgVisitDetailFiltrationList(dto.QueryDto, orgId);
 
-		List<OrgVisitDetailListResp> orders;
-		if (dto.IsExportAll)
-		{
-			orders = await query.ToListAsync(HttpContext.RequestAborted);
-		}
-		else
-		{
-			var (_, items) = await query.ToPagedListAsync(dto.QueryDto, HttpContext.RequestAborted);
-			orders = items;
-		}
-		dynamic? dynamicClass = DynamicClassHelper.CreateDynamicClass(dto.ColumnInfos);
-		var dtos = orders
-			.Select(stu => _mapper.Map(stu, typeof(OrgVisitDetailListResp), dynamicClass))
-			.Cast<object>()
-			.ToList();
-
-		var stream = ExcelHelper.CreateStream(dtos);
-		return ExcelStreamResult(stream, "部门满意度明细");
-	}
+        List<OrgVisitDetailListResp> orders;
+        if (dto.IsExportAll)
+        {
+            orders = await query.ToListAsync(HttpContext.RequestAborted);
+        }
+        else
+        {
+            var (_, items) = await query.ToPagedListAsync(dto.QueryDto, HttpContext.RequestAborted);
+            orders = items;
+        }
+        dynamic? dynamicClass = DynamicClassHelper.CreateDynamicClass(dto.ColumnInfos);
+        var dtos = orders
+            .Select(stu => _mapper.Map(stu, typeof(OrgVisitDetailListResp), dynamicClass))
+            .Cast<object>()
+            .ToList();
 
-	#endregion
+        var stream = ExcelHelper.CreateStream(dtos);
+        return ExcelStreamResult(stream, "部门满意度明细");
+    }
 
-	#region 二次回访申请
+    #endregion
 
-	/// <summary>
-	/// 可二次回访申请列表
-	/// </summary>
-	/// <param name="dto"></param>
-	/// <returns></returns>
-	[HttpGet("visitapply/visitagainlist")]
+    #region 二次回访申请
+
+    /// <summary>
+    /// 可二次回访申请列表
+    /// </summary>
+    /// <param name="dto"></param>
+    /// <returns></returns>
+    [HttpGet("visitapply/visitagainlist")]
     public async Task<PagedDto<OrderCanVisitAgainDto>> OrderVisitAgainList([FromQuery] OrderVisitAgainListDto dto)
     {
         var (total, items) = await _orderVisitedDetailRepository.Queryable()
@@ -1782,7 +1842,7 @@ public class OrderController : BaseController
         try
         {
             dto.NextWorkflow.WorkflowId = delay.WorkflowId;
-            await _workflowApplication.NextAsync(dto.NextWorkflow, cancellationToken: HttpContext.RequestAborted);
+            await _workflowDomainService.NextAsync(_sessionContext, dto.NextWorkflow, cancellationToken: HttpContext.RequestAborted);
         }
         catch (Exception e)
         {
@@ -1903,14 +1963,14 @@ public class OrderController : BaseController
             .WhereIF(dto.IsApply == true, d => d.DelayState != EDelayState.Examining)
             .WhereIF(dto.IsApply == false, d => d.DelayState == EDelayState.Examining)
             .WhereIF(dto.DelayState != null, d => d.DelayState == dto.DelayState)
-            .WhereIF(dto.DataScope is 1, x => x.CreatorId == _sessionContext.RequiredUserId)
+            .WhereIF(dto.DataScope is 1, d => d.CreatorId == _sessionContext.RequiredUserId)
             .WhereIF(dto.QueryDelayState is EQueryDelayState.Examining, d => d.DelayState == EDelayState.Examining)
             .WhereIF(dto.QueryDelayState is EQueryDelayState.Pass, d => d.DelayState == EDelayState.Pass)
             .WhereIF(dto.QueryDelayState is EQueryDelayState.NoPass, d => d.DelayState == EDelayState.NoPass)
-            .WhereIF(!string.IsNullOrEmpty(dto.CurrentStepName),d=>d.Workflow != null && d.Workflow.ActualHandleStepName == dto.CurrentStepName) //当前节点
-            .WhereIF(!string.IsNullOrEmpty(dto.ActualHandlerName),d=>d.Workflow != null && d.Workflow.ActualHandlerName == dto.ActualHandlerName) // 审批人
-            .WhereIF(!string.IsNullOrEmpty(dto.OrgLevelOneName),d=>d.Order.OrgLevelOneName == dto.OrgLevelOneName) //一级部门
-            .WhereIF(dto.StartCreationTime.HasValue && dto.EndCreationTime.HasValue, d=>d.CreationTime >= dto.StartCreationTime && d.CreationTime <= dto.EndCreationTime) // 申请时间
+            .WhereIF(!string.IsNullOrEmpty(dto.CurrentStepName), d => !SqlFunc.IsNullOrEmpty(d.WorkflowId) && d.Workflow.ActualHandleStepName == dto.CurrentStepName) //当前节点
+            .WhereIF(!string.IsNullOrEmpty(dto.ActualHandlerName), d => !SqlFunc.IsNullOrEmpty(d.WorkflowId) && d.Workflow.ActualHandlerName == dto.ActualHandlerName) // 审批人
+            .WhereIF(!string.IsNullOrEmpty(dto.OrgLevelOneName), d => d.Order.OrgLevelOneName == dto.OrgLevelOneName) //一级部门
+            .WhereIF(dto.StartCreationTime.HasValue && dto.EndCreationTime.HasValue, d => d.CreationTime >= dto.StartCreationTime && d.CreationTime <= dto.EndCreationTime) // 申请时间
             .OrderByDescending(d => d.ApplyDelayTime)
             .ToPagedListAsync(dto.PageIndex, dto.PageSize, HttpContext.RequestAborted);
 
@@ -2285,7 +2345,9 @@ public class OrderController : BaseController
         try
         {
             dto.NextWorkflow.WorkflowId = screen.WorkflowId;
-            await _workflowApplication.NextAsync(dto.NextWorkflow, cancellationToken: HttpContext.RequestAborted);
+            //await _workflowApplication.NextAsync(dto.NextWorkflow, cancellationToken: HttpContext.RequestAborted);
+            await _workflowDomainService.NextAsync(_sessionContext, dto.NextWorkflow,
+                cancellationToken: HttpContext.RequestAborted);
         }
         catch (Exception e)
         {
@@ -3763,9 +3825,23 @@ public class OrderController : BaseController
                     {
                         await AverageSendOrderAsync(nextDto, HttpContext.RequestAborted);
                     }
+                    var flowStepHandler = nextDto.NextHandlers.FirstOrDefault();
+                    await _orderRepository.Updateable().SetColumns(o => new Order()
+                    {
+                        CenterToOrgHandlerId = flowStepHandler.UserId,
+                        CenterToOrgHandlerName = flowStepHandler.Username
+                    }).Where(o => o.Id == order.Id).ExecuteCommandAsync(HttpContext.RequestAborted);
                 }
 
-                await _workflowDomainService.NextAsync(_sessionContext, nextDto, order.ExpiredTime, isAutoFillSummaryOpinion, cancellationToken);
+                //if (workflowDto.BusinessType == EBusinessType.Seat)
+                //{
+	               // await _orderRepository.Updateable().SetColumns(o => new Order()
+	               // {
+		              //  Status = EOrderStatus.WaitForAccept
+	               // }).Where(o => o.Id == order.Id).ExecuteCommandAsync(HttpContext.RequestAborted);
+                //}
+
+				await _workflowDomainService.NextAsync(_sessionContext, nextDto, order.ExpiredTime, isAutoFillSummaryOpinion, cancellationToken);
                 break;
             case EOrderAssignMode.CrossLevel:
                 if (!orderHandleFlowDto.CrossSteps.Any())
@@ -3774,7 +3850,7 @@ public class OrderController : BaseController
                 orderHandleFlowDto.CrossSteps = orderHandleFlowDto.CrossSteps.OrderBy(d => d.Sort).ToList();
                 var stepCount = orderHandleFlowDto.CrossSteps.Count;
                 var unhandleSteps = new List<WorkflowStep> { startStep };
-                for (int i = 0;i < stepCount;i++)
+                for (int i = 0; i < stepCount; i++)
                 {
                     var crossStep = orderHandleFlowDto.CrossSteps[i];
                     var tempSteps = new List<WorkflowStep>();
@@ -3922,13 +3998,6 @@ public class OrderController : BaseController
     [HttpPost("endcs")]
     public async Task EndCountersign([FromBody] EndCountersignDto dto)
     {
-        //var workflow = await _workflowDomainService.TerminalCountersignAsync(dto.CountersignId, HttpContext.RequestAborted);
-        //var order = await _orderRepository.GetAsync(d => d.WorkflowId == workflow.Id, HttpContext.RequestAborted);
-        //if (order is null)
-        //    throw new UserFriendlyException($"工单未开启流程, workflowId: {workflow.Id}");
-        //order.UpdateHandlingStatus(workflow.IsInCountersign);
-        //_mapper.Map(workflow, order);
-        //await _orderRepository.UpdateAsync(order, HttpContext.RequestAborted);
         await _orderApplication.EndCountersign(dto, HttpContext.RequestAborted);
     }
 
@@ -4147,7 +4216,8 @@ public class OrderController : BaseController
             query.WhereIF(dto.QueryType is 1, d => d.IsForwarded == false)
                 .WhereIF(dto.QueryType is 2, d => d.IsForwarded == true)
                 .Where(d => SqlFunc.Subqueryable<OrderSpecial>().Where(os => os.OrderId == d.Id && os.IsDeleted == false && os.SpecialType == ESpecialType.ReTransact)
-                    .NotAny());
+                    .NotAny())
+                ;
         }
 
         var (total, items) = await query
@@ -4160,7 +4230,7 @@ public class OrderController : BaseController
             .Where(d => SqlFunc.Subqueryable<OrderDelay>().Where(od => od.OrderId == d.Id && od.IsDeleted == false && od.DelayState == EDelayState.Examining).NotAny())
             .Where(d => SqlFunc.Subqueryable<OrderSendBackAudit>().Where(osba => osba.OrderId == d.Id && osba.IsDeleted == false && osba.State == ESendBackAuditState.Apply)
                 .NotAny())
-            .Where(d=> SqlFunc.Subqueryable<OrderSpecial>().Where(s=>s.OrderId == d.Id).NotAny() || SqlFunc.Subqueryable<OrderSpecial>().Where(s=> s.OrderId == d.Id && s.State > 0).Any())
+            .Where(d => SqlFunc.Subqueryable<OrderSpecial>().Where(s => s.OrderId == d.Id && s.State == 0 && s.IsDeleted == false ).NotAny())
             .WhereIF(dto.IsProvince.HasValue, d => d.IsProvince == dto.IsProvince)
             .WhereIF(!string.IsNullOrEmpty(dto.Keyword), d => d.Title.StartsWith(dto.Keyword))
             .WhereIF(!string.IsNullOrEmpty(dto.No), d => d.No == dto.No)
@@ -4173,7 +4243,8 @@ public class OrderController : BaseController
             .WhereIF(dto.ExpiredOrAlmostOverdue.HasValue && dto.ExpiredOrAlmostOverdue == false,
                 d => d.NearlyExpiredTime < DateTime.Now && d.ExpiredTime > DateTime.Now) //即将超期 未办
             .Where(d => d.Source < ESource.MLSQ || d.Source > ESource.WZSC)
-            .Where(d => d.Status != EOrderStatus.BackToProvince && d.Status < EOrderStatus.Filed)
+            .Where(d => d.Status != EOrderStatus.BackToProvince)
+            .WhereIF(!isHandledStep || _appOptions.Value.IsYiBin,d=>d.Status < EOrderStatus.Filed)
             //.Where(d => SqlFunc.Subqueryable<OrderSpecial>().Where(os => os.OrderId == d.Id).NotAny())
             //.Where(d => d.OrderSpecials.Any() == false || d.OrderSpecials.Any(s => s.State > 0))
             .WhereIF(dto.StartTime.HasValue, d => d.StartTime >= dto.StartTime)
@@ -4217,48 +4288,14 @@ public class OrderController : BaseController
     [HttpGet("waited/center")]
     public async Task<PagedDto<OrderDto>> QueryWaitedForCenter([FromQuery] QueryOrderWaitedCenterDto dto)
     {
-        //if (dto.EndCreationTime.HasValue)
-        //    dto.EndCreationTime = dto.EndCreationTime.Value.AddDays(1).AddSeconds(-1);
-        //if (dto.StartTimeEnd.HasValue)
-        //    dto.StartTimeEnd = dto.StartTimeEnd.Value.AddDays(1).AddSeconds(-1);
-
-        //var (total, items) = await _orderRepository.Queryable()
-        //    //.LeftJoin<WorkflowStep>((d, step) => d.Id == step.ExternalId)
-        //    //.Where((d, step) =>
-        //    // ((string.IsNullOrEmpty(d.WorkflowId) && (string.IsNullOrEmpty(d.SignerId) || d.SignerId == _sessionContext.RequiredUserId)) ||
-        //    //  (!string.IsNullOrEmpty(d.WorkflowId) &&
-        //    //   ((step.FlowAssignType == EFlowAssignType.User && !string.IsNullOrEmpty(step.HandlerId) && step.HandlerId == _sessionContext.RequiredUserId) ||
-        //    //    (step.FlowAssignType == EFlowAssignType.Org && !string.IsNullOrEmpty(step.HandlerOrgId) && step.HandlerOrgId == _sessionContext.RequiredOrgId) ||
-        //    //    (step.FlowAssignType == EFlowAssignType.Role && !string.IsNullOrEmpty(step.RoleId) && _sessionContext.Roles.Contains(step.RoleId))) &&
-        //    //   ( step.Status != EWorkflowStepStatus.Handled )
-        //    //  )))
-        //    //.Where(d => d.Workflow.Steps.Any(s => s.Status < EWorkflowStepStatus.Handled && s.HandlerOrgId == OrgSeedData.CenterId))
-        //    .LeftJoin<WorkflowStep>((d, step) => d.Id == step.ExternalId)
-        //    .Where((d, step) => (step.Id == null || (step.HandlerOrgId == OrgSeedData.CenterId && step.Status < EWorkflowStepStatus.Handled)))
-        //    .Where(d => d.Source < ESource.MLSQ || d.Source > ESource.WZSC)
-        //    .Where(d => d.Status != EOrderStatus.BackToProvince && d.Status < EOrderStatus.Filed)
-        //    .WhereIF(!string.IsNullOrEmpty(dto.No), d => d.No!.Contains(dto.No!))
-        //    .WhereIF(!string.IsNullOrEmpty(dto.Title), d => d.Title!.Contains(dto.Title!))
-        //    .WhereIF(dto is { StCreationTime: not null, EndCreationTime: not null }, d => d.CreationTime >= dto.StCreationTime && d.CreationTime <= dto.EndCreationTime)
-        //    .WhereIF(dto is { StartTimeSt: not null, StartTimeEnd: not null }, d => d.StartTime >= dto.StartTimeSt && d.StartTime <= dto.StartTimeEnd)
-        //    .WhereIF(!string.IsNullOrEmpty(dto.StepName), d => d.ActualHandleStepName == dto.StepName)
-        //    .WhereIF(!string.IsNullOrEmpty(dto.ActualHandleOrgName), d => d.ActualHandleOrgName!.Contains(dto.ActualHandleOrgName!))
-        //    .WhereIF(dto.Status.HasValue, d => d.Status == dto.Status)
-        //    .WhereIF(!string.IsNullOrEmpty(dto.AcceptorName), d => d.AcceptorName!.Contains(dto.AcceptorName!))
-        //    .WhereIF(dto.ExpiredStatus is EExpiredStatus.Normal, d => DateTime.Now < d.NearlyExpiredTime)
-        //    .WhereIF(dto.ExpiredStatus is EExpiredStatus.GoingToExpired, d => DateTime.Now > d.NearlyExpiredTime && DateTime.Now < d.ExpiredTime)
-        //    .WhereIF(dto.ExpiredStatus is EExpiredStatus.Expired, d => DateTime.Now >= d.ExpiredTime)
-        //    .WhereIF(dto.IsUrgent.HasValue, d => d.IsUrgent == dto.IsUrgent.Value)
-        //    .OrderBy(d => d.Status)
-        //    .OrderBy(d => d.CreationTime, OrderByType.Desc)
-        //    .ToPagedListAsync(dto, HttpContext.RequestAborted);
-
         var (total, items) = await _orderRepository.Queryable()
+            .Includes(d => d.Workflow.Steps)
             .Where(d => SqlFunc.Subqueryable<WorkflowTrace>()
+                            .WhereIF(!string.IsNullOrEmpty(dto.CenterToOrgHandlerName), step => step.BusinessType == EBusinessType.Send && step.HandlerName.Contains(dto.CenterToOrgHandlerName))
                             .Where(step => step.ExternalId == d.Id &&
                                            step.HandlerOrgId == OrgSeedData.CenterId &&
-                                           step.Status < EWorkflowStepStatus.Handled).Any() ||
-                        string.IsNullOrEmpty(d.WorkflowId)
+                                           step.Status < EWorkflowStepStatus.Handled).Any()
+                        || (string.IsNullOrEmpty(d.WorkflowId) && string.IsNullOrEmpty(dto.CenterToOrgHandlerName))
             )
             .Where(d => d.Source < ESource.MLSQ || d.Source > ESource.WZSC)
             .Where(d => d.Status != EOrderStatus.BackToProvince && d.Status < EOrderStatus.Filed)
@@ -4274,9 +4311,8 @@ public class OrderController : BaseController
             .WhereIF(dto.ExpiredStatus is EExpiredStatus.Normal, d => DateTime.Now < d.NearlyExpiredTime)
             .WhereIF(dto.ExpiredStatus is EExpiredStatus.GoingToExpired, d => DateTime.Now > d.NearlyExpiredTime && DateTime.Now < d.ExpiredTime)
             .WhereIF(dto.ExpiredStatus is EExpiredStatus.Expired, d => DateTime.Now >= d.ExpiredTime)
-            .WhereIF(!string.IsNullOrEmpty(dto.CenterToOrgHandlerName), d => d.CenterToOrgHandlerName == dto.CenterToOrgHandlerName)
+            //.WhereIF(!string.IsNullOrEmpty(dto.CenterToOrgHandlerName), d => d.CenterToOrgHandlerName.Contains(dto.CenterToOrgHandlerName))
             .WhereIF(dto.IsUrgent.HasValue, d => d.IsUrgent == dto.IsUrgent!.Value)
-            .OrderBy(d => d.Status)
             .OrderByIF(string.IsNullOrEmpty(dto.SortField), d => d.CreationTime, OrderByType.Desc)
             .OrderByIF(dto is { SortField: "startTime", SortRule: 0 }, d => d.StartTime, OrderByType.Asc) //受理时间升序
             .OrderByIF(dto is { SortField: "startTime", SortRule: 1 }, d => d.StartTime, OrderByType.Desc) //受理时间降序
@@ -4446,7 +4482,7 @@ public class OrderController : BaseController
             if (sendBack)
                 throw UserFriendlyException.SameMessage("当前工单已经生成退回记录");
 
-            var specialAny = await _orderSpecialRepository.Queryable().Where(x => x.OrderId == dto.OrderId && x.State == 0)
+            var specialAny = await _orderSpecialRepository.Queryable().Where(x => x.OrderId == order.Id && x.State == 0)
                 .AnyAsync();
             if (specialAny) throw UserFriendlyException.SameMessage("工单已存在待审批特提信息!");
             if (order.Workflow.IsInCountersign) throw UserFriendlyException.SameMessage("工单会签中,无法进行退回!");
@@ -4471,6 +4507,11 @@ public class OrderController : BaseController
                 audit.State = ESendBackAuditState.End;
                 audit.AuditUser = "默认通过";
                 audit.AuditTime = DateTime.Now;
+                if (prevStep.BusinessType == EBusinessType.Send && dto.Handler != null)
+                {
+                    await _orderRepository.Updateable().SetColumns(o => new Orders.Order() { CenterToOrgHandlerId = dto.Handler.UserId, CenterToOrgHandlerName = dto.Handler.Username })
+                        .Where(o => o.Id == order.Id).ExecuteCommandAsync(HttpContext.RequestAborted);
+                }
                 var flowDirection = await _workflowApplication.PreviousAsync(dto, HttpContext.RequestAborted);
                 var processType = flowDirection == EFlowDirection.OrgToCenter || flowDirection == EFlowDirection.CenterToCenter
                     ? EProcessType.Zhiban
@@ -4487,6 +4528,11 @@ public class OrderController : BaseController
             audit.State = ESendBackAuditState.End;
             audit.AuditUser = "默认通过";
             audit.AuditTime = DateTime.Now;
+            if (prevStep.BusinessType == EBusinessType.Send && dto.Handler != null)
+            {
+                await _orderRepository.Updateable().SetColumns(o => new Orders.Order() { CenterToOrgHandlerId = dto.Handler.UserId, CenterToOrgHandlerName = dto.Handler.Username })
+                    .Where(o => o.Id == order.Id).ExecuteCommandAsync(HttpContext.RequestAborted);
+            }
             var flowDirection = await _workflowApplication.PreviousAsync(dto, HttpContext.RequestAborted);
             var processType = flowDirection == EFlowDirection.OrgToCenter || flowDirection == EFlowDirection.CenterToCenter
                 ? EProcessType.Zhiban
@@ -4495,6 +4541,7 @@ public class OrderController : BaseController
                 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
         }
 
@@ -4542,6 +4589,8 @@ 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);
                 }
             }
 
@@ -4612,6 +4661,8 @@ 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);
                     }
                 }
 
@@ -4644,7 +4695,7 @@ public class OrderController : BaseController
     /// </summary>
     /// <param name="dto"></param>
     /// <returns></returns>
-    [Permission(EPermission.OrderPreviousList)]
+    //[Permission(EPermission.OrderPreviousList)]
     [HttpGet("order_previous_list")]
     public async Task<PagedDto<SendBackDto>> AuditList([FromQuery] SendBackListDto dto)
     {
@@ -5077,6 +5128,12 @@ public class OrderController : BaseController
             var orderDto = _mapper.Map<OrderDto>(order);
             await _capPublisher.PublishAsync(Hotline.Share.Mq.EventNames.HotlineOrderExpiredTimeUpdate, orderDto,
                 cancellationToken: HttpContext.RequestAborted);
+            if (dto.BusinessType == EBusinessType.Send && recall.NextHandlers.Any())
+            {
+				var flowStepHandler = recall.NextHandlers.FirstOrDefault();
+				await _orderRepository.Updateable().SetColumns(o => new Orders.Order() { CenterToOrgHandlerId = flowStepHandler.UserId, CenterToOrgHandlerName = flowStepHandler.Username })
+					.Where(o => o.Id == order.Id).ExecuteCommandAsync(HttpContext.RequestAborted);
+			}
             //}
             await _workflowApplication.RecallAsync(recall, expiredTime.ExpiredTime, order.Status >= EOrderStatus.Filed, EWorkflowTraceType.Recall,
                 HttpContext.RequestAborted);
@@ -5146,25 +5203,23 @@ public class OrderController : BaseController
     [LogFilter("工单重办")]
     public async Task Add([FromBody] OrderReTransactDto dto)
     {
-        var specialAny = await _orderSpecialRepository.Queryable().Where(x => x.OrderId == dto.OrderId && x.State == 0)
+	    var order = await _orderRepository
+		    .Queryable()
+		    .Includes(d => d.Workflow)
+		    .FirstAsync(d => d.Id == dto.OrderId);
+	    if (order.Workflow.IsInCountersign) throw UserFriendlyException.SameMessage("工单会签中,无法进行重办!");
+		var specialAny = await _orderSpecialRepository.Queryable().Where(x => x.OrderId == dto.OrderId && x.State == 0)
             .AnyAsync();
-        if (specialAny) throw UserFriendlyException.SameMessage("工单已存在待审批特提信息!");
+        if (specialAny) throw UserFriendlyException.SameMessage(order.No + " 工单已存在待审批特提信息!");
 
         var screen = await _orderScreenRepository.Queryable().Where(x => x.OrderId == dto.OrderId && (int)x.Status < 2).AnyAsync();
-        if (screen) throw UserFriendlyException.SameMessage("工单存在甄别中的信息!");
+        if (screen) throw UserFriendlyException.SameMessage(order.No + " 工单存在甄别中的信息!");
 
         if (await _orderSendBackAuditRepository.AnyAsync(x => x.OrderId == dto.OrderId && x.State == ESendBackAuditState.Apply,
                 HttpContext.RequestAborted))
         {
-            throw UserFriendlyException.SameMessage("该工单存在正在审核中的退回,不能办理");
+            throw UserFriendlyException.SameMessage(order.No + " 工单存在正在审核中的退回,不能办理");
         }
-
-        var order = await _orderRepository
-            .Queryable()
-            .Includes(d => d.Workflow)
-            .FirstAsync(d => d.Id == dto.OrderId);
-        if (order.Workflow.IsInCountersign) throw UserFriendlyException.SameMessage("工单会签中,无法进行重办!");
-
         var model = _mapper.Map<OrderSpecial>(dto);
 
         var workflow = await _workflowDomainService.GetWorkflowAsync(order.WorkflowId, withSteps: true,
@@ -5264,6 +5319,14 @@ public class OrderController : BaseController
             var processType = dto.FlowDirection is EFlowDirection.OrgToCenter or EFlowDirection.CenterToCenter or EFlowDirection.FiledToCenter
                 ? EProcessType.Zhiban
                 : EProcessType.Jiaoban;
+
+            if (dto.BusinessType == EBusinessType.Send && recall.NextHandlers.Any())
+            {
+				var flowStepHandler = recall.NextHandlers.FirstOrDefault();
+				await _orderRepository.Updateable().SetColumns(o => new Orders.Order() { CenterToOrgHandlerId = flowStepHandler.UserId, CenterToOrgHandlerName = flowStepHandler.Username })
+					.Where(o => o.Id == order.Id).ExecuteCommandAsync(HttpContext.RequestAborted);
+			}
+
             await _workflowApplication.RecallAsync(recall, endTime, order.Status >= EOrderStatus.Filed, EWorkflowTraceType.Redo,
                 HttpContext.RequestAborted);
             //var publish = await _orderPublishRepository.GetAsync(x => x.OrderId == dto.OrderId);
@@ -5407,7 +5470,13 @@ public class OrderController : BaseController
             await _capPublisher.PublishAsync(Hotline.Share.Mq.EventNames.HotlineOrderExpiredTimeUpdate, orderDto,
                 cancellationToken: HttpContext.RequestAborted);
             //}
-
+            if (special.BusinessType == EBusinessType.Send && recall.NextHandlers.Any())
+            {
+				var flowStepHandler = recall.NextHandlers.FirstOrDefault();
+				await _orderRepository.Updateable().SetColumns(o => new Orders.Order() { CenterToOrgHandlerId = flowStepHandler.UserId, CenterToOrgHandlerName = flowStepHandler.Username })
+					.Where(o => o.Id == order.Id).ExecuteCommandAsync(HttpContext.RequestAborted);
+			}
+          
             //todo 特提重办,按审批通过时间依据中心派至部门的规则计算期满时间,更新order
 
             await _workflowApplication.RecallAsync(recall, expiredTime.ExpiredTime, order.Status >= EOrderStatus.Filed, EWorkflowTraceType.Recall,
@@ -5554,8 +5623,13 @@ public class OrderController : BaseController
                 var orderDto = _mapper.Map<OrderDto>(order);
                 await _capPublisher.PublishAsync(Hotline.Share.Mq.EventNames.HotlineOrderExpiredTimeUpdate, orderDto,
                     cancellationToken: HttpContext.RequestAborted);
-                //}
-
+				//}
+				if (special.BusinessType == EBusinessType.Send && recall.NextHandlers.Any())
+				{
+					var flowStepHandler = recall.NextHandlers.FirstOrDefault();
+					await _orderRepository.Updateable().SetColumns(o => new Orders.Order() { CenterToOrgHandlerId = flowStepHandler.UserId, CenterToOrgHandlerName = flowStepHandler.Username })
+						.Where(o => o.Id == order.Id).ExecuteCommandAsync(HttpContext.RequestAborted);
+				}
                 //todo 特提重办,按审批通过时间依据中心派至部门的规则计算期满时间,更新order
 
                 await _workflowApplication.RecallAsync(recall, expiredTime.ExpiredTime, order.Status >= EOrderStatus.Filed, EWorkflowTraceType.Recall,
@@ -5803,7 +5877,7 @@ public class OrderController : BaseController
         var order = await _orderRepository.GetAsync(id, HttpContext.RequestAborted);
         var isInstaShot = order.SourceChannel.Contains("随手拍");
         var defaultStepKey = string.Empty;
-		List<Kv> orgs = new();
+        List<Kv> orgs = new();
         if (order == null) throw UserFriendlyException.SameMessage("无效工单信息!");
         //中心会签调取方法
         var org = await _workflowDomainService.GetLevelOneOrgsAsync(order.WorkflowId, HttpContext.RequestAborted);
@@ -5811,14 +5885,14 @@ public class OrderController : BaseController
         var step = await _workflowApplication.GetRecallStepsAsync(order.WorkflowId, HttpContext.RequestAborted);
         if (_appOptions.Value.IsZiGong)
         {
-			step.Steps = step.Steps.Where(x => x.Key.ToLower() != "start").ToList();
-			if (step.Steps.Where(x => x.BusinessType == EBusinessType.Department && x.OrgLevel == 1 ).Any())
-			{
-				var stepdDefault = step.Steps.Where(x => x.BusinessType == EBusinessType.Department && x.OrgLevel == 1)
-					.FirstOrDefault();
-				defaultStepKey = stepdDefault.Key;
-			}
-		}
+            step.Steps = step.Steps.Where(x => x.Key.ToLower() != "start").ToList();
+            if (step.Steps.Where(x => x.BusinessType == EBusinessType.Department && x.OrgLevel == 1).Any())
+            {
+                var stepdDefault = step.Steps.Where(x => x.BusinessType == EBusinessType.Department && x.OrgLevel == 1)
+                    .FirstOrDefault();
+                defaultStepKey = stepdDefault.Key;
+            }
+        }
         //获取部门信息
         var rsp = new
         {
@@ -5832,20 +5906,87 @@ public class OrderController : BaseController
             Step = step,
             Orgs = orgs,
             DefaultStepKey = defaultStepKey
-		};
+        };
         return rsp;
     }
 
+	/// <summary>
+	/// 部门批量重提 该部门及下级部门回访结果 不满意 未评价 视为满意 
+	/// </summary>
+	/// <returns></returns>
+	[HttpPost("org_batch_special")]
+    [AllowAnonymous]
+	public async Task OrgBatchSpecial([FromBody] OrgBatchSpecialDto model)
+	{
+        //部门信息
+		var org = await _organizeRepository.Queryable().Where(o => o.Id == model.OrgId).FirstAsync(HttpContext.RequestAborted);
+		if (org is null)
+			throw UserFriendlyException.SameMessage("部门信息不存在 请检查部门信息是否正确!");
+		//用户信息  && !u.Name.Contains("测试")
+		var user = await _userRepository.Queryable().Where(u => u.OrgId == model.OrgId).FirstAsync(HttpContext.RequestAborted);
+		if (user is null)
+			throw UserFriendlyException.SameMessage("用户信息不存在 请检查部门下用户信息是否正确!");
+		//工单信息 中心会签件不处理
+		var orders = await _orderRepository.Queryable(isAdmin: true)
+			.Includes(o=>o.Workflow)
+	        .Includes(o=>o.OrderVisits)
+	        .Where(o=> (o.CounterSignType == null || o.CounterSignType == ECounterSignType.Department) && o.OrderVisits.Any(ov=> ov.VisitState == EVisitState.Visited && 
+                                             ov.VisitTime >= model.StartTime && ov.VisitTime <= model.EndTime && 
+	                                         ov.OrderVisitDetails.Any(ord=> ord.VisitOrgCode.StartsWith(model.OrgId) &&
+	                                                                        (SqlFunc.JsonField(ord.OrgProcessingResults, "Key") == "2" || 
+	                                                                         SqlFunc.JsonField(ord.OrgProcessingResults, "Key") == "7" ||
+	                                                                         SqlFunc.JsonField(ord.OrgProcessingResults, "Key") == "-1" )
+	                                                                        ))).ToListAsync();
+		if (!orders.Any())
+			throw UserFriendlyException.SameMessage(org.Name + " 部门及下级部门不存在回访结果为不满意、未评价、视为满意的工单!");
+		foreach (var order in orders)
+		{
+			var nextStep = await _workflowStepRepository.Queryable().Where(step =>
+					step.WorkflowId == order.WorkflowId && step.HandlerOrgId == model.OrgId &&
+					step.StepType == EStepType.Normal).OrderByDescending(step => step.CreationTime)
+				.FirstAsync(HttpContext.RequestAborted);
+			if (nextStep is null)
+				continue;
+			var dto = new OrderReTransactDto
+			{
+				AlterTime = true,
+				BusinessType = EBusinessType.Department,
+				FlowDirection = EFlowDirection.FiledToOrg,
+				HandlerType = EHandlerType.OrgLevel,
+				NextHandlers = new List<FlowStepHandler>()
+				{
+					new FlowStepHandler
+					{
+						UserId = user.Id, Username = user.Name, Key = org.Id, Value = org.Name, OrgId = org.Id,
+						OrgName = org.Name
+					}
+				},
+				NextStepCode = nextStep.Code,
+				NextStepName = nextStep.Name,
+				OrderId = order.Id,
+				Cause = "不满意二次办理",
+				Reason = "部门回访不满意  批量特提",
+				SpecialType = ESpecialType.SendBack,
+				StepType = EStepType.Normal,
+                TimeLimit = 5,
+				TimeLimitUnit = ETimeType.WorkDay,
+				WorkflowId = order.WorkflowId
+			};
+			await Add(dto);
+		}
+
+	}
+
     #endregion
 
-    #region 市民信息
+	#region 市民信息
 
-    /// <summary>
-    /// 新增市民信息
-    /// </summary>
-    /// <param name="dtos"></param>
-    /// <returns></returns>
-    [HttpPost("citizen")]
+	/// <summary>
+	/// 新增市民信息
+	/// </summary>
+	/// <param name="dtos"></param>
+	/// <returns></returns>
+	[HttpPost("citizen")]
     [LogFilter("新增市民信息")]
     public async Task Add([FromBody] CitizenAddDto dto)
     {
@@ -6535,7 +6676,7 @@ public class OrderController : BaseController
     /// </summary>
     /// <param name="dto"></param>
     /// <returns></returns>
-   [HttpGet("order/about_expire/list_count")]
+    [HttpGet("order/about_expire/list_count")]
     public async Task<int> AboutListCountAsnc([FromQuery] AboutToExpireListDto dto)
     {
         var version = _systemSettingCacheManager.GetAboutToExpireVersion;
@@ -7524,6 +7665,12 @@ public class OrderController : BaseController
                     .SetColumns(o => new Orders.Order() { Status = status, SignerId = dto.Handler.UserId })
                     .Where(o => o.Id == dto.OrderId).ExecuteCommandAsync(HttpContext.RequestAborted);
             }
+            else if (step.BusinessType == EBusinessType.Send)
+            {
+                await _orderRepository.Updateable()
+                    .SetColumns(o => new Orders.Order() { Status = status, CenterToOrgHandlerId = dto.Handler.UserId, CenterToOrgHandlerName = dto.Handler.Username })
+                    .Where(o => o.Id == dto.OrderId).ExecuteCommandAsync(HttpContext.RequestAborted);
+            }
             else
             {
                 await _orderRepository.Updateable()

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

@@ -206,7 +206,7 @@ namespace Hotline.Api.Controllers
 			try
 			{
 				dto.NextWorkflow.WorkflowId = model.WorkflowId;
-				await _workflowApplication.NextAsync(dto.NextWorkflow, cancellationToken: HttpContext.RequestAborted);
+				await _workflowDomainService.NextAsync(_sessionContext, dto.NextWorkflow, cancellationToken: HttpContext.RequestAborted);
 			}
 			catch (Exception e)
 			{

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

@@ -1,10 +1,12 @@
-using Hotline.Permissions;
+using Hotline.Configurations;
+using Hotline.Permissions;
 using Hotline.Settings;
 using Hotline.Share.Dtos.Org;
 using Hotline.Share.Enums.Order;
 using MapsterMapper;
 using Microsoft.AspNetCore.Authorization;
 using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Options;
 using System.Threading.Channels;
 using XF.Domain.Authentications;
 using XF.Domain.Exceptions;
@@ -22,20 +24,22 @@ namespace Hotline.Api.Controllers
         private readonly ISystemAreaDomainService _areaDomainService;
         private readonly IMapper _mapper;
         private readonly ISessionContext _sessionContext;
-
+		private readonly IOptionsSnapshot<AppConfiguration> _appOptions;
 
         public OrgController(
             ISystemOrganizeRepository systemOrganizeRepository,
             ISystemDomainService systemDomainService,
             ISystemAreaDomainService areaDomainService,
             IMapper mapper,
-            ISessionContext sessionContext)
+            ISessionContext sessionContext,
+            IOptionsSnapshot<AppConfiguration> appOptions)
         {
             _systemOrganizeRepository = systemOrganizeRepository;
             _systemDomainService = systemDomainService;
             _areaDomainService = areaDomainService;
             _mapper = mapper;
             _sessionContext = sessionContext;
+            _appOptions = appOptions;
         }
 
         /// <summary>
@@ -231,7 +235,7 @@ namespace Hotline.Api.Controllers
             new
             {
                 OrgTypeOptions = EnumExts.GetDescriptions<EOrgType>(),
-                Areas = await _areaDomainService.GetAreaTree()
+                Areas = await _areaDomainService.GetAreaTree(_appOptions.Value.IsZiGong ? 6 : 0)
             };
     }
 }

+ 187 - 0
src/Hotline.Api/Controllers/PushProvinceController.cs

@@ -0,0 +1,187 @@
+using DotNetCore.CAP;
+using Hotline.Application.CallCenter;
+using Hotline.Configurations;
+using Hotline.FlowEngine.Workflows;
+using Hotline.Orders;
+using Hotline.Share.Dtos.FlowEngine.Workflow;
+using Hotline.Share.Dtos.Order;
+using Hotline.Share.Dtos.TrCallCenter;
+using Hotline.Share.Enums.FlowEngine;
+using Hotline.Share.Enums.Order;
+using MapsterMapper;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Options;
+using SqlSugar;
+using XF.Domain.Repository;
+
+namespace Hotline.Api.Controllers
+{
+    public class PushProvinceController : BaseController
+    {
+        private readonly ILogger<PushProvinceController> _logger;
+        private readonly IOrderRepository _orderRepository;
+        private readonly IMapper _mapper;
+        private readonly ICapPublisher _capPublisher;
+        private readonly IRepository<WorkflowTrace> _workflowTraceRepository;
+        private readonly ICallApplication _callApplication;
+        private readonly IOptionsSnapshot<AppConfiguration> _appOptions;
+
+        public PushProvinceController(ILogger<PushProvinceController> logger,
+             IOrderRepository orderRepository,
+             IMapper mapper,
+             ICapPublisher capPublisher,
+             IRepository<WorkflowTrace> workflowTraceRepository,
+             ICallApplication callApplication,
+             IOptionsSnapshot<AppConfiguration> appOptions)
+        {
+            _logger = logger;
+            _orderRepository = orderRepository;
+            _mapper = mapper;
+            _capPublisher = capPublisher;
+            _workflowTraceRepository = workflowTraceRepository;
+            _callApplication = callApplication;
+            _appOptions = appOptions;
+        }
+
+        /// <summary>
+        /// 推送最新工单信息
+        /// </summary>
+        /// <param name="Provinces"></param>
+        /// <returns></returns>
+        [AllowAnonymous]
+        [HttpGet("pushneworder")]
+        public async Task PushNewOrder(string Provinces)
+        {
+            Provinces = Provinces.Trim();
+            string[] provinceNos = Provinces.Split(',');
+            for (int i = 0; i < provinceNos.Length; i++)
+            {
+                provinceNos[i] = provinceNos[i].Trim();
+            }
+
+            var list = await _orderRepository.Queryable().Where(x => provinceNos.Contains(x.ProvinceNo)).ToListAsync();
+            if (list != null && list.Any())
+            {
+                foreach (var item in list)
+                {
+                    var orderDto = _mapper.Map<OrderDto>(item);
+                    await _capPublisher.PublishAsync(Hotline.Share.Mq.EventNames.HotlineOrderExpiredTimeUpdate, orderDto,
+                        cancellationToken: HttpContext.RequestAborted);
+                }
+
+            }
+        }
+
+        /// <summary>
+        /// 推送回访到省上
+        /// </summary>
+        /// <param name="Provinces"></param>
+        /// <returns></returns>
+        [HttpPost("publishvisittoprovincebyno")]
+        [AllowAnonymous]
+        public async Task PublishVisitToProvinceByNo(string Provinces)
+        {
+            Provinces = Provinces.Trim();
+            string[] provinceNos = Provinces.Split(',');
+            for (int i = 0; i < provinceNos.Length; i++)
+            {
+                provinceNos[i] = provinceNos[i].Trim();
+            }
+            var list = await _orderRepository.Queryable()
+                 .Includes(x => x.OrderVisits.Where(q => q.VisitState == EVisitState.Visited).ToList(), d => d.OrderVisitDetails)
+                 .Where(x => provinceNos.Contains(x.ProvinceNo) && x.Status == EOrderStatus.Visited).ToListAsync();
+
+            if (list != null && list.Any())
+            {
+                foreach (var item in list)
+                {
+                    try
+                    {
+                        var visit = item.OrderVisits.FirstOrDefault();
+                        if (visit != null)
+                        {
+                            var visitRemark = visit.OrderVisitDetails.Where(q => q.VisitTarget == EVisitTarget.Org).FirstOrDefault()?.VisitContent;
+                            //推省上
+                            await _capPublisher.PublishAsync(Hotline.Share.Mq.EventNames.HotlineOrderVisited,
+                            new PublishVisitDto()
+                            {
+                                Order = _mapper.Map<OrderDto>(item),
+                                No = visit.No,
+                                VisitType = visit.VisitType,
+                                VisitName = visit.CreatorName,
+                                VisitTime = visit.VisitTime,
+                                VisitRemark = string.IsNullOrEmpty(visitRemark) == true ? "满意" : visitRemark,
+                                AreaCode = item.AreaCode!,
+                                SubjectResultSatifyCode = visit.NowEvaluate.Key,
+                                FirstSatisfactionCode = item.FirstVisitResultCode,
+                                ClientGuid = ""
+                            }, cancellationToken: HttpContext.RequestAborted);
+                        }
+                    }
+                    catch { }
+                }
+            }
+        }
+
+        /// <summary>
+        /// 推送工单归档数据
+        /// </summary>
+        /// <param name="Provinces"></param>
+        /// <returns></returns>
+        [HttpPost("pushfiledtoprovince")]
+        [AllowAnonymous]
+        public async Task PushFiledToProvince(string Provinces)
+        {
+            Provinces = Provinces.Trim();
+            string[] provinceNos = Provinces.Split(',');
+            for (int i = 0; i < provinceNos.Length; i++)
+            {
+                provinceNos[i] = provinceNos[i].Trim();
+            }
+
+            var unpublishOrders = await _orderRepository.Queryable()
+                .Where(d => provinceNos.Contains(d.ProvinceNo) && !string.IsNullOrEmpty(d.ProvinceNo) && d.Status >= EOrderStatus.Filed)
+                .ToListAsync(HttpContext.RequestAborted);
+
+            if (unpublishOrders != null && unpublishOrders.Any())
+            {
+                foreach (var order in unpublishOrders)
+                {
+                    var trace = await _workflowTraceRepository.Queryable()
+                        .FirstAsync(d => d.WorkflowId == order.WorkflowId && d.StepType == EStepType.End,
+                            HttpContext.RequestAborted);
+                    var orderFlowDto = new OrderFlowDto
+                    {
+                        Order = _mapper.Map<OrderDto>(order),
+                        WorkflowTrace = _mapper.Map<WorkflowTraceDto>(trace)
+                    };
+                    if (order.SourceChannelCode == AppDefaults.SourceChannel.DianHua &&
+                        !string.IsNullOrEmpty(order.CallId))
+                    {
+                        if (_appOptions.Value.GetDefaultAppScopeConfiguration().CallCenterType == AppDefaults.CallCenterType.TianRun)
+                        {
+                            var callRecord = await _callApplication.GetTianrunCallAsync(order.CallId, HttpContext.RequestAborted);
+                            if (callRecord != null)
+                            {
+                                orderFlowDto.TrCallRecordDto = _mapper.Map<TrCallDto>(callRecord);
+                            }
+                        }
+                        else if (_appOptions.Value.GetDefaultAppScopeConfiguration().CallCenterType == AppDefaults.CallCenterType.XingTang)
+                        {
+                            var call = await _callApplication.GetCallAsync(order.CallId, HttpContext.RequestAborted);
+                            if (call is not null)
+                                orderFlowDto.TrCallRecordDto = _mapper.Map<TrCallDto>(call);
+                        }
+                    }
+
+                    //这里需要判断是否是警情退回
+                    orderFlowDto.IsNonPoliceReturn = false;
+                    await _capPublisher.PublishAsync(Hotline.Share.Mq.EventNames.HotlineOrderFiled, orderFlowDto, cancellationToken: HttpContext.RequestAborted);
+                }
+            }
+        }
+
+
+    }
+}

+ 29 - 0
src/Hotline.Api/Controllers/Snapshot/IndustryController.cs

@@ -0,0 +1,29 @@
+using Hotline.Application.Snapshot;
+using Hotline.Share.Dtos.Snapshot;
+using Hotline.Snapshot;
+using Microsoft.AspNetCore.Mvc;
+
+namespace Hotline.Api.Controllers.Snapshot;
+
+/// <summary>
+/// 随手拍行业管理接口
+/// </summary>
+public class IndustryController : BaseController
+{
+    private readonly IIndustryRepository _industryRepository;
+    private readonly IIndustryApplication _industryApplication;
+
+    public IndustryController(IIndustryRepository industryRepository, IIndustryApplication industryApplication)
+    {
+        _industryRepository = industryRepository;
+        _industryApplication = industryApplication;
+    }
+
+    /// <summary>
+    /// 新增行业
+    /// </summary>
+    /// <returns></returns>
+    [HttpPost]
+    public async Task<string> AddIndustry([FromBody] AddIndustryDto dto)
+        => await _industryApplication.AddIndustryAsync(dto, HttpContext.RequestAborted);
+}

+ 135 - 0
src/Hotline.Api/Controllers/Snapshot/SnapshotController.cs

@@ -0,0 +1,135 @@
+using Hotline.Application.Snapshot;
+using Hotline.Orders;
+using Hotline.Settings;
+using Hotline.Share.Dtos;
+using Hotline.Share.Dtos.Article;
+using Hotline.Share.Dtos.Settings;
+using Hotline.Share.Dtos.Snapshot;
+using Mapster;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+using System.ComponentModel.DataAnnotations;
+using System.Reflection;
+using XF.Domain.Repository;
+
+namespace Hotline.Api.Controllers.Snapshot;
+
+/// <summary>
+/// 随手拍接口
+/// </summary>
+public class SnapshotController : BaseController
+{
+    private readonly IRepository<Order> _orderRepository;
+    private readonly ISnapshotApplication _snapshotApplication;
+    private readonly ISystemAreaDomainService _systemAreaDomainService;
+
+    public SnapshotController(IRepository<Order> orderRepository, ISnapshotApplication snapshotApplication, ISystemAreaDomainService systemAreaDomainService)
+    {
+        _orderRepository = orderRepository;
+        _snapshotApplication = snapshotApplication;
+        _systemAreaDomainService = systemAreaDomainService;
+    }
+
+    /// <summary>
+    /// 首页数据
+    /// </summary>
+    /// <returns></returns>
+    [HttpGet("home")]
+    [AllowAnonymous]
+    public async Task<HomePageOutDto> GetHomePageAsync()
+        => await _snapshotApplication.GetHomePageAsync();
+
+
+    /// <summary>
+    /// 行业界面基础信息
+    /// </summary>
+    /// <param name="id">行业Id</param>
+    /// <returns></returns>
+    [HttpGet("industry/base/{id}")]
+    [AllowAnonymous]
+    public async Task<IndustryBaseOutDto> GetIndustryBaseAsync(string id)
+        => await _snapshotApplication.GetIndustryBaseAsync(id, HttpContext.RequestAborted);
+
+    /// <summary>
+    /// 获取小程序公告列表
+    /// </summary>
+    /// <param name="dto"></param>
+    /// <returns></returns>
+    [HttpGet("bulletions")]
+    [AllowAnonymous]
+    public virtual async Task<IReadOnlyList<BulletinOutDto>> QueryBulletinsAsync([FromQuery] BulletinInDto dto)
+        => await _snapshotApplication.GetBulletinsAsync(dto);
+
+    /// <summary>
+    /// 公告详情
+    /// </summary>
+    /// <param name="id"></param>
+    /// <returns></returns>
+    [HttpGet("bulletions/{id}")]
+    [AllowAnonymous]
+    public virtual async Task<BulletinOutDto> QueryBulletionsDetailAsync(string id)
+        => await _snapshotApplication.GetBulletinsDetailAsync(id);
+
+    /// <summary>
+    /// 获取用户页面数据
+    /// </summary>
+    /// <returns></returns>
+    [AllowAnonymous]
+    [HttpGet("user")]
+    public virtual async Task<SnapshotUserInfoOutDto> GetUserInfo()
+        => await _snapshotApplication.GetSnapshotUserInfoAsync();
+
+    /// <summary>
+    /// 获取我提交的线索列表
+    /// </summary>
+    /// <param name="dto"></param>
+    /// <returns></returns>
+    [HttpGet("order")]
+    public virtual async Task<PagedDto<OrderOutDto>> QueryOrderListAsync([FromQuery] OrderInDto dto)
+        => await _snapshotApplication.GetSnapshotOrdersAsync(dto);
+
+    /// <summary>
+    /// 获取我提交的线索详情
+    /// </summary>
+    /// <param name="id"></param>
+    /// <returns></returns>
+    [HttpGet("order/{id}")]
+    public virtual async Task<OrderDetailOutDto> QueryOrderListAsync([FromQuery] string id)
+        => await _snapshotApplication.GetSnapshotOrderDetailAsync(id);
+
+    /// <summary>
+    /// 统计红包金额, 每月的总金额
+    /// </summary>
+    /// <param name="dto"></param>
+    /// <returns></returns>
+    [HttpGet("redpack")]
+    public virtual async Task<IReadOnlyList<RedPackDateOutDto>> QueryRedPackDateAsync([FromQuery] RedPackDateInDto dto)
+        => await _snapshotApplication.GetRedPackDateAsync(dto);
+
+    /// <summary>
+    /// 获取当月详细红包列表
+    /// </summary>
+    /// <param name="dto"></param>
+    /// <returns></returns>
+    [HttpGet("redpack/month")]
+    public virtual async Task<PagedDto<RedPackOutDto>> QueryRedPackDateAsync([FromQuery] RedPacksInDto dto)
+        => await _snapshotApplication.GetRedPacksAsync(dto);
+
+    /// <summary>
+    /// 获取随手拍电气焊动火作业待处理工单数量
+    /// TODO 条件 电气焊作业申报
+    /// </summary>
+    /// <returns></returns>
+    [HttpGet("wait_accept_count")]
+    public async Task<int> GetSnapshotWaitForAcceptCountAsync()
+        => await _orderRepository
+            .CountAsync(m => m.SourceChannelCode == "ZGSSP" && m.Status == Share.Enums.Order.EOrderStatus.WaitForAccept);
+
+    /// <summary>
+    /// 获取区域
+    /// </summary>
+    /// <returns></returns>
+    [HttpGet("area/tree")]
+    public async Task<List<SystemAreaOutDto>> GetAreaTreeAsync()
+        => (await _systemAreaDomainService.GetAreaTree()).Adapt<List<SystemAreaOutDto>>();
+}

+ 0 - 26
src/Hotline.Api/Controllers/SnapshotController.cs

@@ -1,26 +0,0 @@
-using Hotline.Orders;
-using Microsoft.AspNetCore.Mvc;
-using XF.Domain.Repository;
-
-namespace Hotline.Api.Controllers;
-
-public class SnapshotController : BaseController
-{
-    private readonly IRepository<Order> _orderRepository;
-
-    public SnapshotController(IRepository<Order> orderRepository)
-    {
-        _orderRepository = orderRepository;
-    }
-
-    /// <summary>
-    /// 获取随手拍电气焊动火作业待处理工单数量
-    /// TODO 条件 电气焊作业申报
-    /// </summary>
-    /// <returns></returns>
-    [HttpGet("wait_accept_count")]
-    public async Task<int> GetSnapshotWaitForAcceptCountAsync()
-        => await _orderRepository
-            .CountAsync(m => m.SourceChannelCode == "ZGSSP" && m.Status == Share.Enums.Order.EOrderStatus.WaitForAccept);
-
-}

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

@@ -3,6 +3,7 @@ using Hotline.Application.Systems;
 using Hotline.Caching.Interfaces;
 using Hotline.CallCenter.Ivrs;
 using Hotline.CallCenter.Tels;
+using Hotline.Configurations;
 using Hotline.FlowEngine.WorkflowModules;
 using Hotline.Orders;
 using Hotline.Permissions;
@@ -22,7 +23,9 @@ using Hotline.YbEnterprise.Sdk;
 using MapsterMapper;
 using Microsoft.AspNetCore.Authorization;
 using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Options;
 using NPOI.SS.Formula.Functions;
+using SqlSugar;
 using StackExchange.Redis;
 using Wex.Sdk;
 using XF.Domain.Authentications;
@@ -54,6 +57,7 @@ namespace Hotline.Api.Controllers
         private readonly IServiceScopeFactory _serviceScopeFactory;
         private readonly IRedisCachingProvider _redisCaching;
         private readonly IEasyCachingProvider _easyCaching;
+		private readonly IOptionsSnapshot<AppConfiguration> _appOptions;
 
         /// <summary>
         /// 系统管理相关接口
@@ -88,7 +92,8 @@ namespace Hotline.Api.Controllers
             IServiceScopeFactory serviceScopeFactory,
             IRedisCachingProvider redisCaching,
             IEasyCachingProvider easyCaching
-            )
+,
+            IOptionsSnapshot<AppConfiguration> appOptions)
         {
             _mapper = mapper;
             _systemSettingsRepository = systemSettingsRepository;
@@ -106,6 +111,7 @@ namespace Hotline.Api.Controllers
             _serviceScopeFactory = serviceScopeFactory;
             _redisCaching = redisCaching;
             _easyCaching = easyCaching;
+            _appOptions = appOptions;
         }
 
         #region 菜单管理
@@ -370,7 +376,7 @@ namespace Hotline.Api.Controllers
         [HttpGet("area/tree")]
         public async Task<List<SystemArea>> GetAreaTree()
         {
-            return await _systemAreaDomainService.GetAreaTree();
+            return await _systemAreaDomainService.GetAreaTree(_appOptions.Value.IsZiGong ? 6 : 0);
         }
 
         /// <summary>
@@ -574,14 +580,22 @@ namespace Hotline.Api.Controllers
         /// <param name="dto"></param>
         /// <returns></returns>
         [HttpGet("log_list")]
-        public async Task<PagedDto<SystemLogDto>> List([FromQuery] PagedKeywordRequest dto)
+        public async Task<PagedDto<SystemLogDto>> List([FromQuery] SysLogPagedKeywordRequest dto)
         {
-            var (total, items) = await _systemLogRepository.Queryable()
-                .Where(x => !string.IsNullOrEmpty(x.Name))
-                .WhereIF(!string.IsNullOrEmpty(dto.Keyword), x => x.Name.Contains(dto.Keyword!) || x.CreatorName.Contains(dto.Keyword!))
-                .OrderByDescending(x => x.CreationTime)
-                .ToPagedListAsync(dto.PageIndex, dto.PageSize, HttpContext.RequestAborted);
+	        var (total, items) = await _systemLogRepository.Queryable()
+		        .WhereIF(dto.IsAll.HasValue && !dto.IsAll.Value, x => !string.IsNullOrEmpty(x.Name))
+		        .WhereIF(!string.IsNullOrEmpty(dto.Name), x => x.Name.Contains(dto.Name))
+		        .WhereIF(!string.IsNullOrEmpty(dto.CreatorName), x => x.CreatorName.Contains(dto.CreatorName))
+		        .WhereIF(!string.IsNullOrEmpty(dto.ExecuteUrl), x => x.ExecuteUrl.Contains(dto.ExecuteUrl))
+		        .WhereIF(!string.IsNullOrEmpty(dto.ExecuteParam),
+			        x => SqlFunc.JsonLike(x.ExecuteParam, dto.ExecuteParam))
+		        .WhereIF(dto.StartTime.HasValue && dto.EndTime.HasValue,
+			        x => x.CreationTime >= dto.StartTime && x.CreationTime <= dto.EndTime)
+		        //.WhereIF(!string.IsNullOrEmpty(dto.Keyword), x => x.Name.Contains(dto.Keyword!) || x.CreatorName.Contains(dto.Keyword!))
+		        .OrderByDescending(x => x.CreationTime)
+		        .ToPagedListAsync(dto.PageIndex, dto.PageSize, HttpContext.RequestAborted);
             return new PagedDto<SystemLogDto>(total, _mapper.Map<IReadOnlyList<SystemLogDto>>(items));
+            
         }
 
         /// <summary>

+ 280 - 3
src/Hotline.Api/Controllers/TestController.cs

@@ -1,5 +1,5 @@
 using DocumentFormat.OpenXml.Drawing.Charts;
-using DocumentFormat.OpenXml.Drawing;
+using DocumentFormat.OpenXml.Office2016.Drawing.ChartDrawing;
 using DotNetCore.CAP;
 using Hotline.Ai.Visit;
 using Hotline.Application.CallCenter;
@@ -28,6 +28,7 @@ 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;
@@ -39,12 +40,14 @@ using Hotline.Share.Dtos.FlowEngine.Workflow;
 using Hotline.Share.Dtos.Order;
 using Hotline.Share.Dtos.Realtime;
 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;
@@ -53,8 +56,10 @@ using Microsoft.AspNetCore.Mvc;
 using Microsoft.Extensions.Options;
 using MiniExcelLibs;
 using Newtonsoft.Json;
+using NPOI.POIFS.Crypt.Dsig;
 using SqlSugar;
 using StackExchange.Redis;
+using System.Threading;
 using XC.RSAUtil;
 using XF.Domain.Authentications;
 using XF.Domain.Cache;
@@ -132,6 +137,8 @@ public class TestController : BaseController
     //private readonly ITypedCache<List<User>> _cache;
     //private readonly ICacheManager<User> _cache;
     private readonly ICalcExpireTime _expireTime;
+    private readonly ICallNativeRepository _callNativeRepository;
+    private readonly IRepository<OldSendProData> _oldSendProDataRepository;
 
 
     public TestController(
@@ -184,7 +191,9 @@ ICallApplication callApplication,
         IOptionsSnapshot<AppConfiguration> appOptions,
         ISystemSettingCacheManager systemSettingCacheManager,
         ICalcExpireTime expireTime
-        )
+,
+        ICallNativeRepository callNativeRepository,
+        IRepository<OldSendProData> oldSendProDataRepository)
     {
         _logger = logger;
         //_authorizeGenerator = authorizeGenerator;
@@ -218,7 +227,6 @@ ICallApplication callApplication,
         _qualityApplication = qualityApplication;
         _enforcementApplication = enforcementApplication;
         _workflowDomainService = workflowDomainService;
-        _orderRepository = orderRepository;
         _mapper = mapper;
         _orderReportApplication = orderReportApplication;
         _enforcementOrdersRepository = enforcementOrdersRepository;
@@ -233,9 +241,231 @@ ICallApplication callApplication,
         _appOptions = appOptions;
         _systemSettingCacheManager = systemSettingCacheManager;
         _expireTime = expireTime;
+        _callNativeRepository = callNativeRepository;
+        _oldSendProDataRepository = oldSendProDataRepository;
+    }
+
+    /// <summary>
+    /// 修改泸州省工单编码区域
+    /// </summary>
+    /// <returns></returns>
+    [HttpPost("updateoldsendprodata")]
+    [AllowAnonymous]
+    public async Task UpdateOldSendProData()
+    {
+        var list = await _oldSendProDataRepository.Queryable().Where(p => p.ProvinceNo.Contains("99511500")).ToListAsync();
+        if (list != null && list.Any())
+        {
+            foreach (var item in list)
+            {
+                var prono = item.ProvinceNo;
+                prono = prono.Replace("99511500", "99510500");
+                item.NewProvinceNo = prono;
+                await _oldSendProDataRepository.UpdateAsync(item);
+            }
+        }
+    }
+
+    /// <summary>
+    /// 修改工单省编号
+    /// </summary>
+    /// <returns></returns>
+    [HttpPost("updatesendprodata")]
+    [AllowAnonymous]
+    public async Task UpdateSendProData()
+    {
+        var list = await _oldSendProDataRepository.Queryable().Where(p => p.ProvinceNo.Contains("99511500")).ToListAsync();
+        if (list != null && list.Any())
+        {
+            foreach (var item in list)
+            {
+                var data = await _orderRepository.GetAsync(p => p.Id == item.Id, HttpContext.RequestAborted);
+                if (data != null)
+                {
+                    data.ProvinceNo = item.NewProvinceNo;
+                    await _orderRepository.Updateable(data).UpdateColumns(d => d.ProvinceNo).ExecuteCommandAsync(HttpContext.RequestAborted);
+                }
+            }
+        }
+    }
+
+    /// <summary>
+    /// 从新推送省上数据
+    /// </summary>
+    /// <returns></returns>
+    [HttpGet("sendpushprodata")]
+    [AllowAnonymous]
+    public async Task SendPushProData(string provinceNo)
+    {
+        //推送 受理信息、结果信息、过程信息、话务信息、回访信息
+        var list = await _oldSendProDataRepository.Queryable()
+            .Where(p => p.CreatorName == null)
+            .WhereIF(!string.IsNullOrEmpty(provinceNo), p => p.ProvinceNo == provinceNo).ToListAsync();
+        if (list != null && list.Any())
+        {
+            foreach (var item in list)
+            {
+                var order = await _orderRepository.GetAsync(p => p.Id == item.Id, HttpContext.RequestAborted);
+                if (order != null)
+                {
+                    item.CreatorName = item.NewProvinceNo;
+                    await _oldSendProDataRepository.Updateable(item).UpdateColumns(d => d.CreatorName).ExecuteCommandAsync(HttpContext.RequestAborted);
+                    //受理信息
+                    #region 受理信息
+                    //var publishCallRecordDto = new PublishCallRecrodDto() { };
+                    //if (order.SourceChannelCode == AppDefaults.SourceChannel.DianHua &&
+                    //    !string.IsNullOrEmpty(order.CallId))
+                    //{
+                    //    if (_appOptions.Value.GetDefaultAppScopeConfiguration().CallCenterType == AppDefaults.CallCenterType.TianRun)
+                    //    {
+                    //        var callRecord = await _callApplication.GetTianrunCallAsync(order?.CallId, HttpContext.RequestAborted);
+                    //        if (callRecord != null)
+                    //        {
+                    //            publishCallRecordDto.TrCallRecordDto = _mapper.Map<TrCallDto>(callRecord);
+                    //        }
+                    //    }
+                    //    else if (_appOptions.Value.GetDefaultAppScopeConfiguration().CallCenterType == AppDefaults.CallCenterType.XingTang)
+                    //    {
+                    //        var call = await _callApplication.GetCallAsync(order?.CallId, HttpContext.RequestAborted);
+                    //        if (call is not null)
+                    //        {
+                    //            publishCallRecordDto.TrCallRecordDto = _mapper.Map<TrCallDto>(call);
+
+                    //            // 工单开始办理如果获取的通话记录是呼出并且录音文件是空就不推送通话记录
+                    //            // 如果 通话记录是呼入, 并且没有录音文件
+                    //            if (_systemSettingCacheManager.OrderStartHandlerPushCallIsNull && call.Direction == ECallDirection.Out && !string.IsNullOrEmpty(call.AudioFile))
+                    //            {
+                    //                publishCallRecordDto.TrCallRecordDto = null;
+                    //            }
+                    //        }
+                    //    }
+                    //}
+                    //publishCallRecordDto.Order = _mapper.Map<OrderDto>(order);
+
+                    //await _capPublisher.PublishAsync(EventNames.HotlineOrderFlowStarted, publishCallRecordDto,
+                    //    cancellationToken: HttpContext.RequestAborted);
+                    #endregion
+
+                    //结果信息
+                    #region 受理信息、话务信息、结果信息
+                    if (order != null)
+                    {
+                        var trace = await _workflowTraceRepository.Queryable()
+                            .FirstAsync(d => d.WorkflowId == order.WorkflowId && d.StepType == EStepType.End, HttpContext.RequestAborted);
+                        var orderFlowDto = new OrderFlowDto
+                        {
+                            Order = _mapper.Map<OrderDto>(order),
+                            WorkflowTrace = _mapper.Map<WorkflowTraceDto>(trace)
+                        };
+                        if (order.SourceChannelCode == AppDefaults.SourceChannel.DianHua &&
+                            !string.IsNullOrEmpty(order.CallId))
+                        {
+                            if (_appOptions.Value.GetDefaultAppScopeConfiguration().CallCenterType == AppDefaults.CallCenterType.TianRun)
+                            {
+                                var callRecord = await _callApplication.GetTianrunCallAsync(order?.CallId, HttpContext.RequestAborted);
+                                if (callRecord != null)
+                                {
+                                    orderFlowDto.TrCallRecordDto = _mapper.Map<TrCallDto>(callRecord);
+                                }
+                            }
+                            else if (_appOptions.Value.GetDefaultAppScopeConfiguration().CallCenterType == AppDefaults.CallCenterType.XingTang)
+                            {
+                                var call = await _callApplication.GetCallAsync(order?.CallId, HttpContext.RequestAborted);
+                                if (call is not null)
+                                {
+                                    orderFlowDto.TrCallRecordDto = _mapper.Map<TrCallDto>(call);
+
+                                    // 工单开始办理如果获取的通话记录是呼出并且录音文件是空就不推送通话记录
+                                    // 如果 通话记录是呼入, 并且没有录音文件
+                                    if (_systemSettingCacheManager.OrderStartHandlerPushCallIsNull && call.Direction == ECallDirection.Out && !string.IsNullOrEmpty(call.AudioFile))
+                                    {
+                                        orderFlowDto.TrCallRecordDto = null;
+                                    }
+                                }
+                            }
+                        }
+                        //这里需要判断是否是警情退回
+                        orderFlowDto.IsNonPoliceReturn = false;
+                        await _capPublisher.PublishAsync(Hotline.Share.Mq.EventNames.HotlineOrderFiled, orderFlowDto, cancellationToken: HttpContext.RequestAborted);
+                        //_logger.LogWarning($"推送完成: {order.ProvinceNo}");
+                    }
+
+                    #endregion
+
+                    //过程信息
+                    #region 过程信息
+                    //查询过程数据
+
+                    if (order != null)
+                    {
+                        var listStep = await _workflowTraceRepository.Queryable()
+                             .Where(p => p.WorkflowId == order.WorkflowId && p.TraceState == EWorkflowTraceState.Normal).ToListAsync();
+                        if (listStep != null && listStep.Any())
+                        {
+                            foreach (var itemStep in listStep)
+                            {
+                                var orderDto = _mapper.Map<OrderDto>(order);
+                                var flowDto = new OrderFlowDto
+                                {
+                                    Order = orderDto,
+                                    WorkflowTrace = _mapper.Map<WorkflowTraceDto>(itemStep),
+                                    ExpiredTimeChanged = false,
+                                    HandlerOrgLevel = itemStep.HandlerOrgId.CalcOrgLevel()
+                                };
+                                await _capPublisher.PublishAsync(Hotline.Share.Mq.EventNames.HotlineOrderFlowHandled, flowDto, cancellationToken: HttpContext.RequestAborted);
+                            }
+                        }
+                    }
+
+                    #endregion
+                    //话务信息
+
+                    //回访信息
+                    #region 回访信息
+
+                    if (order != null && order.Status == EOrderStatus.Visited)
+                    {
+                        var listVist = await _orderRepository.Queryable()
+                        .Includes(x => x.OrderVisits.Where(q => q.VisitState == EVisitState.Visited).ToList(), d => d.OrderVisitDetails)
+                        .Where(x => x.Id == item.Id && x.Status == EOrderStatus.Visited).ToListAsync();
+
+                        foreach (var itemVisit in listVist)
+                        {
+                            try
+                            {
+                                var visit = itemVisit.OrderVisits.FirstOrDefault();
+                                if (visit != null)
+                                {
+                                    var visitRemark = visit.OrderVisitDetails.Where(q => q.VisitTarget == EVisitTarget.Org).FirstOrDefault()?.VisitContent;
+                                    //推省上
+                                    await _capPublisher.PublishAsync(Hotline.Share.Mq.EventNames.HotlineOrderVisited,
+                                    new PublishVisitDto()
+                                    {
+                                        Order = _mapper.Map<OrderDto>(itemVisit),
+                                        No = visit.No,
+                                        VisitType = visit.VisitType,
+                                        VisitName = visit.CreatorName,
+                                        VisitTime = visit.VisitTime,
+                                        VisitRemark = string.IsNullOrEmpty(visitRemark) == true ? "满意" : visitRemark,
+                                        AreaCode = itemVisit.AreaCode!,
+                                        SubjectResultSatifyCode = visit.NowEvaluate.Key,
+                                        FirstSatisfactionCode = itemVisit.FirstVisitResultCode,
+                                        ClientGuid = ""
+                                    }, cancellationToken: HttpContext.RequestAborted);
+                                }
+                            }
+                            catch { }
+                        }
+                    }
+
+                    #endregion
+                }
+            }
+        }
     }
 
 
+
     [HttpGet("time")]
     [AllowAnonymous]
     public async Task<OpenResponse> GetTime(string batchId, string taskId)
@@ -343,6 +573,25 @@ ICallApplication callApplication,
         }
     }
 
+    /// <summary>
+    /// 市场监管局数据处理
+    /// </summary>
+    /// <param name="file"></param>
+    /// <returns></returns>
+    [HttpPost("importmarketsupervision")]
+    [AllowAnonymous]
+    public async Task<List<GetCaseReultSendModel>> ImportMarketSupervision(IFormFile file)
+    {
+       
+        using (var stream = new MemoryStream())
+        {
+            file.CopyTo(stream);
+            var list = MiniExcel.Query<GetCaseReultSendModel>(stream).ToList();
+            //Directory.Delete(filePath, true);
+            return list;
+        }
+
+    }
 
     /// <summary>
     /// 推送回访到省上
@@ -1043,4 +1292,32 @@ ICallApplication callApplication,
         _capPublisher.PublishDelay(ExpiredTime - DateTime.Now.AddHours(1), EventNames.HotlineOrderAutomaticDelay, new PublishAutomaticDelayDto() { OrderId = orderId });
     }
 
+
+    /// <summary>
+    /// 根据省编号修复工单和通话记录
+    /// </summary>
+    /// <param name="ProvinceNos">省编号</param>
+    /// <returns></returns>
+    [HttpPost("push_order_call")]
+    [AllowAnonymous]
+    public async Task PushOrderCallAsync([FromBody] List<string> ProvinceNos)
+    {
+        var orders = await _orderRepository.Queryable()
+            .Where(m => ProvinceNos.Contains(m.ProvinceNo))
+            .ToListAsync();
+        foreach (var order in orders)
+        {
+            var call = await _callNativeRepository.Queryable()
+                .Where(m => m.Id == order.CallId)
+                .FirstAsync();
+            if (call == null) continue;
+            var orderDto = order.Adapt<OrderDto>();
+            await _capPublisher.PublishAsync(EventNames.HotlineCallConnectWithOrder, new PublishCallRecrodDto()
+            {
+                Order = orderDto,
+                TrCallRecordDto = call.Adapt<TrCallDto>()
+            }, cancellationToken: HttpContext.RequestAborted);
+        }
+    }
+
 }

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

@@ -343,6 +343,7 @@ public class UserController : BaseController
     /// <returns></returns>
     [Permission(EPermission.RemoveUser)]
     [HttpDelete("{id}")]
+    [LogFilter("删除用户")]
     public async Task Remove(string id)
     {
         var work = await _workRepository.GetCurrentWorkByUserAsync(id, HttpContext.RequestAborted);

+ 114 - 4
src/Hotline.Api/Controllers/WebPortalController.cs

@@ -56,6 +56,7 @@ namespace Hotline.Api.Controllers
         private readonly IRepository<Knowledge> _knowledgeRepository;
         private readonly ISystemDicDataCacheManager _systemDicDataCacheManager;
         private readonly IOptionsSnapshot<AppConfiguration> _appOptions;
+        private readonly ITypedCache<string> _getVailData;
 
         public WebPortalController(IMapper mapper,
             IMediator mediator,
@@ -78,7 +79,8 @@ namespace Hotline.Api.Controllers
             IRepository<KnowledgeType> knowledgeTypeRepository,
             IRepository<Knowledge> knowledgeRepository,
             ISystemDicDataCacheManager systemDicDataCacheManager,
-            IOptionsSnapshot<AppConfiguration> appOptions
+            IOptionsSnapshot<AppConfiguration> appOptions,
+            ITypedCache<string> getVailData
             )
         {
             _mapper = mapper;
@@ -103,6 +105,7 @@ namespace Hotline.Api.Controllers
             _knowledgeRepository = knowledgeRepository;
             _systemDicDataCacheManager = systemDicDataCacheManager;
             _appOptions = appOptions;
+            _getVailData = getVailData;
         }
 
         #region 通知
@@ -719,6 +722,9 @@ namespace Hotline.Api.Controllers
             return OpenResponse.Ok(WebPortalDeResponse<OrderListReturnDto>.Success(returnDto, "成功"));
         }
 
+        /// <summary>
+
+
         /// <summary>
         /// 查询工单发布后公开的数据
         /// </summary>
@@ -786,6 +792,7 @@ namespace Hotline.Api.Controllers
             int nPageCount = Convert.ToInt32(Math.Ceiling(Convert.ToDouble(total) / dto.PageSize));
             OrderListReturnDto returnDto = new()
             {
+                Total = total,
                 PageNum = dto.PageIndex,
                 PageCount = nPageCount,
                 Data = _mapper.Map<IReadOnlyList<OrderListDto>>(items)
@@ -794,6 +801,50 @@ namespace Hotline.Api.Controllers
             return OpenResponse.Ok(WebPortalDeResponse<OrderListReturnDto>.Success(returnDto, "成功"));
         }
 
+        /// <summary>
+        /// 查询工单发布后公开的数据
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [HttpPost("get_order_list_all_bynoortitle")]
+        [AllowAnonymous]
+        public async Task<OpenResponse> GetOrderByListAllByNoOrTitle([FromBody] QueryOrderListDto dto)
+        {
+            var (total, items) = await _orderRepository.Queryable(includeDeleted: true)
+                .LeftJoin<OrderPublish>((p, op) => p.Id == op.OrderId)
+                .WhereIF(!string.IsNullOrEmpty(dto.FlowCode), (p, op) => p.No == dto.FlowCode)
+                .WhereIF(!string.IsNullOrEmpty(dto.FlowName), (p, op) => p.Title.Contains(dto.FlowName))
+                .WhereIF(!string.IsNullOrEmpty(dto.FlowSType), (p, op) => p.AcceptTypeCode == dto.FlowSType)
+                .WhereIF(!string.IsNullOrEmpty(dto.FlowRType), (p, op) => p.HotspotId == dto.FlowRType)
+                .WhereIF(!string.IsNullOrEmpty(dto.FlowSDate), (p, op) => p.StartTime >= DateTime.Parse(DateTime.Parse(dto.FlowSDate).ToString("yyyy-MM-dd 00:00:00")))//dto.FlowSDate
+                .WhereIF(!string.IsNullOrEmpty(dto.FlowEDate), (p, op) => p.StartTime <= DateTime.Parse(DateTime.Parse(dto.FlowEDate).ToString("yyyy-MM-dd 00:00:00")))// dto.FlowEDate
+                .WhereIF(!string.IsNullOrEmpty(dto.FlowFrom), (p, op) => p.FromName.Contains(dto.FlowFrom))
+                .WhereIF(dto.IdentityType.HasValue, (p, op) => p.IdentityType == dto.IdentityType)
+                .OrderByDescending((p, op) => p.CreationTime)
+               .Select((p, op) => new OrderListDto
+               {
+                   FlowID = p.Id,
+                   FlowCode = p.No,
+                   FlowPwd = p.Password,
+                   FlowTitle = p.Title,
+                   FlowFromName = p.SourceChannel,
+                   FlowPurTypeName = p.AcceptType,
+                   ConTypeName = p.HotspotName,
+                   FlowAddDate = p.CreationTime,
+                   PubDate = op.CreationTime,
+                   RSFlagName = p.Status >= EOrderStatus.Filed ? "办理完成" : "办理中"
+               })
+               .ToPagedListAsync(dto.PageIndex, dto.PageSize, HttpContext.RequestAborted);
+
+            OrderListReturnAllDto returnDto = new()
+            {
+                Total = total,
+                Data = _mapper.Map<IReadOnlyList<OrderListDto>>(items)
+            };
+
+            return OpenResponse.Ok(WebPortalDeResponse<OrderListReturnAllDto>.Success(returnDto, "成功"));
+        }
+
         /// <summary>
         /// 办件摘编详情
         /// </summary>
@@ -921,19 +972,34 @@ namespace Hotline.Api.Controllers
         /// <returns></returns>
         [AllowAnonymous]
         [HttpPost("orderacceptance")]
-        public async Task<OpenResponse> OrderAcceptance([FromBody] WebFlowAccept dto)
+        public async Task<OpenResponse> OrderAcceptance([FromBody] WebFlowAcceptDto dto)
         {
             //电话号码去空格
             if (!string.IsNullOrEmpty(dto.Mobile))
                 dto.Mobile = dto.Mobile.Trim();
 
             var data = _mapper.Map<Hotline.Share.Dtos.Order.AddOrderDto>(dto);
+            data.Source = ESource.WebPortal;
             if (string.IsNullOrEmpty(data.SourceChannelCode))
             {
                 data.SourceChannel = "因特网";
                 data.SourceChannelCode = "YTW";
             }
-            data.Source = ESource.WebPortal;
+
+            if (dto.FromID == "2")
+            {
+                data.SourceChannel = "APP";
+                data.SourceChannelCode = "AP";
+                data.Source = ESource.APP;
+            }
+
+            if (dto.FromID == "3")
+            {
+                data.SourceChannel = "微信";
+                data.SourceChannelCode = "WX";
+                data.Source = ESource.WeChat;
+            }
+
             if (!string.IsNullOrEmpty(data.LicenceNo))
             {
                 data.LicenceTypeCode = "10";
@@ -958,7 +1024,8 @@ namespace Hotline.Api.Controllers
                 dto.Pwd = result.Password;
                 dto.Code = result.No;
                 dto.OrderId = result.Id;
-                await _webFlowAcceptRepository.AddAsync(dto, HttpContext.RequestAborted);
+                var dtoData = _mapper.Map<WebFlowAccept>(dto);
+                await _webFlowAcceptRepository.AddAsync(dtoData, HttpContext.RequestAborted);
 
             }
             else
@@ -1220,6 +1287,7 @@ namespace Hotline.Api.Controllers
             }
 
             var (total, items) = await _knowledgeRepository.Queryable()
+                .Where(p => p.IsPublic == true)
                 .WhereIF(!string.IsNullOrEmpty(dto.Title), p => p.Title.Contains(dto.Title))
                 .WhereIF(!string.IsNullOrEmpty(typeSpliceNameTags), p => p.KnowledgeType.Any(t => t.KnowledgeTypeSpliceName.EndsWith(typeSpliceNameTags)))
                 .WhereIF(!string.IsNullOrEmpty(typeSpliceName), x => x.KnowledgeType.Any(t => t.KnowledgeTypeSpliceName.EndsWith(typeSpliceName)))
@@ -1359,5 +1427,47 @@ namespace Hotline.Api.Controllers
             }
             return OpenResponse.Ok(WebPortalDeResponse<IReadOnlyList<OrderFormCount>>.Success(formFileCount));
         }
+
+        /// <summary>
+        /// 缓存值
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [HttpPost("setgetvaildata")]
+        [AllowAnonymous]
+        public async Task<OpenResponse> SetGetVailData([FromBody] GetVailDataDto dto)
+        {
+            if (string.IsNullOrEmpty(dto.Key) || string.IsNullOrEmpty(dto.Value))
+                return OpenResponse.Ok(WebPortalDeResponse<string>.Success("-1"));
+
+            string strGuid = Guid.NewGuid().ToString();
+
+            string keyToken = dto.Key + strGuid;
+            await _getVailData.SetAsync(keyToken, dto.Value, TimeSpan.FromHours(1), cancellationToken: HttpContext.RequestAborted);
+            return OpenResponse.Ok(WebPortalDeResponse<string>.Success(strGuid));
+        }
+
+        /// <summary>
+        /// 缓存值
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [HttpPost("getgetvaildata")]
+        [AllowAnonymous]
+        public async Task<OpenResponse> GetGetVailData([FromBody] GetVailDataDto dto)
+        {
+            if (string.IsNullOrEmpty(dto.Key))
+                return OpenResponse.Ok(WebPortalDeResponse<string>.Success("-1"));
+
+            var data = await _getVailData.GetAsync(dto.Key, cancellationToken: HttpContext.RequestAborted);
+            if (!string.IsNullOrEmpty(data) && data == dto.Value)
+            {
+                await _getVailData.RemoveAsync(dto.Key, cancellationToken: HttpContext.RequestAborted);
+                return OpenResponse.Ok(WebPortalDeResponse<string>.Success("1"));
+            }
+
+
+            return OpenResponse.Ok(WebPortalDeResponse<string>.Success("-1"));
+        }
     }
 }

+ 52 - 71
src/Hotline.Api/Controllers/WorkflowController.cs

@@ -26,6 +26,7 @@ using Hotline.Share.Dtos.Order;
 using Hotline.Share.Dtos.FlowEngine.Definition;
 using Hotline.Settings.TimeLimits;
 using Hotline.SeedData;
+using Hotline.Tools;
 
 namespace Hotline.Api.Controllers;
 
@@ -346,9 +347,10 @@ public class WorkflowController : BaseController
     /// 办理节点
     /// </summary>
     [HttpPost("next")]
+    [Obsolete("即将弃用")]
     public async Task Next([FromBody] NextWorkflowDto dto)
     {
-        await _workflowApplication.NextAsync(dto, cancellationToken: HttpContext.RequestAborted);
+        await _workflowDomainService.NextAsync(_sessionContext, dto, cancellationToken: HttpContext.RequestAborted);
     }
 
     /// <summary>
@@ -556,71 +558,7 @@ public class WorkflowController : BaseController
     {
         RefAsync<int> total = 0;
 
-        var Role = _sessionContext.Roles;
-
-        var query = _workflowCountersignRepository.Queryable()
-            .Includes(x => x.Members)
-            .LeftJoin<Workflow>((c, w) => c.WorkflowId == w.Id)
-            .InnerJoin<Order>((c, w, o) => w.ExternalId == o.Id)
-            .WhereIF(!string.IsNullOrEmpty(dto.Title), (c, w, o) => o.Title.Contains(dto.Title))
-            .WhereIF(!string.IsNullOrEmpty(dto.OrderNo), (c, w, o) => o.No == dto.OrderNo)
-            .WhereIF(!string.IsNullOrEmpty(dto.AcceptType), (c, w, o) => o.AcceptTypeCode == dto.AcceptType)//受理类型
-            .WhereIF(!string.IsNullOrEmpty(dto.Channel), (c, w, o) => o.SourceChannelCode == dto.Channel)//受理类型
-            .WhereIF(!string.IsNullOrEmpty(dto.Hotspot), (c, w, o) => o.HotspotSpliceName != null && o.HotspotSpliceName.Contains(dto.Hotspot))//热点类型
-            .WhereIF(!string.IsNullOrEmpty(dto.OrgId), (c, w, o) => c.FinisherOrgId == dto.OrgId) //接办部门
-            .WhereIF(dto.CounterSignType != null, (c, w, o) => c.CounterSignType == dto.CounterSignType) //会签类型
-        ;
-
-        var rolePaiDan = _systemSettingCacheManager.GetSetting(SettingConstants.RolePaiDan)?.SettingValue[0];//派单员角色
-        var seatsMonitor = _systemSettingCacheManager.GetSetting(SettingConstants.SeatsMonitor)?.SettingValue[0];//班长角色
-        var isAdmin = false;
-        var systemAdministrator = _systemSettingCacheManager.GetSetting(SettingConstants.SystemAdministrator)?.SettingValue[0];//管理员角色
-        if (!string.IsNullOrEmpty(systemAdministrator) && (_sessionContext.Roles.Contains(systemAdministrator) || _sessionContext.Roles.Contains(RoleSeedData.AdminRole)))
-            isAdmin = true;
-
-        //发起会签:班长角色能看所有会签信件;新增管理员能看到所有会签
-        //派单员角色只能看到自己发起的会签信件;
-        //承办部门用户能看到自己发起的和同级部门用户发起的会签件
-        if (dto.InitiatedCountersignature.HasValue && dto.InitiatedCountersignature == true)
-        {
-            if (_sessionContext.Roles.Any(p => p == seatsMonitor) || isAdmin)
-            {
-
-            }
-            else
-            if (_sessionContext.Roles.Any(p => p == rolePaiDan))
-            {
-                query = query.Where((c, w, o) => c.StarterId == _sessionContext.UserId);
-            }
-            else
-            {
-                query = query.Where((c, w, o) => c.StarterOrgId == _sessionContext.RequiredOrgId);
-            }
-        }
-
-        //会签已办(会签状态为已结束):
-        //班长角色能看所有已结束的会签信件;新增管理员能看到所有会签
-        //派单员不会办理会签件只会发起会签件所以这一点派单员角色可以忽略;
-        //承办部门用户能看到和同级部门用户已办理过的会签件
-        if (dto.HandleCountersignature.HasValue && dto.HandleCountersignature == true)
-        {
-            if (_sessionContext.Roles.Any(p => p == seatsMonitor) || isAdmin)
-            {
-                query = query.Where((c, w, o) => c.EndTime.HasValue);
-            }
-            else
-           if (_sessionContext.Roles.Any(p => p == rolePaiDan))
-            {
-                query = query.Where((c, w, o) => c.Members.Any(m => m.Key == _sessionContext.RequiredOrgId) && c.EndTime.HasValue);
-            }
-            else
-            {
-                query = query.Where((c, w, o) => c.Members.Any(m => m.Key == _sessionContext.OrgId || m.Key == _sessionContext.RequiredOrgId) && c.EndTime.HasValue);
-            }
-        }
-
-        var items = await query
-            .OrderByDescending((c, w, o) => o.ExpiredTime)
+        var items = await _workflowApplication.QueryOrderCountersigns(dto,_sessionContext)
             .Select((c, w, o) => new { c, o })
             .ToPageListAsync(dto.PageIndex, dto.PageSize, total, HttpContext.RequestAborted);
 
@@ -635,11 +573,54 @@ public class WorkflowController : BaseController
         return new PagedDto<WorkflowCountersignDto>(total, dtos);
     }
 
-    /// <summary>
-    /// 
-    /// </summary>
-    /// <returns></returns>
-    [HttpGet("order-countersign-base-data")]
+
+
+	/// <summary>
+	/// 会签信息导出
+	/// </summary>
+	/// <returns></returns>
+	[HttpPost("order-countersign/_export")]
+    public async Task<FileStreamResult> ScreenListExport([FromBody] ExportExcelDto<QueryOrderCountersignDto> dto)
+    {
+	    var query = _workflowApplication.QueryOrderCountersigns(dto.QueryDto, _sessionContext)
+			 .Select((c, w, o) => new WorkflowCountersign()
+			 {
+                Id  = c.Id.SelectAll(),
+                Order = o,
+			 });
+	    List<WorkflowCountersign> data;
+
+		if (dto.IsExportAll)
+	    {
+		    data = await query.ToListAsync(HttpContext.RequestAborted);
+		}
+	    else
+		{
+
+			var (_, items) = await query.ToPagedListAsync(dto.QueryDto, HttpContext.RequestAborted);
+			data = items;
+		}
+
+        var dataDtos = _mapper.Map<ICollection<WorkflowCountersignDto>>(data);
+
+        dynamic? dynamicClass = DynamicClassHelper.CreateDynamicClass(dto.ColumnInfos);
+
+	    var dtos = dataDtos
+			.Select(stu => _mapper.Map(stu, typeof(WorkflowCountersignDto), dynamicClass))
+		    .Cast<object>()
+		    .ToList();
+
+	    var stream = ExcelHelper.CreateStream(dtos);
+
+	    return ExcelStreamResult(stream, "会签信息数据");
+    }
+
+
+	/// <summary>
+	/// 
+	/// </summary>
+	/// <returns></returns>
+	[HttpGet("order-countersign-base-data")]
     public async Task<object> QueryOrderCountersignsBaseData()
     {
 

+ 3 - 3
src/Hotline.Api/Filter/LogFilterAttribute.cs

@@ -42,7 +42,7 @@ namespace Hotline.Api.Filter
 				{
 					case "GET":
 						Method = "GET";
-						log.ExecuteParam = context.HttpContext.Request.QueryString.Value;
+						log.ExecuteParam = context.HttpContext.Request.QueryString;
 						break;
 
 					case "POST":
@@ -50,11 +50,11 @@ namespace Hotline.Api.Filter
 						if (context.ActionArguments?.Count > 0)
 						{
 							log.ExecuteUrl += context.HttpContext.Request.QueryString.Value;
-							log.ExecuteParam = JsonConvert.SerializeObject(context.ActionArguments);
+							log.ExecuteParam = context.ActionArguments;
 						}
 						else
 						{
-							log.ExecuteParam = context.HttpContext.Request.QueryString.Value;
+							log.ExecuteParam = context.HttpContext.Request.QueryString;
 						}
 						break;
 				}

+ 2 - 0
src/Hotline.Api/Hotline.Api.csproj

@@ -21,11 +21,13 @@
     <PackageReference Include="Serilog.Sinks.MongoDB" Version="6.0.0" />
     <PackageReference Include="Swashbuckle.AspNetCore" Version="6.8.1" />
     <PackageReference Include="Quartz.AspNetCore" Version="3.8.0" />
+    <PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="6.7.3" />
   </ItemGroup>
 
   <ItemGroup>
     <ProjectReference Include="..\Hotline.Application\Hotline.Application.csproj" />
     <ProjectReference Include="..\Hotline.Logger\Hotline.Logger.csproj" />
+    <ProjectReference Include="..\Hotline.WeChat\Hotline.WeChat.csproj" />
   </ItemGroup>
 
   <ItemGroup>

+ 18 - 5
src/Hotline.Api/StartupExtensions.cs

@@ -37,6 +37,10 @@ using Hotline.XingTang;
 using Hotline.Logger;
 using HotPot.Mvc.Filters;
 using Microsoft.AspNetCore.ResponseCompression;
+using Senparc.Weixin.RegisterServices;
+using Senparc.Weixin.AspNet;
+using Hotline.WeChat;
+
 
 namespace Hotline.Api;
 
@@ -207,6 +211,10 @@ internal static class StartupExtensions
         services.AddScoped<IExpireTimeSupplier, WorkDaySupplier>();
         services.AddScoped<IExpireTimeSupplier, HourSupplier>();
 
+        services.AddScoped<Users.IThirdIdentiyService, WeChatService>();
+
+        services.AddSenparcWeixin(configuration);
+
         //services.AddScoped<LogFilterAttribute>();
         //ServiceLocator.Instance = services.BuildServiceProvider();
         return builder.Build();
@@ -221,11 +229,12 @@ internal static class StartupExtensions
         if (swaggerEnable)
         {
             app.UseSwagger();
-            app.UseSwaggerUI(c =>
-            {
-                //c.DocExpansion(DocExpansion.None);
-                //c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
-            });
+            app.UseSwaggerUI();
+            //app.UseSwaggerUI(c =>
+            //{
+            //    //c.DocExpansion(DocExpansion.None);
+            //    //c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
+            //});
         }
 
         app.UseCors(CorsOrigins);
@@ -239,6 +248,10 @@ internal static class StartupExtensions
             .RequireAuthorization();
         //app.MapSubscribeHandler();
 
+        var registerService = app.UseSenparcWeixin(app.Environment, null, null,
+            register => { },
+            (register, weixinSetting) => { }
+            );
         // 记录交互日志
         //app.UseRequestResponseLogging(app.Configuration);
         

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

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

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

@@ -15,10 +15,10 @@ namespace Hotline.Application.Contracts.Validators.FlowEngine
         {
             RuleFor(d => d.BusinessType).NotNull();
             RuleFor(d => d.HandlerType).NotNull();
-            RuleFor(d => d.Opinion)
-                .Cascade(CascadeMode.Stop)
-                .NotEmpty()
-                .MaximumLength(4000);
+            //RuleFor(d => d.Opinion)
+            //    .Cascade(CascadeMode.Stop)
+            //    .NotEmpty()
+            //    .MaximumLength(4000);
         }
     }
 }

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

@@ -14,10 +14,10 @@ namespace Hotline.Application.Contracts.Validators.FlowEngine
         {
             Include(new BasicWorkflowDtoValidator());
             RuleFor(d => d.WorkflowId).NotEmpty();
-            RuleFor(d => d.Opinion)
-                .Cascade(CascadeMode.Stop)
-                .NotEmpty()
-                .MaximumLength(4000);
+            //RuleFor(d => d.Opinion)
+            //    .Cascade(CascadeMode.Stop)
+            //    .NotEmpty()
+            //    .MaximumLength(4000);
             //RuleFor(d=>d.ExpiredTime).NotEmpty();
             //RuleFor(d=>d.NextStepCode).NotEmpty();
         }

+ 18 - 0
src/Hotline.Application.Contracts/Validators/Snapshot/IndustryValidator.cs

@@ -0,0 +1,18 @@
+using FluentValidation;
+using Hotline.Share.Dtos.Snapshot;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Hotline.Application.Contracts.Validators.Snapshot;
+public class IndustryValidator : AbstractValidator<AddIndustryDto>
+{
+    public IndustryValidator()
+    {
+        RuleFor(m => m.Name).NotEmpty().WithMessage("行业名称不能为空");
+        RuleFor(m => m.CitizenReadPackAmount).NotEqual(0).WithMessage("市民红包不能为空");
+        RuleFor(m => m.GuiderReadPackAmount).NotEqual(0).WithMessage("网格员红包不能为空");
+    }
+}

+ 10 - 2
src/Hotline.Application.Tests/Application/DefaultCallApplicationTest.cs

@@ -9,13 +9,14 @@ using Hotline.Share.Dtos.CallCenter;
 using Hotline.Share.Dtos.Order;
 using Hotline.Share.Enums.CallCenter;
 using Hotline.Users;
+using Microsoft.AspNetCore.Http;
 using Microsoft.Extensions.DependencyInjection;
 using Shouldly;
 using SqlSugar.Extensions;
 using XF.Domain.Repository;
 
 namespace Hotline.Application.Tests.Application;
-public class DefaultCallApplicationTest
+public class DefaultCallApplicationTest : TestBase
 {
     private readonly XingTangCallApplication _defaultCallApplication;
     private readonly IOrderVisitRepository _orderVisitRepository;
@@ -23,7 +24,7 @@ public class DefaultCallApplicationTest
     public readonly IFixture _fixture;
     private readonly IOrderRepository _orderRepository;
 
-    public DefaultCallApplicationTest(XingTangCallApplication defaultCallApplication, IOrderVisitRepository orderVisitRepository, IRepository<CallNative> callNativeRepository, IOrderRepository orderRepository)
+    public DefaultCallApplicationTest(IAccountRepository accountRepository, IRepository<Role> roleRepository, UserController userController, IServiceScopeFactory scopeFactory, IRepository<User> userRepository, IHttpContextAccessor httpContextAccessor, XingTangCallApplication defaultCallApplication, IOrderVisitRepository orderVisitRepository, IRepository<CallNative> callNativeRepository, IOrderRepository orderRepository) : base(accountRepository, roleRepository, userController, scopeFactory, userRepository, httpContextAccessor)
     {
         _fixture = new Fixture();
         _defaultCallApplication = defaultCallApplication;
@@ -132,4 +133,11 @@ public class DefaultCallApplicationTest
         (await _orderRepository.Queryable().Where(m => m.Id == orderId).Select(m => m.CallId).FirstAsync())
             .ShouldBe(inDto.Id);
     }
+
+    //[Fact]
+    public async Task OrderRelateCallHandler2_Test()
+    {
+        var orderId = "08dd0d21-0221-43cf-8230-3179dc6aefca";
+        await _defaultCallApplication.OrderRelateCallHandlerAsync(orderId, CancellationToken.None);
+    }
 }

+ 9 - 3
src/Hotline.Application.Tests/Application/KnowApplicationTest.cs

@@ -1,10 +1,16 @@
-using Hotline.Application.Knowledge;
+using Hotline.Api.Controllers;
+using Hotline.Application.Knowledge;
+using Hotline.Identity.Accounts;
+using Hotline.Identity.Roles;
 using Hotline.KnowledgeBase;
 using Hotline.KnowledgeBase.Notifies;
 using Hotline.Share.Dtos.Knowledge;
 using Hotline.Share.Tools;
+using Hotline.Users;
 using Mapster;
 using MediatR;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.DependencyInjection;
 using Shouldly;
 using System;
 using System.Collections.Generic;
@@ -15,7 +21,7 @@ using System.Threading.Tasks;
 using XF.Domain.Repository;
 
 namespace Hotline.Application.Tests.Application;
-public class KnowApplicationTest
+public class KnowApplicationTest : TestBase
 {
     private readonly IKnowApplication _knowApplication;
     private readonly IRepository<KnowledgeRelationType> _knowledgeRelationTypeRepository;
@@ -25,7 +31,7 @@ public class KnowApplicationTest
     private readonly IRepository<KnowledgeWord> _knowledgeWordRepository;
     private readonly IRepository<KnowledgeHotWord> _knowledgeHotWordRepository;
 
-    public KnowApplicationTest(IKnowApplication knowApplication, IRepository<KnowledgeRelationType> knowledgeRelationTypeRepository, IMediator mediator, IRepository<KnowledgeBase.Knowledge> knowledgeRepository, IKnowledgeDomainService knowledgeDomainService, IRepository<KnowledgeWord> knowledgeWordRepository, IRepository<KnowledgeHotWord> knowledgeHotWordRepository)
+    public KnowApplicationTest(IAccountRepository accountRepository, IRepository<Role> roleRepository, UserController userController, IServiceScopeFactory scopeFactory, IRepository<User> userRepository, IHttpContextAccessor httpContextAccessor, IKnowApplication knowApplication, IRepository<KnowledgeRelationType> knowledgeRelationTypeRepository, IMediator mediator, IRepository<KnowledgeBase.Knowledge> knowledgeRepository, IKnowledgeDomainService knowledgeDomainService, IRepository<KnowledgeWord> knowledgeWordRepository, IRepository<KnowledgeHotWord> knowledgeHotWordRepository) : base(accountRepository, roleRepository, userController, scopeFactory, userRepository, httpContextAccessor)
     {
         _knowApplication = knowApplication;
         _knowledgeRelationTypeRepository = knowledgeRelationTypeRepository;

+ 94 - 0
src/Hotline.Application.Tests/Application/SnapshotApplicationMockTest.cs

@@ -0,0 +1,94 @@
+using System.Threading;
+using System.Threading.Tasks;
+using Hotline.Application.Snapshot;
+using Hotline.Caching.Interfaces;
+using Hotline.File;
+using Hotline.Orders;
+using Hotline.Repository.SqlSugar.Extensions;
+using Hotline.Settings;
+using Hotline.Share.Dtos.Settings;
+using Hotline.Share.Dtos.Snapshot;
+using Hotline.Share.Enums.Snapshot;
+using Hotline.Snapshot;
+using Hotline.Users;
+using Mapster;
+using Moq;
+using XF.Domain.Authentications;
+using XF.Domain.Repository;
+using Xunit;
+
+namespace Hotline.Application.Tests.Snapshot
+{
+    public class SnapshotApplicationTest
+    {
+        private readonly Mock<IThirdIdentiyService> _thirdLoginServiceMock;
+        private readonly Mock<IRepository<Industry>> _industryRepositoryMock;
+        private readonly Mock<IRepository<Article.Bulletin>> _bulletinRepositoryMock;
+        private readonly Mock<ISessionContext> _sessionContextMock;
+        private readonly Mock<IRepository<RedPackRecord>> _redPackRecordRepositoryMock;
+        private readonly Mock<IRepository<Order>> _orderRepositoryMock;
+        private readonly Mock<IThirdAccountRepository> _thirdAccountRepositoryMock;
+        private readonly Mock<IRepository<OrderSnapshot>> _orderSnapshotRepositoryMock;
+        private readonly Mock<ISystemSettingCacheManager> _systemSettingCacheManagerMock;
+        private readonly Mock<ISystemAreaDomainService> _systemAreaDomainServiceMock;
+        private readonly Mock<IFileRepository> _fileRepositoryMock;
+        private readonly Mock<ISystemDicDataCacheManager> _systemDicDataCacheManagerMock;
+
+        private readonly DefaultSnapshotApplication _snapshotApplication;
+
+        public SnapshotApplicationTest()
+        {
+            _thirdLoginServiceMock = new Mock<IThirdIdentiyService>();
+            _industryRepositoryMock = new Mock<IRepository<Industry>>();
+            _bulletinRepositoryMock = new Mock<IRepository<Article.Bulletin>>();
+            _sessionContextMock = new Mock<ISessionContext>();
+            _redPackRecordRepositoryMock = new Mock<IRepository<RedPackRecord>>();
+            _orderRepositoryMock = new Mock<IRepository<Order>>();
+            _thirdAccountRepositoryMock = new Mock<IThirdAccountRepository>();
+            _orderSnapshotRepositoryMock = new Mock<IRepository<OrderSnapshot>>();
+            _systemSettingCacheManagerMock = new Mock<ISystemSettingCacheManager>();
+            _systemAreaDomainServiceMock = new Mock<ISystemAreaDomainService>();
+            _fileRepositoryMock = new Mock<IFileRepository>();
+            _systemDicDataCacheManagerMock = new Mock<ISystemDicDataCacheManager>();
+
+            _snapshotApplication = new DefaultSnapshotApplication(
+                _thirdLoginServiceMock.Object,
+                _industryRepositoryMock.Object,
+                _bulletinRepositoryMock.Object,
+                _sessionContextMock.Object,
+                _redPackRecordRepositoryMock.Object,
+                _orderRepositoryMock.Object,
+                _thirdAccountRepositoryMock.Object,
+                _orderSnapshotRepositoryMock.Object,
+                _systemSettingCacheManagerMock.Object,
+                _systemAreaDomainServiceMock.Object,
+                _fileRepositoryMock.Object,
+                _systemDicDataCacheManagerMock.Object
+            );
+        }
+
+        [Fact]
+        public async Task GetDeclareAsync_WithValidId_ReturnsDeclareBaseOutDto()
+        {
+            // Arrange
+            var id = "123";
+            var cancellationToken = CancellationToken.None;
+            var industry = new Industry { Id = id, IndustryType = EIndustryType.Declare };
+            var areaTree = new List<SystemArea>();
+            var files = new List<Hotline.File.File>();
+            _industryRepositoryMock.Setup(r => r.GetAsync(id, cancellationToken)).ReturnsAsync(industry);
+            _systemAreaDomainServiceMock.Setup(s => s.GetAreaTree(0, "510300")).ReturnsAsync(areaTree);
+            _fileRepositoryMock.Setup(f => f.GetByKeyAsync(id, cancellationToken)).ReturnsAsync(files);
+
+            // Act
+            var result = await _snapshotApplication.GetIndustryBaseAsync(id, cancellationToken);
+
+            // Assert
+            Assert.NotNull(result);
+            Assert.Equal(industry.Id, result.Industry.Id);
+            //Assert.Equal(industry.IndustryType, result.IndustryType);
+            Assert.Equal(areaTree.Adapt<List<SystemAreaOutDto>>(), result.AreaTree);
+            Assert.Equal(files.Adapt<List<IndustryFileDto>>(), result.Files);
+        }
+    }
+}

+ 229 - 0
src/Hotline.Application.Tests/Application/SnapshotApplicationTest.cs

@@ -0,0 +1,229 @@
+using DocumentFormat.OpenXml.Wordprocessing;
+using Hotline.Api.Controllers;
+using Hotline.Application.Identity;
+using Hotline.Application.Snapshot;
+using Hotline.File;
+using Hotline.Identity.Accounts;
+using Hotline.Identity.Roles;
+using Hotline.Share.Dtos.Article;
+using Hotline.Share.Dtos.Snapshot;
+using Hotline.Share.Enums;
+using Hotline.Share.Enums.Snapshot;
+using Hotline.Share.Tools;
+using Hotline.Snapshot;
+using Hotline.Users;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.DependencyInjection;
+using Shouldly;
+using XF.Domain.Repository;
+using XF.Utility.EnumExtensions;
+
+namespace Hotline.Application.Tests.Application;
+public class SnapshotApplicationTest : TestBase
+{
+    private readonly ISnapshotApplication _snapshotApplication;
+    private readonly IIdentityAppService _identityAppService;
+    private readonly IRepository<RedPackRecord> _redPackRecordRepository;
+    private readonly IIndustryApplication _industryApplication;
+    private readonly IIndustryRepository _industryRepository;
+    private readonly IFileRepository _fileRepository;
+
+    public SnapshotApplicationTest(IAccountRepository accountRepository, IRepository<Role> roleRepository, UserController userController, IServiceScopeFactory scopeFactory, IRepository<User> userRepository, IHttpContextAccessor httpContextAccessor, ISnapshotApplication snapshotApplication, IIdentityAppService identityAppService, IRepository<RedPackRecord> redPackRecordRepository, IIndustryApplication industryApplication, IIndustryRepository industryRepository, IFileRepository fileRepository) : base(accountRepository, roleRepository, userController, scopeFactory, userRepository, httpContextAccessor)
+    {
+        _snapshotApplication = snapshotApplication;
+        _identityAppService = identityAppService;
+        _redPackRecordRepository = redPackRecordRepository;
+        _industryApplication = industryApplication;
+        _industryRepository = industryRepository;
+        _fileRepository = fileRepository;
+    }
+
+    [Fact]
+    public async Task GetHomePage_Test()
+    {
+        var result = await _snapshotApplication.GetHomePageAsync();
+        result.Industrys.Any().ShouldBe(true, "首页数据为空");
+        result.Industrys.First().DisplayOrder.ShouldBe(1, "排序异常");
+    }
+
+    [Fact]
+    public async Task GetBulletins_Test()
+    {
+        var homePage = await _snapshotApplication.GetHomePageAsync();
+        var inDto = new BulletinInDto
+        {
+            IndustryId = homePage.Industrys.First(m => m.Name == "文化旅游").Id,
+        };
+        var items = await _snapshotApplication.GetBulletinsAsync(inDto);
+        items.ShouldNotBeNull();
+        items.Any().ShouldBe(true, "公告数据为空");
+        items.Any(m => m.Title.IsNullOrEmpty()).ShouldBe(false, "标题错误");
+        items.Any(m => m.Content.IsNullOrEmpty()).ShouldBe(false, "内容错误");
+        items.Any(m => m.Id.IsNullOrEmpty()).ShouldBe(false, "Id错误");
+    }
+
+    [Fact]
+    public async Task GetSnapshotUserInfo_Test()
+    {
+        var result = await _snapshotApplication.GetSnapshotUserInfoAsync();
+        result.ShouldNotBeNull();
+        result.PhoneNumber.ShouldNotBeNullOrEmpty();
+    }
+
+    [Fact]
+    public async Task GetThirdToken_Test()
+    {
+        var result = await _identityAppService.GetThredTokenAsync(new ThirdTokenInDto { LoginCode = "0c3Adhll2zDMBe413rnl2KvEym2AdhlH" });
+    }
+
+    [Theory]
+    [InlineData("")]
+    [InlineData("测")]
+    public async Task SnapshotOrder_Test(string key)
+    {
+        var dto = new OrderInDto();
+        dto.KeyWords = key;
+        var page = await _snapshotApplication.GetSnapshotOrdersAsync(dto);
+        page.Total.ShouldNotBe(0);
+        page.Items.FirstOrDefault()?.IndustryName.ShouldNotBeNullOrEmpty();
+        page.Items.FirstOrDefault()?.OrderNo.ShouldNotBeNullOrEmpty();
+        page.Items.FirstOrDefault()?.StatusText.ShouldNotBeNullOrEmpty();
+        page.Items.FirstOrDefault()?.Area.ShouldNotBeNullOrEmpty();
+    }
+
+    [Theory]
+    [InlineData(EOrderQueryStatus.All, 3)]
+    [InlineData(EOrderQueryStatus.Reply, 2)]
+    [InlineData(EOrderQueryStatus.NoReply, 1)]
+    [InlineData(EOrderQueryStatus.Appraise, 1)]
+    public async Task SnapshotOrderStatus_Test(EOrderQueryStatus status, int count)
+    {
+        var dto = new OrderInDto { Status = status };
+        var page = await _snapshotApplication.GetSnapshotOrdersAsync(dto);
+        page.Total.ShouldNotBe(0, $"状态:{status.GetDescription()} 数据为空");
+        page.Total.ShouldBe(count, $"状态:{status.GetDescription()} 数据条数错误");
+        page.Items.FirstOrDefault()?.IndustryName.ShouldNotBeNullOrEmpty();
+        page.Items.FirstOrDefault()?.OrderNo.ShouldNotBeNullOrEmpty();
+        page.Items.FirstOrDefault()?.StatusText.ShouldNotBeNullOrEmpty();
+        page.Items.FirstOrDefault()?.Area.ShouldNotBeNullOrEmpty();
+
+        dto.PageIndex = 2;
+        page = await _snapshotApplication.GetSnapshotOrdersAsync(dto);
+        page.Items.Count.ShouldBe(0);
+    }
+
+    [Fact]
+    public async Task GetSnapshotOrderDetail_Test()
+    {
+        var page = await _snapshotApplication.GetSnapshotOrdersAsync(new OrderInDto());
+        var id = page.Items.First().Id;
+        var detail = await _snapshotApplication.GetSnapshotOrderDetailAsync(id);
+        detail.Id.ShouldBe(id);
+        detail.IndustryName.ShouldNotBeNull();
+    }
+
+    [Theory]
+    [InlineData(2, 2)]
+    [InlineData(12, 12)]
+    public async Task GetRedPackDateAsync(int count, int exp)
+    {
+        var items = await _snapshotApplication.GetRedPackDateAsync(new RedPackDateInDto { Count = count });
+        items.Count.ShouldNotBe(0, "0数据");
+        items.Count.ShouldBe(exp, $"应该:{exp}, 实际 {items.Count}");
+    }
+
+    [Theory]
+    [InlineData(ERedPackPickupStatus.Unreceived)]
+    [InlineData(ERedPackPickupStatus.Received)]
+    public async Task GetRedPacksAsync(ERedPackPickupStatus status)
+    {
+        var page = await _snapshotApplication.GetRedPacksAsync(new RedPacksInDto { Status = status });
+        page.Total.ShouldNotBe(0, "数据不应该为空");
+    }
+
+    [Fact]
+    public async Task GetBulletinsDetail_Test()
+    {
+        var detail = await _snapshotApplication.GetBulletinsDetailAsync("08dc788f-20f4-4bf1-83d3-b5a8a4f395b0");
+        detail.Id.ShouldNotBeNullOrEmpty();
+        detail.Title.ShouldNotBeNullOrEmpty();
+        detail.Content.ShouldNotBeNullOrEmpty();
+    }
+
+    [Fact]
+    public async Task InitRedPackDataAsync()
+    {
+        for (int i = 0;i < 12;i++)
+        {
+            var now = DateTime.Now;
+            var entity = new RedPackRecord
+            {
+                OrderId = "111111111",
+                Amount = 10 * 1000,
+                CreationTime = new DateTime(2024, i + 1, 02, now.Hour, now.Minute, now.Second),
+                WXOpenId = "测试生成的OpenId",
+                PickupStatus = ERedPackPickupStatus.Received,
+            };
+            await _redPackRecordRepository.AddAsync(entity);
+        }
+    }
+
+    /// <summary>
+    /// 测试行业
+    /// 测试添加数据是否和获取的数据一致
+    /// </summary>
+    /// <returns></returns>
+    [Fact]
+    public async Task Industry_Test()
+    {
+        var industry = new AddIndustryDto
+        {
+            Name = "测试行业",
+            TitleSuffix = "测试标题",
+            ApproveOrgId = "测试审批部门Id",
+            ApproveOrgName = "测试审批部门名字",
+            AcceptType = "测试受理类型",
+            AcceptTypeCode = "测试受理类型代码",
+            CitizenReadPackAmount = 100,
+            GuiderReadPackAmount = 200,
+            IsEnable = true,
+            Files = new List<IndustryFileDto>
+            {
+                new IndustryFileDto
+                {
+                    Name = "测试文件" + DateTime.Now.ToShortDateString(),
+                    Path = "测试文件地址" + DateTime.Now.ToShortTimeString(),
+                    FileName ="测试文件" + DateTime.Now.ToShortDateString()  + ".doc",
+                    AdditionId = DateTime.Now.ToLongDateString()
+                }
+            }
+        };
+        var industryId = await _industryApplication.AddIndustryAsync(industry, CancellationToken.None);
+        var pageDto = await _snapshotApplication.GetIndustryBaseAsync(industryId, CancellationToken.None);
+        try
+        {
+            pageDto.ShouldNotBeNull();
+            pageDto.Files.ShouldNotBeNull();
+            foreach (var file in pageDto.Files)
+            {
+                file.Id.ShouldNotBeNullOrEmpty();
+                file.Name.ShouldNotBeNullOrEmpty();
+                file.Path.ShouldNotBeNullOrEmpty();
+                file.FileName.ShouldNotBeNullOrEmpty();
+                file.AdditionId.ShouldNotBeNullOrEmpty();
+                file.Key.ShouldBe(industryId);
+            }
+            pageDto.Workplace.ShouldNotBeNull();
+            pageDto.WorkArea.ShouldNotBeNull();
+        }
+        catch (Exception e)
+        {
+            // ignore
+        }
+        finally
+        {
+            await _industryRepository.Removeable().Where(m => m.Id == industryId).ExecuteCommandAsync();
+            await _fileRepository.Removeable().Where(m => m.Id == pageDto.Files.First().Id).ExecuteCommandAsync();
+        }
+    }
+}

+ 28 - 4
src/Hotline.Application.Tests/Application/SystemSettingCacheManagerTest.cs

@@ -1,6 +1,12 @@
-using Hotline.Caching.Interfaces;
+using Hotline.Api.Controllers;
+using Hotline.Caching.Interfaces;
 using Hotline.Caching.Services;
+using Hotline.Identity.Accounts;
+using Hotline.Identity.Roles;
 using Hotline.Settings;
+using Hotline.Users;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.DependencyInjection;
 using Shouldly;
 using System;
 using System.Collections.Generic;
@@ -10,20 +16,24 @@ using System.Threading.Tasks;
 using XF.Domain.Repository;
 
 namespace Hotline.Application.Tests.Application;
-public class SystemSettingCacheManagerTest
+public class SystemSettingCacheManagerTest : TestBase
 {
     private readonly ISystemSettingCacheManager _systemSettingCacheManager;
+    private readonly ISystemDicDataCacheManager _systemDicDataCacheManager;
     private readonly IRepository<SystemSetting> _systemSettingRepository;
 
-    public SystemSettingCacheManagerTest(ISystemSettingCacheManager systemSettingCacheManager, IRepository<SystemSetting> systemSettingRepository)
+    public SystemSettingCacheManagerTest(IAccountRepository accountRepository, IRepository<Role> roleRepository, UserController userController, IServiceScopeFactory scopeFactory, IRepository<User> userRepository, IHttpContextAccessor httpContextAccessor, ISystemSettingCacheManager systemSettingCacheManager, ISystemDicDataCacheManager systemDicDataCacheManager, IRepository<SystemSetting> systemSettingRepository) : base(accountRepository, roleRepository, userController, scopeFactory, userRepository, httpContextAccessor)
     {
         _systemSettingCacheManager = systemSettingCacheManager;
+        _systemDicDataCacheManager = systemDicDataCacheManager;
         _systemSettingRepository = systemSettingRepository;
     }
 
     [Fact]
     public void CancelPublishOrderEnabled_Test()
     {
+        var dd = DateTime.Parse("11/19/2024 18:08:00");
+        _systemSettingCacheManager.CallSyncUnPushDateTime.ShouldBe(DateTime.Parse("2024/11/19 18:08:00"));
         var result = _systemSettingCacheManager.CancelPublishOrderEnabled;
         result.ShouldBeTrue();
         var seconds = _systemSettingCacheManager.VisitCallDelaySecond;
@@ -34,7 +44,21 @@ public class SystemSettingCacheManagerTest
         var delaySecondEntity = _systemSettingRepository.GetAsync("08dc0681-a6d2-4ce7-877d-db65f846d523");
         delaySecondEntity.ShouldNotBeNull("DefaultVisitSmsDelaySecond 系统设置为NULL");
 
-        _systemSettingCacheManager.CallSyncUnPushDateTime.ShouldBe(DateTime.Parse("2024/11/19 18:08:00"));
         _systemSettingCacheManager.GetAboutToExpireVersion.ShouldBe(0);
+
+        _systemSettingCacheManager.WxOpenAppId.ShouldNotBeNull();
+        _systemSettingCacheManager.WxOpenAppSecret.ShouldNotBeNull();
+        _systemSettingCacheManager.VisitCallDelaySecond.ShouldNotBe(0);
+        _systemSettingCacheManager.AutomaticPublishOrder.ShouldBe(true);
+        _systemSettingCacheManager.CancelPublishOrderEnabled.ShouldBe(true);
+
+        var workplace = _systemDicDataCacheManager.Workplace;
+        workplace.ShouldNotBeNull();
+        workplace.Count.ShouldNotBe(0);
+
+        _systemDicDataCacheManager.RemoveSysDicDataCache(SysDicTypeConsts.WorkArea);
+        var workArea = _systemDicDataCacheManager.WorkArea;
+        workArea.ShouldNotBeNull();
+        workArea.Count.ShouldNotBe(0);
     }
 }

+ 2 - 0
src/Hotline.Application.Tests/Application/XingTangCallsSyncJobTest.cs

@@ -34,6 +34,7 @@ public class XingTangCallsSyncJobTest
         inDto.Tries = 0;
         inDto.CallGuid = DateTime.Now.ToString("yyyyMMddhhmmss") + "FLOW";
         inDto.Ver = string.Empty;
+        inDto.Id = new Random().Next(0, 100000);
 
         var inDto2 = _fixture.Create<XingtangCall>();
         inDto2.CallStartTime = DateTime.Now;
@@ -41,6 +42,7 @@ public class XingTangCallsSyncJobTest
         inDto2.IsSync = false;
         inDto2.Tries = 0;
         inDto2.Ver = string.Empty;
+        inDto2.Id = new Random().Next(0, 100000);
 
         await _db.Insertable(inDto).ExecuteCommandAsync();
         await _db.Insertable(inDto2).ExecuteCommandAsync();

+ 20 - 6
src/Hotline.Application.Tests/Application/ZiGongCallReportApplicationTest.cs

@@ -1,4 +1,5 @@
 using Hotline.Application.StatisticalReport.CallReport;
+using Hotline.Caching.Interfaces;
 using Hotline.Share.Dtos.CallCenter;
 using Hotline.Share.Enums.CallCenter;
 using Hotline.Share.Requests;
@@ -15,12 +16,12 @@ namespace Hotline.Application.Tests.Application;
 public class ZiGongCallReportApplicationTest
 {
     private readonly ZiGongCallReportApplication _ziGongCallReportApplication;
-    private CancellationToken cancellation;
+    private readonly ISystemDicDataCacheManager _systemDicDataCacheManager;
 
-    public ZiGongCallReportApplicationTest(ZiGongCallReportApplication ziGongCallReportApplication)
+    public ZiGongCallReportApplicationTest(ZiGongCallReportApplication ziGongCallReportApplication, ISystemDicDataCacheManager systemDicDataCacheManager)
     {
         _ziGongCallReportApplication = ziGongCallReportApplication;
-        cancellation = new CancellationToken();
+        _systemDicDataCacheManager = systemDicDataCacheManager;
     }
 
     [Fact]
@@ -44,7 +45,7 @@ public class ZiGongCallReportApplicationTest
             EndTime = DateTime.Now
         };
 
-        var result = await _ziGongCallReportApplication.QuerySeatCallAsync(inDto, cancellation);
+        var result = await _ziGongCallReportApplication.QuerySeatCallAsync(inDto, CancellationToken.None);
         result.ShouldNotBeNull();
     }
 
@@ -66,7 +67,7 @@ public class ZiGongCallReportApplicationTest
             ToNo = toNo,
             EndBy = endBy?.ToEnum<EEndBy>()
         };
-        var (total, items) = await _ziGongCallReportApplication.QueryCallsStatisticsDetailAsync(inDto, cancellation);
+        var (total, items) = await _ziGongCallReportApplication.QueryCallsStatisticsDetailAsync(inDto, CancellationToken.None);
         total.ShouldNotBe(0);
         items.Any(m => m.OrderNo.NotNullOrEmpty()).ShouldBeTrue();
         items.Any(m => m.OrderTitle.NotNullOrEmpty()).ShouldBeTrue();
@@ -89,7 +90,7 @@ public class ZiGongCallReportApplicationTest
             StartTime = "2024-07-29".ObjToDate(),
             EndTime = "2024-07-29 23:59:59".ObjToDate(),
         };
-        var result = await _ziGongCallReportApplication.QueryCallsDetailStatisticsAsync(inDto, cancellation);
+        var result = await _ziGongCallReportApplication.QueryCallsDetailStatisticsAsync(inDto, CancellationToken.None);
         result.Any(m => m.InConnectionRate.IsNullOrEmpty()).ShouldBeFalse();
         result.ShouldNotBeNull();
     }
@@ -161,4 +162,17 @@ public class ZiGongCallReportApplicationTest
         bhour.Total.ShouldBe(hour.Count, "Count 列表和详情的数量对不上");
 
     }
+
+    [Fact]
+    public async Task GetCallHotLineList_Test()
+    {
+        var dto = new BiQueryGateWayDto
+        {
+            StartTime = DateTime.Now.AddMonths(-1),
+            EndTime = DateTime.Now
+        };
+        var result = await _ziGongCallReportApplication.GetCallHotLineListAsync(dto, CancellationToken.None);
+        var hotlines = _systemDicDataCacheManager.CallForwardingSource.Select(m => m.DicDataValue).ToList();
+        result.Any(m => !hotlines.Contains(m.GateWay)).ShouldBeFalse();
+    }
 }

+ 0 - 78
src/Hotline.Application.Tests/Controller/DefaultHttpContextAccessor.cs

@@ -1,78 +0,0 @@
-using Hotline.Application.Tests.Infrastructure;
-using Hotline.Identity.Accounts;
-using Microsoft.AspNetCore.Http;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Security.Authentication;
-using System.Security.Claims;
-using System.Text;
-using System.Threading.Tasks;
-using XF.Domain.Authentications;
-using XF.Domain.Dependency;
-using XF.Domain.Repository;
-
-namespace Hotline.Application.Tests.Controller;
-public class DefaultHttpContextAccessor : ISessionContext, IScopeDependency
-{
-    private HttpContext _content = new DefaultHttpContext();
-
-    private HttpContext GetContext()
-    {
-        var context = new DefaultHttpContext();
-        //var openId = new Claim(AppClaimTypes.OpenId, "测试生成的OpenId");
-        var id = new ClaimsIdentity("身份");
-        //id.AddClaim(openId);
-        context.User = new ClaimsPrincipal(id);
-        //OpenId = context.User.FindFirstValue(AppClaimTypes.OpenId);
-        return context;
-
-    }
-    public HttpContext? HttpContext { get => GetContext(); set => _content = value; }
-
-
-    public string? OpenId { get { return TestSessionConstants.OpenId; } set { } }
-
-    /// <summary>
-    /// Id of current tenant or null for host
-    /// </summary>
-    public string? UserId
-    {
-        get
-        {
-            return TestSessionConstants.UserId;
-        }
-        init { }
-    }
-
-    /// <summary>
-    /// Id of current user or throw Exception for guest
-    /// </summary>
-    /// <exception cref="AuthenticationException"></exception>
-    public string RequiredUserId { get { return TestSessionConstants.UserId; } }
-    public string? UserName { get { return TestSessionConstants.UserName; } init { } }
-    public string? Phone { get; init; }
-
-    /// <summary>
-    /// Roles
-    /// </summary>
-    public string[] Roles { get { return TestSessionConstants.Roles; } init { } }
-    public string? OrgId { get { return TestSessionConstants.OrgId; } init { } }
-    public string RequiredOrgId { get { return TestSessionConstants.OrgId; } }
-    public string? OrgName { get; init; }
-    public int OrgLevel { get; init; }
-    public string? OrgAreaCode { get; init; }
-    public bool OrgIsCenter { get; init; }
-
-    /// <summary>
-    /// 部门行政区划名称
-    /// </summary>
-    public string? OrgAreaName { get; init; }
-    public string? AreaId { get; init; }
-    public string? ClientId { get; init; }
-
-    /// <summary>
-    /// 工号
-    /// </summary>
-    public string? StaffNo { get; init; }
-}

+ 130 - 0
src/Hotline.Application.Tests/Controller/DefaultSessionContext.cs

@@ -0,0 +1,130 @@
+using Hotline.Application.Tests.Infrastructure;
+using Hotline.Identity.Accounts;
+using IdentityModel;
+using Microsoft.AspNetCore.Http;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Security.Authentication;
+using System.Security.Claims;
+using System.Text;
+using System.Threading.Tasks;
+using XF.Domain.Authentications;
+using XF.Domain.Dependency;
+using XF.Domain.Repository;
+
+namespace Hotline.Application.Tests.Controller;
+public class DefaultSessionContext : ISessionContext, IScopeDependency
+{
+    private readonly IHttpContextAccessor _contextAccessor;
+    public DefaultSessionContext(IHttpContextAccessor httpContextAccessor)
+    {
+        _contextAccessor = httpContextAccessor;
+        //Roles = user.Claims.Where(d => d.Type == JwtClaimTypes.Role).Select(d => d.Value).ToArray();
+    }
+    private HttpContext _content = new DefaultHttpContext();
+
+    private HttpContext GetContext()
+    {
+        return _contextAccessor.HttpContext;
+    }
+    public HttpContext? HttpContext { get => GetContext(); set => _content = value; }
+
+
+    public string? OpenId { get; init; }
+
+    /// <summary>
+    /// Id of current tenant or null for host
+    /// </summary>
+    public string? UserId
+    {
+        get { return _contextAccessor.HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier); }
+        init { }
+    }
+
+    /// <summary>
+    /// Id of current user or throw Exception for guest
+    /// </summary>
+    /// <exception cref="AuthenticationException"></exception>
+    public string RequiredUserId => _contextAccessor.HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier);
+    public string? UserName
+    {
+        get { return _contextAccessor.HttpContext.User.FindFirstValue(AppClaimTypes.UserDisplayName); }
+        init { }
+    }
+
+    public string? Phone
+    {
+        get { return _contextAccessor.HttpContext.User.FindFirstValue(JwtClaimTypes.PhoneNumber); }
+        init { }
+    }
+
+    /// <summary>
+    /// Roles
+    /// </summary>
+    public string[] Roles
+    {
+        get { return _contextAccessor.HttpContext.User.Claims.Where(d => d.Type == ClaimTypes.Role).Select(d => d.Value).ToArray(); }
+        init { }
+    }
+
+    public string? OrgId
+    {
+        get { return _contextAccessor.HttpContext.User.FindFirstValue(AppClaimTypes.DepartmentId); }
+        init { }
+    }
+
+    public string RequiredOrgId => _contextAccessor.HttpContext.User.FindFirstValue(AppClaimTypes.DepartmentId);
+    public string? OrgName
+    {
+        get { return _contextAccessor.HttpContext.User.FindFirstValue(AppClaimTypes.DepartmentName); }
+        init { }
+    }
+
+    public int OrgLevel
+    {
+        get { return _contextAccessor.HttpContext.User.FindIntValue(AppClaimTypes.DepartmentLevel); }
+        init { }
+    }
+
+    public string? OrgAreaCode
+    {
+        get { return _contextAccessor.HttpContext.User.FindFirstValue(AppClaimTypes.DepartmentAreaCode); }
+        init { }
+    }
+
+    public bool OrgIsCenter
+    {
+        get { return _contextAccessor.HttpContext.User.FindBoolValue(AppClaimTypes.DepartmentIsCenter); }
+        init { }
+    }
+
+    /// <summary>
+    /// 部门行政区划名称
+    /// </summary>
+    public string? OrgAreaName
+    {
+        get { return _contextAccessor.HttpContext.User.FindFirstValue(AppClaimTypes.DepartmentAreaName); }
+        init { }
+    }
+
+    public string? AreaId
+    {
+        get { return _contextAccessor.HttpContext.User.FindFirstValue(AppClaimTypes.AreaId); }
+        init { }
+    }
+    public string? ClientId
+    {
+        get { return _contextAccessor.HttpContext.User.FindFirstValue(JwtClaimTypes.ClientId); }
+        init { }
+    }
+
+    /// <summary>
+    /// 工号
+    /// </summary>
+    public string? StaffNo
+    {
+        get { return _contextAccessor.HttpContext.User.FindFirstValue(AppClaimTypes.StaffNo); }
+        init { }
+    }
+}

+ 1 - 1
src/Hotline.Application.Tests/Controller/KnowledgeControllerTest.cs

@@ -27,7 +27,7 @@ public class KnowledgeControllerTest : TestBase
     private readonly KnowledgeController _knowledgeController;
     private readonly IRepository<KnowledgeBase.Knowledge> _knowledgeRepository;
 
-    public KnowledgeControllerTest(IAccountRepository accountRepository, IRepository<Role> roleRepository, UserController userController, IServiceScopeFactory scopeFactory, IRepository<User> userRepository, KnowledgeServiceMock knowledgeServiceMock, KnowledgeController knowledgeController, IRepository<KnowledgeBase.Knowledge> knowledgeRepository) : base(accountRepository, roleRepository, userController, scopeFactory, userRepository)
+    public KnowledgeControllerTest(IAccountRepository accountRepository, IRepository<Role> roleRepository, UserController userController, IServiceScopeFactory scopeFactory, IRepository<User> userRepository, KnowledgeServiceMock knowledgeServiceMock, KnowledgeController knowledgeController, IRepository<KnowledgeBase.Knowledge> knowledgeRepository, IHttpContextAccessor httpContextAccessor) : base(accountRepository, roleRepository, userController, scopeFactory, userRepository, httpContextAccessor)
     {
         _knowledgeServiceMock = knowledgeServiceMock;
         _knowledgeController = knowledgeController;

+ 28 - 3
src/Hotline.Application.Tests/Controller/OrderControllerTest.cs

@@ -2,8 +2,10 @@
 using Castle.DynamicProxy;
 using Hotline.Api.Controllers;
 using Hotline.Application.CallCenter;
+using Hotline.Application.Tests.Dto;
 using Hotline.Application.Tests.Infrastructure;
 using Hotline.Application.Tests.Mock;
+using Hotline.Application.Tests.SqlSuger;
 using Hotline.Caching.Interfaces;
 using Hotline.Caching.Services;
 using Hotline.CallCenter.Calls;
@@ -12,12 +14,14 @@ using Hotline.FlowEngine.WorkflowModules;
 using Hotline.Identity.Accounts;
 using Hotline.Identity.Roles;
 using Hotline.Orders;
+using Hotline.Repository.SqlSugar;
 using Hotline.Settings;
 using Hotline.Settings.Hotspots;
 using Hotline.Share.Dtos.File;
 using Hotline.Share.Dtos.FlowEngine;
 using Hotline.Share.Dtos.Order;
 using Hotline.Share.Dtos.Order.Publish;
+using Hotline.Share.Dtos.TrCallCenter;
 using Hotline.Share.Dtos.Users;
 using Hotline.Share.Enums.CallCenter;
 using Hotline.Share.Enums.FlowEngine;
@@ -32,6 +36,7 @@ using Microsoft.AspNetCore.Mvc.Testing;
 using Microsoft.Extensions.DependencyInjection;
 using Moq;
 using Shouldly;
+using SqlSugar;
 using System;
 using System.Collections.Generic;
 using System.Linq;
@@ -57,8 +62,9 @@ public class OrderControllerTest : TestBase
     private readonly IRepository<CallNative> _callNativeRepository; 
     private readonly IRepository<CallidRelation> _callIdRelationRepository;
     private readonly XingTangCallApplication _defaultCallApplication;
+    private readonly ISqlSugarClient _capSqlClient;
 
-    public OrderControllerTest(IAccountRepository accountRepository, IRepository<Role> roleRepository, UserController userController, IRepository<Hotspot> hotspotRepository, OrderController orderController, IOrderRepository orderRepository, IServiceScopeFactory scopeFactory, IRepository<User> userRepository, OrderServiceMock orderServiceMock, IRepository<OrderPublish> orderPublishRepository, INotificationHandler<EndWorkflowNotify> orderPublishEndWorkflowHandler, IOrderVisitRepository orderVisitRepository, IRepository<SystemSetting> systemSettingRepository, ISystemSettingCacheManager systemSettingCacheManager, IRepository<CallNative> callNativeRepository, IRepository<CallidRelation> callIdRelationRepository, XingTangCallApplication defaultCallApplication) : base(accountRepository, roleRepository, userController, scopeFactory, userRepository)
+    public OrderControllerTest(IAccountRepository accountRepository, IRepository<Role> roleRepository, UserController userController, IRepository<Hotspot> hotspotRepository, OrderController orderController, IOrderRepository orderRepository, IServiceScopeFactory scopeFactory, IRepository<User> userRepository, OrderServiceMock orderServiceMock, IRepository<OrderPublish> orderPublishRepository, INotificationHandler<EndWorkflowNotify> orderPublishEndWorkflowHandler, IOrderVisitRepository orderVisitRepository, IRepository<SystemSetting> systemSettingRepository, ISystemSettingCacheManager systemSettingCacheManager, IRepository<CallNative> callNativeRepository, IRepository<CallidRelation> callIdRelationRepository, XingTangCallApplication defaultCallApplication, ISugarUnitOfWork<CapDbContext> capDbContext, IHttpContextAccessor httpContextAccessor) : base(accountRepository, roleRepository, userController, scopeFactory, userRepository, httpContextAccessor)
     {
         _hotspotRepository = hotspotRepository;
         _orderController = orderController;
@@ -76,11 +82,14 @@ public class OrderControllerTest : TestBase
         _callNativeRepository = callNativeRepository;
         _callIdRelationRepository = callIdRelationRepository;
         _defaultCallApplication = defaultCallApplication;
+        _capSqlClient = capDbContext.Db;
     }
 
     /// <summary>
-    /// 创建工单时传入一个错误callId,
+    /// 创建工单时传入一个错误callId
+    /// 验证: 
     /// 测试在延迟消息后会不会把工单测CallId修复成正确的CallId
+    /// 推送的省上数据是否正确
     /// </summary>
     /// <returns></returns>
     [Fact]
@@ -98,6 +107,7 @@ public class OrderControllerTest : TestBase
             .With(m => m.Id, Ulid.NewUlid().ToString())
             .With(m => m.Direction, ECallDirection.Out)
             .With(m => m.CallNo, callNo)
+            .With(m => m.AudioFile, string.Empty)
             .Create();
         await _callNativeRepository.AddAsync(inDto2);
         var callOrder = new CallidRelation
@@ -116,6 +126,16 @@ public class OrderControllerTest : TestBase
         var orderEntity = await _orderRepository.GetAsync(order.Id);
         orderEntity.ShouldNotBeNull();
         orderEntity.CallId.ShouldBe(inDto.Id);
+        var query = _capSqlClient.Queryable<PublishedEntity>()
+            .Where(m => m.Name == "hotline.call.connect.order" && m.Content.Contains(order.Id));
+        var sql = query.ToSqlString();
+        sql.Contains("id").ShouldBeFalse("字段大小写错误" + sql);
+        var published = await query.FirstAsync();
+        published.ShouldNotBeNull("推送数据不存在");
+        var message = published.Content.FromJson<Message<PublishCallRecrodDto>>();
+        message.Value.Order.ShouldNotBeNull("推送的工单信息为空");
+        message.Value.Order.ProvinceNo.ShouldNotBeNull("省工单编号不能为空");
+        message.Value.Order.No.ShouldBe(order.No, "推送的工单编号错误");
     }
 
     /// <summary>
@@ -133,7 +153,7 @@ public class OrderControllerTest : TestBase
     }
 
     /// <summary>
-    /// 验证中心直办工单归档后自动发布
+    /// 验证中心直办工单归档后 自动发布
     /// 是否推诿, 是否不积极
     /// </summary>
     /// <returns></returns>
@@ -155,6 +175,11 @@ public class OrderControllerTest : TestBase
         orderEntity.ShouldNotBeNull();
         orderEntity.IsEvasive.ShouldBeTrue();
         orderEntity.IsInactively.ShouldBeTrue();
+        orderEntity.Status.ShouldBe(EOrderStatus.Visited);
+        orderEntity.OrgProcessingResults.ShouldNotBeNull();
+        orderEntity.OrgProcessingResults.Key.ShouldBe("4");
+        orderEntity.OrgProcessingResults.Value.ShouldBe("满意");
+        orderEntity.SeatEvaluate.ShouldBe(ESeatEvaluate.Satisfied);
     }
 
     [Fact]

+ 38 - 0
src/Hotline.Application.Tests/Controller/SnapshotControllerTest.cs

@@ -0,0 +1,38 @@
+using Hotline.Api.Controllers;
+using Hotline.Api.Controllers.Snapshot;
+using Hotline.Identity.Accounts;
+using Hotline.Identity.Roles;
+using Hotline.Users;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.DependencyInjection;
+using Shouldly;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using XF.Domain.Repository;
+
+namespace Hotline.Application.Tests.Controller;
+public class SnapshotControllerTest : TestBase
+{
+    private readonly SnapshotController _snapshotController;
+    public SnapshotControllerTest(IAccountRepository accountRepository, IRepository<Role> roleRepository, UserController userController, IServiceScopeFactory scopeFactory, IRepository<User> userRepository, IHttpContextAccessor httpContextAccessor, SnapshotController snapshotController) : base(accountRepository, roleRepository, userController, scopeFactory, userRepository, httpContextAccessor)
+    {
+        _snapshotController = snapshotController;
+    }
+
+    [Fact]
+    public async Task GetAreaTreeTest()
+    {
+        var result = await _snapshotController.GetAreaTreeAsync();
+        result.ShouldNotBeNull();
+        var zgs = result.Where(m => m.AreaName == "自贡市").FirstOrDefault();
+        zgs.ShouldNotBeNull();
+        var rx = zgs.Children.Where(m => m.AreaName == "荣县").FirstOrDefault();
+        rx.ShouldNotBeNull();
+        var zzz = rx.Children.Where(m => m.AreaName == "正紫镇").FirstOrDefault();
+        zzz.ShouldNotBeNull();
+    }
+
+}

+ 4 - 1
src/Hotline.Application.Tests/Domain/OrderVisitDomainServiceTest.cs

@@ -13,6 +13,7 @@ using Hotline.Share.Enums.Push;
 using Hotline.Share.Tools;
 using Hotline.Users;
 using Mapster;
+using Microsoft.AspNetCore.Http;
 using Microsoft.Extensions.DependencyInjection;
 using Shouldly;
 using XF.Domain.Repository;
@@ -27,7 +28,7 @@ public class OrderVisitDomainServiceTest : TestBase
     private readonly IOrderRepository _orderRepository;
     private readonly OrderServiceMock _orderServiceMock;
 
-    public OrderVisitDomainServiceTest(IAccountRepository accountRepository, IRepository<Role> roleRepository, UserController userController, IServiceScopeFactory scopeFactory, IRepository<User> userRepository, IOrderVisitDomainService orderVisitDomainService, IOrderVisitRepository orderVisitRepository, IRepository<OrderVisitDetail> orderVisitDetailRepository, Publisher publisher, IOrderRepository orderRepository, OrderServiceMock orderServiceMock) : base(accountRepository, roleRepository, userController, scopeFactory, userRepository)
+    public OrderVisitDomainServiceTest(IAccountRepository accountRepository, IRepository<Role> roleRepository, UserController userController, IServiceScopeFactory scopeFactory, IRepository<User> userRepository, IOrderVisitDomainService orderVisitDomainService, IOrderVisitRepository orderVisitRepository, IRepository<OrderVisitDetail> orderVisitDetailRepository, Publisher publisher, IOrderRepository orderRepository, OrderServiceMock orderServiceMock, IHttpContextAccessor httpContextAccessor) : base(accountRepository, roleRepository, userController, scopeFactory, userRepository, httpContextAccessor)
     {
         _orderVisitDomainService = orderVisitDomainService;
         _orderVisitRepository = orderVisitRepository;
@@ -102,6 +103,7 @@ public class OrderVisitDomainServiceTest : TestBase
     [InlineData("一般", "Visited", "4", "满意")]
     [InlineData("不满意", "SMSUnsatisfied", "2", "不满意")]
     [InlineData("非常不满意", "SMSUnsatisfied", "2", "不满意")]
+    [InlineData("0", "Visited", "0", "默认满意")]
     public async Task UpdateSmsReply_Test(string content, string visitState, string orgResuktKey, string orgResuktValue)
     {
         SetZuoXi();
@@ -152,6 +154,7 @@ public class OrderVisitDomainServiceTest : TestBase
                 org.OrgProcessingResults.Key.ShouldBe(orgResuktKey);
                 org.OrgProcessingResults.Value.ShouldBe(orgResuktValue);
 
+                // 验证跟新工单上的字段是否成功
                 orderEntity.OrgProcessingResults.ShouldNotBeNull();
                 orderEntity.OrgProcessingResults.Key.ShouldBe(orgResuktKey);
                 orderEntity.OrgProcessingResults.Value.ShouldBe(orgResuktValue);

+ 30 - 0
src/Hotline.Application.Tests/Dto/CapDto.cs

@@ -0,0 +1,30 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Hotline.Application.Tests.Dto;
+public class CapDto
+{
+}
+
+public class Message<T>
+{
+    public Headers Headers { get; set; }
+    public T Value { get; set; }
+}
+
+public class Headers
+{
+    public string CapCallbackName { get; set; }
+    public string CapMsgId { get; set; }
+    public string CapCorrId { get; set; }
+    public string CapCorrSeq { get; set; }
+    public string CapMsgName { get; set; }
+    public string CapMsgType { get; set; }
+    public string CapDelayTime { get; set; }
+    public string CapSentTime { get; set; }
+    public string CapMsgGroup { get; set; }
+    public string CapExecInstanceId { get; set; }
+}   

+ 17 - 0
src/Hotline.Application.Tests/Dto/PublishedEntity.cs

@@ -0,0 +1,17 @@
+using SqlSugar;
+using System;
+
+namespace Hotline.Application.Tests.Dto;
+[SugarTable("published")]
+public class PublishedEntity
+{
+    [SugarColumn(IsPrimaryKey = true)]
+    public long Id { get; set; }
+    public string Version { get; set; }
+    public string Name { get; set; }
+    public string Content { get; set; }
+    public int Retries { get; set; }
+    public DateTime Added { get; set; }
+    public DateTime? ExpiresAt { get; set; }
+    public string StatusName { get; set; }
+}

+ 4 - 0
src/Hotline.Application.Tests/Hotline.Application.Tests.csproj

@@ -26,6 +26,9 @@
     <PackageReference Include="coverlet.collector" Version="3.2.0" />
     <PackageReference Include="Microsoft.AspNetCore.TestHost" Version="7.0.20" />
     <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.1" />
+    <PackageReference Include="Senparc.Weixin" Version="6.19.1" />
+    <PackageReference Include="Senparc.Weixin.AspNet" Version="1.3.1" />
+    <PackageReference Include="Senparc.Weixin.WxOpen" Version="3.20.1" />
     <PackageReference Include="Moq" Version="4.20.72" />
     <PackageReference Include="Shouldly" Version="4.2.1" />
     <PackageReference Include="xunit" Version="2.4.2" />
@@ -39,6 +42,7 @@
     <ProjectReference Include="..\Hotline.Application\Hotline.Application.csproj" />
     <ProjectReference Include="..\Hotline.Repository.SqlSugar\Hotline.Repository.SqlSugar.csproj" />
     <ProjectReference Include="..\Tr.Sdk\Tr.Sdk.csproj" />
+    <ProjectReference Include="..\Hotline.WeChat\Hotline.WeChat.csproj" />
     <ProjectReference Include="..\XF.Domain.Repository\XF.Domain.Repository.csproj" />
     <ProjectReference Include="..\XF.Domain\XF.Domain.csproj" />
   </ItemGroup>

+ 6 - 1
src/Hotline.Application.Tests/Mock/OrderServiceMock.cs

@@ -40,7 +40,12 @@ public class OrderServiceMock
     {
         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>();
-        if (callId.NotNullOrEmpty()) AddOrderDto.CallId = callId;
+        if (callId.NotNullOrEmpty())
+        {
+            AddOrderDto.CallId = callId;
+            AddOrderDto.SourceChannel = "电话";
+            AddOrderDto.SourceChannelCode = AppDefaults.SourceChannel.DianHua;
+        }
         AddOrderDto.Title = "单元测试" + DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss");
         CreateOrderOutDto = _orderController.Add(AddOrderDto).GetAwaiter().GetResult().ToJson().FromJson<CreateOrderOutDto>();
         return this;

+ 23 - 0
src/Hotline.Application.Tests/Mock/ThirdTestService.cs

@@ -0,0 +1,23 @@
+using Hotline.Share.Dtos.Snapshot;
+using Hotline.Users;
+
+namespace Hotline.Application.Tests.Mock;
+public class ThirdTestService : IThirdIdentiyService
+{
+    public async Task<ThirdPhoneOutDto> GetPhoneNumberAsync(ThirdTokenDto dto)
+    {
+        return new ThirdPhoneOutDto
+        {
+            PhoneNumber = "13800138000"
+        };
+    }
+
+    public async Task<ThirdTokenOutDto> GetTokenAsync(ThirdTokenDto dto)
+    {
+        return new ThirdTokenOutDto
+        {
+            SessionKey = "sessionKeyfjdklsafjdskla",
+            OpenId = "测试生成的OpenId"
+        };
+    }
+}

+ 10 - 0
src/Hotline.Application.Tests/Repository/FWMQRepositoryTest.cs

@@ -0,0 +1,10 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Hotline.Application.Tests.Repository;
+internal class FWMQRepositoryTest
+{
+}

+ 11 - 0
src/Hotline.Application.Tests/SqlSuger/CapDbContext.cs

@@ -0,0 +1,11 @@
+using SqlSugar;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Hotline.Application.Tests.SqlSuger;
+public class CapDbContext : SugarUnitOfWork
+{
+}

+ 33 - 0
src/Hotline.Application.Tests/SqlSuger/CapDbExtensions.cs

@@ -0,0 +1,33 @@
+using Hotline.CallCenter.Configs;
+using Hotline.Repository.SqlSugar;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using SqlSugar;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Hotline.Application.Tests.SqlSuger;
+public static class CAPDbExtensions
+{
+    public static IServiceCollection AddCAPDb(this IServiceCollection services, IConfiguration configuration)
+    {
+        SqlSugarScope sqlSugar = new(new ConnectionConfig() 
+        { 
+            DbType = DbType.PostgreSQL,
+            ConnectionString = configuration.GetConnectionString("CAP"),
+            IsAutoCloseConnection = true,
+            InitKeyType = InitKeyType.Attribute,
+            MoreSettings = new ConnMoreSettings()
+            {
+                PgSqlIsAutoToLower = false
+            }
+        });
+
+        ISugarUnitOfWork<CapDbContext> context = new SugarUnitOfWork<CapDbContext>(sqlSugar);
+        services.AddSingleton(context);
+        return services;
+    }
+}

+ 27 - 5
src/Hotline.Application.Tests/Startup.cs

@@ -4,6 +4,12 @@ using Hotline.Repository.SqlSugar.Extensions;
 using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.Hosting;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Hotline.Application.Snapshot;
 using Hotline.Api;
 using Microsoft.AspNetCore.Identity;
 using XF.Domain.Dependency;
@@ -13,8 +19,16 @@ using XF.Domain.Repository;
 using Hotline.Repository.SqlSugar;
 using Hotline.Repository.SqlSugar.DataPermissions;
 using Hotline.Configurations;
+using Microsoft.AspNetCore.Http;
+using Senparc.Weixin.RegisterServices;
+using Senparc.Weixin;
 using Microsoft.AspNetCore.Builder;
+using Senparc.CO2NET.RegisterServices;
 using Xunit.DependencyInjection.AspNetCoreTesting;
+using Polly;
+using Senparc.Weixin.AspNet;
+using Hotline.Share.Tools;
+using Hotline.Users;
 using XF.Domain.Cache;
 using XF.EasyCaching;
 using Mapster;
@@ -41,6 +55,10 @@ using Hotline.Authentications;
 using Hotline.FlowEngine.Notifications;
 using Hotline.Application.Handlers.FlowEngine;
 using Hotline.Application.Jobs;
+using Hotline.Application.Tests.SqlSuger;
+using Microsoft.AspNetCore.Http;
+using Hotline.WeChat;
+using Hotline.Api.Controllers.Snapshot;
 
 namespace Hotline.Application.Tests;
 public class Startup
@@ -94,9 +112,10 @@ public class Startup
 
             //sqlsugar
             services.AddSqlSugar(configuration);
+            services.AddCAPDb(configuration);
 
             // application services
-            // services.AddScoped<ISnapshotApplication, SnapshotApplication>();
+            services.AddScoped<ISnapshotApplication, ZiGongSnapshotApplication>();
 
             //mq
             services.AddTestMq(configuration);
@@ -122,7 +141,7 @@ public class Startup
             services.RegisterMediatR(appConfiguration);
             services.RegisterSignalR(configuration);
 
-            // services.AddSenparcWeixinServices(configuration);
+            services.AddSenparcWeixinServices(configuration);
             AppDomain.CurrentDomain.GetAssemblies().ToList().SelectMany(d => d.GetTypes())
                 .Where(d => d.GetInterfaces().Any(x =>
                     x == typeof(IScopeDependency)
@@ -132,7 +151,8 @@ public class Startup
                 .ToList()
                 .ForEach(d => ServiceRegister.Register(services, d));
 
-            //services.AddScoped<IThirdIdentiyService, ThirdTestService>();
+            //services.AddScoped<IThirdIdentiyService, WeChatService>();
+            services.AddScoped<IThirdIdentiyService, ThirdTestService>();
             // services.AddScoped<IThirdIdentiyService, WeChatService>();
 
             //services.AddScoped<IThirdAccountRepository, ThirdAccountRepository>();
@@ -150,9 +170,11 @@ public class Startup
             services.AddScoped<IExportApplication, ExportApplication>();
             services.AddScoped<OrderController>();
             services.AddScoped<UserController>();
+            services.AddScoped<SnapshotController>();
             services.AddScoped<KnowledgeController>();
             services.AddScoped<PushMessageController>();
-            services.AddScoped<ISessionContext, DefaultHttpContextAccessor>();
+            services.AddHttpContextAccessor();
+            services.AddScoped<ISessionContext, Controller.DefaultSessionContext>();
             services.AddScoped<ISessionContextProvider, SessionContextProvider>();
             services.AddScoped<ICallApplication, XingTangCallApplication>();
             services.AddScoped<XingTangCallApplication>();
@@ -165,7 +187,7 @@ public class Startup
 
         public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
         {
-            // app.UseSenparcWeixin(env, null, null, register => { }, (register, weixinSetting) => { });
+            app.UseSenparcWeixin(env, null, null, register => { }, (register, weixinSetting) => { });
         }
     }
 }

+ 37 - 6
src/Hotline.Application.Tests/TestBase.cs

@@ -3,13 +3,17 @@ using Hotline.Api.Controllers;
 using Hotline.Application.Tests.Infrastructure;
 using Hotline.Identity.Accounts;
 using Hotline.Identity.Roles;
+using Hotline.Settings;
 using Hotline.Share.Dtos.Users;
 using Hotline.Share.Enums.Order;
 using Hotline.Share.Enums.User;
 using Hotline.Users;
+using IdentityModel;
 using Microsoft.AspNetCore.Http;
 using Microsoft.AspNetCore.Mvc;
 using Microsoft.Extensions.DependencyInjection;
+using System.Security.Claims;
+using XF.Domain.Authentications;
 using XF.Domain.Repository;
 
 namespace Hotline.Application.Tests;
@@ -21,9 +25,11 @@ public class TestBase
     public readonly UserController _userController;
     public readonly IFixture _fixture;
     private readonly IServiceScopeFactory _scopeFactory;
+    public readonly IHttpContextAccessor _httpContextAccessor;
 
-    public TestBase(IAccountRepository accountRepository, IRepository<Role> roleRepository, UserController userController, IServiceScopeFactory scopeFactory, IRepository<User> userRepository)
+    public TestBase(IAccountRepository accountRepository, IRepository<Role> roleRepository, UserController userController, IServiceScopeFactory scopeFactory, IRepository<User> userRepository, IHttpContextAccessor httpContextAccessor)
     {
+
         _fixture = new Fixture();
         _accountRepository = accountRepository;
         _roleRepository = roleRepository;
@@ -34,6 +40,13 @@ public class TestBase
         };
         _scopeFactory = scopeFactory;
         _userRepository = userRepository;
+        _httpContextAccessor = httpContextAccessor;
+
+        if (httpContextAccessor.HttpContext is null)
+        {
+            httpContextAccessor.HttpContext = new DefaultHttpContext();
+            SetZuoXi();
+        }
     }
 
     public void SetPaiDanYuan()
@@ -85,15 +98,33 @@ public class TestBase
                 UserName = userName
             };
             var accountId = _userController.Add(newUser).GetAwaiter().GetResult();
-            TestSessionConstants.UserId = accountId;
+            // TestSessionConstants.UserId = accountId;
             account = _accountRepository.GetExtAsync(
                 d => d.UserName == userName,
                 d => d.Includes(x => x.Roles)).GetAwaiter().GetResult();
         }
         var user = _userRepository.GetAsync(account.Id).GetAwaiter().GetResult();
-        TestSessionConstants.UserId = account.Id;
-        TestSessionConstants.Roles = account.Roles.Select(m => m.Id).ToArray();
-        TestSessionConstants.UserName = account.UserName;
-        TestSessionConstants.OrgId = user.OrgId;
+
+        List<Claim> userClaims = [ 
+            new(JwtClaimTypes.Subject, account.Id),
+            new(JwtClaimTypes.PhoneNumber, account.PhoneNo ?? string.Empty),
+            new(ClaimTypes.NameIdentifier, user.Id),
+            new(AppClaimTypes.UserDisplayName, account.Name),
+            new(AppClaimTypes.DepartmentId, user.OrgId ?? string.Empty), 
+            new(AppClaimTypes.DepartmentIsCenter, user.Organization?.IsCenter.ToString() ?? false.ToString()), 
+            new(AppClaimTypes.DepartmentName, user.Organization?.Name ?? string.Empty), 
+            new(AppClaimTypes.DepartmentAreaCode, user.Organization?.AreaCode ?? string.Empty), 
+            new(AppClaimTypes.DepartmentAreaName, user.Organization?.AreaName ?? string.Empty), 
+            new(AppClaimTypes.DepartmentLevel, user.Organization?.Level.ToString() ?? string.Empty), 
+            new(AppClaimTypes.AreaId, user.OrgId?.GetHigherOrgId() ?? string.Empty),
+        ];
+        ClaimsIdentity identity = new ClaimsIdentity(userClaims);
+        var principal = new ClaimsPrincipal(identity);
+        _httpContextAccessor.HttpContext.User = principal;
+
+        //TestSessionConstants.UserId = account.Id;
+        //TestSessionConstants.Roles = account.Roles.Select(m => m.Id).ToArray();
+        //TestSessionConstants.UserName = account.UserName;
+        //TestSessionConstants.OrgId = user.OrgId;
     }
 }

+ 12 - 2
src/Hotline.Application.Tests/appsettings.Development.json

@@ -1,4 +1,13 @@
 {
+    "SenparcWeixinSetting": {
+        "IsDebug": true,
+
+        //小程序
+        "WxOpenAppId": "#{WxOpenAppId}#",
+        "WxOpenAppSecret": "#{WxOpenAppSecret}#",
+        "WxOpenToken": "#{WxOpenToken}#",
+        "WxOpenEncodingAESKey": "#{WxOpenEncodingAESKey}#"
+    },
     "AllowedHosts": "*",
     "AppConfiguration": {
         "AppScope": "ZiGong",
@@ -59,7 +68,8 @@
         }
     },
     "ConnectionStrings": {
-         "Hotline": "PORT=5432;DATABASE=hotline_dev;HOST=110.188.24.182;PASSWORD=fengwo11!!;USER ID=dev;"
+        "Hotline": "PORT=5432;DATABASE=hotline_dev;HOST=110.188.24.182;PASSWORD=fengwo11!!;USER ID=dev;",
+        "CAP": "PORT=5432;DATABASE=fwmq;HOST=110.188.24.182;PASSWORD=fengwo11!!;USER ID=dev;Search Path=cap"
         //"Hotline": "PORT=5432;DATABASE=hotline;HOST=110.188.24.182;PASSWORD=fengwo11!!;USER ID=dev;"
     },
     "Cache": {
@@ -111,7 +121,7 @@
             "UserName": "dev",
             "Password": "123456",
             "HostName": "110.188.24.182",
-            "VirtualHost": "fwt-unit-test"
+            "VirtualHost": "fwt-dev"
         }
     },
     //"SmsAccountInfo": {

+ 25 - 25
src/Hotline.Application/CallCenter/DefaultCallApplication.cs

@@ -242,8 +242,8 @@ public abstract class DefaultCallApplication : ICallApplication
             .LeftJoin<OrderVisit>((d, o, v) => d.Id == v.CallId)
             // .WhereIF(string.IsNullOrEmpty(dto.ToNo), (d, o, v) => d.GroupId != "0")
             .WhereIF(!string.IsNullOrEmpty(dto.OrderNo), (d, o, v) => o.No == dto.OrderNo)
-            .WhereIF(!string.IsNullOrEmpty(dto.FromNo), d => d.FromNo == dto.FromNo)
-            .WhereIF(!string.IsNullOrEmpty(dto.ToNo), d => d.ToNo == dto.ToNo)
+            .WhereIF(!string.IsNullOrEmpty(dto.FromNo), d => d.FromNo.Contains(dto.FromNo!))
+            .WhereIF(!string.IsNullOrEmpty(dto.ToNo), d => d.ToNo.Contains(dto.ToNo!))
             .WhereIF(!string.IsNullOrEmpty(dto.UserName), d => d.UserName == dto.UserName)
             .WhereIF(!string.IsNullOrEmpty(dto.TelNo), d => d.TelNo == dto.TelNo)
             .WhereIF(dto.EndBy != null, d => d.EndBy == dto.EndBy)
@@ -293,6 +293,7 @@ public abstract class DefaultCallApplication : ICallApplication
     public virtual async Task<IReadOnlyList<TelOperation>> QueryTelOperationsAsync(QueryTelOperationsFixedDto dto, CancellationToken cancellationToken)
     {
         return await _telOperationRepository.Queryable()
+            .Where(m => m.OperateStateText != "未知")
             .WhereIF(!string.IsNullOrEmpty(dto.UserName), d => d.UserName == dto.UserName)
             .WhereIF(!string.IsNullOrEmpty(dto.StaffNo), d => d.StaffNo == dto.StaffNo)
             .WhereIF(!string.IsNullOrEmpty(dto.GroupId), d => d.GroupId == dto.GroupId)
@@ -499,35 +500,34 @@ public abstract class DefaultCallApplication : ICallApplication
     /// <returns></returns>
     public virtual async Task OrderRelateCallHandlerAsync(string orderId, CancellationToken cancellationToken)
     {
-        _systemLogRepository.Add("延迟更新工单通话", orderId, $"收到消息", status: 1);
-        var callId = await _orderRepository.Queryable()
-            .Where(m => m.Id == orderId)
-            .Select(m => m.CallId)
-            .FirstAsync(cancellationToken);
-        if (callId.IsNullOrEmpty()) return;
-
-        var callNo = await _callNativeRepository.Queryable()
-            .Where(m => m.Id == callId)
-            .Select(m => m.CallNo)
+        var orderCall = await _orderRepository.Queryable()
+            .LeftJoin<CallNative>((o, c) => o.CallId == c.Id)
+            .Where((o, c) => o.Id == orderId)
+            .Select((o, c) => new { o.CallId, c.CallNo })
             .FirstAsync(cancellationToken);
-        if (callNo.IsNullOrEmpty()) return;
-
-        var callNative = await _callNativeRepository.Queryable()
-            .Where(m => m.CallNo == callNo)
-            .ToListAsync(cancellationToken);
+        if (orderCall is null || orderCall.CallNo.IsNullOrEmpty())
+        {
+            _logger.LogInformation($"延迟更新工单通话, 工单: {orderId} 根据 order.id left join call_native 信息为空; 消息队列无须重试");
+            return;
+        }
 
-        var call = callNative.Where(m => m.Direction == ECallDirection.In)
+        var call = await _callNativeRepository.Queryable()
+            .Where(m => m.CallNo == orderCall.CallNo && m.Direction == ECallDirection.In)
             .OrderByDescending(m => m.Duration)
-            .First();
+            .FirstAsync(cancellationToken);
+        if (call == null)
+        {
+            var message = $"延迟更新工单通话, 工单: {orderId} 根据 CallNo: {orderCall.CallNo} direction = 0 查询 call_native 信息为空; 等兴唐把数据同步过来, 队列重试;";
+            throw new ArgumentNullException(message);
+        }
 
-        // 只有一条通话记录, 不处理
         // 需要更新的callId 和 order.callId 相同, 不处理
-        if (callNative.Count < 2 || callId == call.Id)
+        if (orderCall.CallId == call.Id)
         {
             // 推省上
             await _capPublisher.PublishAsync(EventNames.HotlineCallConnectWithOrder, new PublishCallRecrodDto()
             {
-                Order = _orderRepository.GetAsync(orderId, cancellationToken).Adapt<OrderDto>(),
+                Order = (await _orderRepository.GetAsync(orderId, cancellationToken)).Adapt<OrderDto>(),
                 TrCallRecordDto = call.Adapt<TrCallDto>()
             }, cancellationToken: cancellationToken);
             return;
@@ -540,16 +540,16 @@ public abstract class DefaultCallApplication : ICallApplication
 
         await _callIdRelationRepository.Updateable()
             .SetColumns(m => m.CallId == call.Id)
-            .Where(m => m.Id == callNo)
+            .Where(m => m.Id == orderCall.CallId)
             .ExecuteCommandAsync(cancellationToken);
 
         // 推省上
         await _capPublisher.PublishAsync(EventNames.HotlineCallConnectWithOrder, new PublishCallRecrodDto()
         {
-            Order = _orderRepository.GetAsync(orderId, cancellationToken).Adapt<OrderDto>(),
+            Order = (await _orderRepository.GetAsync(orderId, cancellationToken)).Adapt<OrderDto>(),
             TrCallRecordDto = call.Adapt<TrCallDto>()
         }, cancellationToken: cancellationToken);
-        _systemLogRepository.Add("延迟更新工单通话", orderId, $"原CallId: {callId}, 更新CallId: {call.Id}", status: 1);
+        _systemLogRepository.Add("延迟更新工单通话", orderId, $"原CallId: {orderCall.CallId}, 更新CallId: {call.Id}", status: 1);
     }
 
     /// <summary>

+ 17 - 5
src/Hotline.Application/FlowEngine/IWorkflowApplication.cs

@@ -7,11 +7,13 @@ using Hotline.Share.Dtos;
 using Hotline.Share.Dtos.File;
 using Hotline.Share.Dtos.FlowEngine;
 using Hotline.Share.Dtos.FlowEngine.Definition;
+using Hotline.Share.Dtos.FlowEngine.Workflow;
 using Hotline.Share.Dtos.Order;
 using Hotline.Share.Dtos.Order.Handle;
 using Hotline.Share.Dtos.Settings;
 using Hotline.Share.Enums.FlowEngine;
 using Microsoft.AspNetCore.Mvc;
+using SqlSugar;
 using XF.Domain.Authentications;
 
 namespace Hotline.Application.FlowEngine
@@ -36,10 +38,10 @@ namespace Hotline.Application.FlowEngine
         Task<FlowAssignInfo> GetNextStepFlowAssignInfoAsync(Workflow workflow, WorkflowStep currentStep,
             BasicWorkflowDto dto, StepDefine nextStepDefine, bool isNextDynamic, CancellationToken cancellationToken);
 
-        /// <summary>
-        /// 流转至下一节点(节点办理)
-        /// </summary>
-        Task<Workflow> NextAsync(NextWorkflowDto dto, DateTime? expiredTime = null, CancellationToken cancellationToken = default);
+        ///// <summary>
+        ///// 流转至下一节点(节点办理)
+        ///// </summary>
+        //Task<Workflow> NextAsync(NextWorkflowDto dto, DateTime? expiredTime = null, CancellationToken cancellationToken = default);
 
         /// <summary>
         /// 退回(返回前一节点)
@@ -60,7 +62,7 @@ namespace Hotline.Application.FlowEngine
         /// <summary>
         /// 办理至结束节点(无视流程模板配置,操作人需是当前节点办理对象)
         /// </summary>
-        Task HandleToEndAsync(string workflowId, string opinion, List<FileDto> files,
+        Task HandleToEndAsync(string workflowId, string opinion, List<FileDto> files, DateTime? expiredTime = null,
             EReviewResult reviewResult = EReviewResult.Unknown, CancellationToken cancellationToken = default);
 
         ////////
@@ -120,5 +122,15 @@ namespace Hotline.Application.FlowEngine
         /// 更新省平台办理结果节点附件
         /// </summary>
         Task UpdateProvinceHandleResultFilesAsync(string workflowId, List<FileDto> files, CancellationToken cancellationToken);
+
+		/// <summary>
+		/// 查询会签信息
+		/// </summary>
+		/// <param name="dto"></param>
+		/// <param name="_sessionContext"></param>
+		/// <returns></returns>
+		ISugarQueryable<WorkflowCountersign, Workflow, Order> QueryOrderCountersigns(QueryOrderCountersignDto dto,
+	        ISessionContext _sessionContext);
+
     }
 }

+ 181 - 123
src/Hotline.Application/FlowEngine/WorkflowApplication.cs

@@ -4,15 +4,11 @@ using Hotline.FlowEngine;
 using Hotline.FlowEngine.Definitions;
 using Hotline.FlowEngine.WorkflowModules;
 using Hotline.FlowEngine.Workflows;
-using Hotline.Identity.Accounts;
 using Hotline.Identity.Roles;
 using Hotline.Orders;
 using Hotline.Settings;
-using Hotline.Settings.TimeLimits;
 using Hotline.Share.Dtos;
 using Hotline.Share.Dtos.FlowEngine;
-using Hotline.Share.Dtos.FlowEngine.Definition;
-using Hotline.Share.Dtos.Settings;
 using Hotline.Share.Enums.FlowEngine;
 using Hotline.Share.Enums.Identity;
 using Hotline.Users;
@@ -28,15 +24,12 @@ using XF.Domain.Repository;
 using XF.Utility.EnumExtensions;
 using Hotline.Share.Dtos.File;
 using Microsoft.Extensions.Logging;
-using System.Text;
-using System.Diagnostics;
 using Hotline.Configurations;
 using Hotline.Share.Dtos.Order.Handle;
-using Microsoft.AspNetCore.Http;
-using Microsoft.Extensions.Options;
 using Newtonsoft.Json;
-using NPOI.SS.Formula.Functions;
-using DocumentFormat.OpenXml.Drawing;
+using Hotline.Share.Dtos.FlowEngine.Workflow;
+using SqlSugar;
+using Hotline.SeedData;
 
 namespace Hotline.Application.FlowEngine;
 
@@ -261,76 +254,76 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
         return workflow.Id;
     }
 
-    /// <summary>
-    /// 流转至下一节点(节点办理)
-    /// </summary>
-    public async Task<Workflow> NextAsync(NextWorkflowDto dto, DateTime? expiredTime = null, CancellationToken cancellationToken = default)
-    {
-        var validator = new NextWorkflowDtoValidator();
-        var validResult = await validator.ValidateAsync(dto, cancellationToken);
-        if (!validResult.IsValid)
-            throw new UserFriendlyException(
-                $"非法参数, {string.Join(',', validResult.Errors.Select(d => d.ErrorMessage))}");
-        var workflow = await _workflowDomainService.GetWorkflowAsync(dto.WorkflowId, withDefine: true, withSteps: true,
-            withTraces: true, withCountersigns: true, cancellationToken: cancellationToken);
-
-        //var currentStep = _workflowDomainService.FindCurrentStepWaitForHandle(workflow,
-        //    current.RequiredUserId, current.RequiredOrgId, current.Roles);
-        var currentStep = workflow.Steps.FirstOrDefault(d => d.Id == dto.StepId);
-        if (currentStep == null)
-            throw new UserFriendlyException(
-                $"未找到对应节点, workflowId: {dto.WorkflowId}, stepId: {dto.StepId}", "未找到对应节点");
-        if (currentStep.Status is EWorkflowStepStatus.Handled)
-            throw new UserFriendlyException("该状态不支持继续办理");
-
-        var currentStepDefine = _workflowDomainService.GetStepDefine(workflow.WorkflowDefinition, currentStep.Code);
-
-        //下一节点是否为动态节点
-        var isNextDynamic = currentStepDefine.InstanceMode is EInstanceMode.Dynamic &&
-                            !_workflowDomainService.DynamicShouldTerminal(currentStepDefine, _sessionContextProvider.SessionContext.OrgLevel);
-
-        StepDefine nextStepDefine;
-        if (isNextDynamic
-            || (currentStep.IsInCountersign() && !currentStep.IsTopCountersignEndStep(workflow.TopCountersignStepId))
-            || dto.IsStartCountersign)
-        {
-            //下一步配置为当前节点配置
-            nextStepDefine = currentStepDefine;
-        }
-        else
-        {
-            //下一步配置为下一步节点配置
-            nextStepDefine = _workflowDomainService.GetStepDefine(workflow.WorkflowDefinition, dto.NextStepCode);
-        }
-
-        //需求:按角色选择办理人可以不选,表示该角色下所有人都可以办理,同时依据配置:是否本部门人办理显示待选办理人。角色下只要一人办理即可(即:角色下不发起会签)
-        if (nextStepDefine.HandlerType != EHandlerType.Role && !dto.NextHandlers.Any())
-            throw new UserFriendlyException("未指定节点处理者");
-
-        if (dto.IsStartCountersign)
-        {
-            if (!currentStepDefine.CanStartCountersign)
-                throw new UserFriendlyException("当前节点不支持发起会签");
-            //if (currentStepDefine.HandlerType is EHandlerType.Role)
-            //    throw new UserFriendlyException("当前节点不支持发起会签");
-            //即使当前节点支持发起会签,但下一节点为信息汇总节点、结束节点时也不可发起会签
-            if (nextStepDefine.StepType is EStepType.Summary or EStepType.End)
-                throw new UserFriendlyException("下一节点不允许发起会签");
-            //下一节点是会签汇总节点也不允许发起会签
-            if (dto.BackToCountersignEnd)
-                throw new UserFriendlyException("下一节点不允许发起会签");
-        }
-
-        var flowAssignInfo =
-            await GetNextStepFlowAssignInfoAsync(workflow, currentStep, dto, nextStepDefine, isNextDynamic, cancellationToken);
-
-        //var nextStepHandlers = await GetNextStepHandlersAsync(workflow, nextStepDefine, dto, cancellationToken);
-
-        await _workflowDomainService.NextAsync(workflow, currentStep, dto, nextStepDefine, isNextDynamic,
-            flowAssignInfo, expiredTime, cancellationToken);
-
-        return workflow;
-    }
+    ///// <summary>
+    ///// 流转至下一节点(节点办理)
+    ///// </summary>
+    //public async Task<Workflow> NextAsync(NextWorkflowDto dto, DateTime? expiredTime = null, CancellationToken cancellationToken = default)
+    //{
+    //    var validator = new NextWorkflowDtoValidator();
+    //    var validResult = await validator.ValidateAsync(dto, cancellationToken);
+    //    if (!validResult.IsValid)
+    //        throw new UserFriendlyException(
+    //            $"非法参数, {string.Join(',', validResult.Errors.Select(d => d.ErrorMessage))}");
+    //    var workflow = await _workflowDomainService.GetWorkflowAsync(dto.WorkflowId, withDefine: true, withSteps: true,
+    //        withTraces: true, withCountersigns: true, cancellationToken: cancellationToken);
+
+    //    //var currentStep = _workflowDomainService.FindCurrentStepWaitForHandle(workflow,
+    //    //    current.RequiredUserId, current.RequiredOrgId, current.Roles);
+    //    var currentStep = workflow.Steps.FirstOrDefault(d => d.Id == dto.StepId);
+    //    if (currentStep == null)
+    //        throw new UserFriendlyException(
+    //            $"未找到对应节点, workflowId: {dto.WorkflowId}, stepId: {dto.StepId}", "未找到对应节点");
+    //    if (currentStep.Status is EWorkflowStepStatus.Handled)
+    //        throw new UserFriendlyException("该状态不支持继续办理");
+
+    //    var currentStepDefine = _workflowDomainService.GetStepDefine(workflow.WorkflowDefinition, currentStep.Code);
+
+    //    //下一节点是否为动态节点
+    //    var isNextDynamic = currentStepDefine.InstanceMode is EInstanceMode.Dynamic &&
+    //                        !_workflowDomainService.DynamicShouldTerminal(currentStepDefine, _sessionContextProvider.SessionContext.OrgLevel);
+
+    //    StepDefine nextStepDefine;
+    //    if (isNextDynamic
+    //        || (currentStep.IsInCountersign() && !currentStep.IsTopCountersignEndStep(workflow.TopCountersignStepId))
+    //        || dto.IsStartCountersign)
+    //    {
+    //        //下一步配置为当前节点配置
+    //        nextStepDefine = currentStepDefine;
+    //    }
+    //    else
+    //    {
+    //        //下一步配置为下一步节点配置
+    //        nextStepDefine = _workflowDomainService.GetStepDefine(workflow.WorkflowDefinition, dto.NextStepCode);
+    //    }
+
+    //    //需求:按角色选择办理人可以不选,表示该角色下所有人都可以办理,同时依据配置:是否本部门人办理显示待选办理人。角色下只要一人办理即可(即:角色下不发起会签)
+    //    if (nextStepDefine.HandlerType != EHandlerType.Role && !dto.NextHandlers.Any())
+    //        throw new UserFriendlyException("未指定节点处理者");
+
+    //    if (dto.IsStartCountersign)
+    //    {
+    //        if (!currentStepDefine.CanStartCountersign)
+    //            throw new UserFriendlyException("当前节点不支持发起会签");
+    //        //if (currentStepDefine.HandlerType is EHandlerType.Role)
+    //        //    throw new UserFriendlyException("当前节点不支持发起会签");
+    //        //即使当前节点支持发起会签,但下一节点为信息汇总节点、结束节点时也不可发起会签
+    //        if (nextStepDefine.StepType is EStepType.Summary or EStepType.End)
+    //            throw new UserFriendlyException("下一节点不允许发起会签");
+    //        //下一节点是会签汇总节点也不允许发起会签
+    //        if (dto.BackToCountersignEnd)
+    //            throw new UserFriendlyException("下一节点不允许发起会签");
+    //    }
+
+    //    var flowAssignInfo =
+    //        await GetNextStepFlowAssignInfoAsync(workflow, currentStep, dto, nextStepDefine, isNextDynamic, cancellationToken);
+
+    //    //var nextStepHandlers = await GetNextStepHandlersAsync(workflow, nextStepDefine, dto, cancellationToken);
+
+    //    await _workflowDomainService.NextAsync(workflow, currentStep, dto, nextStepDefine, isNextDynamic,
+    //        flowAssignInfo, expiredTime, cancellationToken);
+
+    //    return workflow;
+    //}
 
     /// <summary>
     /// 退回(返回前一节点)
@@ -417,7 +410,7 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
     /// <summary>
     /// 无视流程模板配置直接将当前节点办理至结束节点
     /// </summary>
-    public async Task HandleToEndAsync(string workflowId, string opinion, List<FileDto> files,
+    public async Task HandleToEndAsync(string workflowId, string opinion, List<FileDto> files, DateTime? expiredTime = null,
         EReviewResult reviewResult = EReviewResult.Unknown, CancellationToken cancellationToken = default)
     {
         var workflow = await _workflowDomainService.GetWorkflowAsync(workflowId, withDefine: true, withSteps: true,
@@ -440,7 +433,8 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
             Files = files,
             StepId = unHandleStep.Id
         };
-        await NextAsync(dto, cancellationToken: cancellationToken);
+        //await NextAsync(dto, cancellationToken: cancellationToken);
+        await _workflowDomainService.NextAsync(_sessionContextProvider.SessionContext, dto, expiredTime: expiredTime, cancellationToken: cancellationToken);
     }
 
     /// <summary>
@@ -559,12 +553,12 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
                             dto.OrgIds.Contains(d.ParentId))
                 .ToListAsync(cancellationToken);
             nextStepOption.Items = orgs.Select(d => new FlowStepHandler
-                {
-                    Key = d.Id,
-                    Value = d.Name,
-                    OrgId = d.Id,
-                    OrgName = d.Name
-                })
+            {
+                Key = d.Id,
+                Value = d.Name,
+                OrgId = d.Id,
+                OrgName = d.Name
+            })
                 .ToList();
             nextStepOption.FlowDirection = _workflowDomainService.GetFlowDirection(dto.BusinessType, stepDefine.BusinessType);
             stepOptions.Add(nextStepOption);
@@ -621,7 +615,7 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
                 // // 宜宾需求:会签汇总节点展示会签办理节点办理意见
                 // var countersignHandleSteps = workflow.Steps.Where(d =>
                 //     d.CountersignId == currentStep.CountersignId &&
-                //     d.CountersignPosition is ECountersignPosition.Multi or ECountersignPosition.Single).ToList();
+                //     d.CountersignPosition is ECountersignPosition.Direct or ECountersignPosition.Single).ToList();
                 // var sb = new StringBuilder();
                 // foreach (var countersignHandleStep in countersignHandleSteps)
                 // {
@@ -634,7 +628,7 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
                 //当前待办节点为会签汇总节点时:检查是否为顶级会签汇总节点,t:按配置往下走,f:继续往上汇总,不需要重复往下指派
                 if (!currentStep.IsTopCountersignEndStep(workflow.TopCountersignStepId))
                 {
-                    var startCountersignStep = GetCsLoopStartStep(workflow, currentStep);
+                    var startCountersignStep = _workflowDomainService.GetCsLoopStartStep(workflow.Steps, currentStep);
 
                     ////查找当前节点对应会签开始节点的上级作为下一个cs汇总节点的汇总对象
                     //var startCountersignStep =
@@ -649,7 +643,9 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
                     var nextStepOption = await GetDynamicStepAsync(currentStep.CountersignPolicy.Value,
                         EStepType.Normal, currentStep.BusinessType, cancellationToken);
 
-                    dto.Steps = new List<NextStepOption> { nextStepOption, countersignEndOption };
+                    dto.Steps = new List<NextStepOption> { nextStepOption };
+                    if (countersignEndOption != null)
+                        dto.Steps.Add(countersignEndOption);
                     return dto;
                 }
             }
@@ -661,7 +657,9 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
                 var nextStepOption =
                     await GetDynamicStepAsync(currentStep.CountersignPolicy.Value,
                         EStepType.Normal, currentStep.BusinessType, cancellationToken);
-                dto.Steps = new List<NextStepOption> { nextStepOption, countersignEndOption };
+                dto.Steps = new List<NextStepOption> { nextStepOption };
+                if (countersignEndOption != null)
+                    dto.Steps.Add(countersignEndOption);
                 return dto;
             }
         }
@@ -684,19 +682,6 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
         return dto;
     }
 
-    private WorkflowStep GetCsLoopStartStep(Workflow workflow, WorkflowStep currentStep)
-    {
-        var startCountersignStep =
-            workflow.Steps.FirstOrDefault(d => d.Id == currentStep.CountersignStartStepId);
-        if (startCountersignStep is null)
-            throw new UserFriendlyException(
-                $"未查询到会签开始节点,workflowId: {workflow.Id}, startStepId: {currentStep.CountersignStartStepId}",
-                "未查询到会签开始节点,数据异常");
-        if (!startCountersignStep.IsCountersignEndStep)
-            return startCountersignStep;
-        return GetCsLoopStartStep(workflow, startCountersignStep);
-    }
-
     /// <summary>
     /// 查询撤回可选节点及办理对象
     /// </summary>
@@ -800,7 +785,9 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
             ? EFlowDirection.CenterToFile
             : EFlowDirection.OrgToFile;
 
-        await NextAsync(nextDto, cancellationToken: cancellationToken);
+        //await NextAsync(nextDto, cancellationToken: cancellationToken);
+        await _workflowDomainService.NextAsync(_sessionContextProvider.SessionContext, nextDto,
+            cancellationToken: cancellationToken);
     }
 
     //供开启流程调用
@@ -988,17 +975,17 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
                     var lowLevels = levels.Where(d => d > _sessionContextProvider.SessionContext.OrgLevel).ToList();
                     orgs1 = await query
                         .Where(d => (upLevels.Contains(d.Level) && d.Id.StartsWith(levelOneOrgId)) ||
-                                    (lowLevels.Contains(d.Level) && d.Id.Contains(orgId)))
+                                    (lowLevels.Contains(d.Level) && d.Id.StartsWith(orgId)))
                         .ToListAsync(cancellationToken);
                 }
 
                 handlers = orgs1.Select(d => new FlowStepHandler
-                    {
-                        Key = d.Id,
-                        Value = d.Name,
-                        OrgId = d.Id,
-                        OrgName = d.Name
-                    })
+                {
+                    Key = d.Id,
+                    Value = d.Name,
+                    OrgId = d.Id,
+                    OrgName = d.Name
+                })
                     .ToList();
                 break;
             case EHandlerType.OrgType:
@@ -1010,12 +997,12 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
                     .ToListAsync(cancellationToken);
 
                 handlers = orgs2.Select(d => new FlowStepHandler
-                    {
-                        Key = d.Id,
-                        Value = d.Name,
-                        OrgId = d.Id,
-                        OrgName = d.Name
-                    })
+                {
+                    Key = d.Id,
+                    Value = d.Name,
+                    OrgId = d.Id,
+                    OrgName = d.Name
+                })
                     .ToList();
                 break;
             default:
@@ -1036,11 +1023,12 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
         };
     }
 
-    private NextStepOption GetCsEndStepByTargetPrev(List<WorkflowStep> steps, WorkflowStep step)
+    private NextStepOption? GetCsEndStepByTargetPrev(List<WorkflowStep> steps, WorkflowStep step)
     {
         var prevStep = steps.FirstOrDefault(d => d.Id == step.PrevStepId);
-        if (prevStep is null)
-            throw new UserFriendlyException("未查找到会签上级节点");
+        //归档以后特提发起会签场景
+        if (prevStep is null || !prevStep.IsStartCountersign) return null;
+
         var text = prevStep.HandlerOrgIsCenter.Value
             ? "热线中心会签汇总"
             : $"{prevStep.HandlerOrgId.CalcOrgLevel().ToChinese()}级部门会签汇总";
@@ -1639,7 +1627,7 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
                 {
                     if (dto.BackToCountersignEnd)
                     {
-                        var csStartStep = GetCsLoopStartStep(workflow, currentStep);
+                        var csStartStep = _workflowDomainService.GetCsLoopStartStep(workflow.Steps, currentStep);
                         var prevStep = workflow.Steps.FirstOrDefault(d => d.Id == csStartStep.PrevStepId);
                         if (prevStep is null)
                             throw new UserFriendlyException("未查询到目标节点的前一节点");
@@ -1729,6 +1717,76 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
         }
     }
 
+    public ISugarQueryable<WorkflowCountersign, Workflow, Order> QueryOrderCountersigns(QueryOrderCountersignDto dto, ISessionContext _sessionContext)
+    {
+        var Role = _sessionContext.Roles;
+
+        var query = _workflowCountersignRepository.Queryable()
+            .Includes(x => x.Members)
+            .LeftJoin<Workflow>((c, w) => c.WorkflowId == w.Id)
+            .InnerJoin<Order>((c, w, o) => w.ExternalId == o.Id)
+            .WhereIF(!string.IsNullOrEmpty(dto.Title), (c, w, o) => o.Title.Contains(dto.Title))
+            .WhereIF(!string.IsNullOrEmpty(dto.OrderNo), (c, w, o) => o.No == dto.OrderNo)
+            .WhereIF(!string.IsNullOrEmpty(dto.AcceptType), (c, w, o) => o.AcceptTypeCode == dto.AcceptType)//受理类型
+            .WhereIF(!string.IsNullOrEmpty(dto.Channel), (c, w, o) => o.SourceChannelCode == dto.Channel)//受理类型
+            .WhereIF(!string.IsNullOrEmpty(dto.Hotspot), (c, w, o) => o.HotspotSpliceName != null && o.HotspotSpliceName.Contains(dto.Hotspot))//热点类型
+            .WhereIF(!string.IsNullOrEmpty(dto.OrgId), (c, w, o) => c.FinisherOrgId == dto.OrgId) //接办部门
+            .WhereIF(dto.CounterSignType != null, (c, w, o) => c.CounterSignType == dto.CounterSignType) //会签类型
+        ;
+
+        var rolePaiDan = _systemSettingCacheManager.GetSetting(SettingConstants.RolePaiDan)?.SettingValue[0];//派单员角色
+        var seatsMonitor = _systemSettingCacheManager.GetSetting(SettingConstants.SeatsMonitor)?.SettingValue[0];//班长角色
+        var isAdmin = false;
+        var systemAdministrator = _systemSettingCacheManager.GetSetting(SettingConstants.SystemAdministrator)?.SettingValue[0];//管理员角色
+        if (!string.IsNullOrEmpty(systemAdministrator) && (_sessionContext.Roles.Contains(systemAdministrator) || _sessionContext.Roles.Contains(RoleSeedData.AdminRole)))
+            isAdmin = true;
+
+        //发起会签:班长角色能看所有会签信件;新增管理员能看到所有会签
+        //派单员角色只能看到自己发起的会签信件;
+        //承办部门用户能看到自己发起的和同级部门用户发起的会签件
+        if (dto.InitiatedCountersignature.HasValue && dto.InitiatedCountersignature == true)
+        {
+            if (_sessionContext.Roles.Any(p => p == seatsMonitor) || isAdmin)
+            {
+
+            }
+            else
+            if (_sessionContext.Roles.Any(p => p == rolePaiDan))
+            {
+                query = query.Where((c, w, o) => c.StarterId == _sessionContext.UserId);
+            }
+            else
+            {
+                query = query.Where((c, w, o) => c.StarterOrgId == _sessionContext.RequiredOrgId);
+            }
+        }
+
+        //会签已办(会签状态为已结束):
+        //班长角色能看所有已结束的会签信件;新增管理员能看到所有会签
+        //派单员不会办理会签件只会发起会签件所以这一点派单员角色可以忽略;
+        //承办部门用户能看到和同级部门用户已办理过的会签件
+        if (dto.HandleCountersignature.HasValue && dto.HandleCountersignature == true)
+        {
+            if (_sessionContext.Roles.Any(p => p == seatsMonitor) || isAdmin)
+            {
+                query = query.Where((c, w, o) => c.EndTime.HasValue);
+            }
+            else
+           if (_sessionContext.Roles.Any(p => p == rolePaiDan))
+            {
+                query = query.Where((c, w, o) => c.Members.Any(m => m.Key == _sessionContext.RequiredOrgId) && c.EndTime.HasValue);
+            }
+            else
+            {
+                query = query.Where((c, w, o) => c.Members.Any(m => m.Key == _sessionContext.OrgId || m.Key == _sessionContext.RequiredOrgId) && c.EndTime.HasValue);
+            }
+        }
+
+        var items = query
+            .OrderByDescending((c, w, o) => o.ExpiredTime);
+        return items;
+    }
+
 
     #region private
 

+ 11 - 0
src/Hotline.Application/Handlers/FlowEngine/WorkflowEndHandler.cs

@@ -17,7 +17,9 @@ using Hotline.Settings.TimeLimits;
 using Hotline.Share.Dtos.FlowEngine.Workflow;
 using Hotline.Share.Dtos.Order;
 using Hotline.Share.Dtos.TrCallCenter;
+using Hotline.Share.Enums.CallCenter;
 using Hotline.Share.Enums.Order;
+using Hotline.Share.Tools;
 using MapsterMapper;
 using MediatR;
 using Microsoft.Extensions.Logging;
@@ -212,7 +214,16 @@ public class WorkflowEndHandler : INotificationHandler<EndWorkflowNotify>
                         {
                             var call = await _callApplication.GetCallAsync(order.CallId, cancellationToken);
                             if (call is not null)
+                            {
                                 orderFlowDto.TrCallRecordDto = _mapper.Map<TrCallDto>(call);
+
+                                // 工单开始办理如果获取的通话记录是呼出并且录音文件是空就不推送通话记录
+                                // 如果 通话记录是呼入, 并且没有录音文件
+                                if (_systemSettingCacheManager.OrderStartHandlerPushCallIsNull && call.Direction == ECallDirection.Out && call.AudioFile.IsNullOrEmpty())
+                                {
+                                    orderFlowDto.TrCallRecordDto = null;
+                                }
+                            }
                         }
                     }
 

+ 20 - 9
src/Hotline.Application/Handlers/FlowEngine/WorkflowStartHandler.cs

@@ -13,9 +13,11 @@ using Hotline.Settings;
 using Hotline.Share.Dtos.FlowEngine.Workflow;
 using Hotline.Share.Dtos.Order;
 using Hotline.Share.Dtos.TrCallCenter;
+using Hotline.Share.Enums.CallCenter;
 using Hotline.Share.Enums.Push;
 using Hotline.Share.Enums.Quality;
 using Hotline.Share.Mq;
+using Hotline.Share.Tools;
 using Hotline.Users;
 using MapsterMapper;
 using MediatR;
@@ -44,7 +46,7 @@ namespace Hotline.Application.Handlers.FlowEngine
         private readonly ISystemSettingCacheManager _systemSettingCacheManager;
         private readonly IRepository<OrderTerminate> _orderTerminateRepository;
 
-		public WorkflowStartHandler(
+        public WorkflowStartHandler(
             IOrderDomainService orderDomainService,
             IKnowledgeDomainService knowledgeDomainService,
             IOrderRepository orderRepository,
@@ -59,7 +61,7 @@ namespace Hotline.Application.Handlers.FlowEngine
             IMediator mediator,
             ISystemSettingCacheManager systemSettingCacheManager,
             IRepository<OrderTerminate> orderTerminateRepository
-		)
+        )
         {
             _orderDomainService = orderDomainService;
             _knowledgeDomainService = knowledgeDomainService;
@@ -76,7 +78,7 @@ namespace Hotline.Application.Handlers.FlowEngine
             _mediator = mediator;
             _systemSettingCacheManager = systemSettingCacheManager;
             _orderTerminateRepository = orderTerminateRepository;
-		}
+        }
 
         /// <summary>Handles a notification</summary>
         /// <param name="notification">The notification</param>
@@ -128,7 +130,16 @@ namespace Hotline.Application.Handlers.FlowEngine
                             {
                                 var call = await _callApplication.GetCallAsync(order?.CallId, cancellationToken);
                                 if (call is not null)
+                                {
                                     publishCallRecordDto.TrCallRecordDto = _mapper.Map<TrCallDto>(call);
+
+                                    // 工单开始办理如果获取的通话记录是呼出并且录音文件是空就不推送通话记录
+                                    // 如果 通话记录是呼入, 并且没有录音文件
+                                    if (_systemSettingCacheManager.OrderStartHandlerPushCallIsNull && call.Direction == ECallDirection.Out && call.AudioFile.IsNullOrEmpty())
+                                    {
+                                        publishCallRecordDto.TrCallRecordDto = null;
+                                    }
+                                }
                             }
                         }
 
@@ -216,7 +227,7 @@ namespace Hotline.Application.Handlers.FlowEngine
                     case WorkflowModuleConsts.KnowledgeUpdate:
                     case WorkflowModuleConsts.KnowledgeDelete:
                     case WorkflowModuleConsts.KnowledgeOffshelf:
-						await _knowledgeDomainService.UpdateWorkFlowId(workflow.ExternalId, workflow.Id,
+                        await _knowledgeDomainService.UpdateWorkFlowId(workflow.ExternalId, workflow.Id,
                             workflow.HandlerUsers, workflow.HandlerOrgs, workflow.FlowedUserIds, workflow.FlowedOrgIds,
                             cancellationToken);
                         break;
@@ -244,15 +255,15 @@ namespace Hotline.Application.Handlers.FlowEngine
                         break;
                     case WorkflowModuleConsts.OrderTerminate:
                         var orderTerminate = await _orderTerminateRepository.Queryable()
-	                        .Where(x => x.Id == workflow.ExternalId).FirstAsync(cancellationToken);
+                            .Where(x => x.Id == workflow.ExternalId).FirstAsync(cancellationToken);
                         if (orderTerminate != null)
                         {
-	                        orderTerminate.WorkflowId = workflow.Id;
-							orderTerminate.Status = ETerminateStatus.Approval;
+                            orderTerminate.WorkflowId = workflow.Id;
+                            orderTerminate.Status = ETerminateStatus.Approval;
                             await _orderTerminateRepository.UpdateAsync(orderTerminate, cancellationToken);
-						}
+                        }
                         break;
-				}
+                }
 
             }
             catch (Exception e)

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

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

+ 10 - 0
src/Hotline.Application/Identity/IIdentityAppService.cs

@@ -4,13 +4,23 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 using Hotline.Settings;
+using Fw.Utility.UnifyResponse;
 using Hotline.Share.Dtos.Identity;
 using Hotline.Users;
+using Hotline.Share.Dtos.Snapshot;
 
 namespace Hotline.Application.Identity
 {
     public interface IIdentityAppService
     {
+        /// <summary>
+        /// 获取三方令牌
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        /// <exception cref="UserFriendlyException"></exception>
+        Task<TokenOutDto> GetThredTokenAsync(ThirdTokenInDto dto);
+
         Task<string> LoginAsync(LoginDto dto, CancellationToken cancellationToken);
 
         Task<string> OldToNewLoginAsync(HotlineLoginOldToNewDto dto, CancellationToken cancellationToken);

+ 76 - 15
src/Hotline.Application/Identity/IdentityAppService.cs

@@ -1,5 +1,7 @@
 using System.Security.Claims;
+using Fw.Utility.UnifyResponse;
 using Hotline.Caching.Interfaces;
+using Hotline.Caching.Services;
 using Hotline.Identity;
 using Hotline.Identity.Accounts;
 using Hotline.Orders;
@@ -9,16 +11,20 @@ using Hotline.SeedData;
 using Hotline.Settings;
 using Hotline.Share.Dtos.FlowEngine;
 using Hotline.Share.Dtos.Identity;
+using Hotline.Share.Dtos.Snapshot;
 using Hotline.Share.Enums.FlowEngine;
 using Hotline.Share.Enums.Identity;
+using Hotline.Share.Enums.Snapshot;
+using Hotline.Share.Enums.User;
+using Hotline.Share.Tools;
+using Hotline.Snapshot;
 using Hotline.Users;
 using IdentityModel;
-using Microsoft.AspNetCore.Http;
+using Mapster;
 using Microsoft.AspNetCore.Identity;
 using Microsoft.Extensions.Options;
 using XF.Domain.Authentications;
 using XF.Domain.Cache;
-using XF.Domain.Constants;
 using XF.Domain.Dependency;
 using XF.Domain.Exceptions;
 using XF.Domain.Options;
@@ -29,6 +35,8 @@ namespace Hotline.Application.Identity;
 public class IdentityAppService : IIdentityAppService, IScopeDependency
 {
     private readonly IAccountRepository _accountRepository;
+    private readonly IRepository<Citizen> _citizenRepository;
+    private readonly ISessionContext _sessionContext;
     private readonly IAccountDomainService _accountDomainService;
     private readonly IRepository<User> _userRepository;
     private readonly IJwtSecurity _jwtSecurity;
@@ -38,8 +46,11 @@ public class IdentityAppService : IIdentityAppService, IScopeDependency
     private readonly IRepository<Scheduling> _schedulingRepository;
     private readonly IOrderDomainService _orderDomainService;
     private readonly ISystemSettingCacheManager _systemSettingCacheManager;
+    private readonly IThirdIdentiyService _thirdIdentiyService;
+    private readonly IThirdAccountRepository _thirdAccountRepository;
+    private readonly IRepository<GuiderInfo> _guiderInfoRepository;
 
-	public IdentityAppService(
+    public IdentityAppService(
         IAccountRepository accountRepository,
         IAccountDomainService accountDomainService,
         IRepository<User> userRepository,
@@ -49,7 +60,12 @@ public class IdentityAppService : IIdentityAppService, IScopeDependency
         IMessageCodeDomainService messageCodeDomainService,
         IRepository<Scheduling> schedulingRepository,
         IOrderDomainService orderDomainService,
-        ISystemSettingCacheManager systemSettingCacheManager)
+        ISystemSettingCacheManager systemSettingCacheManager,
+        IThirdIdentiyService thirdIdentiyService,
+        IThirdAccountRepository thirdAccountRepository,
+        ISessionContext sessionContext,
+        IRepository<Citizen> citizenRepository,
+        IRepository<GuiderInfo> guiderInfoRepository)
     {
         _accountRepository = accountRepository;
         _accountDomainService = accountDomainService;
@@ -61,8 +77,12 @@ public class IdentityAppService : IIdentityAppService, IScopeDependency
         _schedulingRepository = schedulingRepository;
         _orderDomainService = orderDomainService;
         _systemSettingCacheManager = systemSettingCacheManager;
-
-	}
+        _thirdIdentiyService = thirdIdentiyService;
+        _thirdAccountRepository = thirdAccountRepository;
+        _sessionContext = sessionContext;
+        _citizenRepository = citizenRepository;
+        _guiderInfoRepository = guiderInfoRepository;
+    }
 
     public async Task<string> OldToNewLoginAsync(HotlineLoginOldToNewDto dto, CancellationToken cancellationToken)
     {
@@ -214,12 +234,12 @@ public class IdentityAppService : IIdentityAppService, IScopeDependency
             .FirstAsync(d => d.Id == account.Id);
         if (user == null)
             throw UserFriendlyException.SameMessage("未查询到用户数据");
-		//平均派单
-		var averageSendOrder = bool.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.AverageSendOrder).SettingValue[0]);
-		if (averageSendOrder)
-		{
-			await AverageOrderScheduling(account.Id, cancellationToken);
-		}
+        //平均派单
+        var averageSendOrder = bool.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.AverageSendOrder).SettingValue[0]);
+        if (averageSendOrder)
+        {
+            await AverageOrderScheduling(account.Id, cancellationToken);
+        }
         var jwtOptions = _identityOptionsAccessor.Value.Jwt;
         var claims = new List<Claim>
         {
@@ -259,13 +279,13 @@ public class IdentityAppService : IIdentityAppService, IScopeDependency
         try
         {
             DateTime time = DateTime.Parse(DateTime.Now.ToString("yyyy-MM-dd"));
-            
+
             //&& x.AtWork!.Value != true
             //根据当前时间获取排班信息
             var scheduling = await _schedulingRepository.Queryable()
                 .Includes(x => x.SchedulingUser)
                 .Where(x => x.SchedulingTime == time &&
-                            (x.AtWork == true || x.AtWork == null) && 
+                            (x.AtWork == true || x.AtWork == null) &&
                             x.SchedulingUser.UserId == id)
                 .OrderBy(x => x.SendOrderNum).FirstAsync(cancellationToken);
             if (scheduling != null)
@@ -280,4 +300,45 @@ public class IdentityAppService : IIdentityAppService, IScopeDependency
             // ignored
         }
     }
-}
+
+    /// <summary>
+    /// 获取三方令牌
+    /// </summary>
+    /// <param name="dto"></param>
+    /// <returns></returns>
+    /// <exception cref="UserFriendlyException"></exception>
+    public async Task<TokenOutDto> GetThredTokenAsync(ThirdTokenInDto dto)
+    {
+        var thirdDto = dto.Adapt<ThirdTokenDto>();
+        if (dto.ThirdType == EThirdType.WeChat)
+        {
+            thirdDto.AppId = _systemSettingCacheManager.WxOpenAppId;
+            thirdDto.Secret = _systemSettingCacheManager.WxOpenAppSecret;
+        }
+        var thirdToken = await _thirdIdentiyService.GetTokenAsync(thirdDto);
+        var phone = await _thirdIdentiyService.GetPhoneNumberAsync(thirdDto);
+        var thirdAccount = await _thirdAccountRepository.QueryByOpenIdAsync(thirdToken.OpenId);
+
+        // 新用户注册
+        if (thirdAccount is null)
+        {
+            thirdAccount = thirdToken.Adapt<ThirdAccount>();
+            thirdAccount.PhoneNumber = phone.PhoneNumber;
+            thirdAccount.Id = await _thirdAccountRepository.AddAsync(thirdAccount);
+        }
+
+        var jwtOptions = _identityOptionsAccessor.Value.Jwt;
+        var claims = new List<Claim>
+        {
+            new(JwtClaimTypes.Subject, thirdAccount.Id),
+            new(JwtClaimTypes.PhoneNumber, thirdAccount.PhoneNumber ?? string.Empty),
+            new(JwtClaimTypes.Scope, jwtOptions.Scope),
+            new(AppClaimTypes.OpenId, thirdAccount.OpenId),
+        };
+        var audience = new AudienceTicket(thirdAccount.Id);
+        var expiredSeconds = jwtOptions.Expired <= 0 ? 3600 : jwtOptions.Expired;
+        await _cacheAudience.SetAsync(audience.Id, audience, TimeSpan.FromSeconds(expiredSeconds));
+        var token = _jwtSecurity.EncodeJwtToken(claims, audience.Ticket);
+        return new TokenOutDto(thirdAccount.CitizenType, token);
+    }
+  }

+ 46 - 6
src/Hotline.Application/Knowledge/KnowApplication.cs

@@ -63,6 +63,10 @@ namespace Hotline.Application.Knowledge
         /// <returns></returns>
         public async Task<(int, IList<KnowledgeDataDto>)> GetKnowList(KnowPagedListDto pagedDto, CancellationToken cancellationToken)
         {
+	        if (!_sessionContext.OrgIsCenter)
+	        {
+		        pagedDto.Attribution = "部门知识库";
+	        }
             var typeSpliceName = string.Empty;
             var hotspotHotSpotFullName = string.Empty;
             if (!string.IsNullOrEmpty(pagedDto.KnowledgeTypeId))
@@ -87,17 +91,18 @@ namespace Hotline.Application.Knowledge
                 .Where(x => (x.Status == EKnowledgeStatus.Drafts && x.CreatorId == _sessionContext.UserId) || (x.Status != EKnowledgeStatus.Drafts))
                 .WhereIF(!string.IsNullOrEmpty(pagedDto.Title), x => x.Title.Contains(pagedDto.Title!))
                 .WhereIF(!string.IsNullOrEmpty(pagedDto.Keyword), x => x.Title.Contains(pagedDto.Keyword!) || x.CreatorName.Contains(pagedDto.Keyword!) || x.CreatorOrgName.Contains(pagedDto.Keyword!) || x.SourceOrganize.Name.Contains(pagedDto.Keyword!))
-                .WhereIF(pagedDto.Status.HasValue && pagedDto.Status != EKnowledgeStatus.OffShelf && pagedDto.Status != EKnowledgeStatus.NewDrafts, x => x.Status == pagedDto.Status && ((x.ExpiredTime != null && x.ExpiredTime > DateTime.Now) || x.ExpiredTime == null))
-                .WhereIF(pagedDto.Status.HasValue && pagedDto.Status == EKnowledgeStatus.OffShelf, x => x.Status == pagedDto.Status || (x.ExpiredTime != null && x.ExpiredTime < DateTime.Now && x.Status != EKnowledgeStatus.Drafts))
+                .WhereIF(pagedDto.Status.HasValue && pagedDto.Status != EKnowledgeStatus.OffShelf && pagedDto.Status != EKnowledgeStatus.NewDrafts && pagedDto.Status != EKnowledgeStatus.All, x => x.Status == pagedDto.Status && ((x.ExpiredTime != null && x.ExpiredTime > DateTime.Now) || x.ExpiredTime == null))
+                .WhereIF(pagedDto.Status.HasValue && pagedDto.Status == EKnowledgeStatus.OffShelf , x => x.Status == pagedDto.Status || (x.ExpiredTime != null && x.ExpiredTime < DateTime.Now && x.Status != EKnowledgeStatus.Drafts))
                 .WhereIF(pagedDto.IsPublic.HasValue, x => x.IsPublic == pagedDto.IsPublic)
                 .WhereIF(!string.IsNullOrEmpty(pagedDto.Summary), x => x.Summary != null && x.Summary.Contains(pagedDto.Summary!))
                 .WhereIF(!string.IsNullOrEmpty(typeSpliceName), x => x.KnowledgeType.Any(t => t.KnowledgeTypeSpliceName.StartsWith(typeSpliceName)))
                 .WhereIF(!string.IsNullOrEmpty(hotspotHotSpotFullName), x => x.HotspotType.HotSpotFullName.EndsWith(hotspotHotSpotFullName!))
-                .WhereIF(!string.IsNullOrEmpty(pagedDto.CreateOrgId), x => x.SourceOrganizeId != null && x.SourceOrganizeId.EndsWith(pagedDto.CreateOrgId!))
+                .WhereIF(!string.IsNullOrEmpty(pagedDto.CreateOrgId), x => x.CreatorOrgId != null && x.CreatorOrgId.StartsWith(pagedDto.CreateOrgId!))
                 .WhereIF(!string.IsNullOrEmpty(pagedDto.ModuleCode), x => x.Workflow.ModuleCode == pagedDto.ModuleCode)
                 .WhereIF(pagedDto.Status ==  EKnowledgeStatus.NewDrafts ,x=>x.Status == EKnowledgeStatus.Drafts || x.Status == EKnowledgeStatus.Revert)
                 .WhereIF(pagedDto.NewDraftsStatus is EKnowledgeStatus.Drafts , x=>x.Status == EKnowledgeStatus.Drafts)
                 .WhereIF(pagedDto.NewDraftsStatus is EKnowledgeStatus.Revert, x => x.Status == EKnowledgeStatus.Revert)
+                .WhereIF(!string.IsNullOrEmpty(pagedDto.Attribution),x=>x.Attribution == pagedDto.Attribution)
 				.OrderByDescending(d => d.CreationTime)
                 .ToPagedListAsync(pagedDto.PageIndex, pagedDto.PageSize, cancellationToken);
             //返回数据
@@ -161,6 +166,21 @@ namespace Hotline.Application.Knowledge
             return new PagedDto<KnowledgeApplyHandlePageDto>(total, _mapper.Map<IReadOnlyList<KnowledgeApplyHandlePageDto>>(items));
         }
 
+        //public async Task<Dictionary<string, Stream>> KnowledgeInfoListExportAsync(KnowledgeInfoExportInDto dto, CancellationToken cancellationToken)
+        //{
+        //    var streamList = new Dictionary<string, Stream>();
+        //    var knowList = await _knowledgeRepository.Queryable()
+        //        .Where(m => dto.Ids.Contains(m.Id))
+        //        .Select(m => new { m.Title, m.Content })
+        //        .ToListAsync(cancellationToken);
+        //    foreach (var item in knowList)
+        //    {
+        //        Stream stream = item.Content.HtmlToStream(dto.FileType);
+        //        streamList.Add(item.Title + dto.FileType.GetFileExtension(), stream);
+        //    }
+        //    return streamList;
+        //}
+
         public async Task<Dictionary<string, Stream>> KnowledgeInfoListExportAsync(KnowledgeInfoExportInDto dto, CancellationToken cancellationToken)
         {
             var streamList = new Dictionary<string, Stream>();
@@ -168,11 +188,26 @@ namespace Hotline.Application.Knowledge
                 .Where(m => dto.Ids.Contains(m.Id))
                 .Select(m => new { m.Title, m.Content })
                 .ToListAsync(cancellationToken);
-            foreach (var item in knowList)
+
+            var tasks = knowList.Select(async item =>
+            {
+                var stream = await Task.Run(() => item.Content.HtmlToStream(dto.FileType), cancellationToken);
+                return new KeyValuePair<string, Stream>(
+                    item.Title + dto.FileType.GetFileExtension(),
+                    stream
+                );
+            });
+
+            var results = await Task.WhenAll(tasks);
+
+            foreach (var kvp in results)
             {
-                Stream stream = item.Content.HtmlToStream(dto.FileType);
-                streamList.Add(item.Title + dto.FileType.GetFileExtension(), stream);
+                if (!streamList.ContainsKey(kvp.Key))
+                {
+                    streamList.Add(kvp.Key, kvp.Value);
+                }
             }
+
             return streamList;
         }
 
@@ -283,6 +318,11 @@ namespace Hotline.Application.Knowledge
                 hotspotHotSpotFullName = hotspot?.HotSpotFullName;
             }
 
+            if (!_sessionContext.OrgIsCenter)
+            {
+	            dto.Attribution = "部门知识库";
+            }
+
             var sugar = _knowledgeRepository
                 .Queryable(false, false, false)
                 .Includes(x => x.User)

+ 8 - 1
src/Hotline.Application/Mappers/MapperConfigs.cs

@@ -15,7 +15,9 @@ using Hotline.Share.Dtos.Org;
 using Hotline.Share.Dtos.Push.FWMessage;
 using Hotline.Share.Dtos.Settings;
 using Hotline.Share.Dtos.TrCallCenter;
+using Hotline.Share.Dtos.Snapshot;
 using Hotline.Share.Enums.Order;
+using Hotline.Snapshot;
 using Mapster;
 using XF.Domain.Entities;
 
@@ -39,7 +41,8 @@ namespace Hotline.Application.Mappers
             config.ForType<SystemDicData, SystemDicDataOutDto>()
                 .Map(m => m.Id, n => n.Id)
                 .Map(m => m.DicDataName, n => n.DicDataName)
-                .Map(m => m.DicDataValue, n => n.DicDataValue);
+                .Map(m => m.DicDataValue, n => n.DicDataValue)
+                .Map(m => m.Children, n => n.Children);
 
             config.ForType<Tr.Sdk.Tels.QueryTelResponse, TelOutDto>()
                 .Map(m => m.TelPwd, x => x.Password)
@@ -64,6 +67,10 @@ namespace Hotline.Application.Mappers
             config.ForType<TimeLimitSetting, TimeConfig>()
                 .Map(d => d.Count, x => x.TimeValue);
 
+            config.ForType<ThirdAccount, SnapshotUserInfoOutDto>();
+            config.ForType<ThirdTokenOutDto, ThirdAccount>()
+                .Map(d => d.OpenId, m => m.OpenId);
+
             config.ForType<AddBlacklistDto, Blacklist>()
                 .Ignore(d => d.Expired)
                 .AfterMapping((s, t) => t.InitExpired());

+ 22 - 0
src/Hotline.Application/Mappers/SnapshotMapperConfigs.cs

@@ -0,0 +1,22 @@
+using Hotline.Share.Dtos.Snapshot;
+using Hotline.Snapshot;
+using Mapster;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Hotline.Application.Mappers;
+public class SnapshotMapperConfigs : IRegister
+{
+    public void Register(TypeAdapterConfig config)
+    {
+        config.ForType<AddIndustryDto, Industry>();
+        config.ForType<Hotline.File.File, IndustryFileDto>()
+            .Map(m => m.AdditionId, n => n.Additions);
+
+        config.ForType<IndustryFileDto, Hotline.File.File>()
+            .Map(m => m.Additions, n => n.AdditionId);
+    }
+}

+ 1 - 1
src/Hotline.Application/Mappers/WebPortalMapperConfigs.cs

@@ -75,7 +75,7 @@ namespace Hotline.Application.Mappers
             ;
 
             //门户网站写信
-            config.ForType<WebFlowAccept, AddOrderDto>()
+            config.ForType<WebFlowAcceptDto, AddOrderDto>()
             .Map(d => d.FromName, x => x.LKName)
             .Map(d => d.FromGender, x => x.Sex == "男" ? EGender.Male : x.Sex == "女" ? EGender.Female : EGender.Unknown)
             .Map(d => d.IsSecret, x => x.IsSecret == "1" ? true : false)

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

@@ -14,6 +14,7 @@ using Hotline.Share.Dtos.DataSharing.PusherHotlineDto;
 using Hotline.Share.Dtos.FlowEngine;
 using Hotline.Share.Dtos.FlowEngine.Workflow;
 using Hotline.Share.Dtos.Order;
+using Hotline.Share.Dtos.Order.Publish;
 using Hotline.Share.Enums.Order;
 using Hotline.Share.Enums.Settings;
 using Hotline.Share.Requests;
@@ -355,5 +356,7 @@ namespace Hotline.Application.Orders
         Task SpecialVerify(OrderSpecialAddDto dto, Order order, CancellationToken cancellationToken);
 
         Task EndCountersign(EndCountersignDto dto, CancellationToken cancellationToken);
-	}
+        ISugarQueryable<OrderPublish> GetPublishedOrder(PublishedPagedRequest dto);
+        ISugarQueryable<Order> GetPublishOrderList(QueryOrderPublishDto dto);
+    }
 }

+ 107 - 6
src/Hotline.Application/Orders/OrderApplication.cs

@@ -73,6 +73,7 @@ using System.Linq.Dynamic.Core;
 using System.Threading;
 using Hotline.Application.CallCenter;
 using Hotline.Repository.SqlSugar.Orders;
+using Hotline.Share.Dtos.Order.Publish;
 using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
 
 namespace Hotline.Application.Orders;
@@ -245,7 +246,11 @@ public class OrderApplication : IOrderApplication, IScopeDependency
         await _workflowDomainService.UpdateUnhandleExpiredTimeAsync(order.WorkflowId, expiredTimeConfig.ExpiredTime, cancellationToken);
 
         await _orderRepository.UpdateAsync(order, cancellationToken);
-    }
+
+        var orderDto = _mapper.Map<OrderDto>(order);
+        await _capPublisher.PublishAsync(Hotline.Share.Mq.EventNames.HotlineOrderExpiredTimeUpdate, orderDto,
+	        cancellationToken: cancellationToken);
+	}
 
     // /// <summary>
     // /// 新增工单办理流程记录
@@ -674,6 +679,86 @@ public class OrderApplication : IOrderApplication, IScopeDependency
         }
     }
 
+    /// <summary>
+    /// 获取待发布列表
+    /// </summary>
+    /// <param name="dto"></param>
+    /// <returns></returns>
+    public ISugarQueryable<Order> GetPublishOrderList(QueryOrderPublishDto dto)
+    {
+        return _orderRepository.Queryable()
+            //.Includes(d => d.OrderPublish)
+            .Where(x => x.Status == EOrderStatus.Filed)
+            .WhereIF(!string.IsNullOrEmpty(dto.Keyword), d => d.Title.StartsWith(dto.Keyword!))
+            .WhereIF(!string.IsNullOrEmpty(dto.No), d => d.No.Contains(dto.No))
+            .WhereIF(!string.IsNullOrEmpty(dto.Title), d => d.Title.Contains(dto.Title))
+            //.WhereIF(dto.PubState == EPubState.Pub, d => d.Status >= EOrderStatus.Published)
+            //.WhereIF(dto.PubState == EPubState.NoPub, d => d.Status == EOrderStatus.Filed)
+            .WhereIF(!string.IsNullOrEmpty(dto.Channel), d => d.SourceChannelCode == dto.Channel)
+            .WhereIF(!string.IsNullOrEmpty(dto.OrderTag), d => d.OrderTagCode == dto.OrderTag!) //工单标签
+            .WhereIF(!string.IsNullOrEmpty(dto.CenterToOrgHandlerName), d => d.CenterToOrgHandlerName == dto.CenterToOrgHandlerName!) //派单人
+            .WhereIF(!string.IsNullOrEmpty(dto.NameOrNo), d => d.AcceptorName == dto.NameOrNo! || d.AcceptorStaffNo == dto.NameOrNo!) //受理人/坐席
+            .WhereIF(!string.IsNullOrEmpty(dto.ActualHandleOrgName), d => d.ActualHandleOrgName.Contains(dto.ActualHandleOrgName)) //接办部门(综合查询模糊)
+            .WhereIF(!string.IsNullOrEmpty(dto.AcceptType), d => d.AcceptTypeCode == dto.AcceptType) //受理类型
+            .WhereIF(!string.IsNullOrEmpty(dto.Hotspot), d => d.HotspotSpliceName != null && d.HotspotSpliceName.Contains(dto.Hotspot))
+            .WhereIF(!string.IsNullOrEmpty(dto.OrgLevelOneName),d=>d.OrgLevelOneName.Contains(dto.OrgLevelOneName)) //一级部门
+            .WhereIF(!string.IsNullOrEmpty(dto.FromPhone), d => d.FromPhone == dto.FromPhone) //来电号码
+                                                                                              //.WhereIF(!string.IsNullOrEmpty(dto.PubMan),
+                                                                                              //    d => d.AcceptorName.Contains(dto.PubMan!) || d.AcceptorStaffNo.Contains(dto.PubMan!))
+                                                                                              //.WhereIF(dto.PubRange == EPublicState.Pub, d => d.OrderPublish.PublishState)
+                                                                                              //.WhereIF(dto.PubRange == EPublicState.NoPub, d => !d.OrderPublish.PublishState)
+            .WhereIF(dto.IsProvinceOrder.HasValue && dto.IsProvinceOrder == true, d => d.Source == ESource.ProvinceStraight)
+            .WhereIF(dto.IsProvinceOrder.HasValue && dto.IsProvinceOrder == false, d => d.Source != ESource.ProvinceStraight)
+            .WhereIF(dto.FiledType != null && dto.FiledType == FiledType.CenterFiled, d => d.ProcessType == EProcessType.Zhiban)
+            .WhereIF(dto.FiledType != null && dto.FiledType == FiledType.OrgFiled, d => d.ProcessType == EProcessType.Jiaoban)
+            .WhereIF(dto.IsCountersign != null && dto.IsCountersign == true, d => d.CounterSignType != null)
+            .WhereIF(dto.IsCountersign != null && dto.IsCountersign == false, d => d.CounterSignType == null)
+            .WhereIF(dto.AcceptTypes.Any(), d => dto.AcceptTypes.Contains(d.AcceptType))
+            .WhereIF(dto.HotspotIds.Any(), d => dto.HotspotIds.Contains(d.HotspotId))
+            //.WhereIF(dto.Resolve.HasValue, d => d.OrderPublish.Resolve == dto.Resolve)
+            .WhereIF(dto.CreationTimeStart.HasValue, d => d.CreationTime >= dto.CreationTimeStart)
+            .WhereIF(dto.CreationTimeEnd.HasValue, d => d.CreationTime <= dto.CreationTimeEnd)
+            .WhereIF(dto.FiledTimeStart.HasValue, d => d.FiledTime >= dto.FiledTimeStart)
+            .WhereIF(dto.FiledTimeEnd.HasValue, d => d.FiledTime <= dto.FiledTimeEnd)
+            .WhereIF(dto.QuerySelf.HasValue && dto.QuerySelf.Value, d => d.WaitForPublisherId == _sessionContext.RequiredUserId)
+            .WhereIF(dto.IsOverTime == true,
+                d => (d.ExpiredTime < DateTime.Now && d.Status < EOrderStatus.Filed) ||
+                     (d.ExpiredTime < d.ActualHandleTime && d.Status >= EOrderStatus.Filed)) //是 超期
+            .WhereIF(dto.IsOverTime == false,
+                d => (d.ExpiredTime > DateTime.Now && d.Status < EOrderStatus.Filed) ||
+                     (d.ExpiredTime > d.ActualHandleTime && d.Status >= EOrderStatus.Filed)) //否 超期
+            .OrderByDescending(d => d.FiledTime);
+    }
+
+    public ISugarQueryable<OrderPublish> GetPublishedOrder(PublishedPagedRequest dto)
+    {
+        return _orderPublishRepository.Queryable()
+            .Includes(x => x.Order)
+            .WhereIF(!string.IsNullOrEmpty(dto.Keyword), d => d.Order.Title.StartsWith(dto.Keyword!))
+            .WhereIF(!string.IsNullOrEmpty(dto.No), d => d.Order.No.Contains(dto.No!))
+            .WhereIF(!string.IsNullOrEmpty(dto.Title), d => d.Order.Title.Contains(dto.Title!))
+            .WhereIF(dto.CreationTimeStart.HasValue, d => d.Order.CreationTime >= dto.CreationTimeStart)
+            .WhereIF(dto.CreationTimeEnd.HasValue, d => d.Order.CreationTime <= dto.CreationTimeEnd)
+            .WhereIF(!string.IsNullOrEmpty(dto.FromPhone), d => d.Order.FromPhone == dto.FromPhone) //来电号码
+            .WhereIF(!string.IsNullOrEmpty(dto.ActualHandleOrgName),
+                d => d.Order.ActualHandleOrgName.Contains(dto.ActualHandleOrgName)) //接办部门(综合查询模糊)
+            .WhereIF(!string.IsNullOrEmpty(dto.AcceptType), d => d.Order.AcceptTypeCode == dto.AcceptType) //受理类型
+            .WhereIF(!string.IsNullOrEmpty(dto.Hotspot), d => d.Order.HotspotSpliceName != null && d.Order.HotspotSpliceName.Contains(dto.Hotspot))
+            .WhereIF(!string.IsNullOrEmpty(dto.PublishName), d => d.CreatorName.Contains(dto.PublishName!))
+            .WhereIF(!string.IsNullOrEmpty(dto.NameOrNo),
+                d => d.Order.AcceptorName == dto.NameOrNo! || d.Order.AcceptorStaffNo == dto.NameOrNo!) //受理人/坐席
+            .WhereIF(dto.StartTime.HasValue, d => d.CreationTime >= dto.StartTime)
+            .WhereIF(dto.EndTime.HasValue, d => d.CreationTime <= dto.EndTime)
+            .WhereIF(dto.Resolve.HasValue, x => x.Resolve == dto.Resolve)
+            .WhereIF(dto.IsOverTime == true,
+                d => (d.Order.ExpiredTime < DateTime.Now && d.Order.Status < EOrderStatus.Filed) ||
+                     (d.Order.ExpiredTime < d.Order.ActualHandleTime && d.Order.Status >= EOrderStatus.Filed)) //是 超期
+            .WhereIF(dto.IsOverTime == false,
+                d => (d.Order.ExpiredTime > DateTime.Now && d.Order.Status < EOrderStatus.Filed) ||
+                     (d.Order.ExpiredTime > d.Order.ActualHandleTime && d.Order.Status >= EOrderStatus.Filed)) //否 超期
+            .OrderByDescending(d => d.CreationTime);
+    }
+
     /// <summary>
     /// 发布量统计(按账号)
     /// </summary>
@@ -2531,7 +2616,7 @@ public class OrderApplication : IOrderApplication, IScopeDependency
             .WhereIF(!string.IsNullOrEmpty(dto.HotspotSpliceName), d => d.Order!.Hotspot.HotSpotFullName!.StartsWith(dto.HotspotSpliceName!))
             .WhereIF(!string.IsNullOrEmpty(dto.SourceChannel), d => d.Order!.SourceChannelCode! == dto.SourceChannel!)
             .WhereIF(!string.IsNullOrEmpty(dto.VisitOrgName), d => d.VisitDetail.VisitOrgName!.Contains(dto.VisitOrgName!))
-            .WhereIF(!string.IsNullOrEmpty(dto.CreatorOrgName), d => d.CreatorOrgName == dto.CreatorOrgName)
+            .WhereIF(!string.IsNullOrEmpty(dto.CreatorOrgName), d => d.CreatorOrgName!.Contains(dto.CreatorOrgName!))
             .WhereIF(!string.IsNullOrEmpty(dto.CreatorName), d => d.CreatorName == dto.CreatorName)
             .WhereIF(dto.IsProvince.HasValue, d => d.Order!.IsProvince == dto.IsProvince)
             .WhereIF(dto.IsSendBackApplyNum is true, d => d.SendBackApplyNum > 0)
@@ -2558,7 +2643,8 @@ public class OrderApplication : IOrderApplication, IScopeDependency
 
     public ISugarQueryable<OrderVisitDetail> MayScreenList(MayScreenListDto dto)
     {
-        var query = _orderVisitedDetailRepository.Queryable(false, true)
+	    dto.ScreenType = dto.ScreenType is null ? 0 : dto.ScreenType;
+	   var query = _orderVisitedDetailRepository.Queryable(false, true)
             .Includes(x => x.OrderVisit)
             .Includes(x => x.OrderVisit, y => y.Order)
             .Includes(x => x.OrderVisit, y => y.Employee)
@@ -2738,10 +2824,12 @@ public class OrderApplication : IOrderApplication, IScopeDependency
         if (order == null)
         {
             order = _mapper.Map<Order>(dto);
+            
             order.InitId();
             if (files != null && files.Any())
                 order.FileJson = await _fileRepository.AddFileAsync(files, order.Id, "", cancellationToken);
             await _orderDomainService.AddAsync(order, cancellationToken: cancellationToken);
+          
         }
         else
         {
@@ -2832,10 +2920,23 @@ public class OrderApplication : IOrderApplication, IScopeDependency
                 }
 
                 //await _workflowDomainService.RecallToStartStepAsync(order.WorkflowId, "省工单重派", current, order.Status >= EOrderStatus.Filed, cancellationToken);
-                var isPaiDan = await _workflowDomainService.RecallToCenterFirstToSendAsync(order.WorkflowId, "省工单重派", order.Status >= EOrderStatus.Filed,
+                var (isPaiDan, workflow) = await _workflowDomainService.RecallToCenterFirstToSendAsync(order.WorkflowId, "省工单重派", order.Status >= EOrderStatus.Filed,
                     order.ExpiredTime, nextHandler, cancellationToken);
                 order.FileEmpty();
 
+                order.CurrentStepName = workflow.CurrentStepName;
+                order.CurrentStepCode = workflow.CurrentStepCode;
+                order.CurrentStepId = workflow.CurrentStepId;
+                order.CurrentStepCreateTime = workflow.CurrentStepCreateTime;
+                order.CurrentHandleTime = workflow.CurrentHandleTime;
+                order.CurrentHandlerId = workflow.CurrentHandlerId;
+                order.CurrentHandlerName = workflow.CurrentHandlerName;
+                order.CurrentHandleOrgName = workflow.CurrentHandleOrgName;
+                order.CurrentHandleOrgId = workflow.CurrentHandleOrgId;
+                order.CurrentHandleOrgLevel = workflow.CurrentHandleOrgLevel;
+                order.CurrentHandleOrgAreaCode = workflow.CurrentHandleOrgAreaCode;
+                order.CurrentHandleOrgAreaName = workflow.CurrentHandleOrgAreaName;
+
                 order.Status = EOrderStatus.WaitForAccept;
                 if (isPaiDan)
                 {
@@ -3063,7 +3164,7 @@ public class OrderApplication : IOrderApplication, IScopeDependency
         handleStatuses = handleStatuses.WhereIF(dto.IsHandled.HasValue,
                 d => dto.IsHandled!.Value
                     ? d is not EOrderStatus.WaitForAccept and not EOrderStatus.BackToUnAccept and not EOrderStatus.SpecialToUnAccept and not EOrderStatus.HandOverToUnAccept
-                    : d is EOrderStatus.WaitForAccept or EOrderStatus.BackToUnAccept or EOrderStatus.SpecialToUnAccept or EOrderStatus.HandOverToUnAccept)
+                    : d is EOrderStatus.WaitForAccept or EOrderStatus.BackToUnAccept or EOrderStatus.SpecialToUnAccept or EOrderStatus.HandOverToUnAccept or EOrderStatus.Handling)
             .ToArray();
 
         var query = _orderRepository.Queryable();
@@ -3114,7 +3215,7 @@ public class OrderApplication : IOrderApplication, IScopeDependency
              //.OrderBy(d => d.Status)
              .OrderByIF(dto.IsHandled == true, d => d.StartTime, OrderByType.Desc)
             //.OrderByIF(dto.IsHandled == false, d => new { IsUrgent = d.IsUrgent }, OrderByType.Desc)
-            .OrderByIF(dto.IsHandled == false, d => new { d.Status })
+            //.OrderByIF(dto.IsHandled == false, d => new { d.Status })
             .OrderByIF(dto.IsHandled == false && 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) //创建时间降序

+ 1 - 0
src/Hotline.Application/Orders/OrderScreenHandler/OrderScreenEndWorkflowHandler.cs

@@ -74,6 +74,7 @@ public class OrderScreenEndWorkflowHandler : INotificationHandler<EndWorkflowNot
 						else {
 							var screenSatisfy = new Kv() { Key = "-1", Value = "视为满意" };
 							visitDetail.OrgProcessingResults = screenSatisfy;
+							visitDetail.OrgNoSatisfiedReason = new List<Kv>();
 							//visitDetail.OrgHandledAttitude = screenSatisfy;
 							await _orderVisitedDetailRepository.UpdateAsync(visitDetail, cancellationToken);
 							// 修改主表当前评价结果

+ 4 - 2
src/Hotline.Application/Orders/OrderSecondaryHandlingApplication.cs

@@ -232,7 +232,8 @@ namespace Hotline.Application.Orders
                 .Includes(x => x.SecondaryHandling)
                 .Where(x => x.OrderVisit.Order.IsProvince == false)
                 .LeftJoin<OrderScreen>((x, s) => x.Id == s.VisitDetailId && s.Status < EScreenStatus.End && s.IsDeleted == false)
-                .Where((x, s) => s.Id == null && (x.SecondaryHandling.State == ESecondaryHandlingState.NotApply || x.SecondaryHandling.Id == null))
+                //.Where((x, s) => s.Id == null && (x.SecondaryHandling.State == ESecondaryHandlingState.NotApply || x.SecondaryHandling.Id == null))
+                .Where(x=> SqlFunc.Subqueryable<OrderSecondaryHandling>().Where(osh => osh.VisitDetailId == x.Id &&  osh.State == ESecondaryHandlingState.NotApply).NotAny())
                 //.Where(x => x.OrderVisit.VisitTime < dto.CreationTimeEnd && x.OrderVisit.VisitTime > dto.CreationTimeStart)
                 .WhereIF(!string.IsNullOrEmpty(dto.No), x => x.OrderVisit.Order!.No!.Contains(dto.No!))
                 .WhereIF(dto.IsProvince.HasValue, x => x.OrderVisit.Order!.IsProvince == dto.IsProvince)
@@ -250,7 +251,8 @@ namespace Hotline.Application.Orders
                 .WhereIF(!string.IsNullOrEmpty(dto.OrgProcessingResults), x => SqlFunc.JsonField(x.OrgProcessingResults, "Key") == dto.OrgProcessingResults)
                 .WhereIF(!string.IsNullOrEmpty(dto.OrgHandledAttitude), x => SqlFunc.JsonListObjectAny(x.OrgHandledAttitude, "Key", dto.OrgHandledAttitude))
                 .WhereIF(!string.IsNullOrEmpty(dto.OrgNoSatisfiedReason), x => SqlFunc.JsonField(x.OrgNoSatisfiedReason, "Key") == dto.OrgNoSatisfiedReason)
-                .Where((x, s) => x.OrderVisit.VisitState == EVisitState.Visited && x.OrderVisit.IsCanHandle)
+                .Where(x => SqlFunc.Subqueryable<OrderVisit>().Where(ov => ov.Id == x.VisitId && ov.VisitState == EVisitState.Visited && ov.IsCanHandle).Any())
+                //.Where((x, s) => x.OrderVisit.VisitState == EVisitState.Visited && x.OrderVisit.IsCanHandle)
                 .WhereIF(!isAdmin, (x, s) => (x.OrderVisit.Order.CounterSignType ==  ECounterSignType.Department || x.OrderVisit.Order.CounterSignType == null) && x.VisitOrgCode.StartsWith(_sessionContext.OrgId))
                 ;
             if (_sessionContext.OrgId != null && !_sessionContext.OrgIsCenter)

+ 1 - 1
src/Hotline.Application/Orders/OrderSendBackAuditApplication.cs

@@ -35,7 +35,7 @@ namespace Hotline.Application.Orders
 				.WhereIF(dto.EndTime.HasValue, d => d.CreationTime <= dto.EndTime)
 				.WhereIF(dto.AuditState == 1, d => d.State == ESendBackAuditState.Apply)
 				.WhereIF(dto is { AuditState: 2, State: null }, d => d.State > ESendBackAuditState.Apply)
-				.WhereIF(dto.AuditState is 2 or 3 && dto.State.HasValue, d => d.State == dto.State)
+				.WhereIF(dto.AuditState is 2 or 3 && dto.State.HasValue && dto.State != ESendBackAuditState.All, d => d.State == dto.State)
 				.WhereIF(dto.AuditState == 3 && _sessionContext.RequiredOrgId != OrgSeedData.CenterId, x => x.ApplyOrgId.StartsWith(_sessionContext.OrgId))
 				.WhereIF(_sessionContext.Roles.Contains("role_sysadmin") == false && dto.AuditState != 3, x => x.SendBackOrgId == _sessionContext.OrgId) // 123 系统管理员;
 				.OrderByDescending(x => x.CreationTime);

+ 26 - 0
src/Hotline.Application/Snapshot/DefaultSnapshotApplication.cs

@@ -0,0 +1,26 @@
+using Hotline.Caching.Interfaces;
+using Hotline.DI;
+using Hotline.File;
+using Hotline.Orders;
+using Hotline.Settings;
+using Hotline.Snapshot;
+using Hotline.Users;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using XF.Domain.Authentications;
+using XF.Domain.Dependency;
+using XF.Domain.Repository;
+
+namespace Hotline.Application.Snapshot;
+
+[Injection(AppScopes = EAppScope.YiBin | EAppScope.LuZhou)]
+public class DefaultSnapshotApplication : SnapshotApplicationBase
+    , ISnapshotApplication, IScopeDependency
+{
+    public DefaultSnapshotApplication(IThirdIdentiyService thirdLoginService, IRepository<Industry> industryRepository, IRepository<Article.Bulletin> bulletinRepository, ISessionContext sessionContext, IRepository<RedPackRecord> redPackRecordRepository, IRepository<Order> orderRepository, IThirdAccountRepository thirdAccountRepository, IRepository<OrderSnapshot> orderSnapshotRepository, ISystemSettingCacheManager systemSettingCacheManager, ISystemAreaDomainService systemAreaDomainService, IFileRepository fileRepository, ISystemDicDataCacheManager systemDicDataCacheManager) : base(thirdLoginService, industryRepository, bulletinRepository, sessionContext, redPackRecordRepository, orderRepository, thirdAccountRepository, orderSnapshotRepository, systemSettingCacheManager, systemAreaDomainService, fileRepository, systemDicDataCacheManager)
+    {
+    }
+}

+ 16 - 0
src/Hotline.Application/Snapshot/IIndustryApplication.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;
+
+namespace Hotline.Application.Snapshot;
+public interface IIndustryApplication
+{
+    /// <summary>
+    /// 新增行业
+    /// </summary>
+    /// <returns></returns>
+    Task<string> AddIndustryAsync(AddIndustryDto dto, CancellationToken cancellationToken);
+}

+ 67 - 0
src/Hotline.Application/Snapshot/ISnapshotApplication.cs

@@ -0,0 +1,67 @@
+using Hotline.Share.Dtos;
+using Hotline.Share.Dtos.Article;
+using Hotline.Share.Dtos.Snapshot;
+
+namespace Hotline.Application.Snapshot;
+public interface ISnapshotApplication
+{
+    /// <summary>
+    /// 获取用户首页数据
+    /// </summary>
+    /// <returns></returns>
+    Task<SnapshotUserInfoOutDto> GetSnapshotUserInfoAsync();
+
+    /// <summary>
+    /// 获取小程序首页数据
+    /// </summary>
+    /// <returns></returns>
+    Task<HomePageOutDto> GetHomePageAsync();
+
+    /// <summary>
+    /// 获取小程序公告列表
+    /// </summary>
+    /// <param name="dto"></param>
+    /// <returns></returns>
+    Task<IReadOnlyList<BulletinOutDto>> GetBulletinsAsync(BulletinInDto dto);
+    
+    /// <summary>
+    /// 获取工单列表
+    /// </summary>
+    Task<PagedDto<OrderOutDto>> GetSnapshotOrdersAsync(OrderInDto dto);
+
+    /// <summary>
+    /// 获取工单详情
+    /// </summary>
+    /// <param name="id"></param>
+    /// <returns></returns>
+    Task<OrderDetailOutDto> GetSnapshotOrderDetailAsync(string id);
+
+    /// <summary>
+    /// 统计红包金额, 每月的总金额
+    /// </summary>
+    /// <param name="count"></param>
+    /// <returns></returns>
+    Task<IReadOnlyList<RedPackDateOutDto>> GetRedPackDateAsync(RedPackDateInDto dto);
+
+    /// <summary>
+    /// 获取当月详细红包列表
+    /// </summary>
+    /// <param name="dto"></param>
+    /// <returns></returns>
+    Task<PagedDto<RedPackOutDto>> GetRedPacksAsync(RedPacksInDto dto);
+
+    /// <summary>
+    /// 获取公告详情
+    /// </summary>
+    /// <param name="id"></param>
+    /// <returns></returns>
+    Task<BulletinOutDto> GetBulletinsDetailAsync(string id);
+
+    /// <summary>
+    /// 获取行业界面基础数据
+    /// </summary>
+    /// <param name="id"></param>
+    /// <param name="requestAborted"></param>
+    /// <returns></returns>
+    Task<IndustryBaseOutDto> GetIndustryBaseAsync(string id, CancellationToken requestAborted);
+}

+ 43 - 0
src/Hotline.Application/Snapshot/IndustryApplication.cs

@@ -0,0 +1,43 @@
+using Hotline.File;
+using Hotline.Share.Dtos.Snapshot;
+using Hotline.Share.Tools;
+using Hotline.Snapshot;
+using Mapster;
+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 IndustryApplication : IIndustryApplication, IScopeDependency
+{
+    private readonly IIndustryRepository _industryRepository;
+    private readonly IFileRepository _fileRepository;
+
+    public IndustryApplication(IIndustryRepository industryRepository, IFileRepository fileRepository)
+    {
+        _industryRepository = industryRepository;
+        _fileRepository = fileRepository;
+    }
+
+    /// <summary>
+    /// 新增行业
+    /// </summary>
+    /// <param name="dto"></param>
+    /// <returns></returns>
+    /// <exception cref="NotImplementedException"></exception>
+    public async Task<string> AddIndustryAsync(AddIndustryDto dto, CancellationToken cancellationToken)
+    {
+        var entity = dto.Adapt<Industry>();
+        var id = await _industryRepository.AddAsync(entity, cancellationToken);
+        if (dto.Files.NotNullOrEmpty())
+        {
+            var fileEntities = dto.Files.Adapt<List<Hotline.File.File>>();
+            fileEntities.ForEach(m => m.Key = id);
+            await _fileRepository.AddRangeAsync(fileEntities, cancellationToken);
+        }
+        return id;
+    }
+}

+ 280 - 0
src/Hotline.Application/Snapshot/SnapshotApplicationBase.cs

@@ -0,0 +1,280 @@
+using Hotline.Orders;
+using Hotline.Share.Dtos;
+using Hotline.Share.Dtos.Article;
+using Hotline.Share.Dtos.Snapshot;
+using Hotline.Share.Tools;
+using Hotline.Snapshot;
+using Hotline.Users;
+using SqlSugar;
+using XF.Domain.Authentications;
+using XF.Domain.Dependency;
+using XF.Domain.Repository;
+using Hotline.Repository.SqlSugar.Extensions;
+using Hotline.Share.Enums;
+using Hotline.Share.Enums.Order;
+using Hotline.Share.Requests;
+using Hotline.Share.Enums.Snapshot;
+using Mapster;
+using Hotline.Caching.Interfaces;
+using NPOI.Util.ArrayExtensions;
+using XF.Domain.Exceptions;
+using Hotline.Settings;
+using Hotline.Share.Dtos.Settings;
+using Hotline.File;
+
+namespace Hotline.Application.Snapshot;
+
+/// <summary>
+/// 随手拍应用层
+/// </summary>
+public abstract class SnapshotApplicationBase
+{
+    private readonly IThirdAccountRepository _thirdAccountRepository;
+    private readonly IRepository<Order> _orderRepository;
+    private readonly IRepository<Article.Bulletin> _bulletinRepository;
+
+    /// <summary>
+    /// 行业
+    /// </summary>
+    private readonly IRepository<Industry> _industryRepository;
+    private readonly IThirdIdentiyService _thirdLoginService;
+    private readonly ISessionContext _sessionContext;
+    private readonly IRepository<RedPackRecord> _redPackRecordRepository;
+    private readonly IRepository<OrderSnapshot> _orderSnapshotRepository;
+    private readonly ISystemSettingCacheManager _systemSettingCacheManager;
+    private readonly ISystemAreaDomainService _systemAreaDomainService;
+    private readonly IFileRepository _fileRepository;
+    private readonly ISystemDicDataCacheManager _systemDicDataCacheManager;
+
+    public SnapshotApplicationBase(IThirdIdentiyService thirdLoginService, IRepository<Industry> industryRepository, IRepository<Article.Bulletin> bulletinRepository, ISessionContext sessionContext, IRepository<RedPackRecord> redPackRecordRepository, IRepository<Order> orderRepository, IThirdAccountRepository thirdAccountRepository, IRepository<OrderSnapshot> orderSnapshotRepository, ISystemSettingCacheManager systemSettingCacheManager, ISystemAreaDomainService systemAreaDomainService, IFileRepository fileRepository, ISystemDicDataCacheManager systemDicDataCacheManager)
+    {
+        _thirdLoginService = thirdLoginService;
+        _industryRepository = industryRepository;
+        _bulletinRepository = bulletinRepository;
+        _sessionContext = sessionContext;
+        _redPackRecordRepository = redPackRecordRepository;
+        _orderRepository = orderRepository;
+        _thirdAccountRepository = thirdAccountRepository;
+        _orderSnapshotRepository = orderSnapshotRepository;
+        _systemSettingCacheManager = systemSettingCacheManager;
+        _systemAreaDomainService = systemAreaDomainService;
+        _fileRepository = fileRepository;
+        _systemDicDataCacheManager = systemDicDataCacheManager;
+    }
+
+    /// <summary>
+    /// 获取随手拍小程序首页数据
+    /// </summary>
+    /// <returns></returns>
+    public async Task<HomePageOutDto> GetHomePageAsync()
+    {
+        var fileServiceUrl = _systemSettingCacheManager.FileServerUrl;
+        var fileDownloadApi = fileServiceUrl + _systemSettingCacheManager.FileDownloadApi;
+        var items = await _industryRepository.Queryable()
+            .Where(m => m.IsEnable)
+            .OrderBy(m => m.DisplayOrder)
+            .ToListAsync(m => new HomeIndustryOutDto());
+        items.ForEach(m =>
+        {
+            if (m.BackgroundImgUrl.NotNullOrEmpty())
+                m.BackgroundImgUrl = fileDownloadApi + m.BackgroundImgUrl;
+            if (m.BannerImgUrl.NotNullOrEmpty())
+                m.BannerImgUrl = fileDownloadApi + m.BannerImgUrl;
+            if (m.CareCellImgUrl.NotNullOrEmpty())
+                m.CareCellImgUrl = fileDownloadApi + m.CareCellImgUrl;
+            if (m.CellImgUrl.NotNullOrEmpty())
+                m.CellImgUrl = fileDownloadApi + m.CellImgUrl;
+        });
+
+        return new HomePageOutDto
+        {
+            Banners = _systemSettingCacheManager.AppBanner.Split('|').Select(m => fileDownloadApi + m).ToList(),
+            Industrys = items
+        };
+    }
+
+    /// <summary>
+    /// 行业页面基础数据
+    /// </summary>
+    /// <param name="id"></param>
+    /// <param name="requestAborted"></param>
+    /// <returns></returns>
+    public async Task<IndustryBaseOutDto> GetIndustryBaseAsync(string id, CancellationToken requestAborted)
+    {
+        var fileServiceUrl = _systemSettingCacheManager.FileServerUrl;
+        var fileDownloadApi = fileServiceUrl + _systemSettingCacheManager.FileDownloadApi;
+        var indurstry = await _industryRepository.GetAsync(id, requestAborted)
+            ?? throw UserFriendlyException.SameMessage("行业不存在:" + id);
+
+        var outDto = new IndustryBaseOutDto
+        {
+            Industry = indurstry.Adapt<IndustryOutDto>()
+        };
+        if (indurstry.IndustryType == EIndustryType.Declare)
+        {
+            outDto.AreaTree = (await _systemAreaDomainService.GetAreaTree(parentId: "510300")).Adapt<List<SystemAreaOutDto>>();
+            outDto.Files = (await _fileRepository.GetByKeyAsync(indurstry.Id, requestAborted)).Adapt<List<IndustryFileDto>>();
+            outDto.Files.ToList().ForEach(m => m.Url = fileDownloadApi + m.AdditionId);
+            outDto.WorkArea = _systemDicDataCacheManager.WorkArea.Adapt<List<SystemDicDataOutDto>>();
+            outDto.Workplace = _systemDicDataCacheManager.Workplace.Adapt<List<SystemDicDataOutDto>>();
+        }
+        return outDto;
+    }
+
+    /// <summary>
+    /// 获取随手拍小程序公告
+    /// </summary>
+    /// <returns></returns>
+    public async Task<IReadOnlyList<BulletinOutDto>> GetBulletinsAsync(BulletinInDto dto)
+    {
+        var items = await _bulletinRepository.Queryable()
+            .Where(m => m.BulletinState == Share.Enums.Article.EBulletinState.ReviewPass)
+            .LeftJoin<Industry>((bulletin, industry) => bulletin.BulletinTypeId == industry.BulletinTypePublicityId)
+            .Where((bulletin, industry) => industry.Id == dto.IndustryId)
+            .ToPageListAsync(dto.PageIndex, dto.PageSize);
+
+        return items.Adapt<IReadOnlyList<BulletinOutDto>>();
+    }
+
+    /// <summary>
+    /// 获取个人中心数据
+    /// </summary>
+    /// <returns></returns>
+    public async Task<SnapshotUserInfoOutDto> GetSnapshotUserInfoAsync()
+    {
+        var openId = _sessionContext.OpenId;
+        var thirdAccount = await _thirdAccountRepository.QueryByOpenIdAsync(openId);
+        var dayTime = DateTime.Now;
+        var readPack = await _redPackRecordRepository.Queryable()
+            .Where(m => m.WXOpenId == openId && m.PickupStatus == Share.Enums.Snapshot.ERedPackPickupStatus.Received)
+            .Where(m => m.CreationTime.Date == dayTime.Date)
+            .Select(m => SqlFunc.AggregateSum(m.Amount))
+            .FirstAsync();
+
+        var outDto = await _orderRepository.Queryable()
+            .Where(m => m.Contact == thirdAccount.PhoneNumber)
+            .Select(m => new SnapshotUserInfoOutDto
+            {
+                NoReplyCount = SqlFunc.AggregateSum(SqlFunc.IIF(m.Status < Share.Enums.Order.EOrderStatus.Filed, 1, 0)),
+                ReplyCount = SqlFunc.AggregateSum(SqlFunc.IIF(m.Status >= Share.Enums.Order.EOrderStatus.Filed, 1, 0)),
+                AppraiseCount = SqlFunc.AggregateSum(SqlFunc.IIF(m.Status == Share.Enums.Order.EOrderStatus.Visited, 1, 0)),
+            }).FirstAsync();
+
+        outDto.DayAmount = readPack;
+        outDto.TotalAmount = thirdAccount.TotalAmount;
+        outDto.PhoneNumber = thirdAccount.PhoneNumber;
+        return outDto;
+    }
+
+    public async Task<PagedDto<OrderOutDto>> GetSnapshotOrdersAsync(OrderInDto dto)
+    {
+        var member = await _thirdAccountRepository.QueryByOpenIdAsync(_sessionContext.OpenId);
+        if (member == null)
+        {
+            return new PagedDto<OrderOutDto>(0, new List<OrderOutDto>());
+        }
+        var (total, items) = await _orderSnapshotRepository.Queryable()
+            .LeftJoin<Order>((snapshot, order) => snapshot.OrderId == order.Id)
+            .Where((snapshot, order) => order.Contact == member.PhoneNumber)
+            .WhereIF(dto.Status == EOrderQueryStatus.Appraise, (snapshot, order) => order.Status == EOrderStatus.Visited)
+            .WhereIF(dto.Status == EOrderQueryStatus.NoReply, (snapshot, order) => order.Status < EOrderStatus.Filed)
+            .WhereIF(dto.Status == EOrderQueryStatus.Reply, (snapshot, order) => order.Status >= EOrderStatus.Filed)
+            .WhereIF(dto.KeyWords.NotNullOrEmpty(), (snapshot, order) => order.Title.Contains(dto.KeyWords))
+            .Select((snapshot, order) => new OrderOutDto
+            {
+                Id = snapshot.Id,
+                OrderNo = order.No,
+                Title = order.Title,
+                Status = order.Status,
+                IndustryName = snapshot.IndustryName,
+                CreationTime = order.CreationTime,
+                Area = order.City
+            })
+            .ToPagedListAsync(dto.PageIndex, dto.PageSize);
+
+        return new PagedDto<OrderOutDto>(total, items);
+    }
+
+    public async Task<OrderDetailOutDto> GetSnapshotOrderDetailAsync(string id)
+    {
+        var detail = await _orderSnapshotRepository.Queryable()
+            .Where(m => m.Id == id)
+            .LeftJoin<Order>((snapshot, order) => snapshot.OrderId == order.Id)
+            .Select((snapshot, order) => new OrderDetailOutDto
+            {
+                Id = snapshot.Id,
+                OrderNo = order.No,
+                Title = order.Title,
+                Status = order.Status,
+                IndustryName = snapshot.IndustryName,
+                CreationTime = order.CreationTime,
+                Area = order.City
+            })
+            .FirstAsync();
+
+        return detail;
+    }
+
+    /// <summary>
+    /// 获取当月详细红包列表
+    /// </summary>
+    /// <param name="dto"></param>
+    /// <returns></returns>
+    public async Task<PagedDto<RedPackOutDto>> GetRedPacksAsync(RedPacksInDto dto)
+    {
+        var openId = _sessionContext.OpenId;
+        var (total, items) = await _redPackRecordRepository.Queryable()
+            .Where(m => m.WXOpenId == openId)
+            .Where(m => m.PickupStatus == dto.Status)
+            .Where(m => m.CreationTime.ToString("yyyy-MM") == dto.Time)
+            .LeftJoin<Order>((red, order) => red.OrderId == order.Id)
+            .Select((red, order) => new RedPackOutDto
+            {
+                Amount = red.Amount,
+                Title = order.Title,
+                CreationTime = red.CreationTime
+            })
+            .ToPagedListAsync(dto.PageIndex, dto.PageSize);
+
+        return new PagedDto<RedPackOutDto>(total, items);
+    }
+
+    /// <summary>
+    /// 按月统计红包金额
+    /// </summary>
+    /// <param name="count"></param>
+    /// <returns></returns>
+    public async Task<IReadOnlyList<RedPackDateOutDto>> GetRedPackDateAsync(RedPackDateInDto dto)
+    {
+        var openId = _sessionContext.OpenId;
+        var item = await _redPackRecordRepository.Queryable()
+            .Where(m => m.WXOpenId == openId)
+            .Where(m => m.PickupStatus == dto.Status)
+            .GroupBy(m => m.CreationTime.ToString("yyyy-MM"))
+            .OrderByDescending(m => m.CreationTime)
+            .Select(m => new RedPackDateOutDto
+            {
+                CreationTime = SqlFunc.AggregateMax(m.CreationTime.Date),
+                Amount = SqlFunc.AggregateSum(m.Amount)
+            })
+            .Take(dto.Count)
+            .ToListAsync();
+
+        return item;
+    }
+
+    public async Task<BulletinOutDto> GetBulletinsDetailAsync(string id)
+    {
+        var detail = await _bulletinRepository.Queryable()
+            .Where(m => m.Id == id)
+            .Where(m => m.BulletinState == Share.Enums.Article.EBulletinState.ReviewPass)
+            .Select(m => new BulletinOutDto
+            {
+                Id = m.Id,
+                Title = m.Title,
+                Content = m.Content
+            })
+            .FirstAsync();
+        return detail;
+    }
+}

+ 25 - 0
src/Hotline.Application/Snapshot/ZiGongSnapshotApplication.cs

@@ -0,0 +1,25 @@
+using Hotline.Caching.Interfaces;
+using Hotline.DI;
+using Hotline.File;
+using Hotline.Orders;
+using Hotline.Settings;
+using Hotline.Snapshot;
+using Hotline.Users;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using XF.Domain.Authentications;
+using XF.Domain.Dependency;
+using XF.Domain.Repository;
+
+namespace Hotline.Application.Snapshot;
+
+[Injection(AppScopes = EAppScope.ZiGong)]
+public class ZiGongSnapshotApplication : SnapshotApplicationBase, ISnapshotApplication, IScopeDependency
+{
+    public ZiGongSnapshotApplication(IThirdIdentiyService thirdLoginService, IRepository<Industry> industryRepository, IRepository<Article.Bulletin> bulletinRepository, ISessionContext sessionContext, IRepository<RedPackRecord> redPackRecordRepository, IRepository<Order> orderRepository, IThirdAccountRepository thirdAccountRepository, IRepository<OrderSnapshot> orderSnapshotRepository, ISystemSettingCacheManager systemSettingCacheManager, ISystemAreaDomainService systemAreaDomainService, IFileRepository fileRepository, ISystemDicDataCacheManager systemDicDataCacheManager) : base(thirdLoginService, industryRepository, bulletinRepository, sessionContext, redPackRecordRepository, orderRepository, thirdAccountRepository, orderSnapshotRepository, systemSettingCacheManager, systemAreaDomainService, fileRepository, systemDicDataCacheManager)
+    {
+    }
+}

+ 5 - 2
src/Hotline.Application/StatisticalReport/CallReport/CallReportApplicationBase.cs

@@ -25,18 +25,20 @@ namespace Hotline.Application.StatisticalReport.CallReport;
 public abstract class CallReportApplicationBase : ICallReportApplication
 {
     private readonly ISystemSettingCacheManager _systemSettingCacheManager;
+    private readonly ISystemDicDataCacheManager _systemDicDataCacheManager;
     private readonly ICallNativeRepository _callNativeRepository;
     private readonly IRepository<User> _userRepository;
     private readonly IRepository<Work> _workRepository;
     private readonly IRepository<TelRest> _telRestRepository;
 
-    protected CallReportApplicationBase(ISystemSettingCacheManager systemSettingCacheManager, ICallNativeRepository callNativeRepository, IRepository<User> userRepository, IRepository<Work> workRepository, IRepository<TelRest> telRestRepository)
+    protected CallReportApplicationBase(ISystemSettingCacheManager systemSettingCacheManager, ICallNativeRepository callNativeRepository, IRepository<User> userRepository, IRepository<Work> workRepository, IRepository<TelRest> telRestRepository, ISystemDicDataCacheManager systemDicDataCacheManager)
     {
         _systemSettingCacheManager = systemSettingCacheManager;
         _callNativeRepository = callNativeRepository;
         _userRepository = userRepository;
         _workRepository = workRepository;
         _telRestRepository = telRestRepository;
+        _systemDicDataCacheManager = systemDicDataCacheManager;
     }
 
     public virtual async Task<List<CallHotLineDto>> GetCallHotLineListAsync(BiQueryGateWayDto dto, CancellationToken requestAborted)
@@ -45,7 +47,8 @@ public abstract class CallReportApplicationBase : ICallReportApplication
         int effectiveTimes = _systemSettingCacheManager.EffectiveTimes;
         int connectByeTimes = _systemSettingCacheManager.ConnectByeTimes;
         int ringTimes = _systemSettingCacheManager.RingTimes;
-        return await _callNativeRepository.GetCallHotLineListAsync(dto, noConnectByeTimes, effectiveTimes, connectByeTimes, ringTimes);
+        var hotlines = _systemDicDataCacheManager.CallForwardingSource.Select(m => m.DicDataValue).ToList();
+        return await _callNativeRepository.GetCallHotLineListAsync(dto, noConnectByeTimes, effectiveTimes, connectByeTimes, ringTimes, hotlines);
     }
 
     public virtual async Task<List<TrCallHourDto>> GetCallHourListAsync(BiQueryHourCallDto dto, CancellationToken requestAborted)

+ 1 - 1
src/Hotline.Application/StatisticalReport/CallReport/LuZhouCallReportApplication.cs

@@ -11,7 +11,7 @@ namespace Hotline.Application.StatisticalReport.CallReport;
 [Injection(AppScopes = EAppScope.LuZhou)]
 public class LuZhouCallReportApplication : CallReportApplicationBase, ICallReportApplication, IScopeDependency
 {
-    public LuZhouCallReportApplication(ISystemSettingCacheManager systemSettingCacheManager, ICallNativeRepository callNativeRepository, IRepository<User> userRepository, IRepository<Work> workRepository, IRepository<TelRest> telRestRepository) : base(systemSettingCacheManager, callNativeRepository, userRepository, workRepository, telRestRepository)
+    public LuZhouCallReportApplication(ISystemSettingCacheManager systemSettingCacheManager, ICallNativeRepository callNativeRepository, IRepository<User> userRepository, IRepository<Work> workRepository, IRepository<TelRest> telRestRepository, ISystemDicDataCacheManager systemDicDataCacheManager) : base(systemSettingCacheManager, callNativeRepository, userRepository, workRepository, telRestRepository, systemDicDataCacheManager)
     {
     }
 }

+ 2 - 1
src/Hotline.Application/StatisticalReport/CallReport/YiBinCallReportApplication.cs

@@ -41,7 +41,8 @@ public class YiBinCallReportApplication : CallReportApplicationBase, ICallReport
         IRepository<TelRest> telRestRepository,
         ICallNativeRepository callNativeRepository,
         ITrCallRecordRepository trCallRecordRepositoryEx,
-        IMapper mapper) : base(systemSettingCacheManager, callNativeRepository, userRepository, workRepository, telRestRepository)
+        IMapper mapper,
+        ISystemDicDataCacheManager systemDicDataCacheManager) : base(systemSettingCacheManager, callNativeRepository, userRepository, workRepository, telRestRepository, systemDicDataCacheManager)
     {
         _trCallRecordRepository = trCallRecordRepository;
         _systemSettingCacheManager = systemSettingCacheManager;

+ 1 - 1
src/Hotline.Application/StatisticalReport/CallReport/ZiGongCallReportApplication.cs

@@ -11,7 +11,7 @@ namespace Hotline.Application.StatisticalReport.CallReport;
 [Injection(AppScopes = EAppScope.ZiGong)]
 public class ZiGongCallReportApplication : CallReportApplicationBase, ICallReportApplication, IScopeDependency
 {
-    public ZiGongCallReportApplication(ISystemSettingCacheManager systemSettingCacheManager, ICallNativeRepository callNativeRepository, IRepository<User> userRepository, IRepository<Work> workRepository, IRepository<TelRest> telRestRepository) : base(systemSettingCacheManager, callNativeRepository, userRepository, workRepository, telRestRepository)
+    public ZiGongCallReportApplication(ISystemSettingCacheManager systemSettingCacheManager, ICallNativeRepository callNativeRepository, IRepository<User> userRepository, IRepository<Work> workRepository, IRepository<TelRest> telRestRepository, ISystemDicDataCacheManager systemDicDataCacheManager) : base(systemSettingCacheManager, callNativeRepository, userRepository, workRepository, telRestRepository, systemDicDataCacheManager)
     {
     }
 }

File diff suppressed because it is too large
+ 0 - 0
src/Hotline.Application/StatisticalReport/OrderReportApplication.cs


+ 75 - 52
src/Hotline.Application/Subscribers/DatasharingSubscriber.cs

@@ -315,8 +315,7 @@ namespace Hotline.Application.Subscribers
                         nextHandler = new List<FlowStepHandler> { handler };
                     }
                 }
-                var isPaiDan = await _workflowDomainService.RecallToCenterFirstToSendAsync(order.WorkflowId, dto.Opinion,
-                    order.Status >= EOrderStatus.Filed, order.ExpiredTime, nextHandler, cancellationToken);
+                var (isPaiDan, workflow) = await _workflowDomainService.RecallToCenterFirstToSendAsync(order.WorkflowId, dto.Opinion, order.Status >= EOrderStatus.Filed, order.ExpiredTime, nextHandler, cancellationToken);
                 order.FiledTime = null;
                 if (isPaiDan)
                 {
@@ -327,6 +326,19 @@ namespace Hotline.Application.Subscribers
                     order.Status = EOrderStatus.WaitForAccept;
                 }
 
+                order.CurrentStepName = workflow.CurrentStepName;
+                order.CurrentStepCode = workflow.CurrentStepCode;
+                order.CurrentStepId = workflow.CurrentStepId;
+                order.CurrentStepCreateTime = workflow.CurrentStepCreateTime;
+                order.CurrentHandleTime = workflow.CurrentHandleTime;
+                order.CurrentHandlerId = workflow.CurrentHandlerId;
+                order.CurrentHandlerName = workflow.CurrentHandlerName;
+                order.CurrentHandleOrgName = workflow.CurrentHandleOrgName;
+                order.CurrentHandleOrgId = workflow.CurrentHandleOrgId;
+                order.CurrentHandleOrgLevel = workflow.CurrentHandleOrgLevel;
+                order.CurrentHandleOrgAreaCode = workflow.CurrentHandleOrgAreaCode;
+                order.CurrentHandleOrgAreaName = workflow.CurrentHandleOrgAreaName;
+
                 await _orderRepository.UpdateAsync(order, cancellationToken);
             }
         }
@@ -493,56 +505,67 @@ namespace Hotline.Application.Subscribers
             //var setting = _systemSettingCacheManager.GetSetting(SettingConstants.CityBaseConfiguration)?.SettingValue[0];
             //CityBaseConfiguration cityBase = JsonConvert.DeserializeObject<CityBaseConfiguration>(setting);
             SessionContextCreator.CreateSessionContext(_sessionContextProvider, dto.Source);
-            await _workflowApplication.HandleToEndAsync(orderScreen.WorkflowId, "省上推送甄别结果", null,
-                dto.ProvinceScreenResult.AuditResult
-                    ? Share.Enums.FlowEngine.EReviewResult.Approval
-                    : Share.Enums.FlowEngine.EReviewResult.Failed, cancellationToken);
-            orderScreen.Status = dto.ProvinceScreenResult.AuditResult ? Share.Enums.Order.EScreenStatus.End : Share.Enums.Order.EScreenStatus.Refuse;
-            //if (orderScreen.Status == Share.Enums.Order.EScreenStatus.End)
-            //{
-            //    var visitDetail = await _orderVisitedDetailRepository.GetAsync(orderScreen.VisitDetailId, cancellationToken);
-            //    if (visitDetail != null)
-            //    {
-            //        var screenSatisfy = new Kv() { Key = "-1", Value = "视为满意" };
-            //        visitDetail.OrgProcessingResults = screenSatisfy;
-            //        visitDetail.OrgHandledAttitude = screenSatisfy;
-            //        await _orderVisitedDetailRepository.UpdateAsync(visitDetail, cancellationToken);
-            //    }
-            //}
-            orderScreen.ReplyContent = dto.ProvinceScreenResult.AuditOpinion;
-            orderScreen.AuditName = dto.ProvinceScreenResult.AuditPerson;
-            orderScreen.AuditOrgName = dto.ProvinceScreenResult.AuditOuname;
-            orderScreen.AuditTime = dto.ProvinceScreenResult.AuditTime;
+            if (int.Parse(dto.ProvinceScreenResult.AuditResult) == 0)
+            {
+	            PreviousWorkflowDto previous = new PreviousWorkflowDto();
+	            previous.Opinion = dto.ProvinceScreenResult.AuditOpinion;
+				previous.WorkflowId = orderScreen.WorkflowId;
+	            await _workflowApplication.PreviousAsync(previous, cancellationToken);
+			}
 
-            if (dto.Files.Any())
-                orderScreen.FileJson =
-                    await _fileRepository.AddFileAsync(dto.Files, orderScreen.Id, "", cancellationToken);
-            await _orderScreenRepository.UpdateAsync(orderScreen, cancellationToken);
-            //多部门甄别回访 省上结果处理
-            if (orderScreen.Status == Share.Enums.Order.EScreenStatus.End)
+            if (int.Parse(dto.ProvinceScreenResult.AuditResult) > 0)
             {
-                var visitDetails = await _orderVisitedDetailRepository.Queryable().Where(x => x.OrderVisit.OrderId == orderScreen.OrderId &&
-                                                                                              x.VisitTarget == EVisitTarget.Org && (
-                                                                                                  SqlFunc.JsonField(x.OrgProcessingResults, "Key") ==
-                                                                                                  "1" ||
-                                                                                                  SqlFunc.JsonField(x.OrgProcessingResults, "Key") ==
-                                                                                                  "2" ||
-                                                                                                  SqlFunc.JsonField(x.OrgHandledAttitude, "Key") ==
-                                                                                                  "1" ||
-                                                                                                  SqlFunc.JsonField(x.OrgHandledAttitude, "Key") ==
-                                                                                                  "2"
-                                                                                              ) && x.Id != orderScreen.VisitDetailId).ToListAsync();
-                foreach (var visitDetail in visitDetails)
-                {
-                    if (visitDetail != null)
-                    {
-                        var screenSatisfy = new Kv() { Key = "-1", Value = "视为满意" };
-                        visitDetail.OrgProcessingResults = screenSatisfy;
-                        visitDetail.OrgHandledAttitude = screenSatisfy;
-                        await _orderVisitedDetailRepository.UpdateAsync(visitDetail, cancellationToken);
-                    }
-                }
-            }
+				await _workflowApplication.HandleToEndAsync(orderScreen.WorkflowId, dto.ProvinceScreenResult.AuditOpinion, null,
+				 reviewResult: int.Parse(dto.ProvinceScreenResult.AuditResult) == 1
+					? Share.Enums.FlowEngine.EReviewResult.Approval
+					: Share.Enums.FlowEngine.EReviewResult.Failed, cancellationToken: cancellationToken);
+				orderScreen.Status = int.Parse(dto.ProvinceScreenResult.AuditResult) == 1 ? Share.Enums.Order.EScreenStatus.End : Share.Enums.Order.EScreenStatus.Refuse;
+				//if (orderScreen.Status == Share.Enums.Order.EScreenStatus.End)
+				//{
+				//    var visitDetail = await _orderVisitedDetailRepository.GetAsync(orderScreen.VisitDetailId, cancellationToken);
+				//    if (visitDetail != null)
+				//    {
+				//        var screenSatisfy = new Kv() { Key = "-1", Value = "视为满意" };
+				//        visitDetail.OrgProcessingResults = screenSatisfy;
+				//        visitDetail.OrgHandledAttitude = screenSatisfy;
+				//        await _orderVisitedDetailRepository.UpdateAsync(visitDetail, cancellationToken);
+				//    }
+				//}
+				orderScreen.ReplyContent = dto.ProvinceScreenResult.AuditOpinion;
+				orderScreen.AuditName = dto.ProvinceScreenResult.AuditPerson;
+				orderScreen.AuditOrgName = dto.ProvinceScreenResult.AuditOuname;
+				orderScreen.AuditTime = dto.ProvinceScreenResult.AuditTime;
+
+				if (dto.Files.Any())
+					orderScreen.FileJson =
+						await _fileRepository.AddFileAsync(dto.Files, orderScreen.Id, "", cancellationToken);
+				await _orderScreenRepository.UpdateAsync(orderScreen, cancellationToken);
+				//多部门甄别回访 省上结果处理
+				if (orderScreen.Status == Share.Enums.Order.EScreenStatus.End)
+				{
+					var visitDetails = await _orderVisitedDetailRepository.Queryable().Where(x => x.OrderVisit.OrderId == orderScreen.OrderId &&
+																								  x.VisitTarget == EVisitTarget.Org && (
+																									  SqlFunc.JsonField(x.OrgProcessingResults, "Key") ==
+																									  "1" ||
+																									  SqlFunc.JsonField(x.OrgProcessingResults, "Key") ==
+																									  "2" ||
+																									  SqlFunc.JsonField(x.OrgHandledAttitude, "Key") ==
+																									  "1" ||
+																									  SqlFunc.JsonField(x.OrgHandledAttitude, "Key") ==
+																									  "2"
+																								  ) && x.Id != orderScreen.VisitDetailId).ToListAsync();
+					foreach (var visitDetail in visitDetails)
+					{
+						if (visitDetail != null)
+						{
+							var screenSatisfy = new Kv() { Key = "-1", Value = "视为满意" };
+							visitDetail.OrgProcessingResults = screenSatisfy;
+							visitDetail.OrgHandledAttitude = screenSatisfy;
+							await _orderVisitedDetailRepository.UpdateAsync(visitDetail, cancellationToken);
+						}
+					}
+				}
+			}
         }
 
         /// <summary>
@@ -771,7 +794,7 @@ namespace Hotline.Application.Subscribers
 
                     SessionContextCreator.CreateSessionContext(_sessionContextProvider, dto.Source);
                     await _workflowApplication.HandleToEndAsync(orderDelay.WorkflowId, dto.Opinion, dto.Files,
-                        dto.IsPass ? EReviewResult.Approval : EReviewResult.Failed, cancellationToken);
+                      reviewResult: dto.IsPass ? EReviewResult.Approval : EReviewResult.Failed, cancellationToken: cancellationToken);
                 }
             }
         }
@@ -832,7 +855,7 @@ namespace Hotline.Application.Subscribers
                     }
                     else
                     {
-                        await _workflowApplication.HandleToEndAsync(order.WorkflowId, dto.Opinion, dto.Files, cancellationToken: cancellationToken);
+                        await _workflowApplication.HandleToEndAsync(order.WorkflowId, dto.Opinion, dto.Files, expiredTime: order.ExpiredTime, cancellationToken: cancellationToken);
                     }
 
                     break;

+ 8 - 1
src/Hotline.Application/Subscribers/InternalCapSubscriber.cs

@@ -15,6 +15,7 @@ using Hotline.Share.Enums.Order;
 using Hotline.Share.Enums.Push;
 using Hotline.Share.Mq;
 using Hotline.Users;
+using MapsterMapper;
 using MediatR;
 using Microsoft.AspNetCore.Http;
 using NPOI.SS.Formula.Functions;
@@ -37,6 +38,7 @@ namespace Hotline.Application.Subscribers
         private readonly IRepository<OrderDelay> _orderDelayRepository;
         private readonly IWorkflowDomainService _workflowDomainService;
         private readonly ICalcExpireTime _expireTime;
+        private readonly IMapper _mapper;
 
 		public InternalCapSubscriber(
             IOrderRepository orderRepository,
@@ -48,7 +50,8 @@ namespace Hotline.Application.Subscribers
             IOrderApplication orderApplication,
             IRepository<OrderDelay> orderDelayRepository,
             IWorkflowDomainService workflowDomainService,
-            ICalcExpireTime expireTime
+            ICalcExpireTime expireTime,
+            IMapper mapper
 			)
         {
             _orderRepository = orderRepository;
@@ -61,6 +64,7 @@ namespace Hotline.Application.Subscribers
             _orderDelayRepository = orderDelayRepository;
             _workflowDomainService = workflowDomainService;
             _expireTime = expireTime;
+            _mapper = mapper;
 		}
 
         /// <summary>
@@ -340,6 +344,9 @@ namespace Hotline.Application.Subscribers
 					}
 					order = await _orderRepository.GetAsync(dto.OrderId, cancellationToken);
 					_capPublisher.PublishDelay(order.ExpiredTime.Value - DateTime.Now.AddHours(1), EventNames.HotlineOrderAutomaticDelay, new PublishAutomaticDelayDto() { OrderId = order.Id });
+					var orderDto = _mapper.Map<OrderDto>(order);
+					await _capPublisher.PublishAsync(Hotline.Share.Mq.EventNames.HotlineOrderExpiredTimeUpdate, orderDto,
+						cancellationToken: cancellationToken);
 				}
 			}
 		

+ 19 - 19
src/Hotline.Repository.SqlSugar/CallCenter/CallNativeRepository.cs

@@ -37,24 +37,23 @@ public class CallNativeRepository : BaseRepository<CallNative>, ICallNativeRepos
         }
 
         var list = await Db.Reportable(dts).ToQueryable<DateTime>()
-            .LeftJoin<CallNative>((it, o) => o.BeginIvrTime >= it.ColumnName && o.BeginIvrTime < it.ColumnName.AddHours(1) && o.Direction == ECallDirection.In && o.CallState != ECallState.Invalid)
-             //.Where((it, o) => o.CallDirection == ECallDirection.In)
-             .WhereIF(!string.IsNullOrEmpty(source), (it, o) => o.ToNo == source)
-            .GroupBy((it, o) => it.ColumnName)
-            .Select((it, o) => new TrCallHourDto()
-            {
-                DateTimeTo = it.ColumnName,
-                Hour = it.ColumnName.Hour, //小时段
-                EffectiveCount = SqlFunc.AggregateSum(SqlFunc.IIF(o.AnsweredTime != null && o.Duration >= effectiveTimes, 1, 0)),//有效接通
-                ConnectByeCount = SqlFunc.AggregateSum(SqlFunc.IIF(o.AnsweredTime != null && o.Duration <= connectByeTimes, 1, 0)), //接通秒挂
-                NoConnectByeCount = SqlFunc.AggregateSum(SqlFunc.IIF(o.AnsweredTime == null && o.RingDuration <= noConnectByeTimes && o.RingDuration > 0, 1, 0)), //未接通秒挂
-                QueueByeCount = SqlFunc.AggregateSum(SqlFunc.IIF(o.CallState == ECallState.NotAcceptedHang, 1, 0)), //队列挂断
-                IvrByeCount = SqlFunc.AggregateSum(SqlFunc.IIF(o.CallState == ECallState.IVRNoAccept, 1, 0)), //IVR挂断
-            })
-            .MergeTable()
-            .OrderBy(x => x.Hour)
-            .ToListAsync();
-
+             .LeftJoin<CallNative>((it, o) => o.BeginIvrTime >= it.ColumnName && o.BeginIvrTime < it.ColumnName.AddHours(1) && o.Direction == ECallDirection.In && o.CallState != ECallState.Invalid)
+              //.Where((it, o) => o.CallDirection == ECallDirection.In)
+              .WhereIF(!string.IsNullOrEmpty(source), (it, o) => o.ToNo == source)
+             .GroupBy((it, o) => it.ColumnName)
+             .Select((it, o) => new TrCallHourDto()
+             {
+                 DateTimeTo = it.ColumnName,
+                 Hour = it.ColumnName.Hour, //小时段
+                 EffectiveCount = SqlFunc.AggregateSum(SqlFunc.IIF(o.AnsweredTime != null && o.Duration >= effectiveTimes, 1, 0)),//有效接通
+                 ConnectByeCount = SqlFunc.AggregateSum(SqlFunc.IIF(o.AnsweredTime != null && o.Duration <= connectByeTimes, 1, 0)), //接通秒挂
+                 NoConnectByeCount = SqlFunc.AggregateSum(SqlFunc.IIF(o.AnsweredTime == null && o.RingDuration <= noConnectByeTimes && o.RingDuration > 0, 1, 0)), //未接通秒挂
+                 QueueByeCount = SqlFunc.AggregateSum(SqlFunc.IIF(o.CallState == ECallState.NotAcceptedHang, 1, 0)), //队列挂断
+                 IvrByeCount = SqlFunc.AggregateSum(SqlFunc.IIF(o.CallState == ECallState.IVRNoAccept, 1, 0)), //IVR挂断
+             })
+             .MergeTable()
+             .OrderBy(x => x.Hour)
+             .ToListAsync();
         var resultList = list.GroupBy(x => x.Hour)
              .Select(x => new TrCallHourDto()
              {
@@ -226,10 +225,11 @@ public class CallNativeRepository : BaseRepository<CallNative>, ICallNativeRepos
         return new TotalData<BiSeatSwitchDto>(res, total.Value);
     }
 
-    public async Task<List<CallHotLineDto>> GetCallHotLineListAsync(BiQueryGateWayDto dto, int noConnectByeTimes, int effectiveTimes, int connectByeTimes, int ringTims)
+    public async Task<List<CallHotLineDto>> GetCallHotLineListAsync(BiQueryGateWayDto dto, int noConnectByeTimes, int effectiveTimes, int connectByeTimes, int ringTims, List<string> hotLines)
     {
         var list = await Db.Queryable<CallNative>()
             .Where(x => x.BeginIvrTime >= dto.StartTime && x.BeginIvrTime <= dto.EndTime && SqlFunc.Length(x.ToNo) > 4 && x.CallState != ECallState.Invalid)
+            .Where(x => hotLines.Contains(x.ToNo))
             .WhereIF(!string.IsNullOrEmpty(dto.Gateway), x => x.ToNo == dto.Gateway)
             .GroupBy(x => x.ToNo)
             .Select(x => new CallHotLineDto()

+ 14 - 0
src/Hotline.Repository.SqlSugar/Extensions/SqlSugarStartupExtensions.cs

@@ -258,6 +258,20 @@ namespace Hotline.Repository.SqlSugar.Extensions
                 }
             };
 
+            db.Aop.DataChangesExecuted = (sql, entityInfo) =>
+            {
+                if (entityInfo.EntityColumnInfo.IsPrimarykey && entityInfo.OperationType == DataFilterType.UpdateByObject)
+                {
+                    var entityName = entityInfo.EntityName;
+                    Console.WriteLine(entityName);
+                }
+                if (entityInfo.EntityColumnInfo.IsPrimarykey && entityInfo.OperationType == DataFilterType.InsertByObject)
+                {
+                    var entityName = entityInfo.EntityName;
+                    Console.WriteLine(entityName);
+                }
+            };
+
             db.Aop.DataExecuting = (oldValue, entityInfo) =>
             {
                 //inset生效

+ 5 - 0
src/Hotline.Repository.SqlSugar/File/FileRepository.cs

@@ -59,6 +59,11 @@ namespace Hotline.Repository.SqlSugar.File
 			return files.Any() ? _mapper.Map<List<FileDto>>(files) : new List<FileDto>();
 		}
 
+		public async Task<List<Hotline.File.File>> GetByKeyAsync(string key, CancellationToken cancellationToken)
+		{ 
+			return await Queryable().Where(x => x.Key == key && x.IsDeleted == false).ToListAsync(cancellationToken);
+        }
+
 		public async Task<List<WorkflowTraceDto>> WorkflowTraceRecursion(List<WorkflowTraceDto> dto, CancellationToken cancellationToken)
 		{
 			foreach (var item in dto)

+ 51 - 35
src/Hotline.Repository.SqlSugar/Orders/OrderRepository.cs

@@ -19,9 +19,11 @@ using System.Linq.Dynamic.Core;
 using System.Net;
 using System.Reflection;
 using System.Reflection.Emit;
+using Hotline.Caching.Interfaces;
 using XF.Domain.Authentications;
 using XF.Domain.Dependency;
 using XF.Domain.Repository;
+using System.Collections.Generic;
 
 namespace Hotline.Repository.SqlSugar.Orders
 {
@@ -32,20 +34,23 @@ namespace Hotline.Repository.SqlSugar.Orders
         private readonly IRepository<OrderVisit> _orderVisitRepository;
         private readonly IRepository<OrderVisitDetail> _orderVisitDetailRepository;
         private readonly ISessionContext _sessionContext;
+        private readonly ISystemSettingCacheManager _systemSettingCacheManager;
 
-        public OrderRepository(ISugarUnitOfWork<HotlineDbContext> uow,
+		public OrderRepository(ISugarUnitOfWork<HotlineDbContext> uow,
             IDataPermissionFilterBuilder dataPermissionFilterBuilder,
             IRepository<OrderSendBackAudit> orderSendBackAuditRepository,
             IRepository<OrderVisit> orderVisitRepository,
             ISessionContext sessionContext,
-            IRepository<OrderVisitDetail> orderVisitDetailRepository
-            ) : base(uow, dataPermissionFilterBuilder)
+            IRepository<OrderVisitDetail> orderVisitDetailRepository,
+            ISystemSettingCacheManager systemSettingCacheManager
+			) : base(uow, dataPermissionFilterBuilder)
         {
             _dataPermissionFilterBuilder = dataPermissionFilterBuilder;
             _orderSendBackAuditRepository = orderSendBackAuditRepository;
             _orderVisitRepository = orderVisitRepository;
             _sessionContext = sessionContext;
             _orderVisitDetailRepository = orderVisitDetailRepository;
+            _systemSettingCacheManager = systemSettingCacheManager;
 
         }
 
@@ -1265,16 +1270,11 @@ namespace Hotline.Repository.SqlSugar.Orders
                 .WhereIF(dto.ActualHandleTimeEnd.HasValue, x => x.OrderVisit.Order.ActualHandleTime <= dto.ActualHandleTimeEnd)//办结时间结束
                 .WhereIF(dto.VisitTimeStart.HasValue, x => x.OrderVisit.VisitTime >= dto.VisitTimeStart) //回访时间
                 .WhereIF(dto.VisitTimeEnd.HasValue, x => x.OrderVisit.VisitTime < dto.VisitTimeEnd) //回访时间
-                .OrderByIF(string.IsNullOrEmpty(dto.SortField),x=>x.OrderVisit.VisitTime,OrderByType.Desc)
-                .OrderByIF(dto is { SortField: "creationTime", SortRule:0 },x=>x.OrderVisit.Order.CreationTime,OrderByType.Asc) //受理时间升序
-                .OrderByIF(dto is { SortField: "creationTime", SortRule: 1 }, x => x.OrderVisit.Order.CreationTime, OrderByType.Desc) //受理时间降序
-                .OrderByIF(dto is { SortField: "visitTime", SortRule: 0 }, x => x.OrderVisit.VisitTime, OrderByType.Asc) //回访时间升序
-                .OrderByIF(dto is { SortField: "visitTime", SortRule: 1 }, x => x.OrderVisit.VisitTime, OrderByType.Desc) //回访时间降序
-                .OrderByIF(dto is { SortField: "filedTime", SortRule:0 },x=>x.OrderVisit.Order.FiledTime,OrderByType.Asc) //办结时间升序
-                .OrderByIF(dto is { SortField: "filedTime", SortRule: 1 }, x => x.OrderVisit.Order.FiledTime, OrderByType.Desc) //办结时间降序
+
                 .Select(x => new OrgVisitDetailListResp()
                 {
-                    Id = x.OrderVisit.Order.Id,
+                    Id =  x.Id,
+                    OrderId = x.OrderVisit.Order.Id,
                     VisitId = x.OrderVisit.Id,
                     No = x.OrderVisit.No,
                     ReTransactNum = x.OrderVisit.Order.ReTransactNum,
@@ -1295,7 +1295,14 @@ namespace Hotline.Repository.SqlSugar.Orders
                     FiledTime = x.OrderVisit.Order.FiledTime,
                     VisitOrgName = x.VisitOrgName,
                     ActualHandleOrgName = x.OrderVisit.Order.ActualHandleOrgName
-                });
+                }).MergeTable().OrderByIF(string.IsNullOrEmpty(dto.SortField), x => x.VisitTime, OrderByType.Desc)
+                .OrderByIF(dto is { SortField: "creationTime", SortRule: 0 }, x => x.CreationTime, OrderByType.Asc) //受理时间升序
+                .OrderByIF(dto is { SortField: "creationTime", SortRule: 1 }, x => x.CreationTime, OrderByType.Desc) //受理时间降序
+                .OrderByIF(dto is { SortField: "visitTime", SortRule: 0 }, x => x.VisitTime, OrderByType.Asc) //回访时间升序
+                .OrderByIF(dto is { SortField: "visitTime", SortRule: 1 }, x => x.VisitTime, OrderByType.Desc) //回访时间降序
+                .OrderByIF(dto is { SortField: "filedTime", SortRule: 0 }, x => x.FiledTime, OrderByType.Asc) //办结时间升序
+                .OrderByIF(dto is { SortField: "filedTime", SortRule: 1 }, x => x.FiledTime, OrderByType.Desc); //办结时间降序;
+
         }
 
         /// <summary>
@@ -1320,16 +1327,11 @@ namespace Hotline.Repository.SqlSugar.Orders
                 .WhereIF(dto.TypeId is 1, x => SqlFunc.JsonField(x.OrgProcessingResults, "Key") == dto.DateValue)
                 .WhereIF(dto.TypeId is 2, x => SqlFunc.JsonField(x.OrgHandledAttitude, "Key") == dto.DateValue)
                 .WhereIF(!string.IsNullOrEmpty(dto.LineNum), x => x.OrderVisit.Order.CallRecord.Gateway == dto.LineNum)
-                .OrderByIF(string.IsNullOrEmpty(dto.SortField), x => x.OrderVisit.VisitTime, OrderByType.Desc)
-                .OrderByIF(dto is { SortField: "startTime", SortRule: 0 }, x => x.OrderVisit.Order.StartTime, OrderByType.Asc) //受理时间升序
-                .OrderByIF(dto is { SortField: "startTime", SortRule: 1 }, x => x.OrderVisit.Order.StartTime, OrderByType.Desc) //受理时间降序
-                .OrderByIF(dto is { SortField: "visitTime", SortRule: 0 }, x => x.OrderVisit.VisitTime, OrderByType.Asc) //回访时间升序
-                .OrderByIF(dto is { SortField: "visitTime", SortRule: 1 }, x => x.OrderVisit.VisitTime, OrderByType.Desc) //回访时间降序
-                .OrderByIF(dto is { SortField: "filedTime", SortRule: 0 }, x => x.OrderVisit.Order.FiledTime, OrderByType.Asc) //办结时间升序
-                .OrderByIF(dto is { SortField: "filedTime", SortRule: 1 }, x => x.OrderVisit.Order.FiledTime, OrderByType.Desc) //办结时间降序
+
                 .Select(x => new OrgVisitDetailListResp
                 {
-                    Id = x.OrderVisit.Order.Id,
+                    Id = x.Id,
+                    OrderId = x.OrderVisit.Order.Id,
                     VisitId = x.OrderVisit.Id,
                     No = x.OrderVisit.No,
                     ReTransactNum = x.OrderVisit.Order.ReTransactNum,
@@ -1338,7 +1340,7 @@ namespace Hotline.Repository.SqlSugar.Orders
                     HotspotSpliceName = x.OrderVisit.Order.HotspotSpliceName,
                     OrgLevelOneName = x.OrderVisit.Order.OrgLevelOneName,
                     CurrentHandleOrgName = x.OrderVisit.Order.CurrentHandleOrgName,
-                    CreationTime = x.CreationTime,
+                    CreationTime = x.OrderVisit.Order.CreationTime,
                     Title = x.OrderVisit.Order.Title,
                     VisitUser = x.OrderVisit.Employee.Name,
                     VisitType = x.OrderVisit.VisitType,
@@ -1348,7 +1350,14 @@ namespace Hotline.Repository.SqlSugar.Orders
                     FileOpinion = x.OrderVisit.Order.FileOpinion,
                     FiledTime = x.OrderVisit.Order.FiledTime,
                     VisitOrgName = x.VisitOrgName
-                });
+                }).MergeTable()
+                .OrderByIF(string.IsNullOrEmpty(dto.SortField), x => x.VisitTime, OrderByType.Desc)
+                .OrderByIF(dto is { SortField: "creationTime", SortRule: 0 }, x => x.CreationTime, OrderByType.Asc) //受理时间升序
+                .OrderByIF(dto is { SortField: "creationTime", SortRule: 1 }, x => x.CreationTime, OrderByType.Desc) //受理时间降序
+                .OrderByIF(dto is { SortField: "visitTime", SortRule: 0 }, x => x.VisitTime, OrderByType.Asc) //回访时间升序
+                .OrderByIF(dto is { SortField: "visitTime", SortRule: 1 }, x => x.VisitTime, OrderByType.Desc) //回访时间降序
+                .OrderByIF(dto is { SortField: "filedTime", SortRule: 0 }, x => x.FiledTime, OrderByType.Asc) //办结时间升序
+                .OrderByIF(dto is { SortField: "filedTime", SortRule: 1 }, x => x.FiledTime, OrderByType.Desc); //办结时间降序;
         }
 
 		/// <summary>
@@ -1356,15 +1365,17 @@ namespace Hotline.Repository.SqlSugar.Orders
 		/// </summary>
 		/// <param name="dto"></param>
 		/// <returns></returns>
-		public ISugarQueryable<OrgVisitDetailListResp> OrgVisitDetailFiltrationList(OrgVisitDetailListReq dto)
+		public ISugarQueryable<OrgVisitDetailListResp> OrgVisitDetailFiltrationList(OrgVisitDetailListReq dto, string orgId)
 		{
 			var IsCenter = _sessionContext.OrgIsCenter;
+			var hiddenOrder = _systemSettingCacheManager.GetSetting(SettingConstants.OrgVisitDetailHiddenOrderNo).SettingValue;
 
 			return _orderVisitDetailRepository.Queryable()
 				.Includes(x => x.OrderVisit, x => x.Order)
 				.Includes(x => x.OrderVisit, x => x.Employee)
 				.Where(x => x.OrderVisit.VisitState == EVisitState.Visited && x.VisitTarget == EVisitTarget.Org)
-				.WhereIF(IsCenter == false, x => x.VisitOrgCode.StartsWith(_sessionContext.RequiredOrgId))
+                .WhereIF(hiddenOrder.Any(), x =>!hiddenOrder.Contains(x.OrderVisit.Order.No))
+                .WhereIF(IsCenter == false, x => x.VisitOrgCode.StartsWith(orgId))
 				.WhereIF(dto.OrgVisitStatisticsType.HasValue, x => x.OrderVisit.Order.ProcessType == (EProcessType)((int)dto.OrgVisitStatisticsType))
 				.WhereIF(dto.Keyword.NotNullOrEmpty(), x => x.OrderVisit.Order.Title.Contains(dto.Keyword)) // 根据关键字匹配
 				.WhereIF(dto.TypeCode != 0, x => x.OrderVisit.Order.IdentityType == (EIdentityType)dto.TypeCode)
@@ -1381,18 +1392,15 @@ namespace Hotline.Repository.SqlSugar.Orders
 				.WhereIF(dto.ActualHandleTimeEnd.HasValue, x => x.OrderVisit.Order.ActualHandleTime <= dto.ActualHandleTimeEnd)//办结时间结束
 				.WhereIF(dto.VisitTimeStart.HasValue, x => x.OrderVisit.VisitTime >= dto.VisitTimeStart) //回访时间
 				.WhereIF(dto.VisitTimeEnd.HasValue, x => x.OrderVisit.VisitTime < dto.VisitTimeEnd) //回访时间
-				.OrderByIF(string.IsNullOrEmpty(dto.SortField), x => x.OrderVisit.VisitTime, OrderByType.Desc)
-				.OrderByIF(dto is { SortField: "creationTime", SortRule: 0 }, x => x.OrderVisit.Order.CreationTime, OrderByType.Asc) //受理时间升序
-				.OrderByIF(dto is { SortField: "creationTime", SortRule: 1 }, x => x.OrderVisit.Order.CreationTime, OrderByType.Desc) //受理时间降序
-				.OrderByIF(dto is { SortField: "visitTime", SortRule: 0 }, x => x.OrderVisit.VisitTime, OrderByType.Asc) //回访时间升序
-				.OrderByIF(dto is { SortField: "visitTime", SortRule: 1 }, x => x.OrderVisit.VisitTime, OrderByType.Desc) //回访时间降序
-				.OrderByIF(dto is { SortField: "filedTime", SortRule: 0 }, x => x.OrderVisit.Order.FiledTime, OrderByType.Asc) //办结时间升序
-				.OrderByIF(dto is { SortField: "filedTime", SortRule: 1 }, x => x.OrderVisit.Order.FiledTime, OrderByType.Desc) //办结时间降序
-				.Where(x=> SqlFunc.JsonField(x.OrgProcessingResults, "Value") != "不满意" || SqlFunc.JsonField(x.OrgProcessingResults, "Value") != "非常不满意" || SqlFunc.JsonField(x.OrgProcessingResults, "Value") != "未做评价")
-				.Where(x=>x.VisitOrgCode.StartsWith(_sessionContext.RequiredOrgId))
+                    //特殊需求:要求新增查询条件【一级部门】,如查询【富顺县人民政府】则显示该一级部门以及下级部门的工单数据
+                .WhereIF(!string.IsNullOrEmpty(dto.LevelOneOrg), x=> x.OrderVisit.Order.OrgLevelOneName == dto.LevelOneOrg || x.OrderVisit.Order.OrgLevelOneCode == dto.LevelOneOrg)
+				
+				.Where(x=> SqlFunc.JsonField(x.OrgProcessingResults, "Key") != "2" && SqlFunc.JsonField(x.OrgProcessingResults, "Key") != "-1" && SqlFunc.JsonField(x.OrgProcessingResults, "Key") != "0" && SqlFunc.JsonField(x.OrgProcessingResults, "Key") != "7")
+				.Where(x=>x.VisitOrgCode.StartsWith(orgId))
 				.Select(x => new OrgVisitDetailListResp()
 				{
-					Id = x.OrderVisit.Order.Id,
+					Id = x.Id,
+                    OrderId = x.OrderVisit.Order.Id,
 					VisitId = x.OrderVisit.Id,
 					No = x.OrderVisit.No,
 					ReTransactNum = x.OrderVisit.Order.ReTransactNum,
@@ -1413,7 +1421,15 @@ namespace Hotline.Repository.SqlSugar.Orders
 					FiledTime = x.OrderVisit.Order.FiledTime,
 					VisitOrgName = x.VisitOrgName,
 					ActualHandleOrgName = x.OrderVisit.Order.ActualHandleOrgName
-				});
+				}).MergeTable()
+                .OrderByIF(string.IsNullOrEmpty(dto.SortField), x => x.VisitTime, OrderByType.Desc)
+                .OrderByIF(dto is { SortField: "creationTime", SortRule: 0 }, x => x.CreationTime, OrderByType.Asc) //受理时间升序
+                .OrderByIF(dto is { SortField: "creationTime", SortRule: 1 }, x => x.CreationTime, OrderByType.Desc) //受理时间降序
+                .OrderByIF(dto is { SortField: "visitTime", SortRule: 0 }, x => x.VisitTime, OrderByType.Asc) //回访时间升序
+                .OrderByIF(dto is { SortField: "visitTime", SortRule: 1 }, x => x.VisitTime, OrderByType.Desc) //回访时间降序
+                .OrderByIF(dto is { SortField: "filedTime", SortRule: 0 }, x => x.FiledTime, OrderByType.Asc) //办结时间升序
+                .OrderByIF(dto is { SortField: "filedTime", SortRule: 1 }, x => x.FiledTime, OrderByType.Desc) //办结时间降序
+                ;
 
 		}
 	}

+ 18 - 0
src/Hotline.Repository.SqlSugar/Snapshot/IndustryRepository.cs

@@ -0,0 +1,18 @@
+using Hotline.Orders;
+using Hotline.Repository.SqlSugar.DataPermissions;
+using Hotline.Snapshot;
+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 IndustryRepository : BaseRepository<Industry>, IIndustryRepository, IScopeDependency
+{
+    public IndustryRepository(ISugarUnitOfWork<HotlineDbContext> uow, IDataPermissionFilterBuilder dataPermissionFilterBuilder) : base(uow, dataPermissionFilterBuilder)
+    {
+    }
+}

+ 29 - 0
src/Hotline.Repository.SqlSugar/Snapshot/ThirdAccountRepository.cs

@@ -0,0 +1,29 @@
+using Hotline.Snapshot;
+using XF.Domain.Dependency;
+using XF.Domain.Repository;
+
+namespace Hotline.Repository.SqlSugar.Snapshot;
+public class ThirdAccountRepository : IThirdAccountRepository , IScopeDependency
+{
+    private readonly IRepository<ThirdAccount> _repository;
+
+    public ThirdAccountRepository(IRepository<ThirdAccount> repository)
+    {
+        _repository = repository;
+    }
+
+    public async Task<string> AddAsync(ThirdAccount entity)
+    {
+        return await _repository.AddAsync(entity);
+    }
+
+    public async Task<ThirdAccount> QueryByOpenIdAsync(string openId)
+        => await _repository.Queryable()
+        .Where(p => p.OpenId == openId)
+        .FirstAsync();
+
+    public async Task UpdateAsync(ThirdAccount entity)
+    {
+        await _repository.UpdateAsync(entity);
+    }
+}

+ 33 - 0
src/Hotline.Share/Dtos/Article/BulletinDto.cs

@@ -504,4 +504,37 @@ namespace Hotline.Share.Dtos.Article
 
         public int OrgCount { get; set; }
     }
+
+    /// <summary>
+    /// 微信小程序获取宣传学习列表入参
+    /// </summary>
+    public record BulletinInDto : PagedRequest
+    {
+        /// <summary>
+        /// 行业Id
+        /// <inheritdoc cref="Hotline.Snapshot.Industry" />表的Id
+        /// </summary>
+        public string IndustryId { get; set; }
+    }
+
+    /// <summary>
+    /// 微信小程序获取宣传学习列表出参
+    /// </summary>
+    public class BulletinOutDto
+    { 
+        /// <summary>
+        /// 公告ID
+        /// </summary>
+        public string Id { get; set; }
+
+        /// <summary>
+        /// 标题
+        /// </summary>
+        public string Title { get; set; }
+
+        /// <summary>
+        /// 内容
+        /// </summary>
+        public string Content { get; set; }
+    }
 }

+ 124 - 0
src/Hotline.Share/Dtos/GetCaseReultSendModel.cs

@@ -0,0 +1,124 @@
+using System.Text.Json.Serialization;
+
+namespace Hotline.Share.Dtos
+{
+    /// <summary>
+    /// 上报工单处理结果/退回
+    /// </summary>
+    public class GetCaseReultSendModel
+    {
+        /// <summary>
+        /// 答复单位
+        /// </summary>
+        [JsonPropertyName("DEPT_NAME")]
+        public string DEPT_NAME { get; set; }
+
+        /// <summary>
+        /// 答复时间
+        /// </summary>
+        [JsonPropertyName("FINISH_TIME")]
+        public string? FINISH_TIME { get; set; }
+
+        /// <summary>
+        /// 答复人员
+        /// </summary>
+        [JsonPropertyName("FINISH_NAME")]
+        public string FINISH_NAME { get; set; }
+
+        /// <summary>
+        /// 办理情况
+        /// </summary>
+        [JsonPropertyName("FINISH_NOTE")]
+        public string FINISH_NOTE { get; set; }
+
+        /// <summary>
+        /// 办理操作
+        /// </summary>
+        [JsonPropertyName("FINISH_TYPE")]
+        public string FINISH_TYPE { get; set; }
+
+        /// <summary>
+        /// 办理单位
+        /// </summary>
+        [JsonPropertyName("END_DEPT")]
+        public string END_DEPT { get; set; }
+
+        /// <summary>
+        /// 办理人员
+        /// </summary>
+        [JsonPropertyName("END_NAME")]
+        public string END_NAME { get; set; }
+
+        /// <summary>
+        /// 联系时间
+        /// </summary>
+        [JsonPropertyName("CONCACT_TIME")]
+        public string? CONCACT_TIME { get; set; }
+
+        /// <summary>
+        /// 联系方式
+        /// </summary>
+        [JsonPropertyName("CONCACT_TYPE")]
+        public string CONCACT_TYPE { get; set; }
+
+        /// <summary>
+        /// 签收时间
+        /// </summary>
+        [JsonPropertyName("SIGN_TIME")]
+        public string? SIGN_TIME { get; set; }
+
+        /// <summary>
+        /// 反馈时间
+        /// </summary>
+        [JsonPropertyName("FDBACKTIME")]
+        public string? FDBACKTIME { get; set; }
+
+        /// <summary>
+        /// 交办时间
+        /// </summary>
+        [JsonPropertyName("SEND_TIME")]
+        public string? SEND_TIME { get; set; }
+
+        /// <summary>
+        /// 反馈意见
+        /// </summary>
+        [JsonPropertyName("FDBACK")]
+        public string FDBACK { get; set; }
+
+        /// <summary>
+        /// 办理时长
+        /// </summary>
+        [JsonPropertyName("HANDLETIME_LONG")]
+        public decimal? HANDLETIME_LONG { get; set; }
+
+        /// <summary>
+        /// 任务单关联 guid
+        /// </summary>
+        [JsonPropertyName("TROWGUID")]
+        public string TROWGUID { get; set; }
+
+        /// <summary>
+        /// 退回类型
+        /// </summary>
+        [JsonPropertyName("BACKTYPE")]
+        public string BACKTYPE { get; set; }
+
+        /// <summary>
+        /// 服务工单编号
+        /// </summary>
+        [JsonPropertyName("CASE_SERIAL")]
+        public string CASE_SERIAL { get; set; }
+
+        /// <summary>
+        /// 行政区划代码
+        /// </summary>
+        [JsonPropertyName("AREA_CODE")]
+        public string AREA_CODE { get; set; }
+
+        /// <summary>
+        /// 材料标识
+        /// </summary>
+        [JsonPropertyName("CLIENG_GUID")]
+        public string CLIENG_GUID { get; set; }
+    }
+}

+ 50 - 0
src/Hotline.Share/Dtos/JudicialManagement/JudicialManagementAddOrderDto.cs

@@ -1,6 +1,7 @@
 using Hotline.Share.Dtos.File;
 using Hotline.Share.Enums.JudicialManagement;
 using Hotline.Share.Enums.Order;
+using Hotline.Share.Enums.Settings;
 
 namespace Hotline.Share.Dtos.JudicialManagement
 {
@@ -193,6 +194,55 @@ namespace Hotline.Share.Dtos.JudicialManagement
         /// </summary>
         public string IncidentAddressText => IncidentAddress();
 
+        /// <summary>
+        /// 交办时间
+        /// </summary>
+        public DateTime? CenterToOrgTime {  get; set; }
+
+        /// <summary>
+        /// 接办部门
+        /// </summary>
+        public string? ActualHandleOrgName {  get; set; }
+
+        /// <summary>
+        /// 接办时间
+        /// </summary>
+        public DateTime? ActualHandleTime {  get; set; }
+
+        /// <summary>
+        /// 办理时间限制(如:24小时、7个工作日)
+        /// </summary>
+        public string? TimeLimit { get; set; }
+
+        public int? TimeLimitCount { get; set; }
+
+        public ETimeType? TimeLimitUnit { get; set; }
+
+        /// <summary>
+        /// 工单期满时间
+        /// </summary>
+        public DateTime? ExpiredTime {  get; set; }
+
+        /// <summary>
+        /// 部门是否解决
+        /// </summary>
+        public bool IsResolved { get; set; }
+
+        /// <summary>
+        /// 部门处理结果
+        /// </summary>
+        public string IsResolvedText => IsResolved ? "已解决" : "未解决";
+
+        /// <summary>
+        /// 中心意见
+        /// </summary>
+        public string? CenterOpinion { get; set; }
+
+        /// <summary>
+        /// 承办意见
+        /// </summary>
+        public string? FileOpinion { get; set; }
+
         /// <summary>
         /// 组装省市区
         /// </summary>

+ 15 - 8
src/Hotline.Share/Dtos/Knowledge/KnowledgeDto.cs

@@ -33,10 +33,11 @@ namespace Hotline.Share.Dtos.Knowledge
     /// </summary>
     public class AddKnowledgeDto : KnowledgeDto
     {
-        /// <summary>
-        /// 知识Code
-        /// </summary>
-        public string Code { get; set; }
+
+		/// <summary>
+		/// 知识Code
+		/// </summary>
+		public string Code { get; set; }
 
         /// <summary>
         /// 标签名称
@@ -137,10 +138,16 @@ namespace Hotline.Share.Dtos.Knowledge
 	/// </summary>
 	public class KnowledgeDto : KnowledgeBaseDto
 	{
-        /// <summary>
-        /// 知识分类
-        /// </summary>
-        public List<KnowledgeRelationTypeDto> KnowledgeType { get; set; }
+
+		/// <summary>
+		/// 知识来源
+		/// </summary>
+		public string? Source { get; set; }
+
+		/// <summary>
+		/// 知识分类
+		/// </summary>
+		public List<KnowledgeRelationTypeDto> KnowledgeType { get; set; }
 
         /// <summary>
         /// 知识分类名称

+ 59 - 13
src/Hotline.Share/Dtos/Knowledge/KnowledgePagedDto.cs

@@ -21,20 +21,61 @@ namespace Hotline.Share.Dtos.Knowledge
 	/// <param name="EKnowledgeWorkFlowStatus">审核状态</param>
 	public record KnowledgeApprovalPagedListDto(EKnowledgeApplyType? EKnowledgeApplyType, EKnowledgeWorkFlowStatus? EKnowledgeWorkFlowStatus) : PagedKeywordRequest;
 
-    /// <summary>
-    /// 知识检索
-    /// </summary>
-    /// <param name="RetrievalType">检索类型</param>
-    /// <param name="Attribution">归属</param>
-    /// <param name="Sort">排序字段</param>
-    /// <param name="CreateOrgId">部门id</param>
-    /// <param name="HotspotId">热点id</param>
-    /// <param name="HotspotName">热点名称</param>;
-    /// <param name="KnowledgeTypeId">类型id</param>
-    /// <param name="Content">受理内容</param>
-    public record KnowledgeRetrievalPagedListDto(EKnowledgeRetrievalType? RetrievalType,string? Attribution,string Sort, string? CreateOrgId, string? HotspotId, string? HotspotName, string? KnowledgeTypeId, string? Content) : PagedKeywordRequest;
+	/// <summary>
+	/// 知识检索
+	/// </summary>
+	/// <param name="RetrievalType">检索类型</param>
+	/// <param name="Attribution">归属</param>
+	/// <param name="Sort">排序字段</param>
+	/// <param name="CreateOrgId">部门id</param>
+	/// <param name="HotspotId">热点id</param>
+	/// <param name="HotspotName">热点名称</param>;
+	/// <param name="KnowledgeTypeId">类型id</param>
+	/// <param name="Content">受理内容</param>
+	public record KnowledgeRetrievalPagedListDto : PagedKeywordRequest
+	{
+		/// <summary>
+		/// 检索类型
+		/// </summary>
+		public EKnowledgeRetrievalType? RetrievalType { get; set; }
 
-    /// <summary>
+		/// <summary>
+		/// 归属
+		/// </summary>
+		public string? Attribution { get; set; }
+
+		/// <summary>
+		/// 排序字段
+		/// </summary>
+		public string Sort { get; set; }
+
+		/// <summary>
+		/// 部门id
+		/// </summary>
+		public string? CreateOrgId { get; set; }
+
+		/// <summary>
+		/// 热点id
+		/// </summary>
+		public string? HotspotId { get; set; }
+
+		/// <summary>
+		/// 热点名称
+		/// </summary>
+		public string? HotspotName { get; set; }
+
+		/// <summary>
+		/// 类型id
+		/// </summary>
+		public string? KnowledgeTypeId { get; set; }
+
+		/// <summary>
+		/// 受理内容
+		/// </summary>
+		public string? Content { get; set; }
+	};
+
+	/// <summary>
     /// 来电弹屏知识库查询
     /// </summary>
     /// <param name="HotspotId">热点ID</param>
@@ -111,5 +152,10 @@ namespace Hotline.Share.Dtos.Knowledge
         /// </summary>
         public string? ModuleCode { get; set; }
 
+		/// <summary>
+		///   归属
+		/// </summary>
+		public string? Attribution { get; set; }
+
 	}
 }

+ 110 - 2
src/Hotline.Share/Dtos/Knowledge/KnowledgeTypeDto.cs

@@ -1,4 +1,7 @@
-namespace Hotline.Share.Dtos.Knowledge
+using Hotline.Settings;
+using XF.Utility.EnumExtensions;
+
+namespace Hotline.Share.Dtos.Knowledge
 {
     /// <summary>
     /// 编辑
@@ -77,5 +80,110 @@
         /// 树形分类
         /// </summary>
         public List<TreeListDto> TreeLists { get; set; } = new();
-    }
+
+        /// <summary>
+        /// 子级
+        /// </summary>
+        public List<KnowledgeTypeDto> children { get; set; }
+
+        public int KnowledgeNum { get; set; }
+	}
+
+    public record KnowledgeOrgDto
+    {
+	    /// <summary>
+	    /// ID
+	    /// </summary>
+	    public string Id { get; set; }
+
+		/// <summary>
+		/// 组织架构名称
+		/// </summary>
+		public string Name { get; set; }
+
+	    /// <summary>
+	    /// 组织架构简称
+	    /// </summary>
+	    public string ShortName { get; set; }
+
+	    /// <summary>
+	    /// 区域Code(行政区域代码)
+	    /// </summary>
+	    public string? AreaCode { get; set; }
+
+	    /// <summary>
+	    /// 区域名称(行政区域名称)
+	    /// </summary>
+	    public string? AreaName { get; set; }
+
+	    /// <summary>
+	    /// 部门级别
+	    /// </summary>
+	    public int Level { get; set; }
+
+	    /// <summary>
+	    /// 部门类型
+	    /// </summary>
+	    public EOrgType OrgType { get; set; }
+
+	    /// <summary>
+	    /// 上级ID
+	    /// </summary>
+	    public string? ParentId { get; set; }
+
+	    /// <summary>
+	    /// 上级名称
+	    /// </summary>
+	    public string? ParentName { get; set; }
+
+	    /// <summary>
+	    /// 是否启用
+	    /// </summary>
+	    public bool IsEnable { get; set; }
+
+	    /// <summary>
+	    /// 是否为中心
+	    /// </summary>
+	    public bool IsCenter { get; set; }
+
+	    public List<KnowledgeOrgDto> Children { get; set; }
+
+	    public string OrgTypeText => OrgType.GetDescription();
+
+
+	    /// <summary>
+	    /// 旧系统id
+	    /// </summary>
+	    public int? oldBmid { get; set; }
+
+	    public int KnowledgeNum { get; set; }
+	}
+
+    public record KnowledgeHotSpotDto
+    {
+	    /// <summary>
+	    /// ID
+	    /// </summary>
+	    public string Id { get; set; }
+
+	    public int KnowledgeNum { get; set; }
+
+		public string HotSpotName { get; set; }
+	    public string ParentId { get; set; }
+	    public string PYCode { get; set; }
+
+	    public string FullPYCode { get; set; }
+	    public string ProvinceCode { get; set; }
+	    public int OrderBy { get; set; }
+	    public string TrunkNum { get; set; }
+	    /// <summary>
+	    /// 生成的时候写入
+	    /// </summary>
+	    public string HotSpotFullName { get; set; }
+
+	    public List<KnowledgeHotSpotDto> Children { get; set; }
+
+	    public bool HasChild { get; set; }
+
+	}
 }

+ 9 - 4
src/Hotline.Share/Dtos/Order/OrderBiDto.cs

@@ -279,10 +279,15 @@ namespace Hotline.Share.Dtos.Order
 
 	public class OrgVisitDetailListResp
 	{
-		/// <summary>
-		/// 工单ID
-		/// </summary>
-		public string Id { get; set; }
+        /// <summary>
+        /// 回访子ID
+        /// </summary>
+        public string Id { get; set; }
+
+        /// <summary>
+        /// 工单ID
+        /// </summary>
+        public string OrderId { get; set; }
 
 		/// <summary>
 		/// 回访ID

+ 6 - 4
src/Hotline.Share/Dtos/Order/OrderDto.cs

@@ -29,10 +29,12 @@ namespace Hotline.Share.Dtos.Order
 
         public DateTime CreationTime { get; set; }
 
-        /// <summary>
-        /// 工单状态
-        /// </summary>
-        public EOrderStatus Status { get; set; }
+        public string ActualHandleOrgName { get; set; }
+
+		/// <summary>
+		/// 工单状态
+		/// </summary>
+		public EOrderStatus Status { get; set; }
 
         public string StatusText => Status.GetDescription();
     }

+ 345 - 340
src/Hotline.Share/Dtos/Order/OrderScreenDto.cs

@@ -11,365 +11,370 @@ using XF.Utility.EnumExtensions;
 
 namespace Hotline.Share.Dtos.Order
 {
-	public class OrderScreenDto
-	{
-
-		/// <summary>
-		/// 工单编号
-		/// </summary>
-		public string Id { get; set; }
-
-		/// <summary>
-		/// 工单编号
-		/// </summary>
-		public string No { get; set; }
-
-		/// <summary>
-		/// 回访id
-		/// </summary>
-		public string VisitId { get; set; }
-
-		/// <summary>
-		/// 回访明细id
-		/// </summary>
-		public string VisitDetailId { get; set; }
-
-		/// <summary>
-		/// 工单id
-		/// </summary>
-		public string OrderId { get; set; }
-
-		/// <summary>
-		/// 甄别申请类型id
-		/// </summary>
-		public string TypeDicId { get; set; }
-
-		/// <summary>
-		/// 甄别申请类型名称
-		/// </summary>
-		public string TypeDicName { get; set; }
-
-
-		/// <summary>
-		/// 甄别申请原因
-		/// </summary>
-		public string Content { get; set; }
-
-		/// <summary>
-		/// 甄别申请回复
-		/// </summary>
-		public string? ReplyContent { get; set; }
-
-		/// <summary>
-		/// 甄别申请状态
-		/// </summary>
-		public EScreenStatus? Status { get; set; }
-
-		/// <summary>
-		/// 甄别截至申请时间
-		/// </summary>
-		public DateTime? ApplyEndTime { get; set; }
-
-		/// <summary>
-		/// 业务流程ID
-		/// </summary>
-		public string? WorkflowId { get; set; }
-
-		/// <summary>
-		/// 附件列表
-		/// </summary>
-		public List<FileDto> Files { get; set; } = new();
-
-		public List<FileJson>? FileJson { get; set; }
-
-		/// <summary>
-		/// 甄别退回次数
-		/// </summary>
-		public int SendBackNum { get; set; }
-
-		/// <summary>
-		/// 甄别退回到申请人
-		/// </summary>
-		public bool SendBackApply { get; set; }
-
-		/// <summary>
-		/// 甄别重提申请次数
-		/// </summary>
-		public int SendBackApplyNum { get; set; }
-
-		/// <summary>
-		/// 甄别类型
-		/// </summary>
-		public EOrderScreenType? ScreenType { get; set; }
-	}
-
-	public class OrderScreenListDto
-	{
-
-		/// <summary>
-		/// 甄别类型
-		/// </summary>
-		public EOrderScreenType? ScreenType { get; set; }
-
-		public string ScreenTypeText => ScreenType.HasValue ? ScreenType.GetDescription() : string.Empty;
-
-		/// <summary>
-		/// 是否可办理
-		/// </summary>
-		public bool IsCanHandle { get; set; }
-
-
-		/// <summary>
-		/// 办理 true  审批 false 
-		/// </summary>
-		public bool Handle { get; set; }
+    public class OrderScreenDto
+    {
+
+        /// <summary>
+        /// 工单编号
+        /// </summary>
+        public string Id { get; set; }
+
+        /// <summary>
+        /// 工单编号
+        /// </summary>
+        public string No { get; set; }
+
+        /// <summary>
+        /// 回访id
+        /// </summary>
+        public string VisitId { get; set; }
+
+        /// <summary>
+        /// 回访明细id
+        /// </summary>
+        public string VisitDetailId { get; set; }
+
+        /// <summary>
+        /// 工单id
+        /// </summary>
+        public string OrderId { get; set; }
+
+        /// <summary>
+        /// 甄别申请类型id
+        /// </summary>
+        public string TypeDicId { get; set; }
+
+        /// <summary>
+        /// 甄别申请类型名称
+        /// </summary>
+        public string TypeDicName { get; set; }
+
+
+        /// <summary>
+        /// 甄别申请原因
+        /// </summary>
+        public string Content { get; set; }
+
+        /// <summary>
+        /// 甄别申请回复
+        /// </summary>
+        public string? ReplyContent { get; set; }
+
+        /// <summary>
+        /// 甄别申请状态
+        /// </summary>
+        public EScreenStatus? Status { get; set; }
+
+        /// <summary>
+        /// 甄别截至申请时间
+        /// </summary>
+        public DateTime? ApplyEndTime { get; set; }
+
+        /// <summary>
+        /// 业务流程ID
+        /// </summary>
+        public string? WorkflowId { get; set; }
+
+        /// <summary>
+        /// 附件列表
+        /// </summary>
+        public List<FileDto> Files { get; set; } = new();
+
+        public List<FileJson>? FileJson { get; set; }
+
+        /// <summary>
+        /// 甄别退回次数
+        /// </summary>
+        public int SendBackNum { get; set; }
+
+        /// <summary>
+        /// 甄别退回到申请人
+        /// </summary>
+        public bool SendBackApply { get; set; }
+
+        /// <summary>
+        /// 甄别重提申请次数
+        /// </summary>
+        public int SendBackApplyNum { get; set; }
+
+        /// <summary>
+        /// 甄别类型
+        /// </summary>
+        public EOrderScreenType? ScreenType { get; set; }
+    }
+
+    public class OrderScreenListDto
+    {
+
+        /// <summary>
+        /// 甄别类型
+        /// </summary>
+        public EOrderScreenType? ScreenType { get; set; }
+
+        public string ScreenTypeText => ScreenType.HasValue ? ScreenType.GetDescription() : string.Empty;
+
+        /// <summary>
+        /// 是否可办理
+        /// </summary>
+        public bool IsCanHandle { get; set; }
+
+
+        /// <summary>
+        /// 办理 true  审批 false 
+        /// </summary>
+        public bool Handle { get; set; }
 
-		/// <summary>
-		/// 工单编号
-		/// </summary>
-		public string No { get; set; }
+        /// <summary>
+        /// 工单编号
+        /// </summary>
+        public string No { get; set; }
 
-		/// <summary>
-		/// 工单id
-		/// </summary>
-		public string VisitId { get; set; }
+        /// <summary>
+        /// 工单id
+        /// </summary>
+        public string VisitId { get; set; }
 
-		/// <summary>
-		/// 工单id
-		/// </summary>
-		public string OrderId { get; set; }
+        /// <summary>
+        /// 工单id
+        /// </summary>
+        public string OrderId { get; set; }
 
-		/// <summary>
-		/// 回访
-		/// </summary>
-		public OrderVisitDto Visit { get; set; }
+        /// <summary>
+        /// 回访
+        /// </summary>
+        public OrderVisitDto Visit { get; set; }
 
-		/// <summary>
-		/// 工单
-		/// </summary>
-		public OrderDto Order { get; set; }
+        /// <summary>
+        /// 工单
+        /// </summary>
+        public OrderDto Order { get; set; }
 
 
-		public WorkflowDto Workflow { get; set; }
+        public WorkflowDto Workflow { get; set; }
 
-		/// <summary>
-		/// 甄别明细
-		/// </summary>
-		public OrderScreenDetailDto ScreenDetail => ScreenDetails != null && ScreenDetails.Any() ? ScreenDetails.First() : null;
+        /// <summary>
+        /// 甄别明细
+        /// </summary>
+        public OrderScreenDetailDto ScreenDetail => ScreenDetails != null && ScreenDetails.Any() ? ScreenDetails.First() : null;
 
-		/// <summary>
-		/// 甄别明细
-		/// </summary>
-		public List<OrderScreenDetailDto> ScreenDetails { get; set; }
+        /// <summary>
+        /// 甄别明细
+        /// </summary>
+        public List<OrderScreenDetailDto> ScreenDetails { get; set; }
 
-		/// <summary>
-		/// 回访明细id
-		/// </summary>
-		public string VisitDetailId { get; set; }
+        /// <summary>
+        /// 回访明细id
+        /// </summary>
+        public string VisitDetailId { get; set; }
 
-		/// <summary>
-		/// 回访
-		/// </summary>
-		public OrderVisitDetailDto VisitDetail { get; set; }
+        /// <summary>
+        /// 回访
+        /// </summary>
+        public OrderVisitDetailDto VisitDetail { get; set; }
 
-		/// <summary>
-		/// 甄别申请类型id
-		/// </summary>
-		public string? TypeDicId { get; set; }
+        /// <summary>
+        /// 甄别申请类型id
+        /// </summary>
+        public string? TypeDicId { get; set; }
 
-		/// <summary>
-		/// 甄别申请类型名称
-		/// </summary>
-		public string? TypeDicName { get; set; }
+        /// <summary>
+        /// 甄别申请类型名称
+        /// </summary>
+        public string? TypeDicName { get; set; }
 
 
-		/// <summary>
-		/// 甄别申请原因
-		/// </summary>
-		public string? Content { get; set; }
+        /// <summary>
+        /// 甄别申请原因
+        /// </summary>
+        public string? Content { get; set; }
 
-		/// <summary>
-		/// 甄别申请回复
-		/// </summary>
-		public string? ReplyContent { get; set; }
+        /// <summary>
+        /// 甄别申请回复
+        /// </summary>
+        public string? ReplyContent { get; set; }
 
 
-		/// <summary>
-		/// 甄别截至申请时间
-		/// </summary>
-		public DateTime? ApplyEndTime { get; set; }
+        /// <summary>
+        /// 甄别截至申请时间
+        /// </summary>
+        public DateTime? ApplyEndTime { get; set; }
 
-		/// <summary>
-		/// 甄别申请状态
-		/// </summary>
-		public EScreenStatus? Status { get; set; }
+        /// <summary>
+        /// 甄别申请状态
+        /// </summary>
+        public EScreenStatus? Status { get; set; }
 
-		public string StatusText => Status.GetDescription();
+        public string StatusText => Status.GetDescription();
 
-		public DateTime? LastModificationTime { get; set; }
-
-		public bool IsDeleted { get; set; }
-
-		/// <summary>
-		/// 删除时间
-		/// </summary>
-		public DateTime? DeletionTime { get; set; }
-
-
-		/// <summary>
-		/// 创建时间
-		/// </summary>
-		public DateTime CreationTime { get; set; }
-
-		public string Id { get; set; }
-
-		/// <summary>
-		/// 组织Id
-		/// </summary>
-		public string? CreatorOrgId { get; set; }
-
-
-		public string? CreatorOrgName { get; set; }
-
-		/// <summary>
-		/// 创建人
-		/// </summary>
-		public string? CreatorId { get; set; }
-
-		public string? CreatorName { get; set; }
-
-		/// <summary>
-		/// 业务流程ID
-		/// </summary>
-		public string? WorkflowId { get; set; }
-
-		/// <summary>
-		/// 附件
-		/// </summary>
-		public List<FileJson>? FileJson { get; set; } = new();
-
-		public List<FileDto> Files { get; set; }
-
-		/// <summary>
-		/// 甄别耗时
-		/// </summary>
-		public decimal? TimeConsuming { get; set; }
-
-		public string TimeConsuminText => TimeConsuming.HasValue ? TimeConsuming.ToString() + "天" : "-";
-		/// <summary>
-		/// 甄别退回次数
-		/// </summary>
-		public int SendBackNum { get; set; }
-
-		/// <summary>
-		/// 甄别退回到申请人
-		/// </summary>
-		public bool SendBackApply { get; set; }
-
-		/// <summary>
-		/// 甄别退回
-		/// </summary>
-		public string ScreenSendBack => Status  == EScreenStatus.SendBack && SendBackApply ? "是" : "否";
-
-		/// <summary>
-		/// 甄别重提申请次数
-		/// </summary>
-		public int SendBackApplyNum { get; set; }
-
-		/// <summary>
-		/// 重提甄别
-		/// </summary>
-		public string ScreenSendBackApply => SendBackApplyNum > 0 ? "是" : "否";
-
-		/// <summary>
-		/// 甄别退回时间
-		/// </summary>
-		public DateTime? SendBackTime { get; set; }
-
-		/// <summary>
-		/// 甄别审批时间
-		/// </summary>
-		public DateTime? NewestAuditTime { get; set; }
-	}
-
-	public class PublishScreenDto {
-		// <summary>
-		/// 工单对象
-		/// </summary>
-		public OrderDto Order { get; set; }
-
-		/// <summary>
-		/// 甄别
-		/// </summary>
-		public OrderScreenListDto Screen { get; set; }
-
-		/// <summary>
-		/// 附件
-		/// </summary>
-		public string ClientGuid { get; set; }
-	}
-	public class ProvinceScreenResult
-	{
-		/// <summary>
-		/// 省工单编号
-		/// </summary>
-		public string CaseSerial { get; set; }
-
-		/// <summary>
-		/// 审核结果是否通过
-		/// </summary>
-		public bool AuditResult { get; set; }
-
-		/// <summary>
-		/// 审核意见
-		/// </summary>
-		public string AuditOpinion { get; set; }
-
-		/// <summary>
-		/// 审核时间
-		/// </summary>
-		public DateTime AuditTime { get; set; }
-
-		/// <summary>
-		/// 审核部门
-		/// </summary>
-		public string AuditOuname { get; set; }
-
-		/// <summary>
-		/// 审核人
-		/// </summary>
-		public string AuditPerson { get; set; }
-
-		/// <summary>
-		/// 行政区划代码
-		/// </summary>
-		public string AreaCode { get; set; }
-	}
-
-	public class OrderScreenEndTimeDto {
-		/// <summary>
-		/// 回访明细id
-		/// </summary>
-		public string Id { get; set; }
-
-		/// <summary>
-		/// 截至甄别时间
-		/// </summary>
-		public DateTime ScreenByEndTime { get; set; }
-	}
-
-	public enum EOrderScreenType 
-	{
-		/// <summary>
-		/// 部门办件
-		/// </summary>
-		[Description("部门办件")]
-		Org = 0,
-
-		/// <summary>
-		/// 中心话务
-		/// </summary>
-		[Description("中心话务")]
-		Seat =1,
-	}
+        public DateTime? LastModificationTime { get; set; }
+
+        public bool IsDeleted { get; set; }
+
+        /// <summary>
+        /// 删除时间
+        /// </summary>
+        public DateTime? DeletionTime { get; set; }
+
+
+        /// <summary>
+        /// 创建时间
+        /// </summary>
+        public DateTime CreationTime { get; set; }
+
+        public string Id { get; set; }
+
+        /// <summary>
+        /// 组织Id
+        /// </summary>
+        public string? CreatorOrgId { get; set; }
+
+
+        public string? CreatorOrgName { get; set; }
+
+        /// <summary>
+        /// 创建人
+        /// </summary>
+        public string? CreatorId { get; set; }
+
+        public string? CreatorName { get; set; }
+
+        /// <summary>
+        /// 业务流程ID
+        /// </summary>
+        public string? WorkflowId { get; set; }
+
+        /// <summary>
+        /// 附件
+        /// </summary>
+        public List<FileJson>? FileJson { get; set; } = new();
+
+        public List<FileDto> Files { get; set; }
+
+        /// <summary>
+        /// 甄别耗时
+        /// </summary>
+        public decimal? TimeConsuming { get; set; }
+
+        public string TimeConsuminText => TimeConsuming.HasValue ? TimeConsuming.ToString() + "天" : "-";
+        /// <summary>
+        /// 甄别退回次数
+        /// </summary>
+        public int SendBackNum { get; set; }
+
+        /// <summary>
+        /// 甄别退回到申请人
+        /// </summary>
+        public bool SendBackApply { get; set; }
+
+        /// <summary>
+        /// 甄别退回
+        /// </summary>
+        public string ScreenSendBack => Status == EScreenStatus.SendBack && SendBackApply ? "是" : "否";
+
+        /// <summary>
+        /// 甄别重提申请次数
+        /// </summary>
+        public int SendBackApplyNum { get; set; }
+
+        /// <summary>
+        /// 重提甄别
+        /// </summary>
+        public string ScreenSendBackApply => SendBackApplyNum > 0 ? "是" : "否";
+
+        /// <summary>
+        /// 甄别退回时间
+        /// </summary>
+        public DateTime? SendBackTime { get; set; }
+
+        /// <summary>
+        /// 甄别审批时间
+        /// </summary>
+        public DateTime? NewestAuditTime { get; set; }
+    }
+
+    public class PublishScreenDto
+    {
+        // <summary>
+        /// 工单对象
+        /// </summary>
+        public OrderDto Order { get; set; }
+
+        /// <summary>
+        /// 甄别
+        /// </summary>
+        public OrderScreenListDto Screen { get; set; }
+
+        /// <summary>
+        /// 附件
+        /// </summary>
+        public string ClientGuid { get; set; }
+    }
+    public class ProvinceScreenResult
+    {
+        /// <summary>
+        /// 省工单编号
+        /// </summary>
+        public string CaseSerial { get; set; }
+
+        /// <summary>
+        /// 审核结果是否通过
+        /// 初审不通过	0	可以再次申请甄别
+        ///终审通过	    1	通过办结工单
+        ///终审不通过	2	不可以再申请甄别
+        /// </summary>
+        public string AuditResult { get; set; }
+
+        /// <summary>
+        /// 审核意见
+        /// </summary>
+        public string AuditOpinion { get; set; }
+
+        /// <summary>
+        /// 审核时间
+        /// </summary>
+        public DateTime AuditTime { get; set; }
+
+        /// <summary>
+        /// 审核部门
+        /// </summary>
+        public string AuditOuname { get; set; }
+
+        /// <summary>
+        /// 审核人
+        /// </summary>
+        public string AuditPerson { get; set; }
+
+        /// <summary>
+        /// 行政区划代码
+        /// </summary>
+        public string AreaCode { get; set; }
+    }
+
+    public class OrderScreenEndTimeDto
+    {
+        /// <summary>
+        /// 回访明细id
+        /// </summary>
+        public string Id { get; set; }
+
+        /// <summary>
+        /// 截至甄别时间
+        /// </summary>
+        public DateTime ScreenByEndTime { get; set; }
+    }
+
+    public enum EOrderScreenType
+    {
+        /// <summary>
+        /// 部门办件
+        /// </summary>
+        [Description("部门办件")]
+        Org = 0,
+
+        /// <summary>
+        /// 中心话务
+        /// </summary>
+        [Description("中心话务")]
+        Seat = 1,
+    }
 }

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

@@ -477,4 +477,13 @@ namespace Hotline.Share.Dtos.Order
 
 		public OrderSpecialDto OrderSpecial { get; set; }
 	}
+
+	public class OrgBatchSpecialDto
+	{
+		public string OrgId { get; set; }
+
+		public DateTime StartTime { get; set; }
+
+		public DateTime EndTime { get; set; }
+	}
 }

Some files were not shown because too many files changed in this diff