guqiang před 1 měsícem
rodič
revize
968783f240
100 změnil soubory, kde provedl 2996 přidání a 828 odebrání
  1. 1 1
      Hotline.sln
  2. 2 1
      src/Hotline.Api/Controllers/BaseController.cs
  3. 3 1
      src/Hotline.Api/Controllers/Bi/BiCallController.cs
  4. 444 92
      src/Hotline.Api/Controllers/Bi/BiOrderController.cs
  5. 6 8
      src/Hotline.Api/Controllers/Bigscreen/DataScreenController.cs
  6. 3 3
      src/Hotline.Api/Controllers/FwThirdController.cs
  7. 97 80
      src/Hotline.Api/Controllers/IPPbxController.cs
  8. 434 85
      src/Hotline.Api/Controllers/OrderController.cs
  9. 3 3
      src/Hotline.Api/Controllers/Snapshot/BiSnapshotController.cs
  10. 3 3
      src/Hotline.Api/Controllers/Snapshot/IndustryController.cs
  11. 2 2
      src/Hotline.Api/Controllers/Snapshot/InviteCodeController.cs
  12. 40 0
      src/Hotline.Api/Controllers/Snapshot/PointsController.cs
  13. 3 3
      src/Hotline.Api/Controllers/Snapshot/RedPackController.cs
  14. 2 2
      src/Hotline.Api/Controllers/Snapshot/SnapshotBulletinController.cs
  15. 32 5
      src/Hotline.Api/Controllers/Snapshot/SnapshotController.cs
  16. 2 1
      src/Hotline.Api/Controllers/Snapshot/SnapshotOrderController.cs
  17. 90 7
      src/Hotline.Api/Controllers/TelRestController.cs
  18. 21 1
      src/Hotline.Api/Controllers/TestController.cs
  19. 5 2
      src/Hotline.Api/StartupExtensions.cs
  20. 10 1
      src/Hotline.Api/StartupHelper.cs
  21. 50 14
      src/Hotline.Application/CallCenter/DefaultCallApplication.cs
  22. 1 1
      src/Hotline.Application/Handlers/FlowEngine/WorkflowNextHandler.cs
  23. 6 4
      src/Hotline.Application/Identity/IdentityAppService.cs
  24. 14 0
      src/Hotline.Application/Mappers/OrderMapperConfigs.cs
  25. 18 2
      src/Hotline.Application/OrderApp/IOrderApplication.cs
  26. 162 144
      src/Hotline.Application/OrderApp/OrderAnalysisApplication.cs
  27. 242 10
      src/Hotline.Application/OrderApp/OrderApplication.cs
  28. 3 2
      src/Hotline.Application/Snapshot/BiSnapshotApplication.cs
  29. 1 1
      src/Hotline.Application/Snapshot/Contracts/IBiSnapshotApplication.cs
  30. 2 2
      src/Hotline.Application/Snapshot/Contracts/IIndustryApplication.cs
  31. 1 1
      src/Hotline.Application/Snapshot/Contracts/IInviteCodeApplication.cs
  32. 1 1
      src/Hotline.Application/Snapshot/Contracts/IOrderSnapshotApplication.cs
  33. 1 1
      src/Hotline.Application/Snapshot/Contracts/IRedPackApplication.cs
  34. 22 1
      src/Hotline.Application/Snapshot/Contracts/ISnapshotApplication.cs
  35. 1 1
      src/Hotline.Application/Snapshot/Contracts/ISnapshotBulletinApplication.cs
  36. 15 0
      src/Hotline.Application/Snapshot/Contracts/ISnapshotPointsApplication.cs
  37. 2 1
      src/Hotline.Application/Snapshot/DefaultSnapshotApplication.cs
  38. 5 3
      src/Hotline.Application/Snapshot/IndustryApplication.cs
  39. 3 2
      src/Hotline.Application/Snapshot/InviteCodeApplication.cs
  40. 41 1
      src/Hotline.Application/Snapshot/Notifications/SnapshotHandler.cs
  41. 24 7
      src/Hotline.Application/Snapshot/RedPackApplication.cs
  42. 94 9
      src/Hotline.Application/Snapshot/SnapshotApplicationBase.cs
  43. 3 2
      src/Hotline.Application/Snapshot/SnapshotBulletinApplication.cs
  44. 10 5
      src/Hotline.Application/Snapshot/SnapshotOrderApplication.cs
  45. 63 0
      src/Hotline.Application/Snapshot/SnapshotPointsApplication.cs
  46. 1 1
      src/Hotline.Application/Snapshot/SnapshotThirdAccountSupplier.cs
  47. 2 1
      src/Hotline.Application/Snapshot/ZiGongSnapshotApplication.cs
  48. 13 10
      src/Hotline.Application/StatisticalReport/CallReport/CallReportApplicationBase.cs
  49. 26 20
      src/Hotline.Application/StatisticalReport/CallReport/YiBinCallReportApplication.cs
  50. 4 1
      src/Hotline.Application/StatisticalReport/IOrderReportApplication.cs
  51. 69 25
      src/Hotline.Application/StatisticalReport/OrderReportApplication.cs
  52. 0 1
      src/Hotline.Application/Subscribers/DatasharingSubscriber.cs
  53. 8 2
      src/Hotline.Repository.SqlSugar/File/FileRepository.cs
  54. 1 1
      src/Hotline.Repository.SqlSugar/Snapshot/CommunityInfoRepository.cs
  55. 1 1
      src/Hotline.Repository.SqlSugar/Snapshot/IndustryCaseRepository.cs
  56. 1 1
      src/Hotline.Repository.SqlSugar/Snapshot/IndustryLogRepository.cs
  57. 1 1
      src/Hotline.Repository.SqlSugar/Snapshot/IndustryRepository.cs
  58. 1 1
      src/Hotline.Repository.SqlSugar/Snapshot/InviteCodeRecordRepository.cs
  59. 2 2
      src/Hotline.Repository.SqlSugar/Snapshot/InviteCodeRepository.cs
  60. 1 1
      src/Hotline.Repository.SqlSugar/Snapshot/OrderSnapshotRepository.cs
  61. 1 1
      src/Hotline.Repository.SqlSugar/Snapshot/PractitionerRepository.cs
  62. 1 1
      src/Hotline.Repository.SqlSugar/Snapshot/RedPackAuditRepository.cs
  63. 1 1
      src/Hotline.Repository.SqlSugar/Snapshot/RedPackGuiderAuditRepository.cs
  64. 1 1
      src/Hotline.Repository.SqlSugar/Snapshot/RedPackRecordRepository.cs
  65. 1 1
      src/Hotline.Repository.SqlSugar/Snapshot/SnapshotBulletinRepository.cs
  66. 1 1
      src/Hotline.Repository.SqlSugar/Snapshot/SnapshotLabelLogRepository.cs
  67. 1 1
      src/Hotline.Repository.SqlSugar/Snapshot/SnapshotOrderPublishRepository.cs
  68. 24 0
      src/Hotline.Repository.SqlSugar/Snapshot/SnapshotPointsRecordRepository.cs
  69. 1 1
      src/Hotline.Repository.SqlSugar/Snapshot/SnapshotSMSTemplateRepository.cs
  70. 1 1
      src/Hotline.Repository.SqlSugar/Snapshot/SpecialRedPackAuditRepository.cs
  71. 1 1
      src/Hotline.Repository.SqlSugar/Snapshot/SupplementRecordRepository.cs
  72. 1 1
      src/Hotline.Repository.SqlSugar/Snapshot/VolunteerReportRepository.cs
  73. 1 1
      src/Hotline.Repository.SqlSugar/Snapshot/VolunteerRepository.cs
  74. 2 1
      src/Hotline.Share/Dtos/Article/BulletinDto.cs
  75. 89 9
      src/Hotline.Share/Dtos/CallCenter/CenterReportStatisticsDto.cs
  76. 5 2
      src/Hotline.Share/Dtos/CallCenter/QueryCallsFixedDto.cs
  77. 34 4
      src/Hotline.Share/Dtos/File/FileDto.cs
  78. 9 0
      src/Hotline.Share/Dtos/Order/OrderBiDto.cs
  79. 43 12
      src/Hotline.Share/Dtos/Order/OrderDto.cs
  80. 172 165
      src/Hotline.Share/Dtos/Order/OrderSuperviseDto.cs
  81. 68 16
      src/Hotline.Share/Dtos/Order/OrderVisitDto.cs
  82. 6 0
      src/Hotline.Share/Dtos/Order/QueryOrderDto.cs
  83. 45 0
      src/Hotline.Share/Dtos/Snapshot/IndustryDto.cs
  84. 1 1
      src/Hotline.Share/Dtos/Snapshot/IndustryFileDto.cs
  85. 56 12
      src/Hotline.Share/Dtos/Snapshot/OrderDto.cs
  86. 5 0
      src/Hotline.Share/Dtos/Snapshot/OrderPublishDto.cs
  87. 187 0
      src/Hotline.Share/Dtos/Snapshot/PointsDto.cs
  88. 1 0
      src/Hotline.Share/Dtos/Snapshot/ThirdTokenDto.cs
  89. 15 0
      src/Hotline.Share/Enums/CallCenter/ECallDirection.cs
  90. 6 1
      src/Hotline.Share/Enums/Order/ESource.cs
  91. 29 0
      src/Hotline.Share/Enums/Snapshot/EPointsSource.cs
  92. 1 1
      src/Hotline.Share/Hotline.Share.csproj
  93. 9 4
      src/Hotline.Share/Mq/EventNames.Order.cs
  94. 1 1
      src/Hotline.Share/Tools/DataMaskExtensions.cs
  95. 13 0
      src/Hotline.Share/Tools/StringExtensions.cs
  96. 5 0
      src/Hotline/AppDefaults.cs
  97. 12 0
      src/Hotline/Caching/Interfaces/ISysDicDataCacheManager.cs
  98. 24 0
      src/Hotline/Caching/Services/SysDicDataCacheManager.cs
  99. 1 1
      src/Hotline/CallCenter/Tels/TelOperation.cs
  100. 2 1
      src/Hotline/File/IFileRepository.cs

+ 1 - 1
Hotline.sln

@@ -61,7 +61,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hotline.Tests", "test\Hotli
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Hotline.Pdf", "src\Hotline.Pdf\Hotline.Pdf.csproj", "{3AB75B51-A69D-4145-A564-1D9D1695992E}"
 EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hotline.Ai.Jths", "src\Hotline.Ai.Jths\Hotline.Ai.Jths.csproj", "{B05DB089-31DB-2BF5-5959-C816ECD7EFA4}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Hotline.Tests", "test\Hotline.Tests\Hotline.Tests.csproj", "{31855124-4EFC-47B9-A4D5-64822DE036E6}"
 EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution

+ 2 - 1
src/Hotline.Api/Controllers/BaseController.cs

@@ -2,13 +2,14 @@
 using Microsoft.AspNetCore.Mvc;
 using MiniExcelLibs;
 using System.IO;
+using Microsoft.AspNetCore.Authorization;
 
 namespace Hotline.Api.Controllers;
 
 [ApiController]
 [Produces("application/json")]
 [Route("api/v1/[controller]")]
-//[LogFilter]
+[Authorize(Policy = AppDefaults.AuthPolicy.Hotline)]
 public class BaseController : ControllerBase
 {
     protected FileStreamResult ExcelStreamResult(Stream stream, string fileName = null)

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

@@ -734,9 +734,11 @@ public class BiCallController : BaseController
     [HttpGet("query-seat-monthcall-basedata")]
     public async Task<object> QuerySeatMonthCallBaseData()
     {
+        var setting = _systemSettingCacheManager.GetSetting(SettingConstants.RoleZuoXi).SettingValue;
+       
         return new
         {
-            SeatUser =await _userRepository.Queryable().Where(x => x.UserType == Share.Enums.User.EUserType.Seat).ToListAsync()            
+            SeatUser =await _userRepository.Queryable().Includes(d => d.Roles).Where(d => d.Roles.Any(x => setting.Contains(x.Name))).ToListAsync()            
         };
     }
 

+ 444 - 92
src/Hotline.Api/Controllers/Bi/BiOrderController.cs

@@ -93,8 +93,9 @@ namespace Hotline.Api.Controllers.Bi
         private readonly IOptionsSnapshot<AppConfiguration> _appOptions;
         private readonly IRepository<OrderTsDetails> _orderTsDetailsRepository;
         private readonly IRepository<KnowledgeQuote> _knowledgeQuoteRepository;
+		private readonly IRepository<OrderUrge> _orderUrgeRepository;
 
-        public BiOrderController(
+		public BiOrderController(
             IOrderRepository orderRepository,
             IRepository<Hotspot> hotspotTypeRepository,
             ISystemDicDataCacheManager sysDicDataCacheManager,
@@ -131,7 +132,8 @@ namespace Hotline.Api.Controllers.Bi
             IRepository<CallNative> callNativeRepository,
             IOptionsSnapshot<AppConfiguration> appOptions,
             IRepository<OrderTsDetails> orderTsDetailsRepository,
-            IRepository<KnowledgeQuote> knowledgeQuoteRepository)
+            IRepository<KnowledgeQuote> knowledgeQuoteRepository,
+			IRepository<OrderUrge> orderUrgeRepository)
         {
             _orderRepository = orderRepository;
             _hotspotTypeRepository = hotspotTypeRepository;
@@ -170,7 +172,8 @@ namespace Hotline.Api.Controllers.Bi
             _appOptions = appOptions;
             _orderTsDetailsRepository = orderTsDetailsRepository;
             _knowledgeQuoteRepository = knowledgeQuoteRepository;
-        }
+            _orderUrgeRepository = orderUrgeRepository;
+		}
 
         /// <summary>
         /// 部门发布量统计
@@ -1383,82 +1386,89 @@ namespace Hotline.Api.Controllers.Bi
         [HttpPost("hotspot-statistics/export")]
         public async Task<FileStreamResult> HotspotStatisticsExprot([FromBody] ExportExcelDto<HotspotStatisticsRep> dto)
         {
-            var IsCenter = _sessionContext.OrgIsCenter;
-            DataTable data = new DataTable();
-
-            if (string.IsNullOrEmpty(dto.QueryDto.HotspotCode))
-            {
-                data = await _hotspotTypeRepository.Queryable()
-                .LeftJoin<Order>((it, o) => it.Id == o.HotspotId)
-                .Where((it, o) => o.CreationTime >= dto.QueryDto.StartTime && o.CreationTime <= dto.QueryDto.EndTime && o.Id != null)
-                .WhereIF(dto.QueryDto.TypeId == 1, (it, o) => o.IdentityType == EIdentityType.Citizen)
-                .WhereIF(dto.QueryDto.TypeId == 2, (it, o) => o.IdentityType == EIdentityType.Enterprise)
-                .WhereIF(IsCenter == false, (it, o) => o.ActualHandleOrgCode.StartsWith(_sessionContext.RequiredOrgId))
-                .GroupBy((it, o) => new { Id = it.Id.Substring(SqlFunc.MappingColumn<int>("0"), SqlFunc.MappingColumn<int>("2")) })
-                .Select((it, o) => new
-                {
-                    HotspotCode = it.Id.Substring(SqlFunc.MappingColumn<int>("0"), SqlFunc.MappingColumn<int>("2")),
-                    SumCount = SqlFunc.AggregateCount(it.HotSpotName)
-                })
-                .MergeTable()
-                .LeftJoin<Hotspot>((x, q) => x.HotspotCode == q.Id)
-                .Select((x, q) => new
-                {
-                    SumCount = x.SumCount,
-                    HotspotName = q.HotSpotName
-                })
-                .ToDataTableAsync();
-            }
-            else
-            {
-                string count = (dto.QueryDto.HotspotCode.Length + 2).ToString();
-                string countx = dto.QueryDto.HotspotCode.Length.ToString();
-                data = await _hotspotTypeRepository.Queryable()
-               .LeftJoin<Order>((it, o) => it.Id == o.HotspotId)
-               .Where((it, o) => o.CreationTime >= dto.QueryDto.StartTime && o.CreationTime <= dto.QueryDto.EndTime && it.ParentId.Substring(SqlFunc.MappingColumn<int>("0"), SqlFunc.MappingColumn<int>(countx)) == dto.QueryDto.HotspotCode)
-               .WhereIF(dto.QueryDto.TypeId == 1, (it, o) => o.IdentityType == EIdentityType.Citizen)
-               .WhereIF(dto.QueryDto.TypeId == 2, (it, o) => o.IdentityType == EIdentityType.Enterprise)
-               .WhereIF(IsCenter == false, (it, o) => o.ActualHandleOrgCode.StartsWith(_sessionContext.RequiredOrgId))
-               .GroupBy((it, o) => new { Id = it.Id.Substring(SqlFunc.MappingColumn<int>("0"), SqlFunc.MappingColumn<int>(count)) })
-               .Select((it, o) => new
-               {
-                   HotspotCode = it.Id.Substring(SqlFunc.MappingColumn<int>("0"), SqlFunc.MappingColumn<int>(count)),
-                   SumCount = SqlFunc.AggregateCount(it.HotSpotName)
-               })
-               .MergeTable()
-               .LeftJoin<Hotspot>((x, q) => x.HotspotCode == q.Id)
-               .Select((x, q) => new
-               {
-                   SumCount = x.SumCount,
-                   HotspotName = q.HotSpotName,
-               })
-               .ToDataTableAsync();
-
-            }
-            data.Columns["HotspotName"].SetOrdinal(0);
-            data.Columns["SumCount"].ColumnName = "分类统计";
-            data.Columns["HotspotName"].ColumnName = "热点名称";
-            //合计
-            DataRow sumRow = data.NewRow();
-            sumRow["热点名称"] = "合计";
-            decimal totalAmount = 0;
-            foreach (DataRow row in data.Rows)
-            {
-                totalAmount += Convert.ToDecimal(row["分类统计"]);
-            }
-            sumRow["分类统计"] = totalAmount;
-            data.Rows.Add(sumRow);
-
+			DataTable data = new DataTable();
+
+
+            if (_appOptions.Value.IsLuZhou)
+            {
+                data = await _orderReportApplication.HotspotStatisticsExprot_LZ(dto);
+            }
+            else {
+				var IsCenter = _sessionContext.OrgIsCenter;
+				if (string.IsNullOrEmpty(dto.QueryDto.HotspotCode))
+				{
+					data = await _hotspotTypeRepository.Queryable()
+					.LeftJoin<Order>((it, o) => it.Id == o.HotspotId)
+					.Where((it, o) => o.CreationTime >= dto.QueryDto.StartTime && o.CreationTime <= dto.QueryDto.EndTime && o.Id != null)
+					.WhereIF(dto.QueryDto.TypeId == 1, (it, o) => o.IdentityType == EIdentityType.Citizen)
+					.WhereIF(dto.QueryDto.TypeId == 2, (it, o) => o.IdentityType == EIdentityType.Enterprise)
+					.WhereIF(IsCenter == false, (it, o) => o.ActualHandleOrgCode.StartsWith(_sessionContext.RequiredOrgId))
+					.GroupBy((it, o) => new { Id = it.Id.Substring(SqlFunc.MappingColumn<int>("0"), SqlFunc.MappingColumn<int>("2")) })
+					.Select((it, o) => new
+					{
+						HotspotCode = it.Id.Substring(SqlFunc.MappingColumn<int>("0"), SqlFunc.MappingColumn<int>("2")),
+						SumCount = SqlFunc.AggregateCount(it.HotSpotName)
+					})
+					.MergeTable()
+					.LeftJoin<Hotspot>((x, q) => x.HotspotCode == q.Id)
+					.Select((x, q) => new
+					{
+						SumCount = x.SumCount,
+						HotspotName = q.HotSpotName
+					})
+					.ToDataTableAsync();
+				}
+				else
+				{
+					string count = (dto.QueryDto.HotspotCode.Length + 2).ToString();
+					string countx = dto.QueryDto.HotspotCode.Length.ToString();
+					data = await _hotspotTypeRepository.Queryable()
+				   .LeftJoin<Order>((it, o) => it.Id == o.HotspotId)
+				   .Where((it, o) => o.CreationTime >= dto.QueryDto.StartTime && o.CreationTime <= dto.QueryDto.EndTime && it.ParentId.Substring(SqlFunc.MappingColumn<int>("0"), SqlFunc.MappingColumn<int>(countx)) == dto.QueryDto.HotspotCode)
+				   .WhereIF(dto.QueryDto.TypeId == 1, (it, o) => o.IdentityType == EIdentityType.Citizen)
+				   .WhereIF(dto.QueryDto.TypeId == 2, (it, o) => o.IdentityType == EIdentityType.Enterprise)
+				   .WhereIF(IsCenter == false, (it, o) => o.ActualHandleOrgCode.StartsWith(_sessionContext.RequiredOrgId))
+				   .GroupBy((it, o) => new { Id = it.Id.Substring(SqlFunc.MappingColumn<int>("0"), SqlFunc.MappingColumn<int>(count)) })
+				   .Select((it, o) => new
+				   {
+					   HotspotCode = it.Id.Substring(SqlFunc.MappingColumn<int>("0"), SqlFunc.MappingColumn<int>(count)),
+					   SumCount = SqlFunc.AggregateCount(it.HotSpotName)
+				   })
+				   .MergeTable()
+				   .LeftJoin<Hotspot>((x, q) => x.HotspotCode == q.Id)
+				   .Select((x, q) => new
+				   {
+					   SumCount = x.SumCount,
+					   HotspotName = q.HotSpotName,
+				   })
+				   .ToDataTableAsync();
+
+				}
+				data.Columns["HotspotName"].SetOrdinal(0);
+				data.Columns["SumCount"].ColumnName = "分类统计";
+				data.Columns["HotspotName"].ColumnName = "热点名称";
+				//合计
+				DataRow sumRow = data.NewRow();
+				sumRow["热点名称"] = "合计";
+				decimal totalAmount = 0;
+				foreach (DataRow row in data.Rows)
+				{
+					totalAmount += Convert.ToDecimal(row["分类统计"]);
+				}
+				sumRow["分类统计"] = totalAmount;
+				data.Rows.Add(sumRow);
+			}
+            
             var stream = ExcelHelper.CreateStream(data);
             return ExcelStreamResult(stream, "热点类型小类统计");
         }
 
-        /// <summary>
-        /// 热点类型小类统计明细
-        /// </summary>
-        /// <param name="dto"></param>
-        /// <returns></returns>
-        [HttpGet("hotspot-statistics-detail")]
+		/// <summary>
+		/// 热点类型小类统计明细
+		/// </summary>
+		/// <param name="dto"></param>
+		/// <returns></returns>
+		[HttpGet("hotspot-statistics-detail")]
         public async Task<PagedDto<OrderDto>> HotspotStatisticsDetail([FromQuery] HotspotStatisticsRep dto)
         {
             var (total, items) = await _orderApplication.HotspotStatisticsDetail(dto)
@@ -2984,12 +2994,266 @@ namespace Hotline.Api.Controllers.Bi
             return centerReportStatisticsDto;
         }
 
-        /// <summary>
-        /// 部门受理类型统计周期
-        /// </summary>
-        /// <param name="dto"></param>
-        /// <returns></returns>
-        [HttpGet("department_acceptance_type_statistics")]
+
+		/// <summary>
+		/// 中心报表统计--泸州
+		/// </summary>
+		/// <param name="StartTime">开始时间</param>
+		/// <param name="EndTime">结束时间</param>
+		/// <param name="IdentityType">来电主体</param>
+		/// <returns></returns>
+		[HttpGet("center_report_forms_statistics_lz")]
+		public async Task<CenterReportStatisticsDto> LzCenterReportFormsStatistics(DateTime StartTime, DateTime EndTime, EIdentityType? IdentityType)
+		{
+			CenterReportStatisticsDto centerReportStatisticsDto = new();
+
+			//信件总量
+			int sourceChannelCount = await _orderRepository.Queryable().Where(p => p.CreationTime >= StartTime && p.CreationTime <= EndTime).CountAsync();
+
+			#region 1、通话记录
+			//通话记录
+			var callData = await _callNativeRepository.Queryable()
+			   .Where(p => p.CreationTime >= StartTime && p.CreationTime <= EndTime)
+			   // .Where(p => p.Gateway != "82826886" && SqlFunc.Length(p.Gateway) != 4)
+			   .Select(p => new CenterReportCallInfoDto
+			   {
+				   AllCallCount = SqlFunc.AggregateSum(1),//话务总量
+				   InTotal = SqlFunc.AggregateSum(SqlFunc.IIF(p.Direction == ECallDirection.In, 1, 0)),//呼入总量
+				   InConnectionQuantity = SqlFunc.AggregateSum(SqlFunc.IIF(p.Direction == ECallDirection.In && p.AnsweredTime != null, 1, 0)),//呼入接通量
+				   InHanguped = SqlFunc.AggregateSum(SqlFunc.IIF(p.Direction == ECallDirection.In && p.AnsweredTime == null, 1, 0)),//呼入未接通
+				   QueueByeCount = SqlFunc.AggregateSum(SqlFunc.IIF(p.Direction == ECallDirection.In && p.WaitDuration > 0 && p.RingDuration == 0 && p.AnsweredTime == null, 1, 0)), //队列挂断
+				   IvrByeCount = SqlFunc.AggregateSum(SqlFunc.IIF(p.Direction == ECallDirection.In && p.BeginIvrTime.HasValue && !p.BeginQueueTime.HasValue && !p.BeginRingTime.HasValue && p.AnsweredTime == null, 1, 0)), //IVR挂断
+				   OutTotal = SqlFunc.AggregateSum(SqlFunc.IIF(p.Direction == ECallDirection.Out, 1, 0)),//呼出总量
+				   OutConnectionQuantity = SqlFunc.AggregateSum(SqlFunc.IIF(p.TelNo != "0" && p.Direction == ECallDirection.Out && p.AnsweredTime != null, 1, 0)),//呼出接通量
+				   OutHanguped = SqlFunc.AggregateSum(SqlFunc.IIF(p.TelNo != "0" && p.Direction == ECallDirection.Out && p.AnsweredTime == null, 1, 0)),//呼出未接通
+
+			   })
+			   .FirstAsync();
+			centerReportStatisticsDto.CenterReportCallInfoDto = callData;
+
+			#endregion
+
+			#region 2、信件回访量
+			//信件回访量
+			var centerReportVisitd = await _orderVisitRepository.Queryable()
+              .LeftJoin<Order>((x,o)=> x.OrderId == o.Id)
+			  .WhereIF(IdentityType.HasValue, (x, o) => o.IdentityType == IdentityType)
+			  .Where((x, o) => x.VisitTime >= StartTime && x.VisitTime <= EndTime )
+			  .Select((x, o) => new CenterReportVisitdDto
+			  {
+				  Visitd = SqlFunc.AggregateSum(SqlFunc.IIF(x.VisitState == EVisitState.Visited, 1, 0)),//已回访
+				  CallVisitd = SqlFunc.AggregateSum(SqlFunc.IIF(x.VisitState == EVisitState.Visited && x.VisitType == EVisitType.CallVisit, 1, 0)),// 已回访--电话
+				  SmsVisitd = SqlFunc.AggregateSum(SqlFunc.IIF(x.VisitState == EVisitState.Visited && x.VisitType == EVisitType.SmsVisit, 1, 0)),//已回访--短信
+				  OtherVisitd = SqlFunc.AggregateSum(SqlFunc.IIF(x.VisitState == EVisitState.Visited && x.VisitType != EVisitType.CallVisit && x.VisitType != EVisitType.SmsVisit, 1, 0)),//其他回访
+				  WaitVisitd = SqlFunc.AggregateSum(SqlFunc.IIF(x.VisitState != EVisitState.None && x.VisitState != EVisitState.Visited, 1, 0)),//待回访
+			  }).FirstAsync();
+
+			//部门
+			var listOrg = await _orderVisitDetailRepository.Queryable()
+                .LeftJoin<OrderVisit>((x,ov)=>x.VisitId == ov.Id)
+				.LeftJoin<Order>((x,ov, o) => ov.OrderId == o.Id)
+				.WhereIF(IdentityType.HasValue, (x, ov, o) => o.IdentityType == IdentityType)
+				.Where((x, ov, o) => x.VisitTarget == EVisitTarget.Org && ov.VisitTime >= StartTime && ov.VisitTime <= EndTime && ov.VisitState == EVisitState.Visited)
+				 .Select((x, ov, o) => new Satisfaction
+				 {
+					 Dissatisfied = SqlFunc.AggregateSum(SqlFunc.IIF(SqlFunc.JsonField(x.OrgProcessingResults, "Key") == "1" || SqlFunc.JsonField(x.OrgProcessingResults, "Key") == "2", 1, 0)),
+					 Satisfied = SqlFunc.AggregateSum(SqlFunc.IIF(SqlFunc.JsonField(x.OrgProcessingResults, "Key") != "1" && SqlFunc.JsonField(x.OrgProcessingResults, "Key") != "2", 1, 0)),
+				 })
+				.FirstAsync();
+			//计算部门满意度
+			if (listOrg != null)
+			{
+				centerReportVisitd.Dissatisfied = listOrg.Dissatisfied;
+				centerReportVisitd.Satisfied = listOrg.Satisfied;
+				var satisfiedCount = listOrg.Satisfied + listOrg.Dissatisfied;
+				if (satisfiedCount > 0 && listOrg.Satisfied > 0)
+					centerReportVisitd.OrgRate = Math.Round((listOrg.Satisfied / (double)satisfiedCount) * 100, 2);
+			}
+
+			//坐席
+			var listSet = await _orderVisitDetailRepository.Queryable()
+				.LeftJoin<OrderVisit>((x, ov) => x.VisitId == ov.Id)
+				.LeftJoin<Order>((x, ov, o) => ov.OrderId == o.Id)
+				.WhereIF(IdentityType.HasValue, (x, ov, o) => o.IdentityType == IdentityType)
+				.Where((x, ov, o) => x.VisitTarget == EVisitTarget.Seat && ov.VisitTime >= StartTime && ov.VisitTime <= EndTime && ov.VisitState == EVisitState.Visited)
+				.Select((x, ov, o) => new Satisfaction
+				{
+					Dissatisfied = SqlFunc.AggregateSum(SqlFunc.IIF(x.SeatEvaluate == ESeatEvaluate.NoSatisfied, 1, 0)),
+					Satisfied = SqlFunc.AggregateSum(SqlFunc.IIF(x.SeatEvaluate != ESeatEvaluate.NoSatisfied, 1, 0)),
+				})
+				.FirstAsync();
+			//计算坐席满意度
+			if (listSet != null)
+			{
+				var satisfiedCount = listSet.Satisfied + listSet.Dissatisfied;
+				if (satisfiedCount > 0 && listSet.Satisfied > 0)
+					centerReportVisitd.SeatsRate = Math.Round((listSet.Satisfied / (double)satisfiedCount) * 100, 2);
+			}
+
+			centerReportStatisticsDto.CenterReportVisitd = centerReportVisitd;
+			#endregion
+
+			#region 3、工单
+			//工单
+			var orderData = await _orderRepository.Queryable()
+                .WhereIF(IdentityType.HasValue, p=>p.IdentityType == IdentityType)
+				.Where(p => p.CreationTime >= StartTime && p.CreationTime <= EndTime)
+				.Select(x => new CenterReportOrderDto
+				{
+					EffectiveCount = SqlFunc.AggregateSum(SqlFunc.IIF(true, 1, 0)),//有效
+					InvalidCount = 0,//无效
+					CompletedCount = SqlFunc.AggregateSum(SqlFunc.IIF(x.Status >= EOrderStatus.Filed, 1, 0)),//已办结
+					OnTimeCompletedCount = SqlFunc.AggregateSum(SqlFunc.IIF(x.Status >= EOrderStatus.Filed && x.ExpiredTime > x.ActualHandleTime, 1, 0)),//按时办结
+					CenterCompletedCount = SqlFunc.AggregateSum(SqlFunc.IIF(x.Status >= EOrderStatus.Filed && x.FileUserOrgId == OrgSeedData.CenterId, 1, 0)),//中心办结
+					OrgCompletedCount = SqlFunc.AggregateSum(SqlFunc.IIF(x.Status >= EOrderStatus.Filed && x.FileUserOrgId != OrgSeedData.CenterId, 1, 0)),//部门办结
+					InProgressCount = SqlFunc.AggregateSum(SqlFunc.IIF(x.Status < EOrderStatus.Filed, 1, 0)),//在办
+					CenterInProgressCount = SqlFunc.AggregateSum(SqlFunc.IIF(x.Status < EOrderStatus.Filed && (x.ActualHandleOrgCode == OrgSeedData.CenterId || x.ActualHandleOrgCode == null), 1, 0)),//中心在办
+					OrgInProgressCount = SqlFunc.AggregateSum(SqlFunc.IIF(x.Status < EOrderStatus.Filed && x.ActualHandleOrgCode != OrgSeedData.CenterId && x.ActualHandleOrgCode != null, 1, 0))//部门在办
+				})
+				.FirstAsync();
+            //催办
+            var orderUrge = await _orderUrgeRepository.Queryable()
+                .LeftJoin<Order>((p,o)=>p.OrderId == o.Id)
+                .Where((p, o) => p.CreationTime >= StartTime && p.CreationTime <= EndTime)
+                .WhereIF(IdentityType.HasValue, (p, o) => o.IdentityType == IdentityType).Select((p, o) => p.OrderId).Distinct().CountAsync();
+            orderData.orderUrge = orderUrge;
+			centerReportStatisticsDto.CenterReportOrder = orderData;
+			#endregion
+
+			#region 4、信件来源
+
+			//信件来源
+			var sourceChannelData = _orderRepository.Queryable()
+				.WhereIF(IdentityType.HasValue, p => p.IdentityType == IdentityType)
+				.Where(p => p.CreationTime >= StartTime && p.CreationTime <= EndTime)
+				.Select(it => new
+				{
+					SourceChannelCode = SqlFunc.IIF(SqlFunc.IsNullOrEmpty(it.SourceChannelCode), "QT", it.SourceChannelCode)
+				})
+				.MergeTable()//将查询出来的结果合并成一个新表
+				 .GroupBy(it => new { it.SourceChannelCode })//对新表进行分组
+				 .Select(it => new CenterReportOrderSourceChannelDto
+				 {
+					 Code = it.SourceChannelCode,
+					 CountNum = SqlFunc.AggregateCount(it.SourceChannelCode)
+				 });
+
+			var sourceData = await _systemDicDataRepository.Queryable()
+			   .LeftJoin(sourceChannelData, (s, p) => s.DicDataValue == p.Code)
+			  .Where((s, p) => s.DicTypeCode == "SourceChannel" && s.IsShow == true)
+			  .Select((s, p) => new CenterReportOrderSourceChannelDto
+			  {
+                  AllCountNum = sourceChannelCount,
+				  Code = s.DicDataValue,
+				  Name = s.DicDataName,
+				  CountNum = p.CountNum
+			  })
+
+			  .ToListAsync();
+
+			centerReportStatisticsDto.CenterReportOrderSourceChannels = sourceData;
+			#endregion
+
+			#region 5、信件分类
+			//信件来源 AcceptType
+
+			var acceptTypeData = _orderRepository.Queryable(false, false, false)
+				.WhereIF(IdentityType.HasValue, p => p.IdentityType == IdentityType)
+				.Where(p => p.CreationTime >= StartTime && p.CreationTime <= EndTime)
+				.Select(it => new
+				{
+					AcceptTypeCode = SqlFunc.IIF(SqlFunc.IsNullOrEmpty(it.AcceptTypeCode), "40", it.AcceptTypeCode)
+				})
+				.MergeTable()//将查询出来的结果合并成一个新表
+				 .GroupBy(it => new { it.AcceptTypeCode })//对新表进行分组
+				 .Select(it => new CenterReportOrderSourceChannelDto
+				 {
+					 Code = it.AcceptTypeCode,
+					 CountNum = SqlFunc.AggregateCount(it.AcceptTypeCode)
+				 });
+
+			var acceptType = await _systemDicDataRepository.Queryable()
+			  .LeftJoin(acceptTypeData, (s, p) => s.DicDataValue == p.Code)
+			 .Where((s, p) => s.DicTypeCode == "AcceptType" && s.IsShow == true)
+			 .Select((s, p) => new CenterReportOrderSourceChannelDto
+			 {
+				 AllCountNum = sourceChannelCount,
+				 Code = s.DicDataValue,
+				 Name = s.DicDataName,
+				 CountNum = p.CountNum
+			 })
+			 .ToListAsync();
+
+			centerReportStatisticsDto.CenterReportOrderAcceptTypes = acceptType;
+			#endregion
+
+			#region 信件分布情况
+			//市直部门
+
+			var listOrgStatisticsCityAll = await _orderRepository.Queryable()
+				.WhereIF(IdentityType.HasValue, p => p.IdentityType == IdentityType)
+				.Where(o => o.CreationTime >= StartTime && o.CreationTime <= EndTime)
+				.Select(o => new
+				{
+					OrgCode = o.ActualHandleOrgCode == null || o.ActualHandleOrgCode == "" ? "001" : o.ActualHandleOrgCode.Substring(SqlFunc.MappingColumn<int>("0"), SqlFunc.MappingColumn<int>("6")),
+				})
+				.MergeTable()
+				.LeftJoin<SystemOrganize>((o, s) => o.OrgCode == s.Id)
+				 .Where((o, s) => s.OrgType == EOrgType.City || s.OrgType == EOrgType.Province)
+				.GroupBy((o, s) => new
+				{
+					o.OrgCode,
+					s.Name
+				})
+				.Select((o, s) => new OrgStatistics
+				{
+					CountNum = SqlFunc.AggregateCount(o.OrgCode),
+					OrgName = s.Name
+				}).ToListAsync();
+
+			centerReportStatisticsDto.OrgStatisticsCityAll = new OrgStatisticsAll
+			{
+				OrgStatistics = listOrgStatisticsCityAll
+			};
+
+			//区县部门
+			var listOrgStatisticsAreaAll = await _orderRepository.Queryable()
+				.Where(o => o.CreationTime >= StartTime && o.CreationTime <= EndTime)
+				.Select(o => new
+				{
+					OrgCode = o.ActualHandleOrgCode == null || o.ActualHandleOrgCode == "" ? "001" : o.ActualHandleOrgCode.Substring(SqlFunc.MappingColumn<int>("0"), SqlFunc.MappingColumn<int>("6")),
+				})
+				.MergeTable()
+				.LeftJoin<SystemOrganize>((o, s) => o.OrgCode == s.Id)
+				 .Where((o, s) => s.OrgType == EOrgType.County)
+				.GroupBy((o, s) => new
+				{
+					o.OrgCode,
+					s.Name
+				})
+				.Select((o, s) => new OrgStatistics
+				{
+					CountNum = SqlFunc.AggregateCount(o.OrgCode),
+
+					OrgName = s.Name
+				}).ToListAsync();
+
+			centerReportStatisticsDto.OrgStatisticsAreaAll = new OrgStatisticsAll
+			{
+				OrgStatistics = listOrgStatisticsAreaAll
+			};
+			#endregion
+
+			return centerReportStatisticsDto;
+		}
+
+
+		/// <summary>
+		/// 部门受理类型统计周期
+		/// </summary>
+		/// <param name="dto"></param>
+		/// <returns></returns>
+		[HttpGet("department_acceptance_type_statistics")]
         public async Task<object> DepartmentAcceptanceTypeStatistics([FromQuery] DepartmentKeyWordRequest dto)
         {
             var items = await _orderReportApplication.DepartmentAcceptanceTypeStatistics(dto).ToListAsync();
@@ -4012,11 +4276,7 @@ namespace Hotline.Api.Controllers.Bi
         [HttpGet("highmatter-warning")]
         public async Task<PagedDto<HighMatterWarningDto>> HighMatterWarning([FromQuery] HighMatterWarningRequest dto)
         {
-            List<string> filterTitle = new List<string>();
-            filterTitle.Add("无声");
-            filterTitle.Add("骚扰");
-            filterTitle.Add("错拨");
-            filterTitle.Add("测试");
+            var filterTitle = _systemSettingCacheManager.GetSetting(SettingConstants.HighMatterWarningFilterTitle).SettingValue;
 
             var (total, items) = await _orderRepository.Queryable()
                 .Where(x => x.CreationTime >= dto.StartTime && x.CreationTime <= dto.EndTime)
@@ -4074,11 +4334,7 @@ namespace Hotline.Api.Controllers.Bi
         [HttpGet("highmatter-warning-detail")]
         public async Task<PagedDto<OrderDto>> HighMatterWarningDetail([FromQuery] HighMatterWarningDetailRequest dto)
         {
-            List<string> filterTitle = new List<string>();
-            filterTitle.Add("无声");
-            filterTitle.Add("骚扰");
-            filterTitle.Add("错拨");
-            filterTitle.Add("测试");
+            var filterTitle = _systemSettingCacheManager.GetSetting(SettingConstants.HighMatterWarningFilterTitle).SettingValue;
 
             var (total, items) = await _orderRepository.Queryable()
             .Includes(x => x.OrderScreens)
@@ -6336,5 +6592,101 @@ namespace Hotline.Api.Controllers.Bi
             returnList.Add(SZYSM);
             return returnList;
         }
-    }
+
+		/// <summary>
+		/// 部门退回中心统计
+		/// </summary>
+		/// <param name="dto"></param>
+		/// <returns></returns>
+		[HttpGet("org_sendback_statistics")]
+		public async Task<List<OrgSendBackAuditListVo>> OrgSendBackAuditStatistics([FromQuery] OrgSendBackAuditListDto dto)
+		{
+			var quer = _orderApplication.OrgSendBackAuditList(dto);
+			var list = await quer.ToListAsync(HttpContext.RequestAborted);
+			list.Add(new OrgSendBackAuditListVo()
+			{
+				OrgName = "合计",
+				Num = list.Sum(m => m.Num),
+			});
+
+			return list;
+		}
+
+		/// <summary>
+		/// 部门退回中心统计
+		/// </summary>
+		/// <returns></returns>
+		[HttpPost("org_sendback_statistics/_export")]
+		public async Task<FileStreamResult> OrgSendBackAuditStatisticsExport([FromBody] ExportExcelDto<OrgSendBackAuditListDto> dto)
+		{
+			var query = _orderApplication.OrgSendBackAuditList(dto.QueryDto);
+			List<OrgSendBackAuditListVo> data;
+
+			data = await query.ToListAsync(HttpContext.RequestAborted);
+
+			data.Add(new OrgSendBackAuditListVo()
+			{
+				OrgName = "合计",
+				Num = data.Sum(m => m.Num),
+			});
+
+			dynamic? dynamicClass = DynamicClassHelper.CreateDynamicClass<OrgSendBackAuditListVo>(dto.ColumnInfos);
+
+			var dtos = data
+				.Select(stu => _mapper.Map(stu, typeof(OrgSendBackAuditListVo), dynamicClass))
+				.Cast<object>()
+				.ToList();
+
+			var stream = ExcelHelper.CreateStream(dtos);
+
+			return ExcelStreamResult(stream, "部门退回统计");
+		}
+
+		/// <summary>
+		/// 部门退回中心统计明细
+		/// </summary>
+		/// <param name="dto"></param>
+		/// <returns></returns>
+		[HttpGet("org_sendback_detail")]
+		public async Task<PagedDto<SendBackDto>> OrgSendBackDetail([FromQuery] OrgSendBackAuditListDto dto)
+		{
+			var quer = _orderApplication.OrgSendBackAuditDetail(dto);
+			var (total, items) = await quer.ToPagedListAsync(dto.PageIndex, dto.PageSize);
+
+			return new PagedDto<SendBackDto>(total, _mapper.Map<IReadOnlyList<SendBackDto>>(items));
+		}
+
+		/// <summary>
+		/// 部门退回中心统计明细导出
+		/// </summary>
+		/// <returns></returns>
+		[HttpPost("org_sendback_detail/_export")]
+		public async Task<FileStreamResult> OrgSendBackDetailExport([FromBody] ExportExcelDto<OrgSendBackAuditListDto> dto)
+		{
+			var query = _orderApplication.OrgSendBackAuditDetail(dto.QueryDto);
+			List<OrderSendBackAudit> 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<SendBackDto>>(data);
+
+			dynamic? dynamicClass = DynamicClassHelper.CreateDynamicClass<SendBackDto>(dto.ColumnInfos);
+
+			var dtos = dataDtos
+				.Select(stu => _mapper.Map(stu, typeof(SendBackDto), dynamicClass))
+				.Cast<object>()
+				.ToList();
+
+			var stream = ExcelHelper.CreateStream(dtos);
+
+			return ExcelStreamResult(stream, "部门退回统计明细");
+		}
+	}
 }

+ 6 - 8
src/Hotline.Api/Controllers/Bigscreen/DataScreenController.cs

@@ -1,4 +1,5 @@
 using DocumentFormat.OpenXml.Drawing;
+using Hotline.Caching.Interfaces;
 using Hotline.Configurations;
 using Hotline.KnowledgeBase;
 using Hotline.Orders;
@@ -33,12 +34,14 @@ namespace Hotline.Api.Controllers.Bigscreen
         private readonly IRepository<SystemArea> _systemAreaRepository;
         private readonly IOptionsSnapshot<AppConfiguration> _appOptions;
         private readonly IRepository<OrderSecondaryHandling> _orderSecondaryHandlingRepository;
+        private readonly ISystemSettingCacheManager _systemSettingCacheManager;
 
         public DataScreenController(IOrderRepository orderRepository, IRepository<OrderVisit> orderVisitRepository,
             IRepository<OrderDelay> orderDelayRepository, IRepository<Knowledge> knowledgeRepository, IRepository<KnowledgePv> knowledgePvRepository,
             IMapper mapper, IRepository<OrderVisitDetail> orderVisitDetailRepository, IRepository<SystemArea> systemAreaRepository,
             IOptionsSnapshot<AppConfiguration> appOptions,
-            IRepository<OrderSecondaryHandling> orderSecondaryHandlingRepository)
+            IRepository<OrderSecondaryHandling> orderSecondaryHandlingRepository,
+            ISystemSettingCacheManager systemSettingCacheManager)
         {
             _orderRepository = orderRepository;
             _orderVisitRepository = orderVisitRepository;
@@ -50,6 +53,7 @@ namespace Hotline.Api.Controllers.Bigscreen
             _systemAreaRepository = systemAreaRepository;
             _appOptions = appOptions;
             _orderSecondaryHandlingRepository = orderSecondaryHandlingRepository;
+            _systemSettingCacheManager = systemSettingCacheManager;
         }
 
         /// <summary>
@@ -477,13 +481,7 @@ namespace Hotline.Api.Controllers.Bigscreen
             //var endDate = DateTime.Now.Date.AddDays(1).AddSeconds(-1);
             //var startDate = endDate.AddDays(-30).Date;
 
-
-            List<string> filterTitle = new List<string>();
-            filterTitle.Add("无声");
-            filterTitle.Add("骚扰");
-            filterTitle.Add("错拨");
-            filterTitle.Add("测试");
-
+            var filterTitle = _systemSettingCacheManager.GetSetting(SettingConstants.HighMatterWarningFilterTitle).SettingValue;
 
             var list = await _orderRepository.Queryable(false, false, false)
                 .Where(x => x.CreationTime >= StartTime && x.CreationTime <= EndTime)

+ 3 - 3
src/Hotline.Api/Controllers/FwThirdController.cs

@@ -348,7 +348,7 @@ namespace Hotline.Api.Controllers
                 .WhereIF(!string.IsNullOrEmpty(dto.FlowEDate), (op, p) => p.StartTime <= DateTime.Parse(DateTime.Parse(dto.FlowEDate).ToString("yyyy-MM-dd 00:00:00")))// dto.FlowEDate
                 .WhereIF(!string.IsNullOrEmpty(dto.FlowFrom), (op, p) => p.FromName.Contains(dto.FlowFrom))
                 .WhereIF(dto.IdentityType.HasValue, (op, p) => p.IdentityType == dto.IdentityType)
-                .OrderByDescending((op, p) => p.CreationTime)
+                .OrderByDescending((op, p) => op.CreationTime)
                .Select((op, p) => new OrderListDto
                {
                    FlowID = p.Id,
@@ -388,8 +388,8 @@ namespace Hotline.Api.Controllers
             //            });
 
             var (total, items) = await queryNew
-                .OrderByDescending(p => p.PubDate)
-                 .ToPagedListAsync(dto.PageIndex, dto.PageSize, HttpContext.RequestAborted);
+                //.OrderByDescending(op => op.PubDate)
+                .ToPagedListAsync(dto.PageIndex, dto.PageSize, HttpContext.RequestAborted);
 
             //计算总页数
             int nPageCount = Convert.ToInt32(Math.Ceiling(Convert.ToDouble(total) / dto.PageSize));

+ 97 - 80
src/Hotline.Api/Controllers/IPPbxController.cs

@@ -1,38 +1,40 @@
 using DotNetCore.CAP;
-using Hotline.CallCenter.Tels.CallTelDomain;
+using Hotline.Api.Filter;
+using Hotline.Application.CallCenter;
 using Hotline.Application.Systems;
 using Hotline.Application.Tels;
 using Hotline.Caching.Interfaces;
 using Hotline.CallCenter.Calls;
 using Hotline.CallCenter.Tels;
+using Hotline.CallCenter.Tels.CallTelDomain;
+using Hotline.Configurations;
+using Hotline.EventBus;
 using Hotline.Orders;
 using Hotline.Quality;
+using Hotline.Quality.Notifications;
 using Hotline.Repository.SqlSugar.Extensions;
 using Hotline.Settings;
 using Hotline.Share.Dtos;
 using Hotline.Share.Dtos.CallCenter;
 using Hotline.Share.Dtos.Order;
+using Hotline.Share.Dtos.Quality;
 using Hotline.Share.Dtos.TrCallCenter;
 using Hotline.Share.Enums.CallCenter;
 using Hotline.Share.Enums.Quality;
+using Hotline.Tools;
 using Hotline.Users;
+using Mapster;
 using MapsterMapper;
 using Microsoft.AspNetCore.Authorization;
 using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Options;
 using Newtonsoft.Json;
-using Hotline.EventBus;
 using XF.Domain.Authentications;
+using XF.Domain.Cache;
 using XF.Domain.Exceptions;
 using XF.Domain.Filters;
 using XF.Domain.Repository;
 using XF.Utility.EnumExtensions;
-using Hotline.Quality.Notifications;
-using Hotline.Share.Dtos.Quality;
-using Hotline.Tools;
-using Hotline.Api.Filter;
-using XF.Domain.Cache;
-using Hotline.Application.CallCenter;
-using Mapster;
 
 namespace Hotline.Api.Controllers
 {
@@ -65,6 +67,7 @@ namespace Hotline.Api.Controllers
         private readonly IIPPbxApplication _iPPbxApplication;
         private readonly ICallTelClient _callTelClient;
         private readonly IRepository<TelOperation> _telOperationRepository;
+        private readonly IOptionsSnapshot<AppConfiguration> _appOptions;
 
         public IPPbxController(IMapper mapper, IUserDomainService userDomainService,
             ISessionContext sessionContext, IRepository<TrCallRecord> trCallRecordRepository,
@@ -76,7 +79,15 @@ namespace Hotline.Api.Controllers
             ITelApplication telApplication, IRepository<Quality.Quality> qualiteyRepository,
             IRepository<QualityTemplate> qualityTemplate,
             ISystemSettingCacheManager systemSettingCacheManager, IRepository<TelActionRecord> telActionRecordRepository,
-            ISystemMobilAreaApplication systemMobilAreaApplication, IRepository<Work> workRepository, Publisher publisher, ITrCallRecordRepository callRecordRepository, ITypedCache<Work> cacheWork, IIPPbxApplication iPPbxApplication, ICallTelClient callTelClient, IRepository<TelOperation> telOperationRepository)
+            ISystemMobilAreaApplication systemMobilAreaApplication,
+            IRepository<Work> workRepository,
+            Publisher publisher,
+            ITrCallRecordRepository callRecordRepository,
+            ITypedCache<Work> cacheWork,
+            IIPPbxApplication iPPbxApplication,
+            ICallTelClient callTelClient,
+            IRepository<TelOperation> telOperationRepository,
+           IOptionsSnapshot<AppConfiguration> appOptions)
         {
             _mapper = mapper;
             _userDomainService = userDomainService;
@@ -105,6 +116,7 @@ namespace Hotline.Api.Controllers
             _iPPbxApplication = iPPbxApplication;
             _callTelClient = callTelClient;
             _telOperationRepository = telOperationRepository;
+            _appOptions = appOptions;
         }
 
         #region 添添呼
@@ -119,7 +131,7 @@ namespace Hotline.Api.Controllers
         {
             var tels = await _callTelClient.QueryTelsAsync(new QueryTelRequest() { }, HttpContext.RequestAborted);
             var listenTels = _systemSettingCacheManager.GetSetting(SettingConstants.ListenTels)?.SettingValue;
-            tels = tels.Where(m => listenTels.Contains(m.TelNo) ==  false).ToList();
+            tels = tels.Where(m => listenTels.Contains(m.TelNo) == false).ToList();
             var returnlist = _mapper.Map<List<TrTelDto>>(tels);
             string callOutQueueId = _systemSettingCacheManager.GetSetting(SettingConstants.CallOutQueueId).SettingValue[0];
             returnlist.ForEach(x =>
@@ -140,18 +152,18 @@ namespace Hotline.Api.Controllers
         {
             var tels = await _callTelClient.QueryTelStateAsync(new QueryTelStateRequest() { State = state }, HttpContext.RequestAborted);
             var listenTels = _systemSettingCacheManager.GetSetting(SettingConstants.ListenTels)?.SettingValue;
-            var workList = await _workRepository.Queryable().Where(d=> 1 == 1 && !d.EndTime.HasValue).ToListAsync();
+            var workList = await _workRepository.Queryable().Where(d => 1 == 1 && !d.EndTime.HasValue).ToListAsync();
             var query = from tel in tels.AgentList
                         join works in workList on tel.TelNo equals works.TelNo into workD
                         from work in workD.DefaultIfEmpty()
                         select new TrTelStateDto
                         {
                             Id = tel.Id,
-                            TelNo =tel.TelNo,
-                            ChannelUUid =tel.ChannelUUid,
-                            TelName =tel.TelName,
+                            TelNo = tel.TelNo,
+                            ChannelUUid = tel.ChannelUUid,
+                            TelName = tel.TelName,
                             Type = tel.Type,
-                            Weight =tel.Weight,
+                            Weight = tel.Weight,
                             Queue = tel.Queue,
                             State = tel.State,
                             OldState = tel.OldState,
@@ -165,14 +177,14 @@ namespace Hotline.Api.Controllers
                             OtherNumber = tel.OtherNumber,
                             GateWay = tel.GateWay,
                             AnsweredAt = tel.AnsweredAt,
-                            WorkUserId = (work != null) ? work.UserId: "",
-                            WorkUserName = (work != null) ? work.UserName: "",
+                            WorkUserId = (work != null) ? work.UserId : "",
+                            WorkUserName = (work != null) ? work.UserName : "",
                         };
             //if (hasListen == false)
             //{
             //    query = query.Where(m => listenTels.Contains(m.TelNo) == false);
             //}
-            var list = query.OrderBy(x=>x.TelNo).OrderByDescending(x=>x.CreatedAt).ToList();
+            var list = query.OrderBy(x => x.TelNo).OrderByDescending(x => x.CreatedAt).ToList();
             return list;// _mapper.Map<IReadOnlyList<TrTelStateDto>>(tels.AgentList);
         }
 
@@ -183,7 +195,7 @@ namespace Hotline.Api.Controllers
         /// <returns></returns>
         [HttpGet("query-telstatebyno")]
         [AllowAnonymous]
-        public async Task<TrTelStateDto> TrQueryTelStateByTelNo([FromQuery]string? telno)
+        public async Task<TrTelStateDto> TrQueryTelStateByTelNo([FromQuery] string? telno)
         {
             var tels = await _callTelClient.QueryTelStateAsync(new QueryTelStateRequest() { TelNo = telno }, HttpContext.RequestAborted);
 
@@ -278,7 +290,7 @@ namespace Hotline.Api.Controllers
         [HttpPost("on-duty")]
         public async Task<TrOnDutyResponseDto> OnDuty([FromBody] TrOnDutyDto dto)
         {
-            return await _callApplication.SignInAsync(new SignInDto() { TelNo = dto.TelNo, TelModelState = dto.TelModelState}, HttpContext.RequestAborted);
+            return await _callApplication.SignInAsync(new SignInDto() { TelNo = dto.TelNo, TelModelState = dto.TelModelState }, HttpContext.RequestAborted);
         }
 
         /// <summary>
@@ -311,7 +323,7 @@ namespace Hotline.Api.Controllers
         [HttpPost("change-telmodel")]
         public async Task ChangeTelModel([FromBody] ChangeTelModelDto dto)
         {
-            await _callApplication.ChangeTelModel(dto.isCallOut,HttpContext.RequestAborted);
+            await _callApplication.ChangeTelModel(dto.isCallOut, HttpContext.RequestAborted);
         }
 
         /// <summary>
@@ -320,9 +332,9 @@ namespace Hotline.Api.Controllers
         /// <param name="telNo"></param>
         /// <returns></returns>
         [HttpGet("off-duty-manage")]
-        public async Task OffDuty([FromQuery]string telNo)
+        public async Task OffDuty([FromQuery] string telNo)
         {
-           await _telApplication.SignOutByTelNoAsync(telNo, HttpContext.RequestAborted);
+            await _telApplication.SignOutByTelNoAsync(telNo, HttpContext.RequestAborted);
         }
 
         //提供关闭浏览器事件触发调用
@@ -368,7 +380,7 @@ namespace Hotline.Api.Controllers
             var isResting = await _telRestRepository.IsRestingAsync(work.TelNo, HttpContext.RequestAborted);
             if (isResting)
                 throw UserFriendlyException.SameMessage("当前坐席正在休息");
-            
+
             var user = await _userRepository.GetAsync(work.UserId, HttpContext.RequestAborted);
             var telRest = new TelRest(work.TelNo, work.TelNo, work.UserId, work.UserName, dto.Reason, false, user.StaffNo);
             await _telRestRepository.AddAsync(telRest, HttpContext.RequestAborted);
@@ -395,7 +407,7 @@ namespace Hotline.Api.Controllers
             await _telRestRepository.UpdateAsync(telRest, HttpContext.RequestAborted);
 
             var telAction = await _telActionRecordRepository.GetAsync(x => x.TelNo == work.TelNo && x.ActionType == EActionType.TelRest && !x.EndTime.HasValue, HttpContext.RequestAborted);
-            if (telAction!=null)
+            if (telAction != null)
             {
                 telAction.EndAction();
                 await _telActionRecordRepository.UpdateAsync(telAction);
@@ -471,7 +483,7 @@ namespace Hotline.Api.Controllers
             model.RingTimes = 0;
             model.QueueTims = 0;
             model.OnState = Share.Enums.CallCenter.EOnState.NoOn;
-            
+
             //计算通话时长
             if (model.AnsweredTime != null)
             {
@@ -517,7 +529,7 @@ namespace Hotline.Api.Controllers
                         model.OperatorName = areaModel.OperatorName;
                     }
                 }
-                catch{}
+                catch { }
             }
             else
             {
@@ -532,10 +544,10 @@ namespace Hotline.Api.Controllers
                         model.OperatorName = areaModel.OperatorName;
                     }
                 }
-                catch{}
+                catch { }
             }
             //判断是否是内部通话(目前分机都为4位)
-            if (model.CPN.Length==4 && model.CDPN.Length ==4) //是内部通话
+            if (model.CPN.Length == 4 && model.CDPN.Length == 4) //是内部通话
             {
                 model.TelNo = model.CDPN;//如果是内部通话  响应分机为被叫号码
             }
@@ -543,7 +555,7 @@ namespace Hotline.Api.Controllers
             {
                 model.PhoneTypes = (EPhoneTypes)Convert.ToInt32(dto.phoneTypes);
             }
-            
+
 
             //获取关联 工单或是回访
             //var order = await _orderRepository.GetAsync(x => x.CallId == model.CallAccept, HttpContext.RequestAborted);//由CallAccept改为OtherAccept
@@ -563,39 +575,39 @@ namespace Hotline.Api.Controllers
                     if (teAny)
                     {
                         var quality = await _qualiteyRepository.Queryable().Where(x => x.OrderId == order.Id && x.Source == Share.Enums.Quality.EQualitySource.Accepted).FirstAsync();
-                        if (quality !=null)
+                        if (quality != null)
                         {
-							var setting = _systemSettingCacheManager.GetSetting(SettingConstants.ViteRecordPrefix);
-							//await _aiQualityService.CreateAiOrderQualityTask(quality, model, order, setting?.SettingValue[0], HttpContext.RequestAborted);
-
-							try
-							{
-								 //_aiQualityService.CreateAiOrderQualityTask(
-									//quality,
-									//model.RecordingAbsolutePath,
-									//model.CPN,
-									//model.CreatedTime,
-									//order, setting?.SettingValue[0], HttpContext.RequestAborted);
-								 var handler = new AiQualityHandler()
-								 {
-                                     Id = quality.Id,
-                                     Source = quality.Source.ToString(),
-									 AudioFile = model.RecordingAbsolutePath,
-									 FromNo = model.CPN,
-									 CallStartTime = model.CreatedTime,
-									 ViteRecordPrefix = setting?.SettingValue[0],
-								 };
-								await _publisher.PublishAsync(new AiOrderQualityNotify(handler), PublishStrategy.ParallelNoWait, HttpContext.RequestAborted);
-							}
-							catch (Exception e)
-							{
-								_logger.LogError($"写入智能质检异常!, \r\n{e.Message}");
-							}
-						
+                            var setting = _systemSettingCacheManager.GetSetting(SettingConstants.ViteRecordPrefix);
+                            //await _aiQualityService.CreateAiOrderQualityTask(quality, model, order, setting?.SettingValue[0], HttpContext.RequestAborted);
+
+                            try
+                            {
+                                //_aiQualityService.CreateAiOrderQualityTask(
+                                //quality,
+                                //model.RecordingAbsolutePath,
+                                //model.CPN,
+                                //model.CreatedTime,
+                                //order, setting?.SettingValue[0], HttpContext.RequestAborted);
+                                var handler = new AiQualityHandler()
+                                {
+                                    Id = quality.Id,
+                                    Source = quality.Source.ToString(),
+                                    AudioFile = model.RecordingAbsolutePath,
+                                    FromNo = model.CPN,
+                                    CallStartTime = model.CreatedTime,
+                                    ViteRecordPrefix = setting?.SettingValue[0],
+                                };
+                                await _publisher.PublishAsync(new AiOrderQualityNotify(handler), PublishStrategy.ParallelNoWait, HttpContext.RequestAborted);
+                            }
+                            catch (Exception e)
+                            {
+                                _logger.LogError($"写入智能质检异常!, \r\n{e.Message}");
+                            }
+
                         }
                     }
                 }
-                catch{}
+                catch { }
             }
             else
             {
@@ -622,7 +634,7 @@ namespace Hotline.Api.Controllers
             }
             await _trCallRecordRepository.AddAsync(model, HttpContext.RequestAborted);
 
-           
+
             var publishCallRecordDto = new PublishCallRecrodDto() { };
             if (order != null)
             {
@@ -668,7 +680,7 @@ namespace Hotline.Api.Controllers
             var userName = _sessionContext.UserName;
             if (dto.Status == 0) // 签出
             {
-                await _iPPbxApplication.ResetTelStatus(null, dto.TelNo, CancellationToken.None);
+                //await _iPPbxApplication.ResetTelStatus(null, dto.TelNo, CancellationToken.None);
             }
             dto.Status = await _callTelClient.GetStatusAsync(dto.Status);
             await _callApplication.EndActionAsync(dto.Adapt<EndActionInDto>());
@@ -696,7 +708,7 @@ namespace Hotline.Api.Controllers
         /// <param name="dto"></param>
         /// <returns></returns>
         [HttpPost("calls/call-list/export")]
-        public async Task<FileStreamResult> GetCallListExport([FromBody]ExportExcelDto<GetCallListDto> dto)
+        public async Task<FileStreamResult> GetCallListExport([FromBody] ExportExcelDto<GetCallListDto> dto)
         {
             var query = _callRecordRepository.GetCallList(dto.QueryDto);
             List<TrCallRecord> data;
@@ -714,11 +726,11 @@ namespace Hotline.Api.Controllers
 
             dto.ColumnInfos.ForEach(x =>
             {
-                if (x.Prop=="cpn")
+                if (x.Prop == "cpn")
                 {
-                    x.Prop ="CPN";
+                    x.Prop = "CPN";
                 }
-                if (x.Prop=="cdpn")
+                if (x.Prop == "cdpn")
                 {
                     x.Prop = "CDPN";
                 }
@@ -753,16 +765,16 @@ namespace Hotline.Api.Controllers
         }
 
 
-		#endregion
+        #endregion
 
-		#region 关联
+        #region 关联
 
-		/// <summary>
-		/// 可关联工单
-		/// </summary>
-		/// <param name="dto"></param>
-		/// <returns></returns>
-		[HttpGet("canlink-order")]
+        /// <summary>
+        /// 可关联工单
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [HttpGet("canlink-order")]
         public async Task<PagedDto<OrderDto>> CanLinkCallRecordOrder([FromQuery] CanLinkCallRecordOrderDto dto)
         {
             var (total, items) = await _orderRepository.Queryable()
@@ -800,7 +812,7 @@ namespace Hotline.Api.Controllers
         public async Task LinkCallRecord([FromBody] LinkCallRecordDto dto)
         {
             //var trRecord = await _trCallRecordRepository.GetAsync(x => x.CallAccept == dto.CallId, HttpContext.RequestAborted);//由CallAccept改为OtherAccept
-            
+
             if (dto.IsOrder)
             {
                 var trRecords = await _trCallRecordRepository.Queryable().Where(x => x.OtherAccept == dto.CallId).ToListAsync(HttpContext.RequestAborted);
@@ -836,7 +848,7 @@ namespace Hotline.Api.Controllers
                 trRecord.ExternalId = visit.Id;
                 await _trCallRecordRepository.UpdateAsync(trRecord, HttpContext.RequestAborted);
             }
-           
+
         }
 
 
@@ -853,7 +865,7 @@ namespace Hotline.Api.Controllers
         [HttpGet("telaction-list")]
         public async Task<PagedDto<TelActionListRep>> TelActionList([FromQuery] TelActionListDto dto)
         {
-            var (total,items) = await _telActionRecordRepository.Queryable()
+            var (total, items) = await _telActionRecordRepository.Queryable()
                 .WhereIF(string.IsNullOrEmpty(dto.TelNo) == false, x => x.TelNo.Contains(dto.TelNo))
                 .WhereIF(dto.ActionTtype != null, x => x.ActionType == dto.ActionTtype)
                 .WhereIF(string.IsNullOrEmpty(dto.UserName) == false, x => x.UserName.Contains(dto.UserName))
@@ -871,9 +883,14 @@ namespace Hotline.Api.Controllers
         [HttpGet("telaction-basedata")]
         public async Task<object> TelActionBaseData()
         {
+            var actionType = EnumExts.GetDescriptions<EActionType>();
+            if (_appOptions.Value.GetDefaultAppScopeConfiguration().CallCenterType == AppDefaults.CallCenterType.XingTang)
+            {
+                actionType = actionType.Where(m => new int[] { 4, 5 }.Contains(m.Key) == false).ToList();
+            }
             return new
             {
-                ActionType = EnumExts.GetDescriptions<EActionType>(),
+                ActionType = actionType,
             };
         }
         #endregion
@@ -908,7 +925,7 @@ namespace Hotline.Api.Controllers
             try
             {
                 var list = await _workRepository.Queryable().Where(x => 1 == 1 && !x.EndTime.HasValue).ToListAsync();
-                var tellist = await _callTelClient.QueryTelStateAsync(new QueryTelStateRequest { },HttpContext.RequestAborted);
+                var tellist = await _callTelClient.QueryTelStateAsync(new QueryTelStateRequest { }, HttpContext.RequestAborted);
                 foreach (var item in list)
                 {
                     var telmodel = tellist.AgentList.First(x => x.TelNo == item.TelNo);
@@ -916,7 +933,7 @@ namespace Hotline.Api.Controllers
                     {
                         if (telmodel.State == "logout")
                         {
-                            await _iPPbxApplication.ResetTelStatus(item.Id,null, HttpContext.RequestAborted);
+                            await _iPPbxApplication.ResetTelStatus(item.Id, null, HttpContext.RequestAborted);
                             //var telRest = await _telRestRepository.GetAsync(x => x.TelNo == item.TelNo && !x.EndTime.HasValue, HttpContext.RequestAborted);
                             //if (telRest is not null)
                             //{

+ 434 - 85
src/Hotline.Api/Controllers/OrderController.cs

@@ -1,12 +1,13 @@
-using DocumentFormat.OpenXml.Spreadsheet;
+using DocumentFormat.OpenXml.Wordprocessing;
 using DotNetCore.CAP;
 using FluentValidation;
 using Hotline.Api.Filter;
 using Hotline.Application.CallCenter;
 using Hotline.Application.ExportExcel;
 using Hotline.Application.FlowEngine;
+using Hotline.Application.OrderApp;
 using Hotline.Application.Quality;
-using Hotline.Application.Snapshot;
+using Hotline.Application.Snapshot.Contracts;
 using Hotline.Application.Systems;
 using Hotline.Article;
 using Hotline.Authentications;
@@ -48,7 +49,6 @@ using Hotline.Share.Dtos.Settings;
 using Hotline.Share.Dtos.Snapshot;
 using Hotline.Share.Enums.Article;
 using Hotline.Share.Enums.CallCenter;
-using Hotline.Share.Enums.Caselibrary;
 using Hotline.Share.Enums.FlowEngine;
 using Hotline.Share.Enums.Order;
 using Hotline.Share.Enums.Push;
@@ -57,25 +57,22 @@ using Hotline.Share.Mq;
 using Hotline.Share.Requests;
 using Hotline.Share.Tools;
 using Hotline.Snapshot;
-using Hotline.Snapshot.Interfaces;
+using Hotline.Snapshot.IRepository;
 using Hotline.Tools;
 using Hotline.Users;
 using Hotline.Validators.FlowEngine;
 using Hotline.YbEnterprise.Sdk;
 using Mapster;
 using MapsterMapper;
-using MathNet.Numerics.LinearAlgebra.Factorization;
 using MediatR;
 using Microsoft.AspNetCore.Authorization;
 using Microsoft.AspNetCore.Mvc;
 using Microsoft.Extensions.Options;
 using MiniExcelLibs;
-using NPOI.SS.Formula.Functions;
 using SqlSugar;
 using System.Text;
 using System.Text.Json;
 using System.Threading;
-using Hotline.Application.OrderApp;
 using XF.Domain.Authentications;
 using XF.Domain.Cache;
 using XF.Domain.Exceptions;
@@ -167,6 +164,7 @@ public class OrderController : BaseController
     private readonly IRepository<OrderComplement> _orderComplementRepository;
     private readonly ICircularRecordDomainService _circularRecordDomainService;
     private readonly IRepository<Hotline.Special.SpecialNumber> _specialNumberRepository;
+    private readonly IRepository<OrderVisitDetailCopy> _orderVisitDetailCopyRepository;
 
     public OrderController(
         IOrderDomainService orderDomainService,
@@ -243,7 +241,8 @@ public class OrderController : BaseController
         IRepository<SystemOrganize> systemOrganizeRepository,
         IRepository<OrderComplement> orderComplementRepository,
         ICircularRecordDomainService circularRecordDomainService,
-        IRepository<Hotline.Special.SpecialNumber> specialNumberRepository)
+        IRepository<Hotline.Special.SpecialNumber> specialNumberRepository,
+        IRepository<OrderVisitDetailCopy> orderVisitDetailCopyRepository)
     {
         _orderDomainService = orderDomainService;
         _orderRepository = orderRepository;
@@ -320,6 +319,7 @@ public class OrderController : BaseController
         _orderComplementRepository = orderComplementRepository;
         _circularRecordDomainService = circularRecordDomainService;
         _specialNumberRepository = specialNumberRepository;
+        _orderVisitDetailCopyRepository = orderVisitDetailCopyRepository;
     }
 
     #endregion
@@ -679,40 +679,65 @@ public class OrderController : BaseController
         {
             try
             {
-                if (order.Source != ESource.ProvinceStraight && order.FileOrgIsCenter.Value == false)
+                if (_appOptions.Value.IsZiGong)
                 {
-                    var code = "";
-                    //受理类型为“投诉、举报”
-                    if ((order.AcceptTypeCode == "30" || order.AcceptTypeCode == "35") && orderVisit.VisitState != EVisitState.Visited)
+                    if (order.Source == ESource.ProvinceStraight)
                     {
-                        code = "1017";
-                        orderVisit.VisitState = EVisitState.SMSVisiting;
-                        await _orderVisitRepository.UpdateAsync(orderVisit);
+                        //发送查询短信
+                        var messageDto = new Share.Dtos.Push.MessageDto
+                        {
+                            PushBusiness = EPushBusiness.SearchSms,
+                            ExternalId = visitId,
+                            OrderId = order.Id,
+                            PushPlatform = EPushPlatform.Sms,
+                            Remark = order.Title,
+                            Name = order.FromName,
+                            TemplateCode = "1021",
+                            Params = new List<string>() { order.No, order.Password },
+                            TelNumber = order.Contact,
+                        };
+                        await _mediator.Publish(new PushMessageNotify(messageDto), HttpContext.RequestAborted);
                     }
-                    else
-                        code = "1018";
-
-                    var messageDto = new Share.Dtos.Push.MessageDto
+                }
+                else
+                {
+                    if (order.Source != ESource.ProvinceStraight && order.FileOrgIsCenter.Value == false)
                     {
-                        PushBusiness = EPushBusiness.SearchSms,
-                        ExternalId = visitId,
-                        OrderId = order.Id,
-                        PushPlatform = EPushPlatform.Sms,
-                        Remark = order.Title,
-                        Name = order.FromName,
-                        TemplateCode = code,
-                        Params = new List<string>() { order.No, order.Password },
-                        TelNumber = order.Contact,
-                    };
-                    await _mediator.Publish(new PushMessageNotify(messageDto), HttpContext.RequestAborted);
-
-                    // 发送短信后推送一个 48小时的延迟消息队列. 当消息队列收到消息时, 判断用户是否回复了, 如果未回复短信就 默认满意
-                    var delaySecond = _systemSettingCacheManager.DefaultVisitSmsDelaySecond;
-                    await _capPublisher.PublishDelayAsync(
-                        TimeSpan.FromSeconds(delaySecond),
-                        EventNames.UpdateVisitDelaySms,
-                        messageDto,
-                        cancellationToken: HttpContext.RequestAborted);
+                        var isOpenSendVisitSms = _systemSettingCacheManager.GetSetting(SettingConstants.IsOpenSendVisitSms)?.SettingValue[0];
+
+                        var code = "";
+                        //受理类型为“投诉、举报”
+                        if ((order.AcceptTypeCode == "30" || order.AcceptTypeCode == "35") && orderVisit.VisitState != EVisitState.Visited && isOpenSendVisitSms == "true")
+                        {
+                            code = "1017";
+                            orderVisit.VisitState = EVisitState.SMSVisiting;
+                            await _orderVisitRepository.UpdateAsync(orderVisit);
+                        }
+                        else
+                            code = "1018";
+
+                        var messageDto = new Share.Dtos.Push.MessageDto
+                        {
+                            PushBusiness = EPushBusiness.SearchSms,
+                            ExternalId = visitId,
+                            OrderId = order.Id,
+                            PushPlatform = EPushPlatform.Sms,
+                            Remark = order.Title,
+                            Name = order.FromName,
+                            TemplateCode = code,
+                            Params = new List<string>() { order.No, order.Password },
+                            TelNumber = order.Contact,
+                        };
+                        await _mediator.Publish(new PushMessageNotify(messageDto), HttpContext.RequestAborted);
+
+                        // 发送短信后推送一个 48小时的延迟消息队列. 当消息队列收到消息时, 判断用户是否回复了, 如果未回复短信就 默认满意
+                        var delaySecond = _systemSettingCacheManager.DefaultVisitSmsDelaySecond;
+                        await _capPublisher.PublishDelayAsync(
+                            TimeSpan.FromSeconds(delaySecond),
+                            EventNames.UpdateVisitDelaySms,
+                            messageDto,
+                            cancellationToken: HttpContext.RequestAborted);
+                    }
                 }
             }
             catch (Exception)
@@ -1079,6 +1104,53 @@ public class OrderController : BaseController
 
     #region 工单回访
 
+    /// <summary>
+    /// 获取修改历史记录
+    /// </summary>
+    /// <param name="id"></param>
+    /// <returns></returns>
+    [HttpGet("getordervisitdetailcopy/{id}")]
+    public async Task<OrderVisitDetailCopyDto> GetOrderVisitDetailCopy(string id)
+    {
+        var listCopyData = await _orderVisitDetailCopyRepository.Queryable()
+            .Where(p => p.VisitId == id)
+            .OrderByDescending(p => p.CreationTime)
+            .Take(1)
+            .ToListAsync();
+
+        var data = new OrderVisitDetailCopyDto();
+        if (listCopyData != null && listCopyData.Any())
+        {
+            //修改数据
+            var list = await _orderVisitDetailCopyRepository.Queryable().Where(p => p.BacthId == listCopyData[0].BacthId).OrderBy(p => p.VisitTarget).ToListAsync();
+            if (list != null && list.Any())
+            {
+                var orderVisitDetailCopyDtos = new List<OrderVisitDetailDto>();
+                foreach (var item in list)
+                {
+                    orderVisitDetailCopyDtos.Add(_mapper.Map<OrderVisitDetailDto>(item));
+                }
+                data.OrderVisitDetailCopyDtos = orderVisitDetailCopyDtos;
+                data.CreatorName = list[0].CreatorName;
+                data.CreationTime = list[0].CreationTime;
+            }
+
+            //正常数据
+            var listData = await _orderVisitedDetailRepository.Queryable().Where(p => p.VisitId == id).OrderBy(p => p.VisitTarget).ToListAsync();
+            if (listData != null && listData.Any())
+            {
+                var orderVisitDetailDtos = new List<OrderVisitDetailDto>();
+                foreach (var item in listData)
+                {
+                    orderVisitDetailDtos.Add(_mapper.Map<OrderVisitDetailDto>(item));
+                }
+                data.OrderVisitDetailDtos = orderVisitDetailDtos;
+            }
+        }
+
+        return data;
+    }
+
     /// <summary>
     /// 回访列表
     /// </summary>
@@ -1340,12 +1412,15 @@ public class OrderController : BaseController
             Histories = histories,
             IsCanUpdate = isCanUpdate
         };
-        if (_appOptions.Value.IsLuZhou
-            && !_sessionContext.OrgIsCenter
-            && rsp.OrderVisitModel.Order.IsSecret)
+        if (!_sessionContext.OrgIsCenter && rsp.OrderVisitModel.Order.IsSecret)
         {
-            rsp.OrderVisitModel.Order.FromPhone = "****";//rsp.OrderVisitModel.Order.FromPhone?.Replace(rsp.OrderVisitModel.Order.FromPhone.Substring(3, 4), "****");
-            rsp.OrderVisitModel.Order.Contact = "****"; //rsp.OrderVisitModel.Order.Contact?.Replace(rsp.OrderVisitModel.Order.Contact.Substring(3, 4), "****");
+            if (_appOptions.Value.IsZiGong)
+                rsp.OrderVisitModel.Order = rsp.OrderVisitModel.Order.DataMask();
+            if (_appOptions.Value.IsLuZhou)
+            {
+                rsp.OrderVisitModel.Order.FromPhone = "****";//rsp.OrderVisitModel.Order.FromPhone?.Replace(rsp.OrderVisitModel.Order.FromPhone.Substring(3, 4), "****");
+                rsp.OrderVisitModel.Order.Contact = "****"; //rsp.OrderVisitModel.Order.Contact?.Replace(rsp.OrderVisitModel.Order.Contact.Substring(3, 4), "****");
+            }
         }
 
         return rsp;
@@ -2560,7 +2635,7 @@ public class OrderController : BaseController
         }
 
         var (total, items) = await _orderApplication.MayScreenList(dto)
-            .ToPagedListAsync(dto.PageIndex, dto.PageSize, HttpContext.RequestAborted);
+       .ToPagedListAsync(dto.PageIndex, dto.PageSize, HttpContext.RequestAborted);
         var data = _mapper.Map<List<OrderVisitDetailDto>>(items);
         var isAdmin = _orderDomainService.IsCheckAdmin();
         data.ForEach(d => d.IsShowOperate = (dto.ScreenType == EOrderScreenType.Org && d.VisitOrgCode == _sessionContext.OrgId)
@@ -2621,7 +2696,7 @@ public class OrderController : BaseController
     public async Task<PagedDto<OrderScreenListDto>> ScreenList([FromQuery] ScreenListDto dto)
     {
         var (total, items) = await _orderApplication.OrderScreenList(dto)
-            .ToPagedListAsync(dto.PageIndex, dto.PageSize, HttpContext.RequestAborted);
+    .ToPagedListAsync(dto.PageIndex, dto.PageSize, HttpContext.RequestAborted);
         return new PagedDto<OrderScreenListDto>(total, _mapper.Map<IReadOnlyList<OrderScreenListDto>>(items));
     }
 
@@ -2876,6 +2951,19 @@ public class OrderController : BaseController
     [HttpGet("screen/base")]
     public async Task<object> ScreenBaseData()
     {
+        var screenStatisticalNodes = _systemSettingCacheManager.GetSetting(SettingConstants.ScreenStatisticalNodes).SettingValue;
+        var screenStatisticalNodesList = new List<Kv>();
+        if (screenStatisticalNodes != null && screenStatisticalNodes.Any())
+        {
+            foreach (var item in screenStatisticalNodes)
+            {
+                screenStatisticalNodesList.Add(new Kv
+                {
+                    Key = item,
+                    Value = item
+                });
+            }
+        }
         var rsp = new
         {
             ScreenStatus = EnumExts.GetDescriptions<EScreenStatus>(),
@@ -2887,6 +2975,7 @@ public class OrderController : BaseController
             SourceChannel = _sysDicDataCacheManager.GetSysDicDataCache(SysDicTypeConsts.SourceChannel),
             VisitSatisfaction = _sysDicDataCacheManager.GetSysDicDataCache(SysDicTypeConsts.VisitSatisfaction),
             DissatisfiedReason = _sysDicDataCacheManager.GetSysDicDataCache(SysDicTypeConsts.DissatisfiedReason),
+            ScreenStatisticalNodes = screenStatisticalNodesList,
         };
         return rsp;
     }
@@ -3119,6 +3208,86 @@ public class OrderController : BaseController
         return new PagedDto<SuperviseOrderDto>(total, _mapper.Map<IReadOnlyList<SuperviseOrderDto>>(items));
     }
 
+    /// <summary>
+    /// 督办查询部门经办人手机号
+    /// </summary>
+    /// <param name="orgCode"></param>
+    /// <returns></returns>
+    [HttpGet("supervise/geturgetel/{orgCode}")]
+    public async Task<Kv> GetUrgeTel(string orgCode)
+    {
+        Kv k = new Kv();
+        var acceptSmsRoleIds = _systemSettingCacheManager.GetSetting(SettingConstants.AcceptSmsRoleIds)?.SettingValue;
+        //查询部门所有账号
+        var userlist = await _userRepository.Queryable().Where(x =>
+            x.OrgId == orgCode && !string.IsNullOrEmpty(x.PhoneNo) &&
+            x.Roles.Any(d => acceptSmsRoleIds.Contains(d.Id))).ToListAsync();
+        if (userlist != null && userlist.Count > 0)
+        {
+            k.Key = userlist[0].Name;
+            k.Value = userlist[0].PhoneNo;
+        }
+        return k;
+    }
+
+    /// <summary>
+    /// 旅游列表
+    /// </summary>
+    /// <param name="dto"></param>
+    /// <returns></returns>
+    [HttpGet("supervise/travel")]
+    public async Task<IReadOnlyList<OrderDto>> QueryTravel([FromQuery] QueryOrderDto dto)
+    {
+        var orders = await _orderApplication.QueryTravel(dto)
+            .ToPageListWithoutTotalAsync(dto, HttpContext.RequestAborted);
+        var list = _mapper.Map<IReadOnlyList<OrderDto>>(orders);
+        if (_appOptions.Value.IsLuZhou && !_sessionContext.OrgIsCenter)
+            list = list.Select(p => p.DataMask()).ToList();
+        return list;
+    }
+
+    /// <summary>
+    /// 旅游列表查询总数
+    /// </summary>
+    [HttpGet("supervise/travel/count")]
+    public async Task<int> QueryTravelCount([FromQuery] QueryOrderDto dto)
+    {
+        var query = _orderApplication.QueryTravel(dto);
+        return await query.CountAsync(HttpContext.RequestAborted);
+    }
+
+    [HttpPost("supervise/travel/export")]
+    public async Task<FileStreamResult> ExportTravel([FromBody] ExportExcelDto<QueryOrderDto> dto)
+    {
+        var query = _orderApplication.QueryTravel(dto.QueryDto);
+        List<Order> orders;
+        if (dto.IsExportAll)
+        {
+            orders = await query.ToListAsync(HttpContext.RequestAborted);
+        }
+        else
+        {
+            var (_, items) = await query.ToPagedListAsync(dto.QueryDto, HttpContext.RequestAborted);
+            orders = items;
+        }
+
+        var orderDtos = _mapper.Map<ICollection<OrderDto>>(orders);
+
+        if (_appOptions.Value.IsLuZhou)
+            orderDtos = orderDtos.Select(p => p.DataMask()).ToList();
+
+        dynamic? dynamicClass = DynamicClassHelper.CreateDynamicClass<OrderDto>(dto.ColumnInfos);
+
+        var dtos = orderDtos
+            .Select(stu => _mapper.Map(stu, typeof(OrderDto), dynamicClass))
+            .Cast<object>()
+            .ToList();
+
+        var stream = ExcelHelper.CreateStream(dtos);
+
+        return ExcelStreamResult(stream, "旅游数据");
+    }
+
     /// <summary>
     /// 申请督办
     /// </summary>
@@ -3154,27 +3323,64 @@ public class OrderController : BaseController
                 //发送短信
                 try
                 {
-                    var acceptSmsRoleIds = _systemSettingCacheManager.GetSetting(SettingConstants.AcceptSmsRoleIds)?.SettingValue;
-                    //查询部门所有账号
-                    var userlist = await _userRepository.Queryable().Where(x =>
-                        x.OrgId == model.OrgId && !string.IsNullOrEmpty(x.PhoneNo) &&
-                        x.Roles.Any(d => acceptSmsRoleIds.Contains(d.Id))).ToListAsync();
-                    //发送短信
-                    foreach (var user in userlist)
+                    if (string.IsNullOrEmpty(dto.PhoneNo))
                     {
-                        var messageDto = new Share.Dtos.Push.MessageDto
+                        #region 普通督办短信内容
+                        var acceptSmsRoleIds = _systemSettingCacheManager.GetSetting(SettingConstants.AcceptSmsRoleIds)?.SettingValue;
+                        //查询部门所有账号
+                        var userlist = await _userRepository.Queryable().Where(x =>
+                            x.OrgId == model.OrgId && !string.IsNullOrEmpty(x.PhoneNo) &&
+                            x.Roles.Any(d => acceptSmsRoleIds.Contains(d.Id))).ToListAsync();
+                        //发送短信
+                        foreach (var user in userlist)
                         {
-                            PushBusiness = EPushBusiness.OrderSupervise,
-                            ExternalId = order.Id,
-                            OrderId = order.Id,
-                            PushPlatform = EPushPlatform.Sms,
-                            Remark = order.Title,
-                            Name = user.Name,
-                            TemplateCode = "1003",
-                            Params = new List<string>() { order.No },
-                            TelNumber = user.PhoneNo,
-                        };
-                        await _mediator.Publish(new PushMessageNotify(messageDto), HttpContext.RequestAborted);
+                            var messageDto = new Share.Dtos.Push.MessageDto
+                            {
+                                PushBusiness = EPushBusiness.OrderSupervise,
+                                ExternalId = order.Id,
+                                OrderId = order.Id,
+                                PushPlatform = EPushPlatform.Sms,
+                                Remark = order.Title,
+                                Name = user.Name,
+                                TemplateCode = "1003",
+                                Params = new List<string>() { order.No },
+                                TelNumber = user.PhoneNo,
+                            };
+                            await _mediator.Publish(new PushMessageNotify(messageDto), HttpContext.RequestAborted);
+                        }
+                        #endregion
+                    }
+                    else
+                    {
+                        #region 旅游督办短信内容
+
+                        var acceptSmsRoleIds = _systemSettingCacheManager.GetSetting(SettingConstants.AcceptSmsRoleIds)?.SettingValue;
+                        //查询部门所有账号
+                        var userlist = await _userRepository.Queryable().Where(x =>
+                            x.OrgId == model.OrgId && x.PhoneNo == dto.PhoneNo &&
+                            x.Roles.Any(d => acceptSmsRoleIds.Contains(d.Id))).ToListAsync();
+
+                        var Contact = !string.IsNullOrEmpty(order.FromPhone) ? order.FromPhone : order.Contact;
+
+                        //发送短信
+                        if (userlist != null && userlist.Count > 0)
+                        {
+                            var messageDto = new Share.Dtos.Push.MessageDto
+                            {
+                                PushBusiness = EPushBusiness.OrderSupervise,
+                                ExternalId = order.Id,
+                                OrderId = order.Id,
+                                PushPlatform = EPushPlatform.Sms,
+                                Remark = order.Title,
+                                Name = userlist[0].Name,
+                                TemplateCode = "1022",
+                                Params = new List<string>() { dto.ApplyContent, order.No, Contact, order.Content },
+                                TelNumber = userlist[0].PhoneNo,
+                            };
+                            await _mediator.Publish(new PushMessageNotify(messageDto), HttpContext.RequestAborted);
+                        }
+
+                        #endregion
                     }
                 }
                 catch
@@ -4190,11 +4396,14 @@ public class OrderController : BaseController
         {
             await _orderSnapshotRepository.Queryable()
                 .Where(m => m.Id == order.Id)
-                .Select(m => new { m.IndustryId, m.IndustryName })
+                .Select(m => new { m.IndustryId, m.IndustryName, m.IndustryCase })
                 .FirstAsync(HttpContext.RequestAborted)
                 .Then(async snapshot =>
                 {
-                    dto.IndustryName = snapshot.IndustryName;
+                    if (snapshot.IndustryCase.IsNullOrEmpty())
+                        dto.IndustryName = snapshot.IndustryName;
+                    else
+                        dto.IndustryName = snapshot.IndustryName + " " + snapshot.IndustryCase;
                     dto.IndustryId = snapshot.IndustryId;
                 });
         }
@@ -4330,7 +4539,7 @@ public class OrderController : BaseController
             order.PushTypeCode = string.Join(",", pushTypeCode);
             order.PushType = string.Join(",", pushTypes);
         }
-        
+
         if (dto.Tags != null && dto.Tags.Any())
         {
             var orderTags = new List<SystemDicData>();
@@ -4641,6 +4850,7 @@ public class OrderController : BaseController
 
         //处理工单的期满时间
         //首先是工单编辑页面提交的、流程已经开启、工单未归档的工单才能修改期满时间
+        var isUpdateExpiredTime = false;
         if (dto.IsEdit && !string.IsNullOrEmpty(order.WorkflowId) && order.Status < EOrderStatus.Filed && dto.AcceptTypeCode != acceptTypeCode)
         {
             //查询当前工单的实际办理节点,如果在热线中心不处理,如果在部门需要更新期满时间
@@ -4659,6 +4869,7 @@ public class OrderController : BaseController
                     order.Adapt<OrderTimeClacInfo>());
                 if (expiredTimeConfig is not null && expiredTimeConfig.ExpiredTime > order.ExpiredTime)
                 {
+                    isUpdateExpiredTime = true;
                     order.ExpiredTime = expiredTimeConfig.ExpiredTime;
                     order.NearlyExpiredTime = expiredTimeConfig.NearlyExpiredTime;
                     order.NearlyExpiredTimeOne = expiredTimeConfig.NearlyExpiredTimeOne;
@@ -4670,7 +4881,12 @@ public class OrderController : BaseController
             }
         }
 
-        await _orderRepository.UpdateNav(order, new UpdateNavRootOptions { IgnoreColumns = ["CallId"] }).Include(d => d.OrderExtension)
+        var updateNavRootOptions = isUpdateExpiredTime
+            ? new UpdateNavRootOptions { IgnoreColumns = ["CallId"] }
+            : new UpdateNavRootOptions { IgnoreColumns = ["CallId", "ExpiredTime", "NearlyExpiredTime", "NearlyExpiredTimeOne"] };
+
+        await _orderRepository.UpdateNav(order, updateNavRootOptions)
+            .Include(d => d.OrderExtension)
             .Include(d => d.OrderTags).ExecuteCommandAsync();
 
         //订阅此事件的内部处理工单数据只能更新各自业务的字段,不能全部更新
@@ -4789,27 +5005,31 @@ public class OrderController : BaseController
     [HttpGet("startflow")]
     public async Task<NextStepsDto> GetFlowStartOptions([FromQuery] string? orderId)
     {
-        var outDto = await _workflowApplication.GetStartStepsAsync(WorkflowModuleConsts.OrderHandle,
+        var dto = await _workflowApplication.GetStartStepsAsync(WorkflowModuleConsts.OrderHandle,
             HttpContext.RequestAborted);
         if (orderId.NotNullOrEmpty())
         {
-            outDto.Opinion = await _typeCache.GetAsync($"tmp_opinion_{orderId}{_sessionContext.UserId}", HttpContext.RequestAborted);
-            outDto.Content = (await _orderRepository.GetAsync(orderId, HttpContext.RequestAborted))?.Content;
+            dto.Opinion = await _typeCache.GetAsync($"tmp_opinion_{orderId}{_sessionContext.UserId}", HttpContext.RequestAborted);
+            dto.Content = (await _orderRepository.GetAsync(orderId, HttpContext.RequestAborted))?.Content;
         }
 
         //随手拍
-        bool.TryParse(_systemSettingCacheManager.GetSetting(SettingConstants.Snapshot)?.SettingValue[0],
-            out bool isSnapshotEnable);
+        var isSnapshotEnable = _systemSettingCacheManager.Snapshot;
+        var isAqyh = false;//行业类型是否为随手拍安全隐患
         if (isSnapshotEnable)
         {
-            var orderSnapshot = await _orderSnapshotRepository.GetAsync(orderId, HttpContext.RequestAborted);
-            if (orderSnapshot != null && string.CompareOrdinal(orderSnapshot.IndustryName, "安全隐患") == 0)
+            var orderSnapShot = await _orderSnapshotRepository.GetAsync(orderId, HttpContext.RequestAborted);
+            if(orderSnapShot != null && string.CompareOrdinal(orderSnapShot.IndustryName, "安全隐患") == 0)
             {
-                outDto.Steps = outDto.Steps.Where(d => d.BusinessType != EBusinessType.Send).ToList();
+                isAqyh = true;
+                dto.Steps.RemoveAll(d => d.BusinessType == EBusinessType.Send);
             }
         }
+        if (!isAqyh)
+            dto.Steps = dto.Steps.Where(d => string.CompareOrdinal(d.Value, "网格员") != 0
+                                             && string.CompareOrdinal(d.Value, "工单标记") != 0).ToList();
 
-        return outDto;
+        return dto;
     }
 
     /// <summary>
@@ -5041,6 +5261,7 @@ public class OrderController : BaseController
                 var (_, currentStep, _, _) = await _workflowDomainService.NextAsync(nextDto,
                     expiredTime: order.ExpiredTime,
                     isAutoFillSummaryOpinion: isAutoFillSummaryOpinion,
+                    newStepConfig: OrderHandleNewStepConfig(),
                     cancellationToken: cancellationToken);
                 if (currentStep.BusinessType is EBusinessType.Send)
                 {
@@ -5089,7 +5310,9 @@ public class OrderController : BaseController
 
                         var (_, _, _, nextSteps) = await _workflowDomainService.NextAsync(nextflowDto,
                             EHandleMode.CrossLevel, order.ExpiredTime,
-                            isAutoFillSummaryOpinion, cancellationToken: cancellationToken);
+                            isAutoFillSummaryOpinion,
+                            newStepConfig: OrderHandleNewStepConfig(),
+                            cancellationToken: cancellationToken);
                         tempSteps.AddRange(nextSteps);
                     }
 
@@ -5140,6 +5363,18 @@ public class OrderController : BaseController
         }
     }
 
+    private static Action<Workflow, WorkflowStep, StepDefine, WorkflowStep>? OrderHandleNewStepConfig()
+    {
+        return (_, _, stepDefine, newStep) =>
+        {
+            if (stepDefine.BusinessType is EBusinessType.Department or EBusinessType.DepartmentLeader)
+            {
+                newStep.HandlerId = null;
+                newStep.HandlerName = null;
+            }
+        };
+    }
+
     private async Task HandleNextInMainAndSecondaryAsync(WorkflowDefinition definition, List<OrgDto> orgs,
         NextWorkflowDto? flowDto, DateTime? expiredTime, bool isAutoFillSummaryOpinion, CancellationToken cancellation)
     {
@@ -5147,6 +5382,7 @@ public class OrderController : BaseController
         var (_, _, _, currentSteps) = await _workflowDomainService.NextAsync(flowDto,
             expiredTime: expiredTime,
             isAutoFillSummaryOpinion: isAutoFillSummaryOpinion,
+            newStepConfig: OrderHandleNewStepConfig(),
             cancellationToken: cancellation);
 
         foreach (var currentStep in currentSteps)
@@ -5230,21 +5466,35 @@ public class OrderController : BaseController
     [HttpGet("nextsteps/{orderId}")]
     public async Task<NextStepsWithOpinionDto<RecommendStepOption>> GetNextStepsWithRecommend(string orderId)
     {
+        var isSnapshotEnable = _systemSettingCacheManager.Snapshot;
         var order = await _orderDomainService.GetOrderAsync(orderId, cancellationToken: HttpContext.RequestAborted);
+
         if (string.IsNullOrEmpty(order.WorkflowId))
             throw UserFriendlyException.SameMessage("该工单未开启流程");
         var dto = await _workflowApplication.GetNextStepsAsync(order.WorkflowId, HttpContext.RequestAborted);
+        var isAqyh = false;//行业类型是否为随手拍安全隐患
+        if (isSnapshotEnable)
+        {
+            var orderSnapShot = await _orderSnapshotRepository.GetAsync(orderId, HttpContext.RequestAborted);
+            isAqyh = orderSnapShot != null && string.CompareOrdinal(orderSnapShot.IndustryName, "安全隐患") == 0;
+        }
+        if(!isAqyh)
+            dto.Steps = dto.Steps.Where(d => string.CompareOrdinal(d.Value, "网格员") != 0
+                                             && string.CompareOrdinal(d.Value, "工单标记") != 0).ToList();
+
         dto.ExpiredTime = order.ExpiredTime;
         dto.Content = order.Content;
         var rsp = _mapper.Map<NextStepsWithOpinionDto<RecommendStepOption>>(dto);
-        foreach (var step in rsp.Steps)
+        var org = await _organizeRepository.GetAsync(d => d.AreaCode == order.AreaCode, HttpContext.RequestAborted);
+        if (org != null)
         {
-            if (dto.CurrentStepBusinessType is not EBusinessType.Send ||
-                step.BusinessType is not EBusinessType.Department) continue;
-            var org = await _organizeRepository.GetAsync(d => d.AreaCode == order.AreaCode, HttpContext.RequestAborted);
-            if (org is null) continue;
-            step.RecommendOrgId = org.Id;
-            step.RecommendOrgName = org.Name;
+            foreach (var step in rsp.Steps)
+            {
+                if (dto.CurrentStepBusinessType is not EBusinessType.Send ||
+                    step.BusinessType is not EBusinessType.Department) continue;
+                step.RecommendOrgId = org.Id;
+                step.RecommendOrgName = org.Name;
+            }
         }
 
         rsp.LeaderSMS = _sysDicDataCacheManager.GetSysDicDataCache(SysDicTypeConsts.LeaderSMS)
@@ -5542,6 +5792,105 @@ public class OrderController : BaseController
         return order.WorkflowTraces.Count(d => d.IsOrigin && d.BusinessType == EBusinessType.Seat);
     }
 
+    #region 附件列表
+
+    /// <summary>
+    /// 获取工单所有文件
+    /// </summary>
+    /// <param name="id"></param>
+    /// <returns></returns>
+    [HttpGet("all_file/{id}")]
+    public async Task<List<FileDto>> GetOrderAllFile(string id)
+    {
+        var order = await _orderRepository.GetAsync(id);
+        var steps = await _workflowStepRepository.Queryable().Where(x => x.WorkflowId == order.WorkflowId).ToListAsync();
+        var allFiles = new List<FileDto>();
+        if (order != null && _sessionContext.OrgIsCenter)
+        {
+            if (order.FileJson != null)
+            {
+                var ids = order.FileJson.Select(x => x.Id).ToList();
+                allFiles = await _fileRepository.GetFilesAsync(ids, cancellationToken: HttpContext.RequestAborted);
+                allFiles.ForEach(x => x.GetStepName("工单受理"));
+            }
+        }
+        foreach (var step in steps)
+        {
+            if (step.FileJson != null && step.FileJson.Any())
+            {
+                var ids = step.FileJson.Select(x => x.Id).ToList();
+                var stepFiles = await _fileRepository.GetFilesAsync(ids, HttpContext.RequestAborted);
+                stepFiles.ForEach(x => x.GetStepName(step.Name));
+                allFiles.AddRange(stepFiles);
+            }
+        }
+        return allFiles;
+    }
+
+    /// <summary>
+    /// 获取流程信息
+    /// </summary>
+    /// <param name="workflowId"></param>
+    /// <returns></returns>
+    [HttpGet("all_file/workflow/{workflowId}")]
+    public async Task<WorkflowDto> GetOrderWorkflowAsync(string workflowId)
+    {
+        var workflow = await _workflowDomainService.GetWorkflowAsync(workflowId, withSteps: true,
+            cancellationToken: HttpContext.RequestAborted);
+        var steps = _mapper.Map<List<WorkflowTraceDto>>(workflow.Steps);
+        if (!_sessionContext.OrgIsCenter)
+        {
+            steps = steps.Where(x => x.HandlerOrgId != null && x.HandlerOrgId.StartsWith(_sessionContext.RequiredOrgId)).ToList();
+        }
+        var workflowDto = _mapper.Map<WorkflowDto>(workflow);
+        workflowDto.Traces = steps;
+        foreach (var item in workflowDto.Traces)
+        {
+            if (item.FileJson != null)
+            {
+                var ids = item.FileJson.Select(x => x.Id).ToList();
+                item.Files = await _fileRepository.GetFilesAsync(ids, HttpContext.RequestAborted);
+            }
+            item.Name = item.Name + "(" + item.HandlerOrgName + ")";
+        }
+        return workflowDto;
+    }
+
+    /// <summary>
+    /// 附件列表补充附件
+    /// </summary>
+    /// <param name="dtos"></param>
+    /// <returns></returns>
+    [HttpPost("list/file_upload")]
+    [LogFilter("附件列表补充附件")]
+
+    public async Task Add([FromBody] OrderStepUploadFilesDto dto)
+    {
+        var order = await _orderRepository.GetAsync(dto.OrderId);
+        var step = await _workflowStepRepository.GetAsync(dto.StepId);
+
+        var listFileJson = new List<FileJson>();
+        if (dto.Files.Any())
+        {
+            listFileJson = await _fileRepository.AddFileAsync(dto.Files, dto.StepId, "", HttpContext.RequestAborted);
+        }
+        await _workflowStepRepository.Updateable().SetColumns(x => new WorkflowStep { FileJson = listFileJson }).Where(x => x.Id == dto.StepId).ExecuteCommandAsync(HttpContext.RequestAborted);
+        await _workflowTraceRepository.Updateable().SetColumns(x => new WorkflowTrace { FileJson = listFileJson }).Where(x => x.Id == dto.StepId).ExecuteCommandAsync(HttpContext.RequestAborted);
+
+        ////附件上传后推送省上
+        var orderDto = _mapper.Map<OrderDto>(order);
+        var flowDto = new OrderFlowDto
+        {
+            Order = orderDto,
+            WorkflowTrace = _mapper.Map<WorkflowTraceDto>(step),
+            ExpiredTimeChanged = false,
+            HandlerOrgLevel = step.HandlerOrgId.CalcOrgLevel()
+        };
+        await _capPublisher.PublishAsync(Hotline.Share.Mq.EventNames.HotlineOrderFlowFile, flowDto, cancellationToken: HttpContext.RequestAborted);
+    }
+
+    #endregion
+
     #endregion
 
     #region 工单待办

+ 3 - 3
src/Hotline.Api/Controllers/Snapshot/BiSnapshotController.cs

@@ -1,10 +1,8 @@
-using Hotline.Application.Snapshot;
-using Hotline.Share.Dtos;
+using Hotline.Share.Dtos;
 using Hotline.Share.Dtos.Snapshot;
 using Microsoft.AspNetCore.Mvc;
 using Hotline.Repository.SqlSugar.Extensions;
 using Hotline.Share.Tools;
-using Hotline.Snapshot.Interfaces;
 using Hotline.Settings.Hotspots;
 using Hotline.Share.Requests;
 using SqlSugar;
@@ -21,6 +19,8 @@ using Hotline.Share.Dtos.Settings;
 using XF.Domain.Repository;
 using Hotline.Configurations;
 using Microsoft.Extensions.Options;
+using Hotline.Snapshot.IRepository;
+using Hotline.Application.Snapshot.Contracts;
 
 namespace Hotline.Api.Controllers.Snapshot;
 

+ 3 - 3
src/Hotline.Api/Controllers/Snapshot/IndustryController.cs

@@ -1,5 +1,5 @@
 using Amazon.Runtime.Internal.Transform;
-using Hotline.Application.Snapshot;
+using Hotline.Application.Snapshot.Contracts;
 using Hotline.Caching.Interfaces;
 using Hotline.Configurations;
 using Hotline.Repository.SqlSugar.Extensions;
@@ -9,7 +9,7 @@ using Hotline.Share.Dtos.Settings;
 using Hotline.Share.Dtos.Snapshot;
 using Hotline.Share.Tools;
 using Hotline.Snapshot;
-using Hotline.Snapshot.Interfaces;
+using Hotline.Snapshot.IRepository;
 using Microsoft.AspNetCore.Mvc;
 using Microsoft.Extensions.Options;
 using SqlSugar;
@@ -67,7 +67,7 @@ public class IndustryController : BaseController
     /// <returns></returns>
     [HttpGet("{id}")]
     public async Task<IndustryDetailOutDto> GetIndustryDetailAsync(string id)
-        => await _industryApplication.GetIndustryDetailAsync(id);
+        => await _industryApplication.GetIndustryDetailAsync(id, HttpContext.RequestAborted);
 
     /// <summary>
     /// 获取行业集合

+ 2 - 2
src/Hotline.Api/Controllers/Snapshot/InviteCodeController.cs

@@ -1,10 +1,10 @@
-using Hotline.Application.Snapshot;
+using Hotline.Application.Snapshot.Contracts;
 using Hotline.Repository.SqlSugar.Extensions;
 using Hotline.Share.Dtos;
 using Hotline.Share.Dtos.Snapshot;
 using Hotline.Share.Tools;
 using Hotline.Snapshot;
-using Hotline.Snapshot.Interfaces;
+using Hotline.Snapshot.IRepository;
 using Microsoft.AspNetCore.Mvc;
 using System.ComponentModel;
 

+ 40 - 0
src/Hotline.Api/Controllers/Snapshot/PointsController.cs

@@ -0,0 +1,40 @@
+using Hotline.Application.Snapshot.Contracts;
+using Hotline.Share.Dtos;
+using Hotline.Share.Dtos.Snapshot;
+using Microsoft.AspNetCore.Mvc;
+using System.ComponentModel;
+using Hotline.Repository.SqlSugar.Extensions;
+using Hotline.Share.Tools;
+
+namespace Hotline.Api.Controllers.Snapshot;
+
+
+/// <summary>
+/// 积分管理
+/// </summary>
+[Description("积分管理")]
+public class PointsController : BaseController
+{
+    private readonly ISnapshotPointsApplication _pointsRecordApplication;
+
+    public PointsController(ISnapshotPointsApplication pointsRecordApplication)
+    {
+        _pointsRecordApplication = pointsRecordApplication;
+    }
+
+    /// <summary>
+    /// 积分集合
+    /// </summary>
+    /// <returns></returns>
+    [HttpGet]
+    public async Task<PagedDto<PointsItemsOutDto>> GetPointsItemsAsync([FromQuery]PointsItemsInDto dto)
+        => (await _pointsRecordApplication.GetPointsItems(dto).ToPagedListAsync(dto)).ToPaged();
+
+    /// <summary>
+    /// 设置用户是安全卫士
+    /// </summary>
+    /// <returns></returns>
+    [HttpPut("securitymax")]
+    public async Task UpdateIsSecurityMaxAsync([FromBody] UpdateIsSecurityMaxAsync dto)
+        => await _pointsRecordApplication.UpdateIsSecurityMaxAsync(dto, HttpContext.RequestAborted);
+}

+ 3 - 3
src/Hotline.Api/Controllers/Snapshot/RedPackController.cs

@@ -1,10 +1,8 @@
-using Hotline.Application.Snapshot;
-using Hotline.Share.Dtos;
+using Hotline.Share.Dtos;
 using Hotline.Share.Dtos.Snapshot;
 using Microsoft.AspNetCore.Mvc;
 using Hotline.Repository.SqlSugar.Extensions;
 using Hotline.Share.Tools;
-using Hotline.Snapshot.Interfaces;
 using Hotline.Caching.Interfaces;
 using XF.Utility.EnumExtensions;
 using Hotline.Share.Enums.Snapshot;
@@ -12,6 +10,8 @@ using Quartz.Impl.Triggers;
 using Hotline.Orders;
 using XF.Domain.Exceptions;
 using Mapster;
+using Hotline.Snapshot.IRepository;
+using Hotline.Application.Snapshot.Contracts;
 
 namespace Hotline.Api.Controllers.Snapshot;
 

+ 2 - 2
src/Hotline.Api/Controllers/Snapshot/SnapshotBulletinController.cs

@@ -1,10 +1,8 @@
 using Hotline.Share.Dtos;
 using Hotline.Share.Dtos.Article;
-using Hotline.Snapshot.Interfaces;
 using Microsoft.AspNetCore.Mvc;
 using Hotline.Repository.SqlSugar.Extensions;
 using Mapster;
-using Hotline.Application.Snapshot;
 using XF.Domain.Exceptions;
 using Hotline.Share.Enums.Article;
 using XF.Domain.Authentications;
@@ -15,6 +13,8 @@ using Hotline.Snapshot;
 using Hotline.Share.Dtos.Snapshot;
 using Hotline.Share.Tools;
 using XF.Utility.EnumExtensions;
+using Hotline.Snapshot.IRepository;
+using Hotline.Application.Snapshot.Contracts;
 
 namespace Hotline.Api.Controllers.Snapshot;
 

+ 32 - 5
src/Hotline.Api/Controllers/Snapshot/SnapshotController.cs

@@ -1,7 +1,7 @@
 using AngleSharp.Dom;
 using Fw.Utility.UnifyResponse;
 using Hotline.Api.Filter;
-using Hotline.Application.Snapshot;
+using Hotline.Application.Snapshot.Contracts;
 using Hotline.Caching.Interfaces;
 using Hotline.File;
 using Hotline.Orders;
@@ -17,7 +17,7 @@ using Hotline.Share.Enums.Order;
 using Hotline.Share.Enums.Snapshot;
 using Hotline.Share.Tools;
 using Hotline.Snapshot;
-using Hotline.Snapshot.Interfaces;
+using Hotline.Snapshot.IRepository;
 using Hotline.ThirdAccountDomainServices.Interfaces;
 using Hotline.Tools;
 using Mapster;
@@ -103,12 +103,13 @@ public class SnapshotController : BaseController
         order.AcceptTypeCode = industry.AcceptTypeCode;
         order.AcceptType = industry.AcceptType;
         order.FromGender = EGender.Unknown;
-        order.Title = dto.GetTitle(industry.IndustryType, industry.AcceptType);
-        order.Content = dto.GetContent(industry.IndustryType);
+        order.Title = dto.GetTitle(industry.IndustryType, industry.TitleSuffix);
+        order.Content = dto.GetContent(industry.IndustryType, _systemDicDataCacheManager.JobType);
         order.FromPhone = _sessionContext.Phone;
         order.Contact = _sessionContext.Phone;
         order.SourceChannel = ssp.DicDataName;
         order.SourceChannelCode = ssp.DicDataValue;
+        order.FullAddress = dto.Address + dto.FullAddress;
         order.InitId();
         await _orderDomainService.AddAsync(order);
         if (dto.Files.NotNullOrEmpty())
@@ -118,7 +119,6 @@ public class SnapshotController : BaseController
         }
         var orderSnapshot = dto.Adapt<OrderSnapshot>();
         orderSnapshot.Id = order.Id;
-        orderSnapshot.SnapshotUserId = _sessionContext.UserId;
         orderSnapshot.IndustryId = dto.IndustryId;
         orderSnapshot.IndustryName = industry.Name;
         orderSnapshot.CompanyName = dto.CompanyName;
@@ -360,6 +360,33 @@ public class SnapshotController : BaseController
             await _snapshotApplication.PostOrderGuiderSystemAsync(orderId, HttpContext.RequestAborted);
         }
     }
+    #region 积分
+    /// <summary>
+    /// 积分榜
+    /// </summary>
+    /// <returns></returns>
+    [HttpGet("points/rank")]
+    public async Task<PointsRankOutDto> GetPointsRankAsync()
+        => await _snapshotApplication.GetPointsRankAsync();
+
+    /// <summary>
+    /// 积分详情
+    /// </summary>
+    /// <param name="dto"></param>
+    /// <returns></returns>
+    [HttpGet("points")]
+    public async Task<IList<PointItemsOutDto>> GetPointItemsAsync([FromQuery] PointItemsInDto dto)
+        => await _snapshotApplication.GetPointItemsAsync(dto, HttpContext.RequestAborted);
+
+    /// <summary>
+    /// 积分总计
+    /// </summary>
+    /// <param name="dto"></param>
+    /// <returns></returns>
+    [HttpGet("points/total")]
+    public async Task<int> GetPointsTotalAsync([FromQuery] PointItemsInDto dto)
+        => await _snapshotApplication.GetPointsTotalAsync(dto, HttpContext.RequestAborted);
+    #endregion
 
     #region 第三方系统
     /// <summary>

+ 2 - 1
src/Hotline.Api/Controllers/Snapshot/SnapshotOrderController.cs

@@ -1,5 +1,6 @@
 using Amazon.Runtime.Internal.Transform;
 using Hotline.Application.FlowEngine;
+using Hotline.Application.Snapshot.Contracts;
 using Hotline.Application.Snapshot;
 using Hotline.Caching.Interfaces;
 using Hotline.Caching.Services;
@@ -17,7 +18,7 @@ using Hotline.Share.Enums.Order;
 using Hotline.Share.Enums.Snapshot;
 using Hotline.Share.Tools;
 using Hotline.Snapshot;
-using Hotline.Snapshot.Interfaces;
+using Hotline.Snapshot.IRepository;
 using Mapster;
 using Microsoft.AspNetCore.Mvc;
 using Microsoft.Extensions.Options;

+ 90 - 7
src/Hotline.Api/Controllers/TelRestController.cs

@@ -12,9 +12,9 @@ using Hotline.Share.Dtos.Order;
 using Hotline.Share.Enums.Article;
 using Hotline.Share.Enums.CallCenter;
 using Hotline.Tools;
+using Hotline.Users;
 using MapsterMapper;
 using Microsoft.AspNetCore.Mvc;
-using System.Threading;
 using XF.Domain.Authentications;
 using XF.Domain.Exceptions;
 using XF.Domain.Repository;
@@ -32,6 +32,8 @@ namespace Hotline.Api.Controllers
         private readonly IMapper _mapper;
         private readonly IRealtimeService _realtimeService;
         private readonly ICircularRecordDomainService _circularRecordDomainService;
+        private readonly ISystemSettingCacheManager _systemSettingCacheManager;
+        private readonly IRepository<User> _userRepository;
 
         public TelRestController(ISystemDicDataCacheManager systemDicDataCacheManager,
              IRepository<TelRestApply> telRestApplyRepository,
@@ -40,7 +42,9 @@ namespace Hotline.Api.Controllers
               ITelDomainService telDomainService,
               IMapper mapper,
               IRealtimeService realtimeService,
-              ICircularRecordDomainService circularRecordDomainService)
+              ICircularRecordDomainService circularRecordDomainService,
+              ISystemSettingCacheManager systemSettingCacheManager,
+              IRepository<User> userRepository)
         {
             _systemDicDataCacheManager = systemDicDataCacheManager;
             _telRestApplyRepository = telRestApplyRepository;
@@ -50,6 +54,8 @@ namespace Hotline.Api.Controllers
             _mapper = mapper;
             _realtimeService = realtimeService;
             _circularRecordDomainService = circularRecordDomainService;
+            _systemSettingCacheManager = systemSettingCacheManager;
+            _userRepository = userRepository;
         }
 
         #region 小休审批
@@ -69,6 +75,44 @@ namespace Hotline.Api.Controllers
             return rsp;
         }
 
+        /// <summary>
+        /// 查询是否需要小休审批
+        /// </summary>
+        /// <returns></returns>
+        [HttpGet("ischeckaudit")]
+        public async Task<bool> IsCheckAudit()
+        {
+            try
+            {
+                //首先验证是否开启,如果开启需要审批进行下一步判断
+                //未开启则不用审批
+                var isRestApproval = bool.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.IsRestApproval).SettingValue[0]);
+                if (isRestApproval)
+                {
+                    var restAuditTime = _systemSettingCacheManager.GetSetting(SettingConstants.IsRestAuditTime).SettingValue;
+                    if (restAuditTime == null || restAuditTime.Count == 0)
+                        return false;
+                    foreach (var item in restAuditTime)
+                    {
+                        var time = item.Split('-');
+                        var nowTime = DateTime.Now;
+                        var starTime = Convert.ToDateTime(nowTime.ToString("yyyy-MM-dd") + " " + time[0]);
+                        var endTime = Convert.ToDateTime(nowTime.ToString("yyyy-MM-dd") + " " + time[1]);
+                        if (starTime < nowTime && endTime > nowTime)
+                            return true;
+                    }
+
+                    return false;
+                }
+                else
+                    return false;
+            }
+            catch (Exception)
+            {
+                return false;
+            }
+        }
+
         /// <summary>
         /// 小休申请
         /// </summary>
@@ -79,14 +123,15 @@ namespace Hotline.Api.Controllers
         public async Task<string> AddTelRest([FromBody] TelRestApplyAddDto dto)
         {
             if (string.IsNullOrEmpty(dto.TelNo))
-                return "分机号不能为空!";
-            if (dto.Reason == null)
-                return "小休原因不能为空!";
+                throw new  UserFriendlyException("分机号不能为空!", "分机号不能为空!");
+                
+            if (dto.Reason == null) 
+                throw new UserFriendlyException("小休原因不能为空!", "小休原因不能为空!");
 
             var data = await _telRestApplyRepository.GetAsync(p => p.TelNo == dto.TelNo && p.CreatorId == _sessionContext.RequiredUserId &&
             p.AuditStatus == ETelRestAuditStatus.NoAudit, HttpContext.RequestAborted);
             if (data != null)
-                return "小休申请审批中,暂时无法操作!";
+                throw new UserFriendlyException("小休申请审批中,暂时无法操作!", "小休申请审批中,暂时无法操作!");
 
             TelRestApply telRestApply = new()
             {
@@ -98,7 +143,44 @@ namespace Hotline.Api.Controllers
             };
             var id = await _telRestApplyRepository.AddAsync(telRestApply, HttpContext.RequestAborted);
             if (!string.IsNullOrEmpty(id))
+            {
+                //查询小休审批人
+                var setting = _systemSettingCacheManager.GetSetting(SettingConstants.RestAuditRole).SettingValue;
+                var list = await _userRepository.Queryable()
+                        .Includes(u => u.Roles)
+                         .Where(u => u.Roles.Any(x => setting.Contains(x.Name)))
+                         .ToListAsync();
+
+                //给审批人发送提醒
+                if (list != null && list.Any())
+                {
+                    AddCircularDto circularDto = new()
+                    {
+                        Title = "小休审批",
+                        Content = dto.TelNo + "申请了小休,请及时处理。",
+                        CircularTypeId = "7",
+                        CircularTypeName = "小休审批",
+                        IsMustRead = true,
+                        SourceOrgId = _sessionContext.RequiredOrgId,
+                        SourceOrgName = _sessionContext.OrgName,
+                        CircularType = ECircularType.Person
+                    };
+                    List<CircularReadGroupDto> users = [];
+                    foreach (var user in list)
+                    {
+                        users.Add(new CircularReadGroupDto()
+                        {
+                            UserId = user.Id,
+                            UserName = user.Name,
+                        });
+                    }
+                    circularDto.CircularReadGroups = users;
+                    //调用推送消息通用接口
+                    await _circularRecordDomainService.AddCircularMessage(circularDto, HttpContext.RequestAborted);
+
+                }
                 return "小休申请成功!";
+            }
             else
                 return "小休申请失败!";
 
@@ -114,7 +196,8 @@ namespace Hotline.Api.Controllers
         public async Task<string> TelRestApplyAudit([FromBody] TelRestApplyAuditDto dto)
         {
             if (dto.IsPass != true && string.IsNullOrEmpty(dto.AuditOpinion))
-                return "审批原因不能为空!";
+                throw new UserFriendlyException("审批原因不能为空!", "审批原因不能为空!");
+
             foreach (var item in dto.Ids)
             {
                 var data = await _telRestApplyRepository.GetAsync(item, HttpContext.RequestAborted);

+ 21 - 1
src/Hotline.Api/Controllers/TestController.cs

@@ -1,4 +1,4 @@
-using DocumentFormat.OpenXml.Office.CustomUI;
+using System.Text;
 using DotNetCore.CAP;
 using Hotline.Ai.Quality;
 using Hotline.Application.CallCenter;
@@ -1499,5 +1499,25 @@ public class TestController : BaseController
                 cancellationToken: HttpContext.RequestAborted);
         }
     }
+
+    [AllowAnonymous]
+    [HttpGet("ip")]
+    public string GetIp()
+    {
+        var ipv4 = HttpContext.Connection.RemoteIpAddress?.MapToIPv4().ToString();
+        _logger.LogWarning($"ip: {ipv4}");
+
+        var headers = Request.Headers.ToList();
+        var sb = new StringBuilder();
+        foreach (var header in headers)
+        {
+            sb.AppendFormat("key: {0}, value: {1}", header.Key, header.Value)
+                .AppendLine();
+        }
+
+        _logger.LogWarning(sb.ToString());
+
+        return ipv4;
+    }
 }
 

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

@@ -30,7 +30,6 @@ using Hotline.DI;
 using Hotline.Settings.TimeLimitDomain.ExpireTimeSupplier;
 using XF.Domain.Authentications;
 using Hotline.WeChat;
-using Hotline.Snapshot.Interfaces;
 using TianQue.Sdk;
 using Hotline.Orders;
 using XF.Domain.Repository.Events;
@@ -39,6 +38,7 @@ using Hotline.Ai.XingTang;
 using Hotline.Pdf;
 using Hotline.XingTang;
 using Hotline.ThirdAccountDomainServices.Interfaces;
+using Hotline.Snapshot.IRepository;
 
 
 namespace Hotline.Api;
@@ -206,9 +206,12 @@ internal static class StartupExtensions
         services.AddPdfManager();
 
         //compression
-        services.RejisterCompression();
+        services.RegisterCompression();
 
+        //authorization
         services.AddSingleton<IAuthorizationPolicyProvider, AuthorizationPolicyProvider>();
+        services.RegisterAuthorization(configuration);
+
         services.AddSingleton<IAuthorizationHandler, PermissionHandler>();
         services.AddScoped<ExpireTimeFactory>();
         services.AddScoped<IExpireTimeSupplier, DaySupplier>();

+ 10 - 1
src/Hotline.Api/StartupHelper.cs

@@ -126,6 +126,15 @@ namespace Hotline.Api
             return services;
         }
 
+        public static IServiceCollection RegisterAuthorization(this IServiceCollection services, ConfigurationManager configuration)
+        {
+            services.AddAuthorization(options =>
+            {
+                options.AddPolicy(AppDefaults.AuthPolicy.Hotline, d => d.RequireClaim("scope", AppDefaults.AuthPolicy.Hotline));
+            });
+            return services;
+        }
+
         /// <summary>
         /// Swagger
         /// </summary>
@@ -385,7 +394,7 @@ namespace Hotline.Api
             return services;
         }
 
-        public static IServiceCollection RejisterCompression(this IServiceCollection services)
+        public static IServiceCollection RegisterCompression(this IServiceCollection services)
         {
             services.Configure<BrotliCompressionProviderOptions>(options =>
             {

+ 50 - 14
src/Hotline.Application/CallCenter/DefaultCallApplication.cs

@@ -201,20 +201,37 @@ public abstract class DefaultCallApplication : ICallApplication
             dto.GroupId, _sessionContext.StaffNo, null);
         await _workRepository.AddAsync(work, cancellationToken);
 
-        //如果有未结束的小休,先结束
-        var telRest = await _telRestRepository.GetAsync(x => x.TelNo == work.TelNo && !x.EndTime.HasValue, cancellationToken);
-        if (telRest != null)
+        try
         {
-            telRest.EndRest();
-            await _telRestRepository.UpdateAsync(telRest, cancellationToken);
-        }
+            //工单保存特殊身份验证
+            var signInCheckRest = bool.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.SignInCheckRest).SettingValue[0]);
+            if (signInCheckRest)
+            {
+                //如果有未结束的小休,先结束
+                var telRest = await _telRestRepository.GetAsync(x => x.TelNo == work.TelNo && !x.EndTime.HasValue, cancellationToken);
+                if (telRest != null)
+                {
+                    telRest.EndRest();
+                    await _telRestRepository.UpdateAsync(telRest, cancellationToken);
+                }
+
+                //如果有未结束的话机动作先结束话机动作
+                var telAction = await _telActionRecordRepository.GetAsync(x => x.TelNo == work.TelNo && x.ActionType == EActionType.TelRest && !x.EndTime.HasValue, cancellationToken);
+                if (telAction != null)
+                {
+                    telAction.EndAction();
+                    await _telActionRecordRepository.UpdateAsync(telAction);
+                }
+            }
 
-        //如果有未结束的话机动作先结束话机动作
-        var telAction = await _telActionRecordRepository.GetAsync(x => x.TelNo == work.TelNo && x.ActionType == EActionType.TelRest && !x.EndTime.HasValue, cancellationToken);
-        if (telAction != null)
+            //记录签入日志
+            var actionRecord = new TelActionRecord(work.UserId, work.UserName, work.TelNo, work.QueueId, EActionType.SignIn);
+
+            await _telActionRecordRepository.AddAsync(actionRecord, cancellationToken);
+        }
+        catch (Exception)
         {
-            telAction.EndAction();
-            await _telActionRecordRepository.UpdateAsync(telAction);
+
         }
 
         return new TrOnDutyResponseDto
@@ -245,6 +262,19 @@ public abstract class DefaultCallApplication : ICallApplication
         await _workRepository.UpdateAsync(work, cancellationToken);
         await _cacheWork.RemoveAsync(work.GetKey(KeyMode.UserId), cancellationToken);
         await _cacheWork.RemoveAsync(work.GetKey(KeyMode.TelNo), cancellationToken);
+
+        try
+        {
+            var list = await _telActionRecordRepository.Queryable().Where(x => x.TelNo == work.TelNo && !x.EndTime.HasValue).ToListAsync();
+            foreach (var item in list)
+            {
+                item.EndAction();
+                await _telActionRecordRepository.UpdateAsync(item);
+            }
+        }
+        catch (Exception)
+        {
+        }
     }
 
     /// <summary>
@@ -267,6 +297,13 @@ public abstract class DefaultCallApplication : ICallApplication
         await _workRepository.UpdateAsync(work, cancellationToken);
         await _cacheWork.RemoveAsync(work.GetKey(KeyMode.UserId), cancellationToken);
         await _cacheWork.RemoveAsync(work.GetKey(KeyMode.TelNo), cancellationToken);
+
+        var list = await _telActionRecordRepository.Queryable().Where(x => x.TelNo == work.TelNo && !x.EndTime.HasValue).ToListAsync();
+        foreach (var item in list)
+        {
+            item.EndAction();
+            await _telActionRecordRepository.UpdateAsync(item);
+        }
     }
 
     /// <summary>
@@ -320,7 +357,7 @@ public abstract class DefaultCallApplication : ICallApplication
         query = query.WhereIF(dto.Type == 3, (d, o, v) => d.AnsweredTime == null);
         query = query.WhereIF(dto.Type == 1, (d, o, v) => d.Direction == ECallDirection.In && d.AnsweredTime != null);
         query = query.WhereIF(dto.Type == 2, (d, o, v) => d.Direction == ECallDirection.Out && d.AnsweredTime != null);
-        query = query.WhereIF(dto.Type != 3 && !string.IsNullOrEmpty(dto.StaffNo), p => p.StaffNo == dto.StaffNo);
+        query = query.WhereIF(dto.Type != 3 && !string.IsNullOrEmpty(dto.StaffNo), d => d.StaffNo == dto.StaffNo);
 
         if (dto.Type == 2)
         {
@@ -357,7 +394,7 @@ public abstract class DefaultCallApplication : ICallApplication
                         .Max(m => m.TelNo)
                     ),
             }, true)
-                .WhereIF(!string.IsNullOrEmpty(dto.StaffNo), p => p.StaffNo == dto.StaffNo);
+                .WhereIF(!string.IsNullOrEmpty(dto.StaffNo), d => d.StaffNo == dto.StaffNo);
         }
         return query.Select((d, o, v) => new CallNativeDto
         {
@@ -368,7 +405,6 @@ public abstract class DefaultCallApplication : ICallApplication
             IsVisit = !SqlFunc.IsNullOrEmpty(v.Id),
             IsOrder = !SqlFunc.IsNullOrEmpty(o.Id),
         }, true);
-
     }
 
     /// <summary>

+ 1 - 1
src/Hotline.Application/Handlers/FlowEngine/WorkflowNextHandler.cs

@@ -23,7 +23,7 @@ using Hotline.Share.Enums.Order;
 using Hotline.Share.Enums.Push;
 using Hotline.Share.Enums.Quality;
 using Hotline.Share.Mq;
-using Hotline.Snapshot.Interfaces;
+using Hotline.Snapshot.IRepository;
 using Hotline.Snapshot.Notifications;
 using Hotline.Users;
 using Mapster;

+ 6 - 4
src/Hotline.Application/Identity/IdentityAppService.cs

@@ -17,7 +17,7 @@ using Hotline.Share.Enums.Identity;
 using Hotline.Share.Enums.Snapshot;
 using Hotline.Share.Enums.User;
 using Hotline.Share.Tools;
-using Hotline.Snapshot.Interfaces;
+using Hotline.Snapshot.IRepository;
 using Hotline.ThirdAccountDomainServices;
 using Hotline.ThirdAccountDomainServices.Interfaces;
 using Hotline.Users;
@@ -128,7 +128,8 @@ public class IdentityAppService : IIdentityAppService, IScopeDependency
             new(JwtClaimTypes.Subject, account.Id),
             new(JwtClaimTypes.PhoneNumber, account.PhoneNo ?? string.Empty),
             new(AppClaimTypes.UserDisplayName, account.Name),
-            new(JwtClaimTypes.Scope, jwtOptions.Scope),
+            //new(JwtClaimTypes.Scope, jwtOptions.Scope),
+            new(JwtClaimTypes.Scope, account.Scope),
             new(AppClaimTypes.UserPasswordChanged, account.PasswordChanged.ToString()),
             new(AppClaimTypes.StaffNo, user.StaffNo ?? string.Empty),
         };
@@ -247,7 +248,8 @@ public class IdentityAppService : IIdentityAppService, IScopeDependency
             new(JwtClaimTypes.Subject, account.Id),
             new(JwtClaimTypes.PhoneNumber, account.PhoneNo ?? string.Empty),
             new(AppClaimTypes.UserDisplayName, account.Name),
-            new(JwtClaimTypes.Scope, jwtOptions.Scope),
+            //new(JwtClaimTypes.Scope, jwtOptions.Scope),
+            new(JwtClaimTypes.Scope, account.Scope),
             new(AppClaimTypes.UserPasswordChanged, account.PasswordChanged.ToString()),
             new(AppClaimTypes.StaffNo, user.StaffNo ?? string.Empty),
         };
@@ -343,7 +345,7 @@ public class IdentityAppService : IIdentityAppService, IScopeDependency
         var claims = new List<Claim>
         {
             new(JwtClaimTypes.PhoneNumber, thirdAccount.PhoneNumber ?? string.Empty),
-            new(JwtClaimTypes.Scope, jwtOptions.Scope),
+            new(JwtClaimTypes.Scope, jwtOptions.Scope),//todo 三方账号的scope
             new(AppClaimTypes.OpenId, thirdAccount.OpenId),
         };
         var userId = await _thirdAccountDomainFactory.GetClaimAsync(thirdAccount, claims, cancel);

+ 14 - 0
src/Hotline.Application/Mappers/OrderMapperConfigs.cs

@@ -232,6 +232,10 @@ public class OrderMapperConfigs : IRegister
             .Map(d => d.No, s => s.CaseSerial);
 
         config.ForType<WorkflowTrace, OrderFlowTraceDto>()
+            .IgnoreIf((s, d) => s.Status < EWorkflowStepStatus.Handled, d => d.HandlerName)
+            .IgnoreIf((s, d) => s.Status < EWorkflowStepStatus.Handled, d => d.HandlerOrgName)
+            .IgnoreIf((s, d) => s.Status < EWorkflowStepStatus.Handled, d => d.HandleTime)
+
             .IgnoreIf((s, d) => s.TraceStyle != ETraceStyle.Publish || s.OrderPublish == null, d => d.PublishState)
             .Map(d => d.PublishState, s => s.OrderPublish.PublishState)
             .IgnoreIf((s, d) => s.TraceStyle != ETraceStyle.Publish || s.OrderPublish == null, d => d.ArrangeOpinion)
@@ -266,5 +270,15 @@ public class OrderMapperConfigs : IRegister
             .IgnoreIf((s, d) => s.OrgHandledAttitude == null, d => d.OrgHandledAttitude)
             .Map(d => d.OrgHandledAttitude, s => s.OrgHandledAttitude.Value)
             ;
+
+        config.ForType<OrderVisitDetail, OrderVisitDetailCopy>()
+        .Ignore(d => d.CreationTime)
+         .Ignore(d => d.CreatorId)
+          .Ignore(d => d.CreatorName)
+           .Ignore(d => d.CreatorOrgId)
+            .Ignore(d => d.CreatorOrgLevel)
+            .Ignore(d => d.CreatorOrgName)
+            .Ignore(d => d.AreaId)
+        ;
     }
 }

+ 18 - 2
src/Hotline.Application/OrderApp/IOrderApplication.cs

@@ -115,6 +115,7 @@ namespace Hotline.Application.OrderApp
         #region 工单办理
 
         ISugarQueryable<Order> QueryOrders(QueryOrderDto dto);
+        ISugarQueryable<Order> QueryTravel(QueryOrderDto dto);
 
         /// <summary>
         /// 保存工单办理时页面填写的数据
@@ -425,7 +426,7 @@ namespace Hotline.Application.OrderApp
         /// 将工单从网格员节点办理至工单标记节点
         /// </summary>
         /// <returns></returns>
-        Task HandleFromWanggeyuanToMaskAsync(string orderId,string opinion, CancellationToken cancellation);
+        Task HandleFromWanggeyuanToMaskAsync(string orderId, string opinion, CancellationToken cancellation);
 
         /// <summary>
         /// 查询退回操作目标节点的指派对象
@@ -477,5 +478,20 @@ namespace Hotline.Application.OrderApp
         /// 依据当前待办节点随意获取一个合法办理人
         /// </summary>
         Task<User> GetHandlerRandomAsync(WorkflowStep step, CancellationToken cancellationToken);
-    }
+
+        /// <summary>
+        /// 部门退回中心统计
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        ISugarQueryable<OrgSendBackAuditListVo> OrgSendBackAuditList(OrgSendBackAuditListDto dto);
+
+		/// <summary>
+		/// 部门退回中心统计明细
+		/// </summary>
+		/// <param name="dto"></param>
+		/// <returns></returns>
+		ISugarQueryable<OrderSendBackAudit> OrgSendBackAuditDetail(OrgSendBackAuditListDto dto);
+
+	}
 }

+ 162 - 144
src/Hotline.Application/OrderApp/OrderAnalysisApplication.cs

@@ -1,4 +1,5 @@
-using Hotline.Orders;
+using Hotline.Caching.Interfaces;
+using Hotline.Orders;
 using Hotline.Settings;
 using Hotline.Share.Dtos.Order;
 using Hotline.Share.Enums.Order;
@@ -11,147 +12,164 @@ using XF.Utility.SequentialId;
 
 namespace Hotline.Application.OrderApp
 {
-	public class OrderAnalysisApplication : IOrderAnalysisApplication, IScopeDependency
-	{
-		private readonly IRepository<OrderAnalysis> _orderAnalysisRepository;
-		private readonly IMapper _mapper;
-		private readonly IOrderRepository _orderRepository;
-		private readonly IRepository<SystemArea> _systemAreaRepository;
-
-		public OrderAnalysisApplication(
-			IMapper mapper,
-			IOrderRepository orderRepository,
-			IRepository<SystemArea> systemAreaRepository,
-			IRepository<OrderAnalysis> orderAnalysisRepository
-			) 
-		{
-			_mapper = mapper;
-			_orderRepository = orderRepository;
-			_orderAnalysisRepository = orderAnalysisRepository;
-			_systemAreaRepository = systemAreaRepository;
-		}
-
-		/// <summary>
-		/// 新增
-		/// </summary>
-		/// <returns></returns>
-		public async Task AddAsync(AddOrderAnalysisDto dto, CancellationToken cancellationToken) {
-			var analysisAny =  await _orderAnalysisRepository.Queryable().AnyAsync(x => x.AnalysisName == dto.AnalysisName, cancellationToken);
-			if (analysisAny)
-				throw UserFriendlyException.SameMessage("当前报告名称已经存在,请修改报告名称!");
-
-			var analysisId = SequentialStringGenerator.Create();
-			var generatedTime = DateTime.Now;
-			var analysiss = new List<OrderAnalysis>();
-			foreach (var item in dto.AnalysisList)
-			{
-				var analysis = _mapper.Map<OrderAnalysis>(item);
-				analysis.AnalysisId = analysisId;
-				analysis.AnalysisName =  dto.AnalysisName;
-				analysis.Remark = dto.Remark;
-				analysis.GeneratedTime = generatedTime;
-				analysiss.Add(analysis);
-			}
-			await _orderAnalysisRepository.AddRangeAsync(analysiss, cancellationToken);
-		}
-		
-		/// <summary>
-		/// 修改
-		/// </summary>
-		/// <returns></returns>
-		public async Task UpdateAsync(UpdateOrderAnalysisDto dto, CancellationToken cancellationToken) 
-		{
-			var analysisIdAny = await _orderAnalysisRepository.Queryable().AnyAsync(x =>  x.AnalysisId == dto.AnalysisId, cancellationToken);
-			if (!analysisIdAny)
-				throw UserFriendlyException.SameMessage("当前报告不存在,请查询报告是否已变更!");
-			var analysisNameAny = await _orderAnalysisRepository.Queryable().AnyAsync(x => x.AnalysisName == dto.AnalysisName && x.AnalysisId != dto.AnalysisId, cancellationToken);
-			if (analysisNameAny)
-				throw UserFriendlyException.SameMessage("当前报告名称已经存在,请修改报告名称!");
-			await _orderAnalysisRepository.Updateable().SetColumns(x => new OrderAnalysis { AnalysisName = dto.AnalysisName, Remark = dto.Remark })
-				.Where(x => x.AnalysisId == dto.AnalysisId).ExecuteCommandAsync(cancellationToken);
-		}
-
-		/// <summary>
-		/// 删除
-		/// </summary>
-		/// <returns></returns>
-		public async Task DeleteAsync(DeleteOrderAnalysisDto dto, CancellationToken cancellationToken) {
-
-			await _orderAnalysisRepository.Removeable().In(x => x.AnalysisId, dto.AnalysisIds).ExecuteCommandAsync(cancellationToken);
-		}
-
-
-		/// <summary>
-		/// 查询列表
-		/// </summary>
-		/// <returns></returns>
-		public ISugarQueryable<OrderAnalysisDto> ListQuery(OrderAnalysisListDto dto, CancellationToken cancellationToken) {
-			if (dto.GeneratedEndTime.HasValue)
-				dto.GeneratedEndTime = dto.GeneratedEndTime.Value.AddDays(1).AddSeconds(-1);
-			var query = _orderAnalysisRepository.Queryable()
-				.WhereIF(!string.IsNullOrEmpty(dto.AnalysisName), x => x.AnalysisName == dto.AnalysisName)
-				.WhereIF(dto.GeneratedStartTime.HasValue && dto.GeneratedEndTime.HasValue, x => x.GeneratedTime >= dto.GeneratedStartTime && x.GeneratedTime <= dto.GeneratedEndTime)
-				.GroupBy(x => new { x.AnalysisId, x.AnalysisName, x.Remark, x.GeneratedTime })
-				.Select(x => new OrderAnalysisDto { AnalysisId = x.AnalysisId, AnalysisName = x.AnalysisName, Remark = x.Remark, GeneratedTime = x.GeneratedTime })
-				.OrderByDescending(x=> x.GeneratedTime);
-			return query;
-		}
-
-		/// <summary>
-		///  查看预警事件
-		/// </summary>
-		/// <returns></returns>
-		public ISugarQueryable<Order> DetailQuery(OrderAnalysisDetailDto dto, CancellationToken cancellationToken) {
-			var query = _orderAnalysisRepository.Queryable()
-				.LeftJoin<Order>((a, o) => a.AcceptTypeCode == o.AcceptTypeCode && a.HotspotId == o.HotspotId && o.AreaCode.StartsWith(a.AreaCode))
-				.Where((a, o) => a.AnalysisId == dto.AnalysisId)
-				.Select((a, o) => o);
-			return query;
-		}
-
-		/// <summary>
-		/// 生成报告
-		/// </summary>
-		/// <returns></returns>
-		public async Task<object> ReportQuery(OrderAnalysisDetailDto dto, CancellationToken cancellationToken) {
-
-			var analysis = await _orderAnalysisRepository.Queryable()
-				.Where(x => x.AnalysisId == dto.AnalysisId)
-				.GroupBy(x => new { x.AnalysisId, x.AnalysisName, x.GeneratedTime, x.Remark })
-				.Select(x => new { x.AnalysisId, x.AnalysisName, x.GeneratedTime, x.Remark }).FirstAsync(cancellationToken);
-
-			var orders = await _orderAnalysisRepository.Queryable()
-				.LeftJoin<Order>((a, o) => a.AcceptTypeCode == o.AcceptTypeCode && a.HotspotId == o.HotspotId && o.AreaCode.StartsWith(a.AreaCode))
-				.Where((a, o) => a.AnalysisId == dto.AnalysisId)
-				.Select((a, o) => o).ToListAsync(cancellationToken) ;
-			var introductionData = orders.GroupBy(x=>x.SourceChannel)
-				.Select(x=>new { SourceChannel = x.Key, Num  = x.Count(), Rate = Math.Round((x.Count() /(double)orders.Count) * 100, 2) })
-				.OrderByDescending(x=>x.Num);
-			var introduction = new { total = orders.Count, data = introductionData };
-			var orderAreas = orders.GroupBy(x=>x.AreaCode.Substring(0,6))
-				.Select(x=> new { AreaCode =x.Key, Num = x.Count(), Rate = Math.Round((x.Count() / (double)orders.Count) * 100, 2) })
-				.OrderByDescending(x => x.Num);
-			var areaList = await _systemAreaRepository.Queryable().Where(x=>x.Id.Length == 6).ToListAsync(cancellationToken);
-			var areaData = (from t1 in orderAreas
-							join t2 in areaList on t1.AreaCode equals t2.Id into t1_t2
-				from item in t1_t2.DefaultIfEmpty()
-				select new
-				{
-					AreaCode = t1.AreaCode,
-					Num = t1.Num,
-					Rate = t1.Rate,
-					AreaName = t1_t2.Select(x => x.AreaName).FirstOrDefault(),
-				}).OrderByDescending(x=>x.Num).ToList();
-			var areaNames = areaData.Select(x=>x.AreaName).ToList();
-
-			var area = new { AreaNames = areaNames, data = areaData };
-
-			var handle = new { HandleFiled = orders.Count(x => x.Status >= EOrderStatus.Filed), Handle = orders.Count(x => x.Status < Share.Enums.Order.EOrderStatus.Filed) };
-
-			var first = _mapper.Map<List<OrderDto>>(orders.OrderByDescending(x=>x.CreationTime).Take(2).ToList());
-			return new { Analysis = analysis, Introduction = introduction, Area = area, Handle = handle, First = first };
-		}
-
-
-	}
+    public class OrderAnalysisApplication : IOrderAnalysisApplication, IScopeDependency
+    {
+        private readonly IRepository<OrderAnalysis> _orderAnalysisRepository;
+        private readonly IMapper _mapper;
+        private readonly IOrderRepository _orderRepository;
+        private readonly IRepository<SystemArea> _systemAreaRepository;
+        private readonly ISystemSettingCacheManager _systemSettingCacheManager;
+
+        public OrderAnalysisApplication(
+            IMapper mapper,
+            IOrderRepository orderRepository,
+            IRepository<SystemArea> systemAreaRepository,
+            IRepository<OrderAnalysis> orderAnalysisRepository,
+            ISystemSettingCacheManager systemSettingCacheManager
+            )
+        {
+            _mapper = mapper;
+            _orderRepository = orderRepository;
+            _orderAnalysisRepository = orderAnalysisRepository;
+            _systemAreaRepository = systemAreaRepository;
+            _systemSettingCacheManager = systemSettingCacheManager;
+        }
+
+        /// <summary>
+        /// 新增
+        /// </summary>
+        /// <returns></returns>
+        public async Task AddAsync(AddOrderAnalysisDto dto, CancellationToken cancellationToken)
+        {
+            var analysisAny = await _orderAnalysisRepository.Queryable().AnyAsync(x => x.AnalysisName == dto.AnalysisName, cancellationToken);
+            if (analysisAny)
+                throw UserFriendlyException.SameMessage("当前报告名称已经存在,请修改报告名称!");
+
+            var analysisId = SequentialStringGenerator.Create();
+            var generatedTime = DateTime.Now;
+            var analysiss = new List<OrderAnalysis>();
+            foreach (var item in dto.AnalysisList)
+            {
+                var analysis = _mapper.Map<OrderAnalysis>(item);
+                analysis.AnalysisId = analysisId;
+                analysis.AnalysisName = dto.AnalysisName;
+                analysis.Remark = dto.Remark;
+                analysis.GeneratedTime = generatedTime;
+                analysis.StartTime = dto.StartTime;
+                analysis.EndTime = dto.EndTime;
+                analysiss.Add(analysis);
+            }
+            await _orderAnalysisRepository.AddRangeAsync(analysiss, cancellationToken);
+        }
+
+        /// <summary>
+        /// 修改
+        /// </summary>
+        /// <returns></returns>
+        public async Task UpdateAsync(UpdateOrderAnalysisDto dto, CancellationToken cancellationToken)
+        {
+            var analysisIdAny = await _orderAnalysisRepository.Queryable().AnyAsync(x => x.AnalysisId == dto.AnalysisId, cancellationToken);
+            if (!analysisIdAny)
+                throw UserFriendlyException.SameMessage("当前报告不存在,请查询报告是否已变更!");
+            var analysisNameAny = await _orderAnalysisRepository.Queryable().AnyAsync(x => x.AnalysisName == dto.AnalysisName && x.AnalysisId != dto.AnalysisId, cancellationToken);
+            if (analysisNameAny)
+                throw UserFriendlyException.SameMessage("当前报告名称已经存在,请修改报告名称!");
+            await _orderAnalysisRepository.Updateable().SetColumns(x => new OrderAnalysis { AnalysisName = dto.AnalysisName, Remark = dto.Remark })
+                .Where(x => x.AnalysisId == dto.AnalysisId).ExecuteCommandAsync(cancellationToken);
+        }
+
+        /// <summary>
+        /// 删除
+        /// </summary>
+        /// <returns></returns>
+        public async Task DeleteAsync(DeleteOrderAnalysisDto dto, CancellationToken cancellationToken)
+        {
+
+            await _orderAnalysisRepository.Removeable().In(x => x.AnalysisId, dto.AnalysisIds).ExecuteCommandAsync(cancellationToken);
+        }
+
+
+        /// <summary>
+        /// 查询列表
+        /// </summary>
+        /// <returns></returns>
+        public ISugarQueryable<OrderAnalysisDto> ListQuery(OrderAnalysisListDto dto, CancellationToken cancellationToken)
+        {
+            if (dto.GeneratedEndTime.HasValue)
+                dto.GeneratedEndTime = dto.GeneratedEndTime.Value.AddDays(1).AddSeconds(-1);
+            var query = _orderAnalysisRepository.Queryable()
+                .WhereIF(!string.IsNullOrEmpty(dto.AnalysisName), x => x.AnalysisName == dto.AnalysisName)
+                .WhereIF(dto.GeneratedStartTime.HasValue && dto.GeneratedEndTime.HasValue, x => x.GeneratedTime >= dto.GeneratedStartTime && x.GeneratedTime <= dto.GeneratedEndTime)
+                .GroupBy(x => new { x.AnalysisId, x.AnalysisName, x.Remark, x.GeneratedTime })
+                .Select(x => new OrderAnalysisDto { AnalysisId = x.AnalysisId, AnalysisName = x.AnalysisName, Remark = x.Remark, GeneratedTime = x.GeneratedTime })
+                .OrderByDescending(x => x.GeneratedTime);
+            return query;
+        }
+
+        /// <summary>
+        ///  查看预警事件
+        /// </summary>
+        /// <returns></returns>
+        public ISugarQueryable<Order> DetailQuery(OrderAnalysisDetailDto dto, CancellationToken cancellationToken)
+        {
+            var filterTitle = _systemSettingCacheManager.GetSetting(SettingConstants.HighMatterWarningFilterTitle).SettingValue;
+
+            var query = _orderAnalysisRepository.Queryable()
+                .LeftJoin<Order>((a, o) => a.AcceptTypeCode == o.AcceptTypeCode && a.HotspotId == o.HotspotId && o.AreaCode.StartsWith(a.AreaCode))
+                .Where((a, o) => a.AnalysisId == dto.AnalysisId)
+                .Where((a, o) => o.CreationTime >= a.StartTime && o.CreationTime <= a.EndTime)
+                .Where((a, o) => filterTitle.Any(s => o.Title.Contains(s)) == false)
+                .Select((a, o) => o);
+            return query;
+        }
+
+        /// <summary>
+        /// 生成报告
+        /// </summary>
+        /// <returns></returns>
+        public async Task<object> ReportQuery(OrderAnalysisDetailDto dto, CancellationToken cancellationToken)
+        {
+            var filterTitle = _systemSettingCacheManager.GetSetting(SettingConstants.HighMatterWarningFilterTitle).SettingValue;
+
+            var analysis = await _orderAnalysisRepository.Queryable()
+                .Where(x => x.AnalysisId == dto.AnalysisId)
+                .GroupBy(x => new { x.AnalysisId, x.AnalysisName, x.GeneratedTime, x.Remark, x.StartTime, x.EndTime })
+                .Select(x => new { x.AnalysisId, x.AnalysisName, x.GeneratedTime, x.Remark, x.StartTime, x.EndTime }).FirstAsync(cancellationToken);
+
+            var orders = await _orderAnalysisRepository.Queryable()
+                .LeftJoin<Order>((a, o) => a.AcceptTypeCode == o.AcceptTypeCode && a.HotspotId == o.HotspotId && o.AreaCode.StartsWith(a.AreaCode))
+                .Where((a, o) => a.AnalysisId == dto.AnalysisId)
+                .Where((a, o) => o.CreationTime >= a.StartTime && o.CreationTime <= a.EndTime)
+                 .Where((a, o) => filterTitle.Any(s => o.Title.Contains(s)) == false)
+                .Select((a, o) => o).ToListAsync(cancellationToken);
+            var introductionData = orders.GroupBy(x => x.SourceChannel)
+                .Select(x => new { SourceChannel = x.Key, Num = x.Count(), Rate = Math.Round((x.Count() / (double)orders.Count) * 100, 2) })
+                .OrderByDescending(x => x.Num);
+            var introduction = new { total = orders.Count, data = introductionData };
+            var orderAreas = orders.GroupBy(x => x.AreaCode.Substring(0, 6))
+                .Select(x => new { AreaCode = x.Key, Num = x.Count(), Rate = Math.Round((x.Count() / (double)orders.Count) * 100, 2) })
+                .OrderByDescending(x => x.Num);
+            var areaList = await _systemAreaRepository.Queryable().Where(x => x.Id.Length == 6).ToListAsync(cancellationToken);
+            var areaData = (from t1 in orderAreas
+                            join t2 in areaList on t1.AreaCode equals t2.Id into t1_t2
+                            from item in t1_t2.DefaultIfEmpty()
+                            select new
+                            {
+                                AreaCode = t1.AreaCode,
+                                Num = t1.Num,
+                                Rate = t1.Rate,
+                                AreaName = t1_t2.Select(x => x.AreaName).FirstOrDefault(),
+                            }).OrderByDescending(x => x.Num).ToList();
+            var areaNames = areaData.Select(x => x.AreaName).ToList();
+
+            var area = new { AreaNames = areaNames, data = areaData };
+
+            var handle = new { HandleFiled = orders.Count(x => x.Status >= EOrderStatus.Filed), Handle = orders.Count(x => x.Status < Share.Enums.Order.EOrderStatus.Filed) };
+
+            var first = _mapper.Map<List<OrderDto>>(orders.OrderByDescending(x => x.CreationTime).Take(2).ToList());
+            return new { Analysis = analysis, Introduction = introduction, Area = area, Handle = handle, First = first };
+        }
+
+
+    }
 }

+ 242 - 10
src/Hotline.Application/OrderApp/OrderApplication.cs

@@ -51,6 +51,7 @@ using MapsterMapper;
 using MediatR;
 using Microsoft.Extensions.Options;
 using PanGu;
+using Quartz.Simpl;
 using SqlSugar;
 using XF.Domain.Authentications;
 using XF.Domain.Dependency;
@@ -112,7 +113,7 @@ public class OrderApplication : IOrderApplication, IScopeDependency
     private readonly ICircularRecordDomainService _circularRecordDomainService;
     private readonly ISessionContextManager _sessionContextManager;
     private readonly IOrderVisitApplication _orderVisitApplication;
-    private readonly IRepository<OrderEarlyWarning> _orderEarlyWarningRepository;
+    private readonly IRepository<OrderVisitDetailCopy> _orderVisitDetailCopyRepository;
 
     public OrderApplication(
         IOrderDomainService orderDomainService,
@@ -165,7 +166,8 @@ public class OrderApplication : IOrderApplication, IScopeDependency
         ISessionContextManager sessionContextManager,
         IOrderVisitApplication orderVisitApplication,
         IRepository<Role> roleRepository,
-        IRepository<OrderEarlyWarning> orderEarlyWarningRepository)
+        IRepository<OrderVisitDetailCopy> orderVisitDetailCopyRepository
+        )
     {
         _orderDomainService = orderDomainService;
         _workflowDomainService = workflowDomainService;
@@ -216,7 +218,7 @@ public class OrderApplication : IOrderApplication, IScopeDependency
         _sessionContextManager = sessionContextManager;
         _orderVisitApplication = orderVisitApplication;
         _roleRepository = roleRepository;
-        _orderEarlyWarningRepository = orderEarlyWarningRepository;
+        _orderVisitDetailCopyRepository = orderVisitDetailCopyRepository;
     }
 
     /// <summary>
@@ -780,7 +782,7 @@ public class OrderApplication : IOrderApplication, IScopeDependency
         if (_appOptions.Value.IsLuZhou)
             query = query.Includes(d => d.FwCallRecord);
         //.Includes(d => d.OrderPublish)
-        return query.Where(x => x.Status == EOrderStatus.Filed)
+        return query.Where(d => d.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))
@@ -837,7 +839,15 @@ public class OrderApplication : IOrderApplication, IScopeDependency
                 d.SourceChannelCode == "S12345" && d.IsProvince == true) //省12345
             .OrderBy(d => new { IsUrgent = d.IsUrgent }, OrderByType.Desc)
             .OrderByIF(string.IsNullOrEmpty(dto.SortField), d => d.IsUrgent, OrderByType.Desc)
-            .OrderByIF(string.IsNullOrEmpty(dto.SortField), d => d.FiledTime, OrderByType.Desc);
+            .OrderByIF(string.IsNullOrEmpty(dto.SortField), d => d.FiledTime, OrderByType.Desc)
+            .OrderByIF(dto is { SortRule: 0, SortField: "filedTime" }, d => d.FiledTime, OrderByType.Asc)
+            .OrderByIF(dto is { SortRule: 1, SortField: "filedTime" }, d => d.FiledTime, OrderByType.Desc)
+            .OrderByIF(dto is { SortRule: 0, SortField: "actualHandleTime" }, d => d.ActualHandleTime, OrderByType.Asc)
+            .OrderByIF(dto is { SortRule: 1, SortField: "actualHandleTime" }, d => d.ActualHandleTime, OrderByType.Desc)
+            .OrderByIF(dto is { SortRule: 0, SortField: "creationTime" }, d => d.CreationTime, OrderByType.Asc)
+            .OrderByIF(dto is { SortRule: 1, SortField: "creationTime" }, d => d.CreationTime, OrderByType.Desc)
+            .OrderByIF(dto is { SortRule: 0, SortField: "expiredTimeProvince" }, d => d.ExpiredTimeProvince, OrderByType.Asc)
+            .OrderByIF(dto is { SortRule: 1, SortField: "expiredTimeProvince" }, d => d.ExpiredTimeProvince, OrderByType.Desc);
     }
 
     public ISugarQueryable<OrderPublish> GetPublishedOrder(PublishedPagedRequest dto)
@@ -1310,6 +1320,27 @@ public class OrderApplication : IOrderApplication, IScopeDependency
         if (_appOptions.Value.IsYiBin && visit.VisitState == EVisitState.Visited)
             throw UserFriendlyException.SameMessage("已回访,不能重复回访");
 
+        //记录修改历史
+        try
+        {
+            if (dto.IsUpdate.HasValue && dto.IsUpdate == true)
+            {
+                if (visit.OrderVisitDetails != null && visit.OrderVisitDetails.Count > 0)
+                {
+                    var guid = Guid.NewGuid().ToString();
+                    var visitDetailsCopy = _mapper.Map<List<OrderVisitDetailCopy>>(visit.OrderVisitDetails);
+                    foreach (var item in visitDetailsCopy)
+                    {
+                        item.BacthId = guid;
+                    }
+                    await _orderVisitDetailCopyRepository.AddRangeAsync(visitDetailsCopy, cancellationToken);
+                    visit.IsUpdate = true;
+                }
+            }
+        }
+        catch (Exception)
+        { }
+
         var first = dto.VisitDetails.FirstOrDefault(x => x.VisitTarget == EVisitTarget.Org);
 
         visit.IsPutThrough = dto.IsPutThrough;
@@ -1386,6 +1417,37 @@ public class OrderApplication : IOrderApplication, IScopeDependency
             _sessionContext.OrgName);
         await _workflowDomainService.HandleVisitTraceAsync(visit.Id, visitor, visit.VisitTime ?? DateTime.Now, cancellationToken);
 
+        var isOpenSendEndSms = _systemSettingCacheManager.GetSetting(SettingConstants.IsOpenSendEndSms)?.SettingValue[0];
+        if (isOpenSendEndSms == "true")
+        {
+            try
+            {
+                if (_appOptions.Value.IsZiGong)
+                {
+                    if (visit.Order.Source != ESource.ProvinceStraight)
+                    {
+                        //发送查询短信
+                        var messageDto = new Share.Dtos.Push.MessageDto
+                        {
+                            PushBusiness = EPushBusiness.SearchSms,
+                            ExternalId = visit.Id,
+                            OrderId = visit.Order.Id,
+                            PushPlatform = EPushPlatform.Sms,
+                            Remark = visit.Order.Title,
+                            Name = visit.Order.FromName,
+                            TemplateCode = "1021",
+                            Params = new List<string>() { visit.Order.No, visit.Order.Password },
+                            TelNumber = visit.Order.Contact,
+                        };
+                        await _mediator.Publish(new PushMessageNotify(messageDto), cancellationToken);
+                    }
+                }
+            }
+            catch (Exception)
+            {
+            }
+        }
+
         var orderDto = _mapper.Map<OrderDto>(visit.Order);
         if (first != null)
         {
@@ -1422,8 +1484,7 @@ public class OrderApplication : IOrderApplication, IScopeDependency
         if (first != null)
         {
             //写入质检
-            await _qualityApplication.AddQualityAsync(EQualitySource.Visit, visit.Order.Id, visit.Id,
-                cancellationToken);
+            await _qualityApplication.AddQualityAsync(EQualitySource.Visit, visit.Order.Id, visit.Id, cancellationToken);
 
             if (_appOptions.Value.IsZiGong == true)
             {
@@ -1438,8 +1499,8 @@ public class OrderApplication : IOrderApplication, IScopeDependency
                 }, cancellationToken);
 
                 //回访成功后结果不满意发送甄别提醒短信
-                var isOpenSendEndSms = _systemSettingCacheManager.IsSendDissatisfiedScreenSms;
-                if (isOpenSendEndSms)
+                var isSendDissatisfiedScreenSms = _systemSettingCacheManager.IsSendDissatisfiedScreenSms;
+                if (isSendDissatisfiedScreenSms)
                 {
                     try
                     {
@@ -1705,6 +1766,136 @@ public class OrderApplication : IOrderApplication, IScopeDependency
         return query;
     }
 
+    public ISugarQueryable<Order> QueryTravel(QueryOrderDto dto)
+    {
+        var query = _orderRepository.Queryable(canView: false);
+        query = query.Includes(d => d.OrderScreens).Includes(d => d.OrderTags);
+
+        query = query
+                .Where(d => d.FocusOnEvents.Contains("7"))
+                .WhereIF(!string.IsNullOrEmpty(dto.Keyword), d => d.Title.Contains(dto.Keyword!)) //标题
+                .WhereIF(!string.IsNullOrEmpty(dto.ProvinceNo), d => d.ProvinceNo.Contains(dto.ProvinceNo)) //省本地编号
+                .WhereIF(!string.IsNullOrEmpty(dto.ReceiveProvinceNo), d => d.ReceiveProvinceNo.Contains(dto.ReceiveProvinceNo)) //省编号
+                .WhereIF(dto.IsSecret.HasValue, d => d.IsSecret == dto.IsSecret.Value)
+                .WhereIF(dto.IsReTransact.HasValue && dto.IsReTransact.Value, d => d.ReTransactNum > 0)
+                .WhereIF(dto.IsReTransact.HasValue && dto.IsReTransact.Value == false, d => d.ReTransactNum == null || d.ReTransactNum == 0)
+                .WhereIF(!string.IsNullOrEmpty(dto.No), d => d.No.Contains(dto.No)) //工单编码
+                .WhereIF(!string.IsNullOrEmpty(dto.AcceptType), d => d.AcceptTypeCode == dto.AcceptType) //受理类型
+                                                                                                         //.WhereIF(dto.AcceptTypes.Any(), d => dto.AcceptTypes.Contains(d.AcceptTypeCode)) //受理类型
+                .WhereIF(!string.IsNullOrEmpty(dto.Channel), d => d.SourceChannelCode == dto.Channel)
+                .WhereIF(!string.IsNullOrEmpty(dto.Hotspot), d => d.HotspotSpliceName != null && d.HotspotSpliceName.Contains(dto.Hotspot))
+                .WhereIF(!string.IsNullOrEmpty(dto.TransferPhone), d => d.TransferPhone == dto.TransferPhone!) //转接号码
+                                                                                                               //.WhereIF(dto.OrgCodes.Any(), d => d.Workflow.Assigns.Any(s => dto.OrgCodes.Contains(s.OrgCode)))
+                                                                                                               //.WhereIF(dto.OrgCodes.Any(), d => dto.OrgCodes.Contains(d.ActualHandleOrgCode)) //接办部门
+                                                                                                               //.WhereIF(!string.IsNullOrEmpty(dto.OrgId), d => d.CurrentHandleOrgId == dto.OrgId)//接办部门
+                .WhereIF(!string.IsNullOrEmpty(dto.OrgLevelOneName), d => d.OrgLevelOneName.Contains(dto.OrgLevelOneName)) //一级部门
+                .WhereIF(!string.IsNullOrEmpty(dto.ActualHandleOrgName), d => d.ActualHandleOrgName.Contains(dto.ActualHandleOrgName)) //接办部门(综合查询模糊)
+                .WhereIF(!string.IsNullOrEmpty(dto.NameOrNo), d => d.AcceptorName == dto.NameOrNo! || d.AcceptorStaffNo == dto.NameOrNo!) //受理人/坐席
+                .WhereIF(dto.CreationTimeStart.HasValue, d => d.CreationTime >= dto.CreationTimeStart) //受理时间开始
+                .WhereIF(dto.CreationTimeEnd.HasValue, d => d.CreationTime <= dto.CreationTimeEnd) //受理时间结束
+                .WhereIF(dto.StartTimeStart.HasValue, d => d.StartTime >= dto.StartTimeStart) //流程开启时间开始
+                .WhereIF(dto.StartTimeEnd.HasValue, d => d.StartTime <= dto.StartTimeEnd) //流程开启时间结束
+                                                                                          //.WhereIF(dto.EmergencyLevels.Any(), d => dto.EmergencyLevels.Contains(d.EmergencyLevel))  //紧急程度
+                .WhereIF(!string.IsNullOrEmpty(dto.FromPhone), d => d.FromPhone.Contains(dto.FromPhone)) //来电号码
+                .WhereIF(!string.IsNullOrEmpty(dto.PhoneNo), d => d.Contact.Contains(dto.PhoneNo)) //联系电话
+                                                                                                   //.WhereIF(!string.IsNullOrEmpty(dto.PushTypeCode), d => d.PushTypeCode == dto.PushTypeCode) //推送分类
+                .WhereIF(!string.IsNullOrEmpty(dto.PushTypeCode), x => x.OrderPushTypes.Any(opt => opt.PushTypeCode == dto.PushTypeCode)) //推送分类
+                .WhereIF(dto.ExpiredTimeStart.HasValue, d => d.ExpiredTime >= dto.ExpiredTimeStart) //超期时间开始
+                .WhereIF(dto.ExpiredTimeEnd.HasValue, d => d.ExpiredTime <= dto.ExpiredTimeEnd) //超期时间结束
+                                                                                                //.WhereIF(dto.Statuses.Any(), d => dto.Statuses.Contains(d.Status))  //工单状态
+                .WhereIF(dto.Status.HasValue, d => d.Status == dto.Status) //工单状态
+                                                                           //.WhereIF(dto.Statuses.Any(d => d == EOrderStatus.SpecialToUnAccept), d => d.Status <= EOrderStatus.SpecialToUnAccept)
+                .WhereIF(!string.IsNullOrEmpty(dto.ActualHandlerName), d => d.ActualHandlerName == dto.ActualHandlerName) //接办人
+                .WhereIF(dto.IsScreen == true, d => d.OrderScreens.Any(x => x.Status != EScreenStatus.Refuse)) //有甄别
+                .WhereIF(dto.IsScreen == false, d => !d.OrderScreens.Any(x => x.Status != EScreenStatus.Refuse)) //无甄别
+                .WhereIF(!string.IsNullOrEmpty(dto.CurrentStepCode), d => d.CurrentStepCode == dto.CurrentStepCode) //当前办理节点
+                .WhereIF(dto.ActualHandleTimeStart.HasValue, d => d.FiledTime >= dto.ActualHandleTimeStart) //办结时间开始
+                .WhereIF(dto.ActualHandleTimeEnd.HasValue, d => d.FiledTime <= dto.ActualHandleTimeEnd) //办结时间结束
+                .WhereIF(dto.IsOverTime == true,
+                    d => (d.ExpiredTime < DateTime.Now && d.Status < EOrderStatus.Filed) ||
+                         (d.ExpiredTime < d.FiledTime && d.Status >= EOrderStatus.Filed)) //是 超期
+                .WhereIF(dto.IsOverTime == false,
+                    d => (d.ExpiredTime > DateTime.Now && d.Status < EOrderStatus.Filed) ||
+                         (d.ExpiredTime > d.FiledTime && d.Status >= EOrderStatus.Filed)) //否 超期
+                .WhereIF(dto.IdentityType != null, d => d.IdentityType == dto.IdentityType) //来电主体
+                .WhereIF(!string.IsNullOrEmpty(dto.FromName), d => d.FromName == dto.FromName) //来电人姓名
+                .WhereIF(!string.IsNullOrEmpty(dto.AreaCode) && dto.AreaCode.LastIndexOf("00") > 0,
+                    d => d.AreaCode.StartsWith(SqlFunc.Substring(dto.AreaCode, 0, 4)))
+                .WhereIF(!string.IsNullOrEmpty(dto.AreaCode) && dto.AreaCode.LastIndexOf("00") <= 0, d => d.AreaCode.StartsWith(dto.AreaCode))
+                .WhereIF(dto.IsProvinceOrder.HasValue && dto.IsProvinceOrder == true && dto.ProvinceSearch != true, d => d.IsProvince == true)
+                .WhereIF(dto.IsProvinceOrder.HasValue && dto.IsProvinceOrder == false && dto.ProvinceSearch != true, d => d.IsProvince == false)
+                .WhereIF(dto.IsProvinceOrder.HasValue && dto.IsProvinceOrder == true && dto.ProvinceSearch == true, d => d.Source == ESource.ProvinceStraight)
+                .WhereIF(!string.IsNullOrEmpty(dto.SensitiveWord), d => SqlFunc.JsonArrayAny(d.Sensitive, dto.SensitiveWord))
+                .WhereIF(dto.IsSensitiveWord.HasValue && dto.IsSensitiveWord == true,
+                    d => d.Sensitive != null && SqlFunc.JsonArrayLength(d.Sensitive) > 0)
+                .WhereIF(dto.IsUrgent.HasValue, d => d.IsUrgent == dto.IsUrgent.Value)
+                .WhereIF(!string.IsNullOrEmpty(dto.ProvinceChannel) && dto.ProvinceChannel == "1", d => d.Source == ESource.ProvinceStraight &&
+                    d.SourceChannelCode == "SZMHD" && d.IsProvince == false) //政民互动直派
+                .WhereIF(!string.IsNullOrEmpty(dto.ProvinceChannel) && dto.ProvinceChannel == "2", d => d.Source == ESource.ProvinceStraight &&
+                    d.SourceChannelCode == "SZMHD" && d.IsProvince == true) //政民互动
+                .WhereIF(!string.IsNullOrEmpty(dto.ProvinceChannel) && dto.ProvinceChannel == "3", d => d.Source == ESource.ProvinceStraight &&
+                    d.SourceChannelCode == "S12345" && d.IsProvince == true) //省12345
+                                                                             //.WhereIF(!string.IsNullOrEmpty(dto.ContentRetrieval),d => d.Title.Contains(dto.ContentRetrieval) || d.Content.Contains(dto.ContentRetrieval) || d.FileOpinion.Contains(dto.ContentRetrieval) || d.ActualOpinion.Contains(dto.ContentRetrieval))
+                .WhereIF(!string.IsNullOrEmpty(dto.ContentRetrieval), d => d.Content.Contains(dto.ContentRetrieval!))
+                .WhereIF(!string.IsNullOrEmpty(dto.FileOption), d => d.FileOpinion.Contains(dto.FileOption!))
+                .WhereIF(dto.IsSgin.HasValue && dto.IsSgin == true, d => d.CurrentStepAcceptTime != null)
+                .WhereIF(dto.IsSgin.HasValue && dto.IsSgin == false, d => d.CurrentStepAcceptTime == null)
+                .WhereIF(dto.FiledType is FiledType.CenterFiled, d => d.FileOrgIsCenter == true) //d => d.ProcessType == EProcessType.Zhiban
+                .WhereIF(dto.FiledType is FiledType.OrgFiled, d => d.FileOrgIsCenter == false) //d => d.ProcessType == EProcessType.Jiaoban
+                                                                                               //.WhereIF(!string.IsNullOrEmpty(dto.OrderTagCode), d => d.OrderTagCode == dto.OrderTagCode)// 工单标签
+                .WhereIF(!string.IsNullOrEmpty(dto.OrderTagCode), d => d.OrderTags.Any(ot => ot.DicDataValue == dto.OrderTagCode)) //工单标签
+                .OrderByIF(string.IsNullOrEmpty(dto.SortField), d => d.CreationTime, OrderByType.Desc) //默认排序时间为创建时间
+                .OrderByIF(dto is { SortField: "no", SortRule: 0 }, d => d.No, OrderByType.Asc) //工单编号升序
+                .OrderByIF(dto is { SortField: "no", SortRule: 1 }, d => d.No, OrderByType.Desc) //工单编号降序
+                .OrderByIF(dto is { SortField: "isProvinceText", SortRule: 0 }, d => d.IsProvince, OrderByType.Asc) //是否省工单升序
+                .OrderByIF(dto is { SortField: "isProvinceText", SortRule: 1 }, d => d.IsProvince, OrderByType.Desc) //是否省工单降序
+                .OrderByIF(dto is { SortField: "reTransactNum", SortRule: 0 }, d => d.ReTransactNum, OrderByType.Asc) //重办次数升序
+                .OrderByIF(dto is { SortField: "reTransactNum", SortRule: 1 }, d => d.ReTransactNum, OrderByType.Desc) //重办次数降序
+                .OrderByIF(dto is { SortField: "isUrgentText", SortRule: 0 }, d => d.IsUrgent, OrderByType.Asc) //是否紧急升序
+                .OrderByIF(dto is { SortField: "isUrgentText", SortRule: 1 }, d => d.IsUrgent, OrderByType.Desc) //是否紧急降序
+                .OrderByIF(dto is { SortField: "isSecret", SortRule: 0 }, d => d.IsSecret, OrderByType.Asc) //是否紧急升序
+                .OrderByIF(dto is { SortField: "isSecret", SortRule: 1 }, d => d.IsSecret, OrderByType.Desc) //是否紧急降序
+                .OrderByIF(dto is { SortField: "currentStepName", SortRule: 0 }, d => d.CurrentStepName, OrderByType.Asc) //当前节点升序
+                .OrderByIF(dto is { SortField: "currentStepName", SortRule: 1 }, d => d.CurrentStepName, OrderByType.Desc) //当前节点降序
+                .OrderByIF(dto is { SortField: "actualStepAcceptText", SortRule: 0 }, d => d.ActualHandleStepAcceptTime.HasValue,
+                    OrderByType.Asc) //受理情况升序
+                .OrderByIF(dto is { SortField: "actualStepAcceptText", SortRule: 1 }, d => d.ActualHandleStepAcceptTime.HasValue,
+                    OrderByType.Desc) //受理情况降序
+                .OrderByIF(dto is { SortField: "statusText", SortRule: 0 }, d => d.Status, OrderByType.Asc) //工单状态升序
+                .OrderByIF(dto is { SortField: "statusText", SortRule: 1 }, d => d.Status, OrderByType.Desc) //工单状态降序
+                .OrderByIF(dto is { SortField: "creationTime", SortRule: 0 }, d => d.StartTime, OrderByType.Asc) //受理时间升序
+                .OrderByIF(dto is { SortField: "creationTime", SortRule: 1 }, d => d.StartTime, OrderByType.Desc) //受理时间降序
+                .OrderByIF(dto is { SortField: "expiredTime", SortRule: 0 }, d => d.ExpiredTime, OrderByType.Asc) //超期时间升序
+                .OrderByIF(dto is { SortField: "expiredTime", SortRule: 1 }, d => d.ExpiredTime, OrderByType.Desc) //超期时间降序
+                .OrderByIF(dto is { SortField: "filedTime", SortRule: 0 }, d => d.FiledTime, OrderByType.Asc) //办结时间升序
+                .OrderByIF(dto is { SortField: "filedTime", SortRule: 1 }, d => d.FiledTime, OrderByType.Desc) //办结时间降序
+                .OrderByIF(dto is { SortField: "orgLevelOneName", SortRule: 0 }, d => d.OrgLevelOneName, OrderByType.Asc) //一级部门升序
+                .OrderByIF(dto is { SortField: "orgLevelOneName", SortRule: 1 }, d => d.OrgLevelOneName, OrderByType.Desc) //一级部门降序
+                .OrderByIF(dto is { SortField: "orgLevelTwoName", SortRule: 0 }, d => d.OrgLevelTwoName, OrderByType.Asc) //二级部门升序
+                .OrderByIF(dto is { SortField: "orgLevelTwoName", SortRule: 1 }, d => d.OrgLevelTwoName, OrderByType.Desc) //二级部门降序
+                .OrderByIF(dto is { SortField: "actualHandleOrgName", SortRule: 0 }, d => d.ActualHandleOrgName, OrderByType.Asc) //接办部门升序
+                .OrderByIF(dto is { SortField: "actualHandleOrgName", SortRule: 1 }, d => d.ActualHandleOrgName, OrderByType.Desc) //接办部门降序
+                .OrderByIF(dto is { SortField: "acceptType", SortRule: 0 }, d => d.AcceptTypeCode, OrderByType.Asc) //受理类型升序
+                .OrderByIF(dto is { SortField: "acceptType", SortRule: 1 }, d => d.AcceptTypeCode, OrderByType.Desc) //受理类型降序
+                .OrderByIF(dto is { SortField: "counterSignTypeText", SortRule: 0 }, d => d.CounterSignType, OrderByType.Asc) //是否会签升序
+                .OrderByIF(dto is { SortField: "counterSignTypeText", SortRule: 1 }, d => d.CounterSignType, OrderByType.Desc) //是否会签降序
+                .OrderByIF(dto is { SortField: "hotspotSpliceName", SortRule: 0 }, d => d.HotspotSpliceName, OrderByType.Asc) //热点分类全称升序
+                .OrderByIF(dto is { SortField: "hotspotSpliceName", SortRule: 1 }, d => d.HotspotSpliceName, OrderByType.Desc) //热点分类全称降序
+                .OrderByIF(dto is { SortField: "hotspotName", SortRule: 0 }, d => d.HotspotName, OrderByType.Asc) //热点分类升序
+                .OrderByIF(dto is { SortField: "hotspotName", SortRule: 1 }, d => d.HotspotName, OrderByType.Desc) //热点分类降序
+                .OrderByIF(dto is { SortField: "acceptorName", SortRule: 0 }, d => d.AcceptorName, OrderByType.Asc) //受理人升序
+                .OrderByIF(dto is { SortField: "acceptorName", SortRule: 1 }, d => d.AcceptorName, OrderByType.Desc) //受理人降序
+                .OrderByIF(dto is { SortField: "currentStepAcceptTime", SortRule: 0 }, d => d.CurrentStepAcceptTime, OrderByType.Asc) //接办时间升序
+                .OrderByIF(dto is { SortField: "currentStepAcceptTime", SortRule: 1 }, d => d.CurrentStepAcceptTime, OrderByType.Desc) //接办时间降序
+            ;
+
+        query = query
+            .Includes(x => x.OrderSupervises.OrderByDescending(d => d.CreationTime).Take(2).ToList())
+            .Includes(x => x.OrderVisits, d => d.OrderVisitDetails.Where(c => c.VisitTarget == EVisitTarget.Org).Take(1).ToList());
+
+        return query;
+    }
+
     /// <summary>
     /// 未签收统计
     /// </summary>
@@ -3626,6 +3817,8 @@ public class OrderApplication : IOrderApplication, IScopeDependency
             .WhereIF(!string.IsNullOrEmpty(dto.OrderTagCode),
                 d => SqlFunc.Subqueryable<OrderRelationTag>().InnerJoin<SystemDicData>((s, p) => s.TagId == p.Id)
                     .Where((s, p) => p.DicDataValue == dto.OrderTagCode && d.OrderId == s.OrderId).Any()) //工单标签
+            .WhereIF(dto.IsUpdate.HasValue && dto.IsUpdate == true, d => d.IsUpdate == true)
+            .WhereIF(dto.IsUpdate.HasValue && dto.IsUpdate == false, d => d.IsUpdate == false || d.IsUpdate == null)
             .OrderByIF(_appOptions.Value.IsYiBin && dto.VisitStateQuery != EVisitStateQuery.Visited, d => d.Order.IsUrgent, OrderByType.Desc)
             .OrderByIF(_appOptions.Value.IsZiGong == false, d => d.PublishTime, OrderByType.Desc)
             //.OrderByDescending(d => d.PublishTime)
@@ -4355,8 +4548,8 @@ public class OrderApplication : IOrderApplication, IScopeDependency
 
         var orderExtension = await _orderDomainService.GetOrderExtensionsAsync(dto.ReceiveProvinceNo, cancellationToken);
 
-        //  var order = await _orderRepository.GetAsync(d => d.ReceiveProvinceNo == dto.ReceiveProvinceNo, cancellationToken);
         var order = await _orderRepository.Queryable().Includes(d => d.Workflow).FirstAsync(d => d.ReceiveProvinceNo == dto.ReceiveProvinceNo);
+
         //处理省下行数据,热点名称为空
         if (!string.IsNullOrEmpty(dto.HotspotId) && string.IsNullOrEmpty(dto.HotspotName))
         {
@@ -5534,4 +5727,43 @@ public class OrderApplication : IOrderApplication, IScopeDependency
     }
 
     #endregion
+
+    /// <summary>
+    /// 部门退回中心统计
+    /// </summary>
+    /// <param name="dto"></param>
+    /// <returns></returns>
+
+    public ISugarQueryable<OrgSendBackAuditListVo> OrgSendBackAuditList(OrgSendBackAuditListDto dto)
+    {
+        var query = _orderSendBackAuditRepository.Queryable()
+            .Where(x => x.State == ESendBackAuditState.End && x.ApplyOrgId != OrgSeedData.CenterId && x.SendBackOrgId == OrgSeedData.CenterId)
+            .WhereIF(dto.StartTime.HasValue && dto.EndTime.HasValue, x => x.AuditTime >= dto.StartTime && x.AuditTime <= dto.EndTime)
+            .GroupBy(x => new { x.ApplyOrgId, x.ApplyOrgName })
+            .Select(x => new OrgSendBackAuditListVo
+            {
+                OrgId = x.ApplyOrgId,
+                OrgName = x.ApplyOrgName,
+                Num = SqlFunc.AggregateCount(1)
+            });
+        return query;
+    }
+
+    public ISugarQueryable<OrderSendBackAudit> OrgSendBackAuditDetail(OrgSendBackAuditListDto dto)
+    {
+        var query = _orderSendBackAuditRepository.Queryable()
+                .Includes(x => x.Order)
+                .Where(x => x.State == ESendBackAuditState.End && x.ApplyOrgId != OrgSeedData.CenterId && x.SendBackOrgId == OrgSeedData.CenterId)
+                .WhereIF(dto.StartTime.HasValue && dto.EndTime.HasValue, x => x.AuditTime >= dto.StartTime && x.AuditTime <= dto.EndTime)
+                .WhereIF(!string.IsNullOrEmpty(dto.OrgId), x => x.ApplyOrgId == dto.OrgId)
+                .OrderByIF(string.IsNullOrEmpty(dto.SortField), x => x.CreationTime, OrderByType.Desc)
+                .OrderByIF(dto is { SortField: "order.startTime", SortRule: 0 }, x => x.Order.StartTime, OrderByType.Asc)
+                .OrderByIF(dto is { SortField: "order.startTime", SortRule: 1 }, x => x.Order.StartTime, 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: "auditTime", SortRule: 0 }, x => x.AuditTime, OrderByType.Asc)
+                .OrderByIF(dto is { SortField: "auditTime", SortRule: 1 }, x => x.AuditTime, OrderByType.Desc)
+            ;
+        return query;
+    }
 }

+ 3 - 2
src/Hotline.Application/Snapshot/BiSnapshotApplication.cs

@@ -1,4 +1,5 @@
-using Hotline.Configurations;
+using Hotline.Application.Snapshot.Contracts;
+using Hotline.Configurations;
 using Hotline.Orders;
 using Hotline.Settings;
 using Hotline.Settings.Hotspots;
@@ -9,7 +10,7 @@ using Hotline.Share.Enums.Order;
 using Hotline.Share.Enums.Snapshot;
 using Hotline.Share.Tools;
 using Hotline.Snapshot;
-using Hotline.Snapshot.Interfaces;
+using Hotline.Snapshot.IRepository;
 using Hotline.Tools;
 using Mapster;
 using Microsoft.AspNetCore.Mvc;

+ 1 - 1
src/Hotline.Application/Snapshot/IBiSnapshotApplication.cs → src/Hotline.Application/Snapshot/Contracts/IBiSnapshotApplication.cs

@@ -8,7 +8,7 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 
-namespace Hotline.Application.Snapshot;
+namespace Hotline.Application.Snapshot.Contracts;
 public interface IBiSnapshotApplication
 {
     /// <summary>

+ 2 - 2
src/Hotline.Application/Snapshot/IIndustryApplication.cs → src/Hotline.Application/Snapshot/Contracts/IIndustryApplication.cs

@@ -7,7 +7,7 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 
-namespace Hotline.Application.Snapshot;
+namespace Hotline.Application.Snapshot.Contracts;
 public interface IIndustryApplication
 {
     /// <summary>
@@ -28,7 +28,7 @@ public interface IIndustryApplication
     /// </summary>
     /// <param name="id"></param>
     /// <returns></returns>
-    Task<IndustryDetailOutDto> GetIndustryDetailAsync(string id);
+    Task<IndustryDetailOutDto> GetIndustryDetailAsync(string id, CancellationToken token);
 
     /// <summary>
     /// 修改行业

+ 1 - 1
src/Hotline.Application/Snapshot/IInviteCodeApplication.cs → src/Hotline.Application/Snapshot/Contracts/IInviteCodeApplication.cs

@@ -8,7 +8,7 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 
-namespace Hotline.Application.Snapshot;
+namespace Hotline.Application.Snapshot.Contracts;
 public interface IInviteCodeApplication
 {
     Task<string> AddInviteCodeAsync(AddInviteCodeInDto dto);

+ 1 - 1
src/Hotline.Application/Snapshot/IOrderSnapshotApplication.cs → src/Hotline.Application/Snapshot/Contracts/IOrderSnapshotApplication.cs

@@ -11,7 +11,7 @@ using System.Text;
 using System.Threading.Tasks;
 using Hotline.Share.Dtos.FlowEngine.Workflow;
 
-namespace Hotline.Application.Snapshot;
+namespace Hotline.Application.Snapshot.Contracts;
 public interface IOrderSnapshotApplication
 {
 

+ 1 - 1
src/Hotline.Application/Snapshot/IRedPackApplication.cs → src/Hotline.Application/Snapshot/Contracts/IRedPackApplication.cs

@@ -7,7 +7,7 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 
-namespace Hotline.Application.Snapshot;
+namespace Hotline.Application.Snapshot.Contracts;
 public interface IRedPackApplication
 {
     /// <summary>

+ 22 - 1
src/Hotline.Application/Snapshot/ISnapshotApplication.cs → src/Hotline.Application/Snapshot/Contracts/ISnapshotApplication.cs

@@ -3,7 +3,7 @@ using Hotline.Share.Dtos.Article;
 using Hotline.Share.Dtos.Snapshot;
 using Hotline.Snapshot;
 
-namespace Hotline.Application.Snapshot;
+namespace Hotline.Application.Snapshot.Contracts;
 public interface ISnapshotApplication
 {
     /// <summary>
@@ -212,4 +212,25 @@ public interface ISnapshotApplication
     /// <param name="id"></param>
     /// <returns></returns>
     Task<IList<OrderVisitItemsOutDto>> GetOrderVisitDetailAsync(string id);
+
+    /// <summary>
+    /// 积分榜
+    /// </summary>
+    /// <returns></returns>
+    Task<PointsRankOutDto> GetPointsRankAsync();
+
+    /// <summary>
+    /// 积分详情集合
+    /// </summary>
+    /// <param name="dto"></param>
+    /// <returns></returns>
+    Task<IList<PointItemsOutDto>> GetPointItemsAsync(PointItemsInDto dto, CancellationToken token);
+
+    /// <summary>
+    /// 积分总计
+    /// </summary>
+    /// <param name="dto"></param>
+    /// <param name="requestAborted"></param>
+    /// <returns></returns>
+    Task<int> GetPointsTotalAsync(PointItemsInDto dto, CancellationToken requestAborted);
 }

+ 1 - 1
src/Hotline.Application/Snapshot/ISnapshotBulletinApplication.cs → src/Hotline.Application/Snapshot/Contracts/ISnapshotBulletinApplication.cs

@@ -6,7 +6,7 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 
-namespace Hotline.Application.Snapshot;
+namespace Hotline.Application.Snapshot.Contracts;
 public interface ISnapshotBulletinApplication
 {
 

+ 15 - 0
src/Hotline.Application/Snapshot/Contracts/ISnapshotPointsApplication.cs

@@ -0,0 +1,15 @@
+using Hotline.Share.Dtos.Snapshot;
+using SqlSugar;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Hotline.Application.Snapshot.Contracts;
+public interface ISnapshotPointsApplication
+{
+    ISugarQueryable<PointsItemsOutDto> GetPointsItems(PointsItemsInDto dto);
+    Task UpdateIsSecurityMaxAsync(UpdateIsSecurityMaxAsync dto, CancellationToken token);
+}
+

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 2 - 1
src/Hotline.Application/Snapshot/DefaultSnapshotApplication.cs


+ 5 - 3
src/Hotline.Application/Snapshot/IndustryApplication.cs

@@ -1,5 +1,6 @@
 using DocumentFormat.OpenXml.Office2010.Excel;
 using DocumentFormat.OpenXml.Wordprocessing;
+using Hotline.Application.Snapshot.Contracts;
 using Hotline.Caching.Interfaces;
 using Hotline.File;
 using Hotline.Repository.SqlSugar.Extensions;
@@ -9,7 +10,7 @@ using Hotline.Share.Dtos;
 using Hotline.Share.Dtos.Snapshot;
 using Hotline.Share.Tools;
 using Hotline.Snapshot;
-using Hotline.Snapshot.Interfaces;
+using Hotline.Snapshot.IRepository;
 using Hotline.Tools;
 using Mapster;
 using SqlSugar;
@@ -58,6 +59,7 @@ public class IndustryApplication : IIndustryApplication, IScopeDependency
     /// <exception cref="NotImplementedException"></exception>
     public async Task<string> AddIndustryAsync(AddIndustryDto dto, CancellationToken cancellationToken)
     {
+        dto.ValidateObject();
         if (dto.ApproveOrgId.NotNullOrEmpty() && dto.ApproveOrgName.IsNullOrEmpty())
         {
             await _systemOrganizeRepository.GetAsync(dto.ApproveOrgId, cancellationToken)
@@ -87,12 +89,12 @@ public class IndustryApplication : IIndustryApplication, IScopeDependency
         return query;
     }
 
-    public async Task<IndustryDetailOutDto> GetIndustryDetailAsync(string id)
+    public async Task<IndustryDetailOutDto> GetIndustryDetailAsync(string id, CancellationToken token)
     {
         var fileServiceUrl = _sysSetting.FileServerUrl;
         var fileDownloadApi = fileServiceUrl + _sysSetting.FileDownloadApi;
         var industry = await _industryRepository.GetAsync(id);
-        var files = await _fileRepository.GetByKeyAsync(id, CancellationToken.None);
+        var files = await _fileRepository.GetByKeyAsync(id, token);
         var outDto = industry.Adapt<IndustryDetailOutDto>();
         outDto.Files = files.Adapt<IList<IndustryFileDto>>();
         return outDto;

+ 3 - 2
src/Hotline.Application/Snapshot/InviteCodeApplication.cs

@@ -1,7 +1,8 @@
-using Hotline.Share.Dtos.Snapshot;
+using Hotline.Application.Snapshot.Contracts;
+using Hotline.Share.Dtos.Snapshot;
 using Hotline.Share.Tools;
 using Hotline.Snapshot;
-using Hotline.Snapshot.Interfaces;
+using Hotline.Snapshot.IRepository;
 using Hotline.Tools;
 using Mapster;
 using SqlSugar;

+ 41 - 1
src/Hotline.Application/Snapshot/Notifications/SnapshotHandler.cs

@@ -13,7 +13,6 @@ using Hotline.Share.Enums.FlowEngine;
 using Hotline.Share.Mq;
 using Hotline.Share.Tools;
 using Hotline.Snapshot;
-using Hotline.Snapshot.Interfaces;
 using Hotline.Snapshot.Notifications;
 using MediatR;
 using System;
@@ -27,6 +26,12 @@ using XF.Domain.Dependency;
 using XF.Domain.Exceptions;
 using Microsoft.Extensions.Logging;
 using Hotline.Orders.Notifications;
+using Hotline.Snapshot.IRepository;
+using Hotline.Snapshot.Contracts;
+using Hotline.FlowEngine.WorkflowModules;
+using XF.Domain.Entities;
+using Hotline.Share.Enums.Snapshot;
+using Hotline.Application.Snapshot.Contracts;
 
 namespace Hotline.Application.Snapshot.Notifications;
 
@@ -158,3 +163,38 @@ public class AddOrderSpecialHandler : INotificationHandler<AddOrderSpecialNotify
         }
     }
 }
+
+public class SnapshotStartWorkFlow : INotificationHandler<StartWorkflowNotify>
+{
+    private readonly ISnapshotPointsDomainService _snapshotPointsDomainService;
+    private readonly ISystemSettingCacheManager _sysSetting;
+
+    public SnapshotStartWorkFlow(ISnapshotPointsDomainService snapshotPointsDomainService, ILogger<SnapshotStartWorkFlow> logger, ISystemSettingCacheManager sysSetting)
+    {
+        _snapshotPointsDomainService = snapshotPointsDomainService;
+        _logger = logger;
+        _sysSetting = sysSetting;
+    }
+
+    private readonly ILogger<SnapshotStartWorkFlow> _logger;
+
+    /// <summary>
+    /// 用户提交工单后,启动工作流, 给用户加积分
+    /// </summary>
+    /// <param name="notification"></param>
+    /// <param name="cancellationToken"></param>
+    /// <returns></returns>
+    /// <exception cref="NotImplementedException"></exception>
+    public async Task Handle(StartWorkflowNotify notification, CancellationToken cancellationToken)
+    {
+        try
+        {
+            if (_sysSetting.Snapshot == false && notification.Workflow.ModuleCode != WorkflowModuleConsts.OrderHandle) return;
+            await _snapshotPointsDomainService.AddPointsAsync(notification.Workflow.ExternalId, EPointsSource.Report, ESnapshotSMSStatus.Agree, 0);
+        }
+        catch (Exception e)
+        {
+            _logger.LogError("增加用户上报积分失败", e);
+        }
+    }
+}

+ 24 - 7
src/Hotline.Application/Snapshot/RedPackApplication.cs

@@ -1,4 +1,5 @@
 using DocumentFormat.OpenXml.Office2010.Excel;
+using Hotline.Application.Snapshot.Contracts;
 using Hotline.Caching.Interfaces;
 using Hotline.Orders;
 using Hotline.Push.FWMessage;
@@ -11,7 +12,8 @@ using Hotline.Share.Enums.Push;
 using Hotline.Share.Enums.Snapshot;
 using Hotline.Share.Tools;
 using Hotline.Snapshot;
-using Hotline.Snapshot.Interfaces;
+using Hotline.Snapshot.Contracts;
+using Hotline.Snapshot.IRepository;
 using Hotline.ThirdAccountDomainServices.Interfaces;
 using Mapster;
 using Microsoft.AspNetCore.Http;
@@ -49,8 +51,10 @@ public class RedPackApplication : IRedPackApplication, IScopeDependency
     private readonly IPushDomainService _pushDomainService;
     private readonly ILogger<RedPackApplication> _logger;
     private readonly ISystemDicDataCacheManager _systemDic;
+    private readonly ISnapshotPointsRecordRepository _snapshotPointsRecordRepository;
+    private readonly ISnapshotPointsDomainService _snapshotPointsDomainService;
 
-    public RedPackApplication(IOrderSnapshotRepository orderSnapshotRepository, ISnapshotSMSTemplateRepository snapshotSMSTemplateRepository, IOrderRepository orderRepository, IIndustryRepository industryRepository, IRedPackAuditRepository redPackAuditRepository, IRedPackRecordRepository redPackRecordRepository, IRepository<OrderSpecial> orderSpecialRepository, ISessionContext sessionContext, IRedPackGuiderAuditRepository redPackGuiderAuditRepository, IThirdAccountRepository thirdAccountRepository, ISupplementRecordRepository supplementRecordRepository, IPushDomainService pushDomainService, ILogger<RedPackApplication> logger, ISpecialRedPackAuditRepository specialRedPackAuditRepository, ISystemDicDataCacheManager systemDic)
+    public RedPackApplication(IOrderSnapshotRepository orderSnapshotRepository, ISnapshotSMSTemplateRepository snapshotSMSTemplateRepository, IOrderRepository orderRepository, IIndustryRepository industryRepository, IRedPackAuditRepository redPackAuditRepository, IRedPackRecordRepository redPackRecordRepository, IRepository<OrderSpecial> orderSpecialRepository, ISessionContext sessionContext, IRedPackGuiderAuditRepository redPackGuiderAuditRepository, IThirdAccountRepository thirdAccountRepository, ISupplementRecordRepository supplementRecordRepository, IPushDomainService pushDomainService, ILogger<RedPackApplication> logger, ISpecialRedPackAuditRepository specialRedPackAuditRepository, ISystemDicDataCacheManager systemDic, ISnapshotPointsRecordRepository snapshotPointsRecordRepository, ISnapshotPointsDomainService snapshotPointsDomainService)
     {
         _orderSnapshotRepository = orderSnapshotRepository;
         _snapshotSMSTemplateRepository = snapshotSMSTemplateRepository;
@@ -67,6 +71,8 @@ public class RedPackApplication : IRedPackApplication, IScopeDependency
         _logger = logger;
         _specialRedPackAuditRepository = specialRedPackAuditRepository;
         _systemDic = systemDic;
+        _snapshotPointsRecordRepository = snapshotPointsRecordRepository;
+        _snapshotPointsDomainService = snapshotPointsDomainService;
     }
 
     /// <summary>
@@ -79,7 +85,7 @@ public class RedPackApplication : IRedPackApplication, IScopeDependency
         var status = ERedPackAuditStatus.Refuse;
         if (dto.Status == ESnapshotSMSStatus.Agree)
             status = ERedPackAuditStatus.Agree;
-        var redPackAudit = await _redPackAuditRepository.GetAsync(dto.RedPackAuditId) ?? throw UserFriendlyException.SameMessage("审核记录不存在");
+        var redPackAudit = await _redPackAuditRepository.GetAsync(dto.RedPackAuditId, token) ?? throw UserFriendlyException.SameMessage("审核记录不存在");
         if (redPackAudit.Status != ERedPackAuditStatus.Pending) throw UserFriendlyException.SameMessage("已审核, 不可重复审核");
         redPackAudit.SMSTemplateId = dto.SMSTemplateId;
         redPackAudit.Status = status;
@@ -91,14 +97,20 @@ public class RedPackApplication : IRedPackApplication, IScopeDependency
         redPackAudit.AuditOrgId = _sessionContext.OrgId;
         redPackAudit.AuditOrgName = _sessionContext.OrgName;
         redPackAudit.ApprovedAmount = redPackAudit.ShouldAmount;
+        redPackAudit.Points = dto.Points;
+        redPackAudit.PointsStatus = dto.PointsStatus;
+        redPackAudit.PointsOpinion = dto.PointsOpinion;
+        redPackAudit.ExtraDeductedPoints = dto.ExtraDeductedPoints;
+        redPackAudit.ExtraDeductionPointsTypeId = dto.ExtraDeductionPointsTypeId;
+        redPackAudit.ExtraDeductionPointsTypeName = dto.ExtraDeductionPointsTypeName;
         var order = await _orderRepository.Queryable()
             .LeftJoin<OrderSnapshot>((order, snapshot) => order.Id == snapshot.Id)
             .Where((order, snapshot) => order.Id == redPackAudit.OrderId)
-            .Select((order, snapshot) => new { order.Id, order.No, order.FromName, order.FromPhone, snapshot.SnapshotUserId })
+            .Select((order, snapshot) => new { order.Id, order.No, order.FromName, order.FromPhone, snapshot.CreatorId })
             .FirstAsync(token) ?? throw UserFriendlyException.SameMessage("工单不存在");
         if (status == ERedPackAuditStatus.Agree)
         {
-            var third = await _thirdAccountRepository.GetByExternalIdAsync(order.SnapshotUserId, token);
+            var third = await _thirdAccountRepository.GetByExternalIdAsync(order.CreatorId, token);
             var entity = new RedPackRecord
             {
                 OrderId = redPackAudit.OrderId,
@@ -115,7 +127,8 @@ public class RedPackApplication : IRedPackApplication, IScopeDependency
                 entity.Amount = redPackAudit.ApprovedAmount.Value;
             await _redPackRecordRepository.AddAsync(entity);
         }
-        await _redPackAuditRepository.UpdateAsync(redPackAudit);
+        await _redPackAuditRepository.UpdateAsync(redPackAudit, token);
+        await _snapshotPointsDomainService.AddPointsAsync(order.Id, EPointsSource.Audit, dto.PointsStatus, dto.ExtraDeductedPoints);
         if (dto.IsSendSms)
         {
             var smsTemplate = await _snapshotSMSTemplateRepository.GetAsync(dto.SMSTemplateId);
@@ -232,9 +245,12 @@ public class RedPackApplication : IRedPackApplication, IScopeDependency
         var outDto = new SnapshotOrderAuditDetailOutDto { Order = order.Adapt<SnapshotOrderAuditOrderDetailOutDto>() };
         var industry = await _industryRepository.Queryable(includeDeleted: true)
             .LeftJoin<OrderSnapshot>((i, o) => i.Id == o.IndustryId)
-            .Select((i, o) => new { i.Id, i.CitizenReadPackAmount })
+            .Select((i, o) => new { i.Id, i.CitizenReadPackAmount , i.ArgeePoints, i.ExtraDeductedPoints, i.RefusePoints})
             .FirstAsync();
         outDto.Amount = industry.CitizenReadPackAmount;
+        outDto.ArgeePoints = industry.ArgeePoints;
+        outDto.ExtraDeductedPoints = industry.ExtraDeductedPoints;
+        outDto.RefusePoints = industry.RefusePoints;
         outDto.RedPackTxt = $"{order.FromPhone}【】元; ";
         var dayStart = DateTime.Now.ToString("yyyy-MM-dd 00:00:00").ObjToDate();
         var dayEnd = DateTime.Now.ToString("yyyy-MM-dd 23:59:59").ObjToDate();
@@ -249,6 +265,7 @@ public class RedPackApplication : IRedPackApplication, IScopeDependency
             outDto.RedPackTxt += $"今天审批【{count}】个";
 
         outDto.AuditComBox = EnumExts.GetDescriptions<ESnapshotSMSStatus>();
+        outDto.ExtraDeductionPointsType = _systemDic.ExtraDeductionPointsType;
         return outDto;
     }
 

+ 94 - 9
src/Hotline.Application/Snapshot/SnapshotApplicationBase.cs

@@ -22,7 +22,6 @@ using Hotline.Share.Dtos.Settings;
 using Hotline.File;
 using Hotline.Share.Enums.Article;
 using Hotline.Tools;
-using Hotline.Snapshot.Interfaces;
 using DotNetCore.CAP;
 using Microsoft.AspNetCore.Http;
 using Hotline.Share.Dtos.FlowEngine;
@@ -35,6 +34,9 @@ using Hotline.EventBus;
 using Hotline.Quality.Notifications;
 using XF.Utility.EnumExtensions;
 using Hotline.ThirdAccountDomainServices.Interfaces;
+using Hotline.Snapshot.IRepository;
+using Hotline.Share.Enums.CallCenter;
+using Hotline.Repository.SqlSugar.Snapshot;
 
 namespace Hotline.Application.Snapshot;
 
@@ -78,8 +80,9 @@ public abstract class SnapshotApplicationBase
     private readonly IInviteCodeRecordRepository _inviteCodeRecordRepository;
     private readonly IInviteCodeRepository _inviteCodeRepository;
     private readonly ICitizenRepository _citizenRepository;
+    private readonly ISnapshotPointsRecordRepository _pointsRecordRepository;
 
-    public SnapshotApplicationBase(IThirdIdentiyService thirdLoginService, IIndustryRepository industryRepository, ISnapshotBulletinRepository bulletinRepository, ISessionContext sessionContext, IRepository<RedPackRecord> redPackRecordRepository, IRepository<Order> orderRepository, IThirdAccountRepository thirdAccountRepository, IOrderSnapshotRepository orderSnapshotRepository, ISystemSettingCacheManager systemSettingCacheManager, ISystemAreaDomainService systemAreaDomainService, IFileRepository fileRepository, ISystemDicDataCacheManager systemDicDataCacheManager, ISnapshotOrderPublishRepository snapshotOrderPublishRepository, IRepository<WorkflowTrace> workflowTraceRepository, IPractitionerRepository practitionerRepository, IRepository<SystemArea> systemAreaRepository, IVolunteerRepository volunteerRepository, IVolunteerReportRepository volunteerReportRepository, ISystemLogRepository systemLog, IGuiderSystemService guiderSystemService, ICapPublisher capPublisher, Publisher publisher, IFileDomainService fileDomainService, ICommunityInfoRepository communityInfoRepository, IRedPackAuditRepository redPackAuditRepository, IOrderVisitRepository orderVisitRepository, IOrderVisitDetailRepository orderVisitDetailRepository, IRedPackGuiderAuditRepository redPackGuiderAuditRepository, IInviteCodeRecordRepository inviteCodeRecordRepository, IInviteCodeRepository inviteCodeRepository, ICitizenRepository citizenRepository)
+    public SnapshotApplicationBase(IThirdIdentiyService thirdLoginService, IIndustryRepository industryRepository, ISnapshotBulletinRepository bulletinRepository, ISessionContext sessionContext, IRepository<RedPackRecord> redPackRecordRepository, IRepository<Order> orderRepository, IThirdAccountRepository thirdAccountRepository, IOrderSnapshotRepository orderSnapshotRepository, ISystemSettingCacheManager systemSettingCacheManager, ISystemAreaDomainService systemAreaDomainService, IFileRepository fileRepository, ISystemDicDataCacheManager systemDicDataCacheManager, ISnapshotOrderPublishRepository snapshotOrderPublishRepository, IRepository<WorkflowTrace> workflowTraceRepository, IPractitionerRepository practitionerRepository, IRepository<SystemArea> systemAreaRepository, IVolunteerRepository volunteerRepository, IVolunteerReportRepository volunteerReportRepository, ISystemLogRepository systemLog, IGuiderSystemService guiderSystemService, ICapPublisher capPublisher, Publisher publisher, IFileDomainService fileDomainService, ICommunityInfoRepository communityInfoRepository, IRedPackAuditRepository redPackAuditRepository, IOrderVisitRepository orderVisitRepository, IOrderVisitDetailRepository orderVisitDetailRepository, IRedPackGuiderAuditRepository redPackGuiderAuditRepository, IInviteCodeRecordRepository inviteCodeRecordRepository, IInviteCodeRepository inviteCodeRepository, ICitizenRepository citizenRepository, ISnapshotPointsRecordRepository snapshotPointsRecordRepository)
     {
         _thirdLoginService = thirdLoginService;
         _industryRepository = industryRepository;
@@ -112,6 +115,7 @@ public abstract class SnapshotApplicationBase
         _inviteCodeRecordRepository = inviteCodeRecordRepository;
         _inviteCodeRepository = inviteCodeRepository;
         _citizenRepository = citizenRepository;
+        _pointsRecordRepository = snapshotPointsRecordRepository;
     }
 
     #region 小程序
@@ -204,10 +208,10 @@ public abstract class SnapshotApplicationBase
         if (indurstry.IndustryType == EIndustryType.Declare)
         {
             outDto.AreaTree = (await _systemAreaDomainService.GetAreaTree(parentId: "510300")).Adapt<List<SystemAreaOutDto>>();
-            outDto.AreaTree.ToList().ForEach(m => 
+            outDto.AreaTree.ToList().ForEach(m =>
             {
                 if (m.Children.IsNullOrEmpty())
-                    m.Children = new List<SystemAreaOutDto>() { new SystemAreaOutDto { Id = "0",  AreaName = "无"} };
+                    m.Children = new List<SystemAreaOutDto>() { new SystemAreaOutDto { Id = "0", AreaName = "无" } };
             });
             outDto.Files = (await _fileRepository.GetByKeyAsync(indurstry.Id, requestAborted)).Adapt<List<IndustryFileDto>>();
             outDto.Files.ToList().ForEach(m => m.Url = fileDownloadApi + m.AdditionId);
@@ -284,7 +288,7 @@ public abstract class SnapshotApplicationBase
     public async Task<IReadOnlyList<BulletinOutDto>> GetBulletinsAsync(BulletinInDto dto, CancellationToken cancellationToken)
     {
         var items = await _bulletinRepository.Queryable()
-            .Where(m => m.BulletinState == EBulletinState.ReviewPass)
+            .Where(m => m.BulletinState == EBulletinState.ReviewPass && m.IsArrive == true)
             .LeftJoin<Industry>((bulletin, industry) => bulletin.SnapshotBulletinTypeId == industry.BulletinTypePublicityId)
             .Where((bulletin, industry) => industry.Id == dto.IndustryId)
             .ToFixedListAsync(dto, cancellationToken);
@@ -334,6 +338,7 @@ public abstract class SnapshotApplicationBase
             .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) || order.No.Contains(dto.KeyWords))
+            .OrderByDescending((snapshot, order) => snapshot.CreationTime)
             .Select((snapshot, order) => new OrderOutDto
             {
                 Id = snapshot.Id,
@@ -349,6 +354,35 @@ public abstract class SnapshotApplicationBase
         return items;
     }
 
+    /// <summary>
+    /// 积分详情集合
+    /// </summary>
+    /// <param name="dto"></param>
+    /// <returns></returns>
+    public async Task<IList<PointItemsOutDto>> GetPointItemsAsync(PointItemsInDto dto, CancellationToken token)
+    {
+        var items = await _pointsRecordRepository.Queryable()
+            .Where(m => m.UserId == _sessionContext.UserId && dto.Direction == m.Direction)
+            .Select(m => new PointItemsOutDto(), true)
+            .OrderByDescending(m => m.CreationTime)
+            .ToFixedListAsync(dto, token);
+        return items;
+    }
+
+    /// <summary>
+    /// 积分总计
+    /// </summary>
+    /// <param name="dto"></param>
+    /// <param name="requestAborted"></param>
+    /// <returns></returns>
+    public async Task<int> GetPointsTotalAsync(PointItemsInDto dto, CancellationToken token)
+    {
+        return await _pointsRecordRepository.Queryable()
+            .Where(m => m.UserId == _sessionContext.UserId && dto.Direction == m.Direction)
+            .SumAsync(m => m.Points);
+    }
+
+
     /// <summary>
     /// 获取工单详情
     /// </summary>
@@ -409,7 +443,7 @@ public abstract class SnapshotApplicationBase
     /// <param name="id"></param>
     /// <returns></returns>
     public async Task<IList<OrderVisitItemsOutDto>> GetOrderVisitDetailAsync(string id)
-    { 
+    {
         var orderVisitId = await _orderVisitRepository.Queryable()
             .Where(m => m.OrderId == id)
             .Select(m => m.Id)
@@ -427,7 +461,7 @@ public abstract class SnapshotApplicationBase
         }
         var org = orderVisitDetail.Where(m => m.VisitTarget == EVisitTarget.Org).First();
         if (org != null)
-        { 
+        {
             if (org.OrgProcessingResults != null) item.OrgProcessingResults = org.OrgProcessingResults.Value;
             if (org.OrgHandledAttitude != null) item.OrgHandledAttitude = org.OrgHandledAttitude.Value;
         }
@@ -526,7 +560,7 @@ public abstract class SnapshotApplicationBase
     {
         dto.ValidateObject();
         if (int.TryParse(dto.InvitationCode, out var invitationCode) == false)
-        { 
+        {
             throw new UserFriendlyException(200, "邀请码格式错误");
         }
         var userInfo = await _citizenRepository.GetAsync(_sessionContext.UserId)
@@ -833,7 +867,7 @@ public abstract class SnapshotApplicationBase
     {
         var order = await _orderRepository.Queryable()
             .Where(m => m.Id == orderId)
-            .Select(m => new { m.Id, m.Status, m.No , m.FromPhone})
+            .Select(m => new { m.Id, m.Status, m.No, m.FromPhone })
             .FirstAsync(cancellationToken) ?? throw new UserFriendlyException($"{orderId} 工单不存在");
         var snapshot = await _orderSnapshotRepository.GetAsync(orderId) ?? throw new UserFriendlyException("工单不存在");
         if (order.Status != EOrderStatus.Filed) return $"{order.No} 工单状态非 {EOrderStatus.Filed} 不处理;";
@@ -901,4 +935,55 @@ public abstract class SnapshotApplicationBase
         await _bulletinRepository.UpdateAsync(bulletin);
     }
     #endregion
+
+    #region 积分
+    public async Task<PointsRankOutDto> GetPointsRankAsync()
+    {
+        var outDto = new PointsRankOutDto();
+        var record = await _pointsRecordRepository.Queryable()
+            .Where(m => m.UserId == _sessionContext.UserId)
+            .Select(m => new
+            {
+                Total = SqlFunc.AggregateSum(m.Points),
+                Out = SqlFunc.AggregateSum(SqlFunc.IIF(m.Direction == EPointsDirection.Out, m.Points, 0))
+            }).FirstAsync();
+        outDto.ValidPoints = record.Total - record.Out;
+        var startTime = new DateTime(DateTime.Now.Year, 1, 1, 0, 0, 0);
+        var endTime = new DateTime(DateTime.Now.Year, 12, 31, 23, 59, 59);
+        var query = _pointsRecordRepository.Queryable()
+            .LeftJoin<Citizen>((points, citizen) => points.UserId == citizen.Id)
+            .Where((points, citizen) => points.CreationTime >= startTime && points.CreationTime <= endTime)
+            .GroupBy((points, citizen) => new { citizen.Id, points.UserId, citizen.IsSecurityMax, citizen.Name, citizen.PhoneNumber })
+            .Select((points, citizen) => new PointsRankUserDto
+            {
+                IsSecurityMax = citizen.IsSecurityMax ?? false,
+                Rank = SqlFunc.MappingColumn<int>($@"
+            CASE 
+                WHEN citizen.""Id"" = '{_sessionContext.UserId}' THEN 1 
+                ELSE DENSE_RANK() OVER (ORDER BY SUM(CASE WHEN ""points"".""Direction"" = 1 THEN ""points"".""Points"" ELSE 0 END) DESC)
+            END"),
+                Points = SqlFunc.AggregateSum(points.Points),
+                UserName = citizen.Name!,
+                PhoneNumber = citizen.PhoneNumber,
+                CitizenId = citizen.Id,
+            }).MergeTable()
+            .OrderByDescending(points => points.Rank)
+            .Take(11);
+
+#if DEBUG
+        var sql = query.ToSqlString();
+#endif
+        var item = await query.ToListAsync();
+
+        item.ForEach(m =>
+        {
+            if (m.IsSecurityMax)
+                m.HeadUrl = _systemDicDataCacheManager.HeaderImages("aqws");
+            else
+                m.HeadUrl = _systemDicDataCacheManager.HeaderImages("default");
+        });
+        outDto.Ranks = item;
+        return outDto;
+    }
+    #endregion
 }

+ 3 - 2
src/Hotline.Application/Snapshot/SnapshotBulletinApplication.cs

@@ -1,8 +1,9 @@
-using Hotline.Caching.Interfaces;
+using Hotline.Application.Snapshot.Contracts;
+using Hotline.Caching.Interfaces;
 using Hotline.Settings;
 using Hotline.Share.Attributes;
 using Hotline.Share.Dtos.Snapshot;
-using Hotline.Snapshot.Interfaces;
+using Hotline.Snapshot.IRepository;
 using Microsoft.AspNetCore.Http;
 using SqlSugar;
 using System;

+ 10 - 5
src/Hotline.Application/Snapshot/SnapshotOrderApplication.cs

@@ -14,7 +14,6 @@ using Hotline.Share.Enums.Order;
 using Hotline.Share.Enums.Snapshot;
 using Hotline.Share.Tools;
 using Hotline.Snapshot;
-using Hotline.Snapshot.Interfaces;
 using Hotline.Tools;
 using Mapster;
 using Novacode.NETCorePort;
@@ -31,6 +30,8 @@ using XF.Domain.Authentications;
 using XF.Domain.Dependency;
 using XF.Domain.Exceptions;
 using XF.Utility.EnumExtensions;
+using Hotline.Snapshot.IRepository;
+using Hotline.Application.Snapshot.Contracts;
 
 namespace Hotline.Application.Snapshot;
 public class SnapshotOrderApplication : IOrderSnapshotApplication, IScopeDependency
@@ -71,6 +72,9 @@ public class SnapshotOrderApplication : IOrderSnapshotApplication, IScopeDepende
     public async Task<string> AddOrderPublishAsync(AddSnapshotOrderPublishInDto dto, CancellationToken cancellation)
     {
         dto.ValidateObject();
+        var oldPublish = await _snapshotOrderPublishRepository.Queryable().Where(m => m.OrderId == dto.OrderId).FirstAsync(cancellation);
+        if (oldPublish != null)
+            await _snapshotOrderPublishRepository.RemoveAsync(oldPublish,cancellationToken: cancellation);
         var snapshotOrder = await _orderSnapshotRepository.GetAsync(dto.OrderId)
             ?? throw UserFriendlyException.SameMessage("工单不存在");
         var order = await _orderRepository.Queryable()
@@ -188,7 +192,7 @@ public class SnapshotOrderApplication : IOrderSnapshotApplication, IScopeDepende
             .WhereIF(dto.No.NotNullOrEmpty(), (snapshot, order) => order.No.Contains(dto.No))
             .WhereIF(dto.OrderStatus.HasValue, (snapshot, order) => order.Status == dto.OrderStatus)
             .WhereIF(dto.Title.NotNullOrEmpty(), (snapshot, order) => order.Title.Contains(dto.Title))
-            //.Where((snapshot, order, publish) => order.Status == EOrderStatus.Filed)
+            .Where((snapshot, order, publish) => order.Status >= EOrderStatus.Filed)
             .OrderByDescending((snapshot, order, publish) => new { publish.Status, snapshot.CreationTime })
             .Select((snapshot, order, publish) =>
             new GetOrderSnapshotPublishItemsOutDto
@@ -210,9 +214,10 @@ public class SnapshotOrderApplication : IOrderSnapshotApplication, IScopeDepende
         var query = _orderSnapshotRepository.Queryable()
             .LeftJoin<Order>((snapshot, order) => snapshot.Id == order.Id)
             .LeftJoin<WorkflowStep>((snapshot, order, step) => step.ExternalId == order.Id)
-            .WhereIF(dto.Status == 0, (snapshot, order, step) => step.Tag == TagDefaults.OrderMark) // 全部
-            .WhereIF(dto.Status == 1, (snapshot, order, step) => step.Tag == TagDefaults.OrderMark && step.Status != EWorkflowStepStatus.Handled && step.HandlerId == _sessionContext.UserId) // 待标记
-            .WhereIF(dto.Status == 2, (snapshot, order, step) => step.Tag == TagDefaults.OrderMark && step.Status == EWorkflowStepStatus.Handled) // 已标记
+            .Where((snapshot, order, step) => snapshot.IndustryName == "安全隐患")
+            //.WhereIF(dto.Status == 0, (snapshot, order, step) => step.Tag == TagDefaults.OrderMark) // 全部
+            .WhereIF(dto.Status == 1, (snapshot, order, step) => step.Tag == TagDefaults.OrderMark && step.Status != EWorkflowStepStatus.Handled &&  _sessionContext.Roles.Contains(step.RoleId)) // 待标记
+            .WhereIF(dto.Status == 2, (snapshot, order, step) => snapshot.IsSafetyDepartment != null) // 已标记
             .WhereIF(dto.No.NotNullOrEmpty(), (snapshot, order, step) => order.No.Contains(dto.No))
             .WhereIF(dto.Title.NotNullOrEmpty(), (snapshot, order, step) => order.Title.Contains(dto.Title))
             .OrderByDescending((snapshot, order, step) => snapshot.CreationTime)

+ 63 - 0
src/Hotline.Application/Snapshot/SnapshotPointsApplication.cs

@@ -0,0 +1,63 @@
+using Hotline.Application.Snapshot.Contracts;
+using Hotline.Orders;
+using Hotline.Share.Dtos.Snapshot;
+using Hotline.Share.Enums.CallCenter;
+using Hotline.Share.Enums.Snapshot;
+using Hotline.Share.Tools;
+using Hotline.Snapshot.IRepository;
+using SqlSugar;
+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 SnapshotPointsApplication : ISnapshotPointsApplication, IScopeDependency
+{
+    private readonly ISnapshotPointsRecordRepository _snapshotPointsRecordRepository;
+    private readonly ICitizenRepository _citizenRepository;
+
+    public SnapshotPointsApplication(ISnapshotPointsRecordRepository snapshotPointsRecordRepository, ICitizenRepository citizenRepository)
+    {
+        _snapshotPointsRecordRepository = snapshotPointsRecordRepository;
+        _citizenRepository = citizenRepository;
+    }
+
+    public ISugarQueryable<PointsItemsOutDto> GetPointsItems(PointsItemsInDto dto)
+    {
+        var query = _snapshotPointsRecordRepository.Queryable()
+            .LeftJoin<Citizen>((points, citizen) => points.UserId == citizen.Id)
+            .WhereIF(dto.PhoneNumber.NotNullOrEmpty(), (points, citizen) => citizen.PhoneNumber.Contains(dto.PhoneNumber!))
+            .Where((points, citizen) => points.CreationTime >= dto.StartTime && points.CreationTime <= dto.EndTime)
+            .GroupBy((points, citizen) => new { points.UserId, citizen.IsSecurityMax, citizen.Name, citizen.PhoneNumber })
+            .Select((points, citizen) => new PointsItemsOutDto
+            {
+                IsSecurityMax = citizen.IsSecurityMax ?? false,
+                Rank = SqlFunc.MappingColumn<int>("DENSE_RANK() OVER (ORDER BY SUM(CASE WHEN \"points\".\"Direction\" = 1 THEN \"points\".\"Points\" ELSE 0 END) DESC)"),
+                OutTotalPoint = SqlFunc.AggregateSum(SqlFunc.IIF(points.Direction == EPointsDirection.Out, points.Points, 0)),
+                OutPoints = SqlFunc.AggregateSum(SqlFunc.IIF(points.Direction == EPointsDirection.Out && points.Source == EPointsSource.Audit, points.Points, 0)),
+                TotalPoints = SqlFunc.AggregateSum(points.Points),
+                InTotalPoint = SqlFunc.AggregateSum(SqlFunc.IIF(points.Direction == EPointsDirection.In, points.Points, 0)),
+                UserName = citizen.Name!,
+                PhoneNumber = citizen.PhoneNumber,
+                UserId = points.UserId,
+            })
+            .OrderByPropertyNameIF(dto.SortField.NotNullOrEmpty() && dto.SortRule == 0, dto.SortField, OrderByType.Desc)
+            .OrderByPropertyNameIF(dto.SortField.NotNullOrEmpty() && dto.SortRule == 1, dto.SortField, OrderByType.Asc);
+#if DEBUG
+        var sql = query.ToSqlString();
+#endif
+        return query;
+    }
+
+    public async Task UpdateIsSecurityMaxAsync(UpdateIsSecurityMaxAsync dto, CancellationToken token)
+    {
+        await _citizenRepository.Updateable()
+            .SetColumns(citizen => citizen.IsSecurityMax, dto.IsSecurityMax)
+            .Where(citizen => citizen.Id == dto.UserId)
+            .ExecuteCommandAsync(token);
+    }
+}

+ 1 - 1
src/Hotline.Application/Snapshot/SnapshotThirdAccountSupplier.cs

@@ -7,7 +7,7 @@ using Hotline.Share.Enums.Snapshot;
 using Hotline.Share.Enums.User;
 using Hotline.Share.Tools;
 using Hotline.Snapshot;
-using Hotline.Snapshot.Interfaces;
+using Hotline.Snapshot.IRepository;
 using Hotline.ThirdAccountDomainServices;
 using Hotline.ThirdAccountDomainServices.Interfaces;
 using IdentityModel;

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 2 - 1
src/Hotline.Application/Snapshot/ZiGongSnapshotApplication.cs


+ 13 - 10
src/Hotline.Application/StatisticalReport/CallReport/CallReportApplicationBase.cs

@@ -59,15 +59,15 @@ public abstract class CallReportApplicationBase : ICallReportApplication
         return await _callNativeRepository.GetCallHourList(dto.StartTime, dto.EndTime, noConnectByeTimes, effectiveTimes, connectByeTimes, dto.Source);
     }
 
-	public virtual async Task<TotalData<BiSeatSwitchDto>> GetCallListAsync(QueryCallListDto dto, CancellationToken requestAborted)
-	{
-		int noConnectByeTimes = _systemSettingCacheManager.NoConnectByeTimes;
-		int effectiveTimes = _systemSettingCacheManager.EffectiveTimes;
-		int connectByeTimes = _systemSettingCacheManager.ConnectByeTimes;
-		return await _callNativeRepository.GetCallList(dto, noConnectByeTimes, effectiveTimes, connectByeTimes);
-	}
+    public virtual async Task<TotalData<BiSeatSwitchDto>> GetCallListAsync(QueryCallListDto dto, CancellationToken requestAborted)
+    {
+        int noConnectByeTimes = _systemSettingCacheManager.NoConnectByeTimes;
+        int effectiveTimes = _systemSettingCacheManager.EffectiveTimes;
+        int connectByeTimes = _systemSettingCacheManager.ConnectByeTimes;
+        return await _callNativeRepository.GetCallList(dto, noConnectByeTimes, effectiveTimes, connectByeTimes);
+    }
 
-	public virtual async Task<List<QueryCallDateStatisticsDetailResp>> QueryCallDateStatisticsDetail(QueryCallDateStatisticsDetailDto dto)
+    public virtual async Task<List<QueryCallDateStatisticsDetailResp>> QueryCallDateStatisticsDetail(QueryCallDateStatisticsDetailDto dto)
     {
         throw new NotImplementedException();
     }
@@ -233,7 +233,7 @@ public abstract class CallReportApplicationBase : ICallReportApplication
         if (dto.FieldName == "inconnectionquantity") // 呼入接通
             query = query.Where((c, o) => c.Direction == ECallDirection.In && c.AnsweredTime != null);
         if (dto.FieldName == "innotanswered") // 挂机量
-            query = query.Where((c, o) => c.Direction == ECallDirection.In && c.Duration ==0 && c.TelNo != "0");
+            query = query.Where((c, o) => c.Direction == ECallDirection.In && c.Duration == 0 && c.TelNo != "0");
         if (dto.FieldName == "notacceptedhang") // 呼入队列挂断
             query = query.Where((c, o) => c.Duration == 0 && c.RingDuration <= noConnectByeTimes && c.RingDuration > 0 && c.Direction == ECallDirection.In);
         if (dto.FieldName == "ivrbyecount") // 呼入IVR挂断
@@ -277,11 +277,14 @@ public abstract class CallReportApplicationBase : ICallReportApplication
         int noConnectByeTimes = _systemSettingCacheManager.NoConnectByeTimes;
         int effectiveTimes = _systemSettingCacheManager.EffectiveTimes;
         int connectByeTimes = _systemSettingCacheManager.ConnectByeTimes;
+        var setting = _systemSettingCacheManager.GetSetting(SettingConstants.RoleZuoXi).SettingValue;
 
         var list = await _userRepository.Queryable()
+              .Includes(u => u.Roles)
               .LeftJoin<CallNative>((u, c) => u.Id == c.UserId)
+              .Where((u, c) => u.Roles.Any(x => setting.Contains(x.Name)))
               .Where((u, c) => c.CallState != ECallState.Invalid)
-              .Where(u => !u.IsDeleted && u.UserType == EUserType.Seat)
+              .Where(u => !u.IsDeleted)
               .Where((u, c) => c.BeginIvrTime >= dto.StartTime)
               .Where((u, c) => c.BeginIvrTime <= dto.EndTime)
               .GroupBy((u, c) => new { c.UserName, c.UserId })

+ 26 - 20
src/Hotline.Application/StatisticalReport/CallReport/YiBinCallReportApplication.cs

@@ -147,10 +147,13 @@ public class YiBinCallReportApplication : CallReportApplicationBase, ICallReport
         int effectiveTimes = _systemSettingCacheManager.EffectiveTimes;
         int connectByeTimes = _systemSettingCacheManager.ConnectByeTimes;
         int ringTims = _systemSettingCacheManager.RingTimes;
+        var setting = _systemSettingCacheManager.GetSetting(SettingConstants.RoleZuoXi).SettingValue;
 
         var list = await _userRepository.Queryable()
+           .Includes(u => u.Roles)
               .LeftJoin<TrCallRecord>((u, c) => u.Id == c.UserId)
-              .Where(u => !u.IsDeleted && u.UserType == EUserType.Seat)
+              .Where((u, c) => u.Roles.Any(x => setting.Contains(x.Name)))
+              .Where(u => !u.IsDeleted)
               .Where((u, c) => c.CreatedTime >= dto.StartTime)
               .Where((u, c) => c.CreatedTime <= dto.EndTime)
               .GroupBy((u, c) => new { c.UserName, c.UserId })
@@ -163,7 +166,7 @@ public class YiBinCallReportApplication : CallReportApplicationBase, ICallReport
                   InAnswered = SqlFunc.AggregateSum(SqlFunc.IIF(c.CallDirection == ECallDirection.In && c.AnsweredTime != null, 1, 0)),
                   OutAnswered = SqlFunc.AggregateSum(SqlFunc.IIF(c.CallDirection == ECallDirection.Out && c.AnsweredTime != null, 1, 0)),
                   InHangupImmediate = SqlFunc.AggregateSum(SqlFunc.IIF(c.CallDirection == ECallDirection.In && c.AnsweredTime == null && c.RingTimes < noConnectByeTimes, 1, 0)),
-                  InHanguped = SqlFunc.AggregateSum(SqlFunc.IIF(c.CallDirection == ECallDirection.In && c.AnsweredTime == null && c.RingTimes> ringTims, 1, 0)),
+                  InHanguped = SqlFunc.AggregateSum(SqlFunc.IIF(c.CallDirection == ECallDirection.In && c.AnsweredTime == null && c.RingTimes > ringTims, 1, 0)),
                   InDurationAvg = SqlFunc.Ceil(SqlFunc.AggregateAvg(SqlFunc.IIF(c.CallDirection == ECallDirection.In && c.AnsweredTime != null, c.Duration, 0))),
                   OutDurationAvg = SqlFunc.Ceil(SqlFunc.AggregateAvg(SqlFunc.IIF(c.CallDirection == ECallDirection.Out && c.AnsweredTime != null, c.Duration, 0))),
                   InAvailableAnswer = SqlFunc.AggregateSum(SqlFunc.IIF(c.CallDirection == ECallDirection.In && c.AnsweredTime != null && c.Duration >= effectiveTimes, 1, 0)),
@@ -259,7 +262,7 @@ public class YiBinCallReportApplication : CallReportApplicationBase, ICallReport
     /// <returns></returns>
     public override async Task<List<QueryCallDateStatisticsDetailResp>> QueryCallDateStatisticsDetail(QueryCallDateStatisticsDetailDto dto)
     {
-        return await _trCallRecordRepositoryEx.QueryCallDateStatisticsDetail(dto.StartTime.Value,dto.EndTime.Value);
+        return await _trCallRecordRepositoryEx.QueryCallDateStatisticsDetail(dto.StartTime.Value, dto.EndTime.Value);
     }
 
     /// <summary>
@@ -287,9 +290,9 @@ public class YiBinCallReportApplication : CallReportApplicationBase, ICallReport
     /// </summary>
     /// <param name="dto"></param>
     /// <returns></returns>
-    public override async Task<List<QueryCallOutDateStatisticsDetailResp>> QueryCallOutDateStatisticsDetail(QueryCallDateStatisticsDetailDto dto,List<string> enterpriseTels)
+    public override async Task<List<QueryCallOutDateStatisticsDetailResp>> QueryCallOutDateStatisticsDetail(QueryCallDateStatisticsDetailDto dto, List<string> enterpriseTels)
     {
-        return await _trCallRecordRepositoryEx.QueryCallOutDateStatisticsDetail(dto.StartTime.Value, dto.EndTime.Value,enterpriseTels);
+        return await _trCallRecordRepositoryEx.QueryCallOutDateStatisticsDetail(dto.StartTime.Value, dto.EndTime.Value, enterpriseTels);
     }
 
     public override async Task<List<QuerySeatMonthCallResp>> QuerySeatMonthCall(QuerySeatMonthCallRequest dto)
@@ -299,10 +302,13 @@ public class YiBinCallReportApplication : CallReportApplicationBase, ICallReport
         int effectiveTimes = _systemSettingCacheManager.EffectiveTimes;
         int connectByeTimes = _systemSettingCacheManager.ConnectByeTimes;
         int ringTimes = _systemSettingCacheManager.RingTimes;
+        var setting = _systemSettingCacheManager.GetSetting(SettingConstants.RoleZuoXi).SettingValue;
 
         var list = await _userRepository.Queryable()
+              .Includes(u => u.Roles)
               .LeftJoin<TrCallRecord>((u, c) => u.Id == c.UserId)
-              .Where(u => !u.IsDeleted && u.UserType == EUserType.Seat)
+              .Where((u, c) => u.Roles.Any(x => setting.Contains(x.Name)))
+              .Where(u => !u.IsDeleted)
               .Where((u, c) => c.BeginRingTime >= dto.StartTime)
               .Where((u, c) => c.BeginRingTime <= dto.EndTime)
               .Where((u, c) => c.CallDirection == ECallDirection.In)
@@ -336,35 +342,35 @@ public class YiBinCallReportApplication : CallReportApplicationBase, ICallReport
         int connectByeTimes = _systemSettingCacheManager.ConnectByeTimes;
         int ringTimes = _systemSettingCacheManager.RingTimes;
 
-        return  _trCallRecordRepository.Queryable()
+        return _trCallRecordRepository.Queryable()
             .WhereIF(dto.RingStartTime.HasValue, x => x.BeginRingTime >= dto.RingStartTime)
             .WhereIF(dto.RingEndTime.HasValue, x => x.BeginRingTime <= dto.RingEndTime)
-            .WhereIF(dto.EndRingStartTime.HasValue,x=>x.EndRingTimg >= dto.EndRingStartTime)
-            .WhereIF(dto.EndRingEndTime.HasValue,x=>x.EndRingTimg <= dto.EndRingEndTime)
+            .WhereIF(dto.EndRingStartTime.HasValue, x => x.EndRingTimg >= dto.EndRingStartTime)
+            .WhereIF(dto.EndRingEndTime.HasValue, x => x.EndRingTimg <= dto.EndRingEndTime)
             .WhereIF(!string.IsNullOrEmpty(dto.EmpId), x => x.UserId == dto.EmpId)
             .WhereIF(dto.AnsweredStartTime.HasValue, x => x.AnsweredTime >= dto.AnsweredStartTime)
             .WhereIF(dto.AnsweredEndTime.HasValue, x => x.AnsweredTime <= dto.AnsweredEndTime)
             .WhereIF(!string.IsNullOrEmpty(dto.TelNo), x => x.TelNo.Contains(dto.TelNo!))
-            .WhereIF(!string.IsNullOrEmpty(dto.Cpn),x=>x.CPN.Contains(dto.Cpn!))
+            .WhereIF(!string.IsNullOrEmpty(dto.Cpn), x => x.CPN.Contains(dto.Cpn!))
             .WhereIF(!string.IsNullOrEmpty(dto.Cdpn), x => x.CDPN.Contains(dto.Cdpn!))
-            .WhereIF(dto.QueryType.HasValue && dto.QueryType == 1,x=>x.AnsweredTime != null) //呼入接通总量
-            .WhereIF(dto.QueryType.HasValue && dto.QueryType == 2,x=> x.AnsweredTime != null && x.Duration >= effectiveTimes) //有效接通量
-            .WhereIF(dto.QueryType.HasValue && dto.QueryType == 3,x=> x.AnsweredTime != null && x.Duration < connectByeTimes) //接通秒挂量
-            .WhereIF(dto.QueryType.HasValue && dto.QueryType == 4,x=> x.AnsweredTime != null && x.RingTimes > ringTimes) //超时接通量
-            .WhereIF(dto.QueryType.HasValue && dto.QueryType == 5,x=> x.AnsweredTime != null && x.RingTimes <= ringTimes) //按时接通量
-            .WhereIF(dto.QueryType.HasValue && dto.QueryType == 6,x=> x.AnsweredTime == null) //呼入未接通量
-            .WhereIF(dto.QueryType.HasValue && dto.QueryType == 7,x=> x.AnsweredTime == null && x.RingTimes <= noConnectByeTimes) //未接通秒挂量
-            .WhereIF(dto.QueryType.HasValue && dto.QueryType == 8,x=> x.AnsweredTime == null && x.RingTimes > ringTimes) //超时未接通量
+            .WhereIF(dto.QueryType.HasValue && dto.QueryType == 1, x => x.AnsweredTime != null) //呼入接通总量
+            .WhereIF(dto.QueryType.HasValue && dto.QueryType == 2, x => x.AnsweredTime != null && x.Duration >= effectiveTimes) //有效接通量
+            .WhereIF(dto.QueryType.HasValue && dto.QueryType == 3, x => x.AnsweredTime != null && x.Duration < connectByeTimes) //接通秒挂量
+            .WhereIF(dto.QueryType.HasValue && dto.QueryType == 4, x => x.AnsweredTime != null && x.RingTimes > ringTimes) //超时接通量
+            .WhereIF(dto.QueryType.HasValue && dto.QueryType == 5, x => x.AnsweredTime != null && x.RingTimes <= ringTimes) //按时接通量
+            .WhereIF(dto.QueryType.HasValue && dto.QueryType == 6, x => x.AnsweredTime == null) //呼入未接通量
+            .WhereIF(dto.QueryType.HasValue && dto.QueryType == 7, x => x.AnsweredTime == null && x.RingTimes <= noConnectByeTimes) //未接通秒挂量
+            .WhereIF(dto.QueryType.HasValue && dto.QueryType == 8, x => x.AnsweredTime == null && x.RingTimes > ringTimes) //超时未接通量
             .Where(x => x.CallDirection == ECallDirection.In)
             .OrderBy(x => x.BeginRingTime)
             .MergeTable()
             .Select(x => new QuerySeatMonthCallDetailResp
-            { 
+            {
                 Cpn = x.CPN,
                 Cdpn = x.CDPN,
                 TelNo = x.TelNo,
                 AnsweredTime = x.AnsweredTime,
-                RingTimeBegin  = x.BeginRingTime,
+                RingTimeBegin = x.BeginRingTime,
                 RingTimeEnd = x.EndRingTimg,
                 SeatName = x.UserName
             });

+ 4 - 1
src/Hotline.Application/StatisticalReport/IOrderReportApplication.cs

@@ -221,5 +221,8 @@ namespace Hotline.Application.StatisticalReport
         /// <param name="dto"></param>
         /// <returns></returns>
         ISugarQueryable<OrderDelayStatisicalReturnDto> OrderDelayStatisicalList(OrderDelayStatisicalRequestDto dto);
-    }
+
+        Task<DataTable> HotspotStatisticsExprot_LZ(ExportExcelDto<HotspotStatisticsRep> dto);
+
+	}
 }

+ 69 - 25
src/Hotline.Application/StatisticalReport/OrderReportApplication.cs

@@ -19,6 +19,7 @@ using Hotline.Share.Enums.Order;
 using Hotline.Share.Requests;
 using Hotline.Share.Tools;
 using Hotline.Statistics;
+using Hotline.Tools;
 using Hotline.Users;
 using MapsterMapper;
 using Microsoft.AspNetCore.Mvc;
@@ -51,27 +52,28 @@ namespace Hotline.Application.StatisticalReport
         private readonly IRepository<SystemOrganize> _systemOrganizerepository;
         private readonly IRepository<OrderSpecial> _orderSpecialRepository;
         private readonly IRepository<OrderSendBackAudit> _orderSendBackAuditRepository;
-
-        /// <summary>
-        /// 
-        /// </summary>
-        /// <param name="orderRepository"></param>
-        /// <param name="orderVisitDetailRepository"></param>
-        /// <param name="orderDelayRepository"></param>
-        /// <param name="mapper"></param>
-        /// <param name="orderPublishRepository"></param>
-        /// <param name="sessionContext"></param>
-        /// <param name="workflowTraceRepository"></param>
-        /// <param name="orderScreenRepository"></param>
-        /// <param name="sysDicDataCacheManager"></param>
-        /// <param name="trCallRecordRepository"></param>
-        /// <param name="systemSettingCacheManager"></param>
-        /// <param name="userRepository"></param>
-        /// <param name="accountRepository"></param>
-        /// <param name="statisticsDepartRepository"></param>
-        /// <param name="systemOrganizerepository"></param>
-        /// <param name="orderSpecialRepository"></param>
-        public OrderReportApplication(
+		private readonly IRepository<Hotspot> _hotspotTypeRepository;
+
+		/// <summary>
+		/// 
+		/// </summary>
+		/// <param name="orderRepository"></param>
+		/// <param name="orderVisitDetailRepository"></param>
+		/// <param name="orderDelayRepository"></param>
+		/// <param name="mapper"></param>
+		/// <param name="orderPublishRepository"></param>
+		/// <param name="sessionContext"></param>
+		/// <param name="workflowTraceRepository"></param>
+		/// <param name="orderScreenRepository"></param>
+		/// <param name="sysDicDataCacheManager"></param>
+		/// <param name="trCallRecordRepository"></param>
+		/// <param name="systemSettingCacheManager"></param>
+		/// <param name="userRepository"></param>
+		/// <param name="accountRepository"></param>
+		/// <param name="statisticsDepartRepository"></param>
+		/// <param name="systemOrganizerepository"></param>
+		/// <param name="orderSpecialRepository"></param>
+		public OrderReportApplication(
             IOrderRepository orderRepository,
             IRepository<OrderVisitDetail> orderVisitDetailRepository,
             IRepository<OrderDelay> orderDelayRepository,
@@ -88,8 +90,9 @@ namespace Hotline.Application.StatisticalReport
             IRepository<StatisticsDepart> statisticsDepartRepository,
             IRepository<SystemOrganize> systemOrganizerepository,
              IRepository<OrderSpecial> orderSpecialRepository,
-             IRepository<OrderSendBackAudit> orderSendBackAuditRepository
-            )
+             IRepository<OrderSendBackAudit> orderSendBackAuditRepository,
+			IRepository<Hotspot> hotspotTypeRepository
+			)
         {
             _orderRepository = orderRepository;
             _orderVisitDetailRepository = orderVisitDetailRepository;
@@ -108,7 +111,9 @@ namespace Hotline.Application.StatisticalReport
             _systemOrganizerepository = systemOrganizerepository;
             _orderSpecialRepository = orderSpecialRepository;
             _orderSendBackAuditRepository = orderSendBackAuditRepository;
-        }
+            _hotspotTypeRepository = hotspotTypeRepository;
+
+		}
 
         #region 宜宾、自贡
         /// <summary>
@@ -2997,5 +3002,44 @@ namespace Hotline.Application.StatisticalReport
                     });
             return query;
         }
-    }
+
+		public async Task<DataTable> HotspotStatisticsExprot_LZ(ExportExcelDto<HotspotStatisticsRep> dto)
+		{
+			var IsCenter = _sessionContext.OrgIsCenter;
+			DataTable data = new DataTable();
+
+			data = await _hotspotTypeRepository.Queryable()
+			.LeftJoin<Order>((it, o) => o.HotspotId.StartsWith(it.Id))
+			.Where((it, o) => o.CreationTime >= dto.QueryDto.StartTime && o.CreationTime <= dto.QueryDto.EndTime && o.Id != null)
+			.WhereIF(dto.QueryDto.TypeId == 1, (it, o) => o.IdentityType == EIdentityType.Citizen)
+			.WhereIF(dto.QueryDto.TypeId == 2, (it, o) => o.IdentityType == EIdentityType.Enterprise)
+			.WhereIF(IsCenter == false, (it, o) => o.ActualHandleOrgCode.StartsWith(_sessionContext.RequiredOrgId))
+            .GroupBy((it, o) => it.Id)
+			.OrderBy((it, o) => new { it.Id }, OrderByType.Asc)
+			.Select((it, o) => new
+			{
+				HotspotName = it.HotSpotName,
+				HotSpotFullName = it.HotSpotFullName,
+				SumCount = SqlFunc.AggregateSum(SqlFunc.IIF(o.HotspotId.StartsWith(it.Id), 1, 0))
+			})
+			.ToDataTableAsync();
+
+			data.Columns["HotspotName"].SetOrdinal(0);
+			data.Columns["HotSpotFullName"].SetOrdinal(1);
+			data.Columns["SumCount"].ColumnName = "分类统计";
+			data.Columns["HotspotName"].ColumnName = "热点名称";
+			data.Columns["HotSpotFullName"].ColumnName = "热点分级";
+			//合计
+			DataRow sumRow = data.NewRow();
+			sumRow["热点名称"] = "合计";
+			decimal totalAmount = 0;
+			foreach (DataRow row in data.Rows)
+			{
+				totalAmount += Convert.ToDecimal(row["分类统计"]);
+			}
+			sumRow["分类统计"] = totalAmount;
+			data.Rows.Add(sumRow);
+            return data;
+		}
+	}
 }

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

@@ -248,7 +248,6 @@ namespace Hotline.Application.Subscribers
         [CapSubscribe(Hotline.Share.Mq.EventNames.SharingOrderRevoke)]
         public async Task RecCancelOrderAsync(CancelOrderDto dto, CancellationToken cancellationToken)
         {
-            //  var order = await _orderRepository.GetAsync(p => p.ReceiveProvinceNo == dto.ProvinceNo, cancellationToken);
             var order = await _orderRepository.Queryable().Includes(d => d.Workflow).FirstAsync(p => p.ReceiveProvinceNo == dto.ProvinceNo);
             if (order is null)
                 throw new UserFriendlyException("未查询到工单");

+ 8 - 2
src/Hotline.Repository.SqlSugar/File/FileRepository.cs

@@ -91,9 +91,15 @@ namespace Hotline.Repository.SqlSugar.File
 			return newFiles.Select(x=> new FileJson { Id = x.Id,FileId = x.Additions,Path = x.Path,FileName =x.Name ,FileType =x.Type}).ToList();
 		}
 
-		public async Task<List<FileDto>> GetFilesAsync(List<string> ids, CancellationToken cancellationToken) 
+		public async Task<List<FileDto>> GetFilesAsync(List<string> ids, CancellationToken cancellationToken = default) 
 		{
-			var files = await Queryable().In(x=>x.Id,ids).ToListAsync(cancellationToken);
+			var files = await Queryable().In(x => x.Id, ids).ToListAsync(cancellationToken);
+			return files.Any() ? _mapper.Map<List<FileDto>>(files) : new List<FileDto>();
+		}
+
+		public async Task<List<FileDto>> GetPermissionFilesAsync(List<string> ids, CancellationToken cancellationToken = default)
+		{
+			var files  = await Queryable().In(x => x.Id, ids).Where(x => x.CreatorOrgId.StartsWith(_sessionContext.RequiredOrgId)).ToListAsync(cancellationToken);
 			return files.Any() ? _mapper.Map<List<FileDto>>(files) : new List<FileDto>();
 		}
 

+ 1 - 1
src/Hotline.Repository.SqlSugar/Snapshot/CommunityInfoRepository.cs

@@ -1,6 +1,6 @@
 using Hotline.Repository.SqlSugar.DataPermissions;
 using Hotline.Snapshot;
-using Hotline.Snapshot.Interfaces;
+using Hotline.Snapshot.IRepository;
 using SqlSugar;
 using System;
 using System.Collections.Generic;

+ 1 - 1
src/Hotline.Repository.SqlSugar/Snapshot/IndustryCaseRepository.cs

@@ -1,6 +1,6 @@
 using Hotline.Repository.SqlSugar.DataPermissions;
 using Hotline.Snapshot;
-using Hotline.Snapshot.Interfaces;
+using Hotline.Snapshot.IRepository;
 using SqlSugar;
 using System;
 using System.Collections.Generic;

+ 1 - 1
src/Hotline.Repository.SqlSugar/Snapshot/IndustryLogRepository.cs

@@ -1,6 +1,6 @@
 using Hotline.Repository.SqlSugar.DataPermissions;
 using Hotline.Snapshot;
-using Hotline.Snapshot.Interfaces;
+using Hotline.Snapshot.IRepository;
 using SqlSugar;
 using System;
 using System.Collections.Generic;

+ 1 - 1
src/Hotline.Repository.SqlSugar/Snapshot/IndustryRepository.cs

@@ -1,7 +1,7 @@
 using Hotline.Orders;
 using Hotline.Repository.SqlSugar.DataPermissions;
 using Hotline.Snapshot;
-using Hotline.Snapshot.Interfaces;
+using Hotline.Snapshot.IRepository;
 using SqlSugar;
 using System;
 using System.Collections.Generic;

+ 1 - 1
src/Hotline.Repository.SqlSugar/Snapshot/InviteCodeRecordRepository.cs

@@ -1,6 +1,6 @@
 using Hotline.Repository.SqlSugar.DataPermissions;
 using Hotline.Snapshot;
-using Hotline.Snapshot.Interfaces;
+using Hotline.Snapshot.IRepository;
 using SqlSugar;
 using System;
 using System.Collections.Generic;

+ 2 - 2
src/Hotline.Repository.SqlSugar/Snapshot/InviteCodeRepository.cs

@@ -1,5 +1,4 @@
-using Hotline.Snapshot.Interfaces;
-using Hotline.Snapshot;
+using Hotline.Snapshot;
 using System;
 using System.Collections.Generic;
 using System.Linq;
@@ -8,6 +7,7 @@ using System.Threading.Tasks;
 using XF.Domain.Dependency;
 using SqlSugar;
 using Hotline.Repository.SqlSugar.DataPermissions;
+using Hotline.Snapshot.IRepository;
 
 namespace Hotline.Repository.SqlSugar.Snapshot;
 public class InviteCodeRepository : BaseRepository<InviteCode>, IInviteCodeRepository, IScopeDependency

+ 1 - 1
src/Hotline.Repository.SqlSugar/Snapshot/OrderSnapshotRepository.cs

@@ -2,7 +2,7 @@
 using Hotline.Share.Dtos;
 using Hotline.Share.Tools;
 using Hotline.Snapshot;
-using Hotline.Snapshot.Interfaces;
+using Hotline.Snapshot.IRepository;
 using Microsoft.AspNetCore.Http;
 using SqlSugar;
 using System;

+ 1 - 1
src/Hotline.Repository.SqlSugar/Snapshot/PractitionerRepository.cs

@@ -1,6 +1,6 @@
 using Hotline.Repository.SqlSugar.DataPermissions;
 using Hotline.Snapshot;
-using Hotline.Snapshot.Interfaces;
+using Hotline.Snapshot.IRepository;
 using SqlSugar;
 using System;
 using System.Collections.Generic;

+ 1 - 1
src/Hotline.Repository.SqlSugar/Snapshot/RedPackAuditRepository.cs

@@ -1,7 +1,7 @@
 using Hotline.Repository.SqlSugar.DataPermissions;
 using Hotline.Share.Dtos.FlowEngine.Workflow;
 using Hotline.Snapshot;
-using Hotline.Snapshot.Interfaces;
+using Hotline.Snapshot.IRepository;
 using SqlSugar;
 using System;
 using System.Collections.Generic;

+ 1 - 1
src/Hotline.Repository.SqlSugar/Snapshot/RedPackGuiderAuditRepository.cs

@@ -1,6 +1,6 @@
 using Hotline.Repository.SqlSugar.DataPermissions;
 using Hotline.Snapshot;
-using Hotline.Snapshot.Interfaces;
+using Hotline.Snapshot.IRepository;
 using SqlSugar;
 using System;
 using System.Collections.Generic;

+ 1 - 1
src/Hotline.Repository.SqlSugar/Snapshot/RedPackRecordRepository.cs

@@ -1,6 +1,6 @@
 using Hotline.Repository.SqlSugar.DataPermissions;
 using Hotline.Snapshot;
-using Hotline.Snapshot.Interfaces;
+using Hotline.Snapshot.IRepository;
 using SqlSugar;
 using System;
 using System.Collections.Generic;

+ 1 - 1
src/Hotline.Repository.SqlSugar/Snapshot/SnapshotBulletinRepository.cs

@@ -1,6 +1,6 @@
 using Hotline.Repository.SqlSugar.DataPermissions;
 using Hotline.Snapshot;
-using Hotline.Snapshot.Interfaces;
+using Hotline.Snapshot.IRepository;
 using SqlSugar;
 using System;
 using System.Collections.Generic;

+ 1 - 1
src/Hotline.Repository.SqlSugar/Snapshot/SnapshotLabelLogRepository.cs

@@ -1,6 +1,6 @@
 using Hotline.Repository.SqlSugar.DataPermissions;
 using Hotline.Snapshot;
-using Hotline.Snapshot.Interfaces;
+using Hotline.Snapshot.IRepository;
 using SqlSugar;
 using System;
 using System.Collections.Generic;

+ 1 - 1
src/Hotline.Repository.SqlSugar/Snapshot/SnapshotOrderPublishRepository.cs

@@ -1,6 +1,6 @@
 using Hotline.Repository.SqlSugar.DataPermissions;
 using Hotline.Snapshot;
-using Hotline.Snapshot.Interfaces;
+using Hotline.Snapshot.IRepository;
 using SqlSugar;
 using System;
 using System.Collections.Generic;

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

@@ -0,0 +1,24 @@
+using Hotline.Repository.SqlSugar.DataPermissions;
+using Hotline.Snapshot;
+using Hotline.Snapshot.IRepository;
+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 SnapshotPointsRecordRepository : BaseRepository<SnapshotPointsRecord>, ISnapshotPointsRecordRepository, IScopeDependency
+{
+    public SnapshotPointsRecordRepository(ISugarUnitOfWork<HotlineDbContext> uow, IDataPermissionFilterBuilder dataPermissionFilterBuilder, IServiceProvider serviceProvider) : base(uow, dataPermissionFilterBuilder, serviceProvider)
+    {
+    }
+
+    public async Task<IList<SnapshotPointsRecord>> GetByOrderIdAsync(string orderId, CancellationToken token)
+    {
+        return await Queryable().Where(m => m.OrderId == orderId).ToListAsync(token);
+    }
+}

+ 1 - 1
src/Hotline.Repository.SqlSugar/Snapshot/SnapshotSMSTemplateRepository.cs

@@ -1,6 +1,6 @@
 using Hotline.Repository.SqlSugar.DataPermissions;
 using Hotline.Snapshot;
-using Hotline.Snapshot.Interfaces;
+using Hotline.Snapshot.IRepository;
 using SqlSugar;
 using System;
 using System.Collections.Generic;

+ 1 - 1
src/Hotline.Repository.SqlSugar/Snapshot/SpecialRedPackAuditRepository.cs

@@ -1,6 +1,6 @@
 using Hotline.Repository.SqlSugar.DataPermissions;
 using Hotline.Snapshot;
-using Hotline.Snapshot.Interfaces;
+using Hotline.Snapshot.IRepository;
 using MediatR;
 using SqlSugar;
 using System;

+ 1 - 1
src/Hotline.Repository.SqlSugar/Snapshot/SupplementRecordRepository.cs

@@ -1,6 +1,6 @@
 using Hotline.Repository.SqlSugar.DataPermissions;
 using Hotline.Snapshot;
-using Hotline.Snapshot.Interfaces;
+using Hotline.Snapshot.IRepository;
 using SqlSugar;
 using System;
 using System.Collections.Generic;

+ 1 - 1
src/Hotline.Repository.SqlSugar/Snapshot/VolunteerReportRepository.cs

@@ -1,6 +1,6 @@
 using Hotline.Repository.SqlSugar.DataPermissions;
 using Hotline.Snapshot;
-using Hotline.Snapshot.Interfaces;
+using Hotline.Snapshot.IRepository;
 using SqlSugar;
 using System;
 using System.Collections.Generic;

+ 1 - 1
src/Hotline.Repository.SqlSugar/Snapshot/VolunteerRepository.cs

@@ -2,7 +2,7 @@
 using Hotline.Share.Dtos.FlowEngine.Workflow;
 using Hotline.Share.Tools;
 using Hotline.Snapshot;
-using Hotline.Snapshot.Interfaces;
+using Hotline.Snapshot.IRepository;
 using SqlSugar;
 using System;
 using System.Collections.Generic;

+ 2 - 1
src/Hotline.Share/Dtos/Article/BulletinDto.cs

@@ -560,10 +560,11 @@ namespace Hotline.Share.Dtos.Article
         /// </summary>
         public string Title { get; set; }
 
+        private string content;
         /// <summary>
         /// 内容
         /// </summary>
-        public string Content { get; set; }
+        public string Content { get { return content.RemoveHtmlTags(); } set { content = value; } }
 
         public DateTime CreationTime { get; set; }
     }

+ 89 - 9
src/Hotline.Share/Dtos/CallCenter/CenterReportStatisticsDto.cs

@@ -133,7 +133,23 @@ namespace Hotline.Share.Dtos.CallCenter
         /// 呼入未接通
         /// </summary>
         public int CallInHanguped => InHanguped - QueueByeCount - IvrByeCount;
-    }
+
+		/// <summary>
+		/// 呼入接通率
+		/// </summary>
+		public double InConnectionQuantityRate => GetInConnectionQuantityRate();
+
+		/// <summary>
+		/// 呼入接通率
+		/// </summary>
+		/// <returns></returns>
+		public double GetInConnectionQuantityRate()
+		{
+			if (InTotal > 0 && InConnectionQuantity > 0)
+				return Math.Round(((double)InConnectionQuantity / InTotal) * 100, 2);
+			return 0;
+		}
+	}
 
     /// <summary>
     /// 话务量
@@ -303,6 +319,22 @@ namespace Hotline.Share.Dtos.CallCenter
 		/// </summary>
 		public int CenterCompletedCount { get; set; }
 
+		/// <summary>
+		/// 中心已办结占比
+		/// </summary>
+		public double CenterCompletedCountRate => GetCenterCompletedCountRate();
+
+		/// <summary>
+		/// 中心已办结占比
+		/// </summary>
+		/// <returns></returns>
+		public double GetCenterCompletedCountRate()
+		{
+			if (CenterCompletedCount > 0 && AllCallCount > 0)
+				return Math.Round(((double)CenterCompletedCount / AllCallCount) * 100, 2);
+			return 0;
+		}
+
 		/// <summary>
 		/// 有效
 		/// </summary>
@@ -383,6 +415,23 @@ namespace Hotline.Share.Dtos.CallCenter
 		/// </summary>
 		public int CompletedCount { get; set; }
 
+		/// <summary>
+		/// 已办结占比
+		/// </summary>
+		public double CompletedCountRate => GetCompletedCountRate();
+
+		/// <summary>
+		/// 已办结占比
+		/// </summary>
+		/// <returns></returns>
+		public double GetCompletedCountRate()
+		{
+			if (CompletedCount > 0 && AllCallCount > 0)
+				return Math.Round(((double)CompletedCount / AllCallCount) * 100, 2);
+			return 0;
+		}
+
+
 		/// <summary>
 		/// 按时办结
 		/// </summary>
@@ -409,15 +458,25 @@ namespace Hotline.Share.Dtos.CallCenter
         /// </summary>
         public int OrgCompletedCount { get; set; }
 
-        /// <summary>
-        /// 在办
-        /// </summary>
-        public int InProgressCount { get; set; }
+		/// <summary>
+		/// 已办结总量
+		/// </summary>
+		public int AllCompletedCount => CenterCompletedCount + OrgCompletedCount;
 
-        /// <summary>
-        /// 中心在办
-        /// </summary>
-        public int CenterInProgressCount { get; set; }
+		/// <summary>
+		/// 在办
+		/// </summary>
+		public int InProgressCount { get; set; }
+
+		/// <summary>
+		/// 在办总量
+		/// </summary>
+		public int AllInProgressCount => CenterInProgressCount + OrgInProgressCount;
+
+		/// <summary>
+		/// 中心在办
+		/// </summary>
+		public int CenterInProgressCount { get; set; }
 
         /// <summary>
         /// 部门在办
@@ -436,6 +495,11 @@ namespace Hotline.Share.Dtos.CallCenter
 				return Math.Round(((double)CompletedCount / AllCallCount) * 100, 2);
 			return 0;
 		}
+		 
+		/// <summary>
+		/// 催办总数
+		/// </summary>
+		public int orderUrge { get; set; }
 
 	}
 
@@ -773,6 +837,16 @@ namespace Hotline.Share.Dtos.CallCenter
         /// </summary>
         public string Remark { get; set; }
 
+		/// <summary>
+		/// 占比
+		/// </summary>
+		public double CountNumRate => GetCountNumRate();
+		public double GetCountNumRate()
+		{
+			if (CountNum > 0 && AllCountNum > 0)
+				return Math.Round(((double)CountNum / AllCountNum) * 100, 2);
+			return 0;
+		}
 
 		/// <summary>
 		/// 办理总时长
@@ -872,6 +946,12 @@ namespace Hotline.Share.Dtos.CallCenter
 		/// </summary>
 		public double CenterRate { get; set; }
 
+		/// <summary>
+		/// 满意
+		/// </summary>
+		public int Satisfied { get; set; }
+
+
 		/// <summary>
 		/// 不满意
 		/// </summary>

+ 5 - 2
src/Hotline.Share/Dtos/CallCenter/QueryCallsFixedDto.cs

@@ -23,7 +23,10 @@ namespace Hotline.Share.Dtos.CallCenter
 
         public EEndBy? EndBy { get; set; }
 
-        public string? StaffNo {  get; set; }
+        /// <summary>
+        /// 工号
+        /// </summary>
+        public string? StaffNo { get; set; }
 
         /// <summary>
         /// 呼入时间
@@ -49,7 +52,7 @@ namespace Hotline.Share.Dtos.CallCenter
         /// 等待时长
         /// </summary>
         public int? WaitDurationStart { get; set; }
-        
+
         /// <summary>
         /// 等待时长
         /// </summary>

+ 34 - 4
src/Hotline.Share/Dtos/File/FileDto.cs

@@ -83,8 +83,36 @@ namespace Hotline.Share.Dtos.File
 		/// </summary>
 		public string? AllPath { get; set; }
 
+		/// <summary>
+		/// 仅限制附件列表使用字段
+		/// </summary>
+		public bool IsDelete { get; set; }
+
+		public string? CreatorId { get; set; }
+
+
+		public bool GetIsDelete(string userId)
+		{
+			return Classify == "附件列表上传" && userId == CreatorId;
+		}
+
+		/// <summary>
+		/// 创建时间
+		/// </summary>
+		public DateTime? CreationTime { get; set; }
+
+		/// <summary>
+		/// 节点名称
+		/// </summary>
+		public string? StepName { get; set; }
+
+		public string GetStepName(string name) {
+
+			return StepName = name;
+		}
+
 	}
-	public class UpdateFileDto: FileDto
+	public class UpdateFileDto : FileDto
 	{
 		public string Id { get; set; }
 	}
@@ -139,9 +167,10 @@ namespace Hotline.Share.Dtos.File
 		/// </summary>
 		public string? Path { get; set; }
 	}
-	
-	public class FileJson {
-	
+
+	public class FileJson
+	{
+
 		public string Id { get; set; }
 
 		public string FileName { get; set; }
@@ -158,3 +187,4 @@ namespace Hotline.Share.Dtos.File
 		public string Path { get; set; }
 	}
 }
+

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

@@ -1616,4 +1616,13 @@ namespace Hotline.Share.Dtos.Order
         /// </summary>
         public double Chain => PreviousCount == 0 ? 0 : Math.Round((SumCount / (double)PreviousCount) * 100, 2);
     }
+
+	public class OrgSendBackAuditListVo
+	{
+		public string OrgId { get; set; }
+
+		public string OrgName { get; set; }
+
+		public int Num { get; set; }
+	}
 }

+ 43 - 12
src/Hotline.Share/Dtos/Order/OrderDto.cs

@@ -7,6 +7,7 @@ using Hotline.Share.Enums.FlowEngine;
 using Hotline.Share.Enums.Order;
 using Hotline.Share.Enums.Settings;
 using Hotline.Share.Requests;
+using Hotline.Share.Tools;
 using Novacode;
 using XF.Utility.EnumExtensions;
 
@@ -622,20 +623,20 @@ namespace Hotline.Share.Dtos.Order
         /// </summary>
 		public string ThreeHotspotName => !string.IsNullOrEmpty(HotspotSpliceName) && HotspotSpliceName.Split("-").Length >= 3 ? HotspotSpliceName.Split("-")[2] : string.Empty;
 
-		/// <summary>
-		/// 四级热点
-		/// </summary>
-		public string FourHotspotName => !string.IsNullOrEmpty(HotspotSpliceName) && HotspotSpliceName.Split("-").Length >=4 ? HotspotSpliceName.Split("-")[3] : string.Empty;
+        /// <summary>
+        /// 四级热点
+        /// </summary>
+        public string FourHotspotName => !string.IsNullOrEmpty(HotspotSpliceName) && HotspotSpliceName.Split("-").Length >= 4 ? HotspotSpliceName.Split("-")[3] : string.Empty;
 
-		/// <summary>
-		/// 五级热点
-		/// </summary>
-		public string FiveHotspotName => !string.IsNullOrEmpty(HotspotSpliceName) && HotspotSpliceName.Split("-").Length >= 5 ? HotspotSpliceName.Split("-")[4] : string.Empty;
+        /// <summary>
+        /// 五级热点
+        /// </summary>
+        public string FiveHotspotName => !string.IsNullOrEmpty(HotspotSpliceName) && HotspotSpliceName.Split("-").Length >= 5 ? HotspotSpliceName.Split("-")[4] : string.Empty;
 
-		/// <summary>
-		/// 特提次数
-		/// </summary>
-		public int SpecialNum { get; set; }
+        /// <summary>
+        /// 特提次数
+        /// </summary>
+        public int SpecialNum { get; set; }
 
         private string CalculateExpiredText()
         {
@@ -1071,6 +1072,29 @@ namespace Hotline.Share.Dtos.Order
 
             return overDays;
         }
+
+        /// <summary>
+        /// 工单督办
+        /// </summary>
+        public List<OrderSuperviseDto> OrderSupervises { get; set; }
+
+        /// <summary>
+        /// 督办情况1
+        /// </summary>
+        public string? SuperviseInfo1 => OrderSupervises != null && OrderSupervises.Count > 0 ? OrderSupervises[0].CreationTime.ToString("yyyy-MM-dd HH:mm:ss") + "已通知" + OrderSupervises[0].OrgName : "";
+
+        /// <summary>
+        /// 督办情况2
+        /// </summary>
+        public string? SuperviseInfo2 => OrderSupervises != null && OrderSupervises.Count > 1 ? OrderSupervises[1].CreationTime.ToString("yyyy-MM-dd HH:mm:ss") + "已通知" + OrderSupervises[1].OrgName : "";
+
+        /// <summary>
+        /// 不满意原因
+        /// </summary>
+        public string? orgNoSatisfiedReasonText =>
+            OrderVisits != null && OrderVisits.Count > 0 ?
+            OrderVisits.FirstOrDefault().OrderVisitDetails != null && OrderVisits.FirstOrDefault().OrderVisitDetails.Count > 0 ?
+            OrderVisits.FirstOrDefault().OrderVisitDetails.FirstOrDefault().OrgNoSatisfiedReasonText : "" : "";
     }
 
 
@@ -1860,4 +1884,11 @@ namespace Hotline.Share.Dtos.Order
 
         public string? SensitiveText => (Sensitive != null && Sensitive.Any()) ? string.Join(',', Sensitive) : "";
     }
+
+    public class OrderStepUploadFilesDto
+    {
+        public string OrderId { get; set; }
+        public string StepId { get; set; }
+        public List<FileDto> Files { get; set; } = new();
+    }
 }

+ 172 - 165
src/Hotline.Share/Dtos/Order/OrderSuperviseDto.cs

@@ -8,170 +8,177 @@ using System.Threading.Tasks;
 
 namespace Hotline.Share.Dtos.Order
 {
-	public class OrderSuperviseDto: SuperviseOrderDto
-	{
-		/// <summary>
-		/// 附件列表
-		/// </summary>
-		public List<FileDto> Files { get; set; } = new();
-
-		/// <summary>
-		/// 附件列表
-		/// </summary>
-		public List<FileDto> ReplyFiles { get; set; } = new();
-	}
-
-	public class ApplyOrderSuperviseDto 
-	{
-		/// <summary>
-		/// 工单ID
-		/// </summary>
-		public string OrderId { get; set; }
-
-		public List<ApplyOrderSuperviseOrgDto> SuperviseOrgDtos { get; set; }
-
-		/// <summary>
-		/// 督办回复时限
-		/// </summary>
-		public DateTime ReplyLimitTime { get; set; }
-
-		/// <summary>
-		/// 督办申请内容
-		/// </summary>
-		public string ApplyContent { get; set; }
-
-		/// <summary>
-		/// 是否接受短信,勾选校验手机号
-		/// </summary>
-		public bool AcceptSms { get; set; }
-
-		/// <summary>
-		/// 督办省编号
-		/// </summary>
-		public string? SUPERVISE_SERIAL { get; set; }
-
-		/// <summary>
-		/// 附件列表
-		/// </summary>
-		public List<FileDto> Files { get; set; } = new();
-	}
-	public class ReplyOrderSuperviseDto 
-	{
-
-		public string Id { get; set; }
-
-		/// <summary>
-		/// 督办回复内容
-		/// </summary>
-		public string ReplyContent { get; set; }
-
-
-		/// <summary>
-		/// 回复人
-		/// </summary>
-		public string? ReplyId { get; set; }
-
-		/// <summary>
-		/// 附件列表
-		/// </summary>
-		public List<FileDto> Files { get; set; } = new();
-	}
-
-	public class SignOrderSuperviseDto 
-	{
-		public string Id { get; set; }
-	}
-
-	public class ApplyOrderSuperviseOrgDto {
-		/// <summary>
-		/// 被督办部门ID
-		/// </summary>
-		public string OrgId { get; set; }
-
-		/// <summary>
-		/// 被督办部门名称
-		/// </summary>
-		public string OrgName { get; set; }
-
-	}
-
-
-	public class ProvinceOrderSuperviseDto
-	{
-		/// <summary>
-		/// 督办编号
-		/// </summary>
-		public string SuperviseSerial { get; set; }
-
-		/// <summary>
-		/// 省工单编号
-		/// </summary>
-		public string CaseSerial { get; set; }
-
-		/// <summary>
-		/// 督办标题
-		/// </summary>
-		public string SuperviseTitle { get; set; }
-
-		/// <summary>
-		/// 督办内容
-		/// </summary>
-		public string SuperviseContent { get; set; }
-
-		/// <summary>
-		/// 督办发起人
-		/// </summary>
-		public string SupervisePerson { get; set; }
-
-		/// <summary>
-		/// 督办发起时间
-		/// </summary>
-		public DateTime SuperviseTime { get; set; }
-
-		/// <summary>
-		/// 督办发起单位
-		/// </summary>
-		public string SuperviseSendDept { get; set; }
-
-		/// <summary>
-		/// 被督办单位
-		/// </summary>
-		public string SuperviseRsvDept { get; set; }
-
-		/// <summary>
-		/// 督办类型
-		/// </summary>
-		public string SuperviseType { get; set; }
-
-		/// <summary>
-		/// 督办反馈截止时间
-		/// </summary>
-		public DateTime SuperviseReturnDate { get; set; }
-
-		/// <summary>
-		/// 行政区划代码
-		/// </summary>
-		public string AreaCode { get; set; }
-
-		/// <summary>
-		/// 附件列表
-		/// </summary>
-		public List<FileDto> Files { get; set; } = new();
-	}
-	public class PublishSuperviseDto
-	{
-		// <summary>
-		/// 工单对象
-		/// </summary>
-		public OrderDto Order { get; set; }
-
-
-		public SuperviseOrderDto Supervise { get; set; }
-
-		/// <summary>
-		/// 附件
-		/// </summary>
-		public string ClientGuid { get; set; }
-	}
+    public class OrderSuperviseDto : SuperviseOrderDto
+    {
+        /// <summary>
+        /// 附件列表
+        /// </summary>
+        public List<FileDto> Files { get; set; } = new();
+
+        /// <summary>
+        /// 附件列表
+        /// </summary>
+        public List<FileDto> ReplyFiles { get; set; } = new();
+    }
+
+    public class ApplyOrderSuperviseDto
+    {
+        /// <summary>
+        /// 工单ID
+        /// </summary>
+        public string OrderId { get; set; }
+
+        public List<ApplyOrderSuperviseOrgDto> SuperviseOrgDtos { get; set; }
+
+        /// <summary>
+        /// 督办回复时限
+        /// </summary>
+        public DateTime ReplyLimitTime { get; set; }
+
+        /// <summary>
+        /// 督办申请内容
+        /// </summary>
+        public string ApplyContent { get; set; }
+
+        /// <summary>
+        /// 是否接受短信,勾选校验手机号
+        /// </summary>
+        public bool AcceptSms { get; set; }
+
+        /// <summary>
+        /// 督办省编号
+        /// </summary>
+        public string? SUPERVISE_SERIAL { get; set; }
+
+        /// <summary>
+        /// 附件列表
+        /// </summary>
+        public List<FileDto> Files { get; set; } = new();
+
+        /// <summary>
+        /// 旅游督办手机号
+        /// </summary>
+        public string? PhoneNo { get; set; }
+
+    }
+    public class ReplyOrderSuperviseDto
+    {
+
+        public string Id { get; set; }
+
+        /// <summary>
+        /// 督办回复内容
+        /// </summary>
+        public string ReplyContent { get; set; }
+
+
+        /// <summary>
+        /// 回复人
+        /// </summary>
+        public string? ReplyId { get; set; }
+
+        /// <summary>
+        /// 附件列表
+        /// </summary>
+        public List<FileDto> Files { get; set; } = new();
+    }
+
+    public class SignOrderSuperviseDto
+    {
+        public string Id { get; set; }
+    }
+
+    public class ApplyOrderSuperviseOrgDto
+    {
+        /// <summary>
+        /// 被督办部门ID
+        /// </summary>
+        public string OrgId { get; set; }
+
+        /// <summary>
+        /// 被督办部门名称
+        /// </summary>
+        public string OrgName { get; set; }
+
+    }
+
+
+    public class ProvinceOrderSuperviseDto
+    {
+        /// <summary>
+        /// 督办编号
+        /// </summary>
+        public string SuperviseSerial { get; set; }
+
+        /// <summary>
+        /// 省工单编号
+        /// </summary>
+        public string CaseSerial { get; set; }
+
+        /// <summary>
+        /// 督办标题
+        /// </summary>
+        public string SuperviseTitle { get; set; }
+
+        /// <summary>
+        /// 督办内容
+        /// </summary>
+        public string SuperviseContent { get; set; }
+
+        /// <summary>
+        /// 督办发起人
+        /// </summary>
+        public string SupervisePerson { get; set; }
+
+        /// <summary>
+        /// 督办发起时间
+        /// </summary>
+        public DateTime SuperviseTime { get; set; }
+
+        /// <summary>
+        /// 督办发起单位
+        /// </summary>
+        public string SuperviseSendDept { get; set; }
+
+        /// <summary>
+        /// 被督办单位
+        /// </summary>
+        public string SuperviseRsvDept { get; set; }
+
+        /// <summary>
+        /// 督办类型
+        /// </summary>
+        public string SuperviseType { get; set; }
+
+        /// <summary>
+        /// 督办反馈截止时间
+        /// </summary>
+        public DateTime SuperviseReturnDate { get; set; }
+
+        /// <summary>
+        /// 行政区划代码
+        /// </summary>
+        public string AreaCode { get; set; }
+
+        /// <summary>
+        /// 附件列表
+        /// </summary>
+        public List<FileDto> Files { get; set; } = new();
+    }
+    public class PublishSuperviseDto
+    {
+        // <summary>
+        /// 工单对象
+        /// </summary>
+        public OrderDto Order { get; set; }
+
+
+        public SuperviseOrderDto Supervise { get; set; }
+
+        /// <summary>
+        /// 附件
+        /// </summary>
+        public string ClientGuid { get; set; }
+    }
 
 }

+ 68 - 16
src/Hotline.Share/Dtos/Order/OrderVisitDto.cs

@@ -102,25 +102,25 @@ namespace Hotline.Share.Dtos.Order
         /// </summary>
         public bool? IsOverTime { get; set; }
 
-		/// <summary>
-		/// 发布时间
-		/// </summary>
-		public DateTime? StartPublishTime { get; set; }
+        /// <summary>
+        /// 发布时间
+        /// </summary>
+        public DateTime? StartPublishTime { get; set; }
 
-		/// <summary>
-		/// 发布时间
-		/// </summary>
-		public DateTime? EndPublishTime { get; set; }
+        /// <summary>
+        /// 发布时间
+        /// </summary>
+        public DateTime? EndPublishTime { get; set; }
 
-		/// <summary>
-		/// 接办部门
-		/// </summary>
-		public string? ActualHandleOrgName { get;  set; }
+        /// <summary>
+        /// 接办部门
+        /// </summary>
+        public string? ActualHandleOrgName { get; set; }
 
-		/// <summary>
-		/// 一级部门
-		/// </summary>
-		public string? OrgLevelOneName { get; set; }
+        /// <summary>
+        /// 一级部门
+        /// </summary>
+        public string? OrgLevelOneName { get; set; }
 
         /// <summary>
         /// 来电主体
@@ -148,6 +148,11 @@ namespace Hotline.Share.Dtos.Order
         /// </summary>
         public string? OrderTagCode { get; set; }
 
+        /// <summary>
+        /// 是否回访结果修改
+        /// </summary>
+        public bool? IsUpdate { get; set; }
+
     }
 
     public record QueryOrderPublishStatisticsAllDto : PagedRequest
@@ -465,6 +470,11 @@ namespace Hotline.Share.Dtos.Order
         /// </summary>
         public bool? SeatJudge { get; set; }
 
+        /// <summary>
+        /// 是否回访结果修改
+        /// </summary>
+        public bool? IsUpdate { get; set; }
+
         public List<VisitDetailDto> VisitDetails { get; set; }
     }
 
@@ -860,6 +870,21 @@ namespace Hotline.Share.Dtos.Order
         public string? OrderTagCode { get; set; }
 
         public List<SystemDicDataOutDto>? OrderTags { get; set; }
+
+        /// <summary>
+        /// 是否回访结果修改
+        /// </summary>
+        public bool? IsUpdate { get; set; }
+
+        /// <summary>
+        /// 不满意原因
+        /// </summary>
+        public List<Kv>? OrgNoSatisfiedReason { get; set; }
+
+        /// <summary>
+        /// 回访对象类型 10:话务员 20:部门
+        /// </summary>
+        public EVisitTarget VisitTarget { get; set; }
     }
 
     public class OrderVisitDetailDto
@@ -1218,4 +1243,31 @@ namespace Hotline.Share.Dtos.Order
         /// </summary>
         public DateTime PublishTime { get; set; }
     }
+
+    /// <summary>
+    /// 修改记录
+    /// </summary>
+    public class OrderVisitDetailCopyDto
+    {
+        /// <summary>
+        /// 回访信息
+        /// </summary>
+        public List<OrderVisitDetailDto> OrderVisitDetailDtos { get; set; }
+
+        /// <summary>
+        /// 修改记录
+        /// </summary>
+        public List<OrderVisitDetailDto> OrderVisitDetailCopyDtos { get; set; }
+
+
+        /// <summary>
+        /// 创建时间
+        /// </summary>
+        public DateTime CreationTime { get; set; }
+
+        /// <summary>
+        /// 创建人
+        /// </summary>
+        public string? CreatorName { get; set; }
+    }
 }

+ 6 - 0
src/Hotline.Share/Dtos/Order/QueryOrderDto.cs

@@ -923,6 +923,12 @@ namespace Hotline.Share.Dtos.Order
         public string? Address { get; set; }
     }
 
+    public record OrgSendBackAuditListDto : PagedKeywordRequest 
+    {
+		public string? OrgName { get; set; }
+		public string? OrgId { get; set; }
+	}
+
 
     public enum EPublicState
     {

+ 45 - 0
src/Hotline.Share/Dtos/Snapshot/IndustryDto.cs

@@ -168,6 +168,27 @@ public class IndustryOutDto
     /// 关怀页面Url
     /// </summary>
     public string? PageCareUrl { get; set; }
+
+    /// <summary>
+    /// 上报积分
+    /// </summary>
+    public int ReportPoints { get; set; }
+
+    /// <summary>
+    /// 审核同意积分
+    /// </summary>
+    public int ArgeePoints { get; set; }
+
+    /// <summary>
+    /// 审核不同意扣除积分
+    /// </summary>
+    public int RefusePoints { get; set; }
+
+    /// <summary>
+    /// 额外扣除积分
+    /// </summary>
+    public int ExtraDeductedPoints { get; set; }
+
 }
 
 /// <summary>
@@ -375,6 +396,30 @@ public class AddIndustryDto
     /// 附件集合(小程序上面可以下载的 doc 文件)
     /// </summary>
     public IList<IndustryFileDto> Files { get; set; }
+
+    /// <summary>
+    /// 上报积分
+    /// </summary>
+    [Required]
+    public int ReportPoints { get; set; }
+
+    /// <summary>
+    /// 审核同意积分
+    /// </summary>
+    [Required]
+    public int ArgeePoints { get; set; }
+
+    /// <summary>
+    /// 审核不同意扣除积分
+    /// </summary>
+    [Required]
+    public int RefusePoints { get; set; }
+
+    /// <summary>
+    /// 额外扣除积分
+    /// </summary>
+    [Required]
+    public int ExtraDeductedPoints { get; set; }
 }
 
 public class IndustryCaseItemOutDto : AddIndustryCaseDto

+ 1 - 1
src/Hotline.Share/Dtos/Snapshot/IndustryFileDto.cs

@@ -62,6 +62,6 @@ public class SnapshotFileInDto
     /// <summary>
     /// 时长(秒)
     /// </summary>
-    public long Duration { get; set; }
+    public double? Duration { get; set; }
 }
 

+ 56 - 12
src/Hotline.Share/Dtos/Snapshot/OrderDto.cs

@@ -183,29 +183,25 @@ public class AddSnapshotOrderInDto : Position
     /// </summary>
     public string? Attach { get; set; }
 
-    public string GetContent(EIndustryType industryType)
+    public string GetContent(EIndustryType industryType, IReadOnlyList<SystemDicDataOutDto> dic)
     {
+        var name = (dic.FirstOrDefault(m => m.DicDataValue == JobType.ToString()) ?? new SystemDicDataOutDto()).DicDataName;
         if (industryType == EIndustryType.Declare)
         {
             return $"经营单位类别: {this.BusinessUnitType}\r\n" + // 个人
                     $"作业场所:{this.Workplace}\r\n" + // 化工、民爆物品生产经营企业
                     $"场所名称:{this.WorkplaceName}\r\n" + // 多业态混合经营场所 - 存储多种功能的劳动密集型企业
-                    $"作业区域: {this.County + this.Town}\r\n" + // 自流井区舒坪街道
-                    $"作业类型: {this.JobType}\r\n" + // 电焊
+                    $"作业区域: {this.WorkArea}\r\n" + // 自流井区舒坪街道
+                    $"作业类型: {name}\r\n" + // 电焊
                     $"作业时间:{this.StartWorkTime}~{this.EndWorkTime}\r\n" +
-                    $"作业地点: {this.Town}({this.County})({this.Street})"; // 舒平(自流井区)(油库)"
+                    $"作业地点: {Address}{FullAddress}"; // 舒平(自流井区)(油库)"
         }
         return $"{Description}";
     }
 
-    public string GetTitle(EIndustryType industryType, string acceptType)
+    public string GetTitle(EIndustryType industryType, string titleSuffix)
     {
-        if (industryType == EIndustryType.Declare)
-        {
-            return $"【随手拍】关于{Town}({County})的申报";
-        }
-
-        return $"【随手拍】关于{Town}({County})的" + acceptType;
+        return $"【随手拍】关于{Address}" + titleSuffix;
     }
 }
 
@@ -804,6 +800,27 @@ public class SnapshotOrderAuditDetailOutDto
     /// 审核状态
     /// </summary>
     public IEnumerable<KeyValuePair<int, string>> AuditComBox { get; set; }
+
+    /// <summary>
+    /// 额外扣除积分类型
+    /// </summary>
+    public IReadOnlyCollection<SystemDicDataOutDto> ExtraDeductionPointsType { get; set; }
+
+    /// <summary>
+    /// 审核同意积分
+    /// </summary>
+    public int? ArgeePoints { get; set; }
+
+    /// <summary>
+    /// 审核不同意扣除积分
+    /// </summary>
+    public int? RefusePoints { get; set; }
+
+    /// <summary>
+    /// 额外扣除积分
+    /// </summary>
+    public int? ExtraDeductedPoints { get; set; }
+
 }
 
 public class SnapshotOrderAuditOrderDetailOutDto
@@ -998,6 +1015,33 @@ public class UpdateRedPackAuditInDto
     /// 是否发送短信
     /// </summary>
     public bool IsSendSms { get; set; }
+
+    /// <summary>
+    /// 积分审核状态
+    /// </summary>
+    public ESnapshotSMSStatus PointsStatus { get; set; }
+
+    /// <summary>
+    /// 积分
+    /// </summary>
+    public int? Points { get; set; }
+
+    /// <summary>
+    /// 额外扣除积分类型
+    /// </summary>
+    public string? ExtraDeductionPointsTypeId { get; set; }
+
+    public string? ExtraDeductionPointsTypeName { get; set; }
+
+    /// <summary>
+    /// 额外扣除积分
+    /// </summary>
+    public int? ExtraDeductedPoints { get; set; }
+
+    /// <summary>
+    /// 积分审核意见
+    /// </summary>
+    public string? PointsOpinion { get; set; }
 }
 
 public class GetRedPackRecordBaseDataOutDto
@@ -1395,7 +1439,7 @@ public class GetOrderSnapshotPublishItemsOutDto
     /// <summary>
     /// 公开状态
     /// </summary>
-    public string? PublishStatusTxt => PublishStatus?.GetDescription() ?? "";
+    public string? PublishStatusTxt =>  PublishStatus.HasValue && PublishStatus.Value == EOrderSnapshotPublishStatus.Agree ? "公开" : PublishStatus?.GetDescription() ?? "";
 
     /// <summary>
     /// 信件状态

+ 5 - 0
src/Hotline.Share/Dtos/Snapshot/OrderPublishDto.cs

@@ -120,6 +120,11 @@ public class OrderPublishDetailOutDto : OrderPublishOutDto
     /// 线索名称
     /// </summary>
     public string HotspotSpliceName { get; set; }
+
+    /// <summary>
+    /// 上报时间
+    /// </summary>
+    public DateTime CreationTime { get; set; }
 }
 
 public class SnapshotWorkflow

+ 187 - 0
src/Hotline.Share/Dtos/Snapshot/PointsDto.cs

@@ -0,0 +1,187 @@
+using Hotline.Share.Enums.CallCenter;
+using Hotline.Share.Enums.Snapshot;
+using Hotline.Share.Requests;
+using Hotline.Share.Tools;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.Linq;
+using System.Text;
+using System.Text.Json.Serialization;
+using System.Threading.Tasks;
+using XF.Utility.EnumExtensions;
+
+namespace Hotline.Share.Dtos.Snapshot;
+
+public class PointsDto
+{
+}
+
+public class UpdateIsSecurityMaxAsync
+{
+    /// <summary>
+    /// 用户Id
+    /// </summary>
+    [Required]
+    public string UserId { get; set; }
+
+    /// <summary>
+    /// 是否安全卫士
+    /// </summary>
+    [Required]
+    public bool IsSecurityMax { get; set; }
+}
+
+public record PointsItemsInDto : PagedKeywordRequest
+{
+    /// <summary>
+    /// 联系方式
+    /// </summary>
+    public string? PhoneNumber { get; set; }
+}
+
+public class PointsItemsOutDto
+{
+    /// <summary>
+    /// 联系方式
+    /// </summary>
+    public string PhoneNumber { get; set; }
+
+    /// <summary>
+    /// 姓名
+    /// </summary>
+    public string UserName { get; set; }
+
+    /// <summary>
+    /// 总积分
+    /// </summary>
+    public int TotalPoints { get; set; }
+
+    /// <summary>
+    /// 已兑换积分
+    /// </summary>
+    public int OutPoints { get; set; }
+
+    /// <summary>
+    /// 当前可用积分
+    /// </summary>
+    public int ValidPoints => TotalPoints - OutPoints;
+
+    /// <summary>
+    /// 已使用积分
+    /// </summary>
+    [JsonIgnore]
+    [Newtonsoft.Json.JsonIgnore]
+    public int OutTotalPoint { get; set; }
+
+    /// <summary>
+    /// 排名
+    /// </summary>
+    public int Rank { get; set; }
+
+    /// <summary>
+    /// 是否安全卫士
+    /// </summary>
+    public bool IsSecurityMax { get; set; }
+
+    [JsonIgnore]
+    [Newtonsoft.Json.JsonIgnore]
+    public int InTotalPoint { get; set; }
+
+    /// <summary>
+    /// UserId
+    /// </summary>
+    public string UserId { get; set; }
+}
+
+
+public class PointsRankOutDto
+{
+    /// <summary>
+    /// 当前可用积分
+    /// </summary>
+    public int ValidPoints { get; set; }
+
+    /// <summary>
+    /// 排行
+    /// </summary>
+    public IList<PointsRankUserDto> Ranks { get; set; }
+}
+
+public class PointsRankUserDto
+{
+    /// <summary>
+    /// 用户名
+    /// </summary>
+    public string UserName { get; set; }
+
+
+    private string _phoneNumber;
+    /// <summary>
+    /// 联系电话
+    /// </summary>
+    public string PhoneNumber
+    {
+        get
+        {
+            return _phoneNumber.MaskPhoneNumber();
+        }
+        set { _phoneNumber = value; }
+    }
+
+    /// <summary>
+    /// 排名
+    /// </summary>
+    public int Rank { get; set; }
+
+    /// <summary>
+    /// 积分
+    /// </summary>
+    public int Points { get; set; }
+
+    /// <summary>
+    /// UserId
+    /// </summary>
+    public string CitizenId { get; set; }
+
+    /// <summary>
+    /// 是否安全卫士
+    /// </summary>
+    public bool IsSecurityMax { get; set; }
+
+    /// <summary>
+    /// 头像地址
+    /// </summary>
+    public string HeadUrl { get; set; }
+}
+
+public class PointItemsInDto : QueryFixedDto
+{
+    /// <summary>
+    /// 收入或者支出
+    /// </summary>
+    public EPointsDirection Direction { get; set; }
+}
+
+public class PointItemsOutDto
+{
+    /// <summary>
+    /// 创建时间
+    /// </summary>
+    public DateTime CreationTime { get; set; }
+
+    /// <summary>
+    /// 积分来源
+    /// </summary>
+    public EPointsSource Source { get; set; }
+
+    /// <summary>
+    /// 积分来源
+    /// </summary>
+    public string SourceTxt => Source.GetDescription();
+
+    /// <summary>
+    /// 积分
+    /// </summary>
+    public int Points { get; set; }
+}

+ 1 - 0
src/Hotline.Share/Dtos/Snapshot/ThirdTokenDto.cs

@@ -102,3 +102,4 @@ public class SaveInvitationCodeInDto
     [Required]
     public string InvitationCode { get; set; }
 }
+

+ 15 - 0
src/Hotline.Share/Enums/CallCenter/ECallDirection.cs

@@ -18,4 +18,19 @@ public enum ECallDirection
     /// </summary>
     [Description("呼出")]
     Out = 1,
+}
+
+public enum EPointsDirection
+{ 
+    /// <summary>
+    /// 收入
+    /// </summary>
+    [Description("收入")]
+    In = 0,
+
+    /// <summary>
+    /// 支出
+    /// </summary>
+    [Description("支出")]
+    Out = 1
 }

+ 6 - 1
src/Hotline.Share/Enums/Order/ESource.cs

@@ -102,13 +102,18 @@ public enum ESource
     /// <summary>
     /// 宜宾人社APP
     /// </summary>
-    YBHumanSocietyAPP=408,
+    YBHumanSocietyAPP = 408,
 
     /// <summary>
     /// 泸州酒城一通
     /// </summary>
     WineCity = 409,
 
+    /// <summary>
+    /// 泸州市政府门户网站
+    /// </summary>
+    LZCityPortalSite = 410,
+
     #region 导入类型(>=500  <530为导入来源)
     /// <summary>
     /// 麻辣社区导入

+ 29 - 0
src/Hotline.Share/Enums/Snapshot/EPointsSource.cs

@@ -0,0 +1,29 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Hotline.Share.Enums.Snapshot;
+
+public enum EPointsSource
+{
+    /// <summary>
+    /// 上报积分
+    /// </summary>
+    [Description("上报积分")]
+    Report = 0,
+
+    /// <summary>
+    /// 审核积分
+    /// </summary>
+    [Description("审核积分")]
+    Audit = 1,
+
+    /// <summary>
+    /// 积分兑换
+    /// </summary>
+    [Description("积分兑换")]
+    Exchange = 3,
+}

+ 1 - 1
src/Hotline.Share/Hotline.Share.csproj

@@ -7,7 +7,7 @@
     <GenerateDocumentationFile>True</GenerateDocumentationFile>
     <NoWarn>$(NoWarn);1591;8618;</NoWarn>
     <GeneratePackageOnBuild>True</GeneratePackageOnBuild>
-    <Version>1.0.119</Version>
+    <Version>1.0.121</Version>
   </PropertyGroup>
 
   <ItemGroup>

+ 9 - 4
src/Hotline.Share/Mq/EventNames.Order.cs

@@ -20,10 +20,15 @@ namespace Hotline.Share.Mq
         /// </summary>
         public const string HotlineOrderFlowHandled = "hotline.order.flow.handled";
 
-        /// <summary>
-        /// 撤回
-        /// </summary>
-        public const string HotlineOrderFlowRecalled = "hotline.order.flow.recalled";
+		/// <summary>
+		/// 补传附件
+		/// </summary>
+		public const string HotlineOrderFlowFile = "hotline.order.flow.file";
+
+		/// <summary>
+		/// 撤回
+		/// </summary>
+		public const string HotlineOrderFlowRecalled = "hotline.order.flow.recalled";
 
         /// <summary>
         /// 退回

+ 1 - 1
src/Hotline.Share/Tools/DataMaskExtensions.cs

@@ -23,7 +23,7 @@ public static class DataMaskExtensions
 
     public static string MaskPhoneNumber(this string original)
     {
-        if (original.Length != 11) return string.Empty;
+        if (original.Length < 11) return string.Empty;
         return original.Mask(3, 4);
     }
 

+ 13 - 0
src/Hotline.Share/Tools/StringExtensions.cs

@@ -143,4 +143,17 @@ public static class StringExtensions
         }
         return string.Empty;
     }
+
+    /// <summary>
+    /// 移除字符串中的所有 HTML 标签
+    /// </summary>
+    /// <param name="input">包含 HTML 的字符串</param>
+    /// <returns>去除 HTML 标签后的字符串</returns>
+    public static string RemoveHtmlTags(this string input)
+    {
+        if (string.IsNullOrWhiteSpace(input))
+            return string.Empty;
+
+        return Regex.Replace(input, "<.*?>", string.Empty);
+    }
 }

+ 5 - 0
src/Hotline/AppDefaults.cs

@@ -34,5 +34,10 @@ namespace Hotline
             public const string ZiGong = "ZiGong";
             public const string LuZhou = "LuZhou";
         }
+
+        public class AuthPolicy
+        {
+            public const string Hotline = "hotline_api";
+        }
     }
 }

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

@@ -63,6 +63,13 @@ namespace Hotline.Caching.Interfaces
         /// <returns></returns>
         SystemDicDataOutDto GetSnapshotBulletinType(string dicDataValue);
 
+        /// <summary>
+        /// 用户头像
+        /// </summary>
+        /// <param name="v"></param>
+        /// <returns></returns>
+        string HeaderImages(string name);
+
         /// <summary>
         /// 红包补充发放类型
         /// </summary>
@@ -89,5 +96,10 @@ namespace Hotline.Caching.Interfaces
         /// 随手拍特殊红包审核类型
         /// </summary>
         IReadOnlyCollection<SystemDicDataOutDto> SnapshotRedPackSpecialType { get; }
+
+        /// <summary>
+        /// 额外扣除积分类型
+        /// </summary>
+        IReadOnlyCollection<SystemDicDataOutDto> ExtraDeductionPointsType { get; }
     }
 }

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

@@ -145,6 +145,11 @@ namespace Hotline.Caching.Services
         /// </summary>
         public IReadOnlyCollection<SystemDicDataOutDto> SnapshotRedPackSpecialType => GetOrAdd(SysDicTypeConsts.SnapshotRedPackSpecialType);
 
+        /// <summary>
+        /// 额外扣除积分类型
+        /// </summary>
+        public IReadOnlyCollection<SystemDicDataOutDto> ExtraDeductionPointsType => GetOrAdd(SysDicTypeConsts.ExtraDeductionPointsType);
+
         /// <summary>
         /// 兴唐动作状态映射
         /// </summary>
@@ -155,5 +160,24 @@ namespace Hotline.Caching.Services
             _cacheSysDicData.Remove(code);
         }
 
+        /// <summary>
+        /// 获取用户头像图片
+        /// </summary>
+        /// <param name="name"></param>
+        /// <returns></returns>
+        /// <exception cref="NotImplementedException"></exception>
+        public string HeaderImages(string value)
+        {
+            var dto = GetOrAdd(SysDicTypeConsts.HeaderImages).Where(m => m.DicDataValue == value).FirstOrDefault() ?? new SystemDicDataOutDto();
+            if (dto.DicDataName == "LocalHeadImageDefault")
+            {
+                return SystemDicDataSeedData.LocalHeadImageDefault;
+            }
+            if (dto.DicDataName == "LocalHeadImageAQWS")
+            {
+                return SystemDicDataSeedData.LocalHeadImageAQWS;
+            }
+            return dto.DicDataName;
+        }
     }
 }

+ 1 - 1
src/Hotline/CallCenter/Tels/TelOperation.cs

@@ -57,7 +57,7 @@ namespace Hotline.CallCenter.Tels
         public void EndAction()
         {
             EndTime = DateTime.Now;
-            Duration = (EndTime.Value - OperateTime.Value).TotalSeconds;
+            Duration = Math.Round(((double)(EndTime.Value - OperateTime.Value).TotalSeconds), 2);
         }
 
     }

+ 2 - 1
src/Hotline/File/IFileRepository.cs

@@ -14,7 +14,8 @@ namespace Hotline.File
 	public interface IFileRepository : IRepository<File>
 	{
 		Task<List<FileJson>> AddFileAsync(List<FileDto> files ,string id, string flowId = "",CancellationToken cancellationToken = default);
-		Task<List<FileDto>> GetFilesAsync(List<string> ids, CancellationToken cancellationToken);
+		Task<List<FileDto>> GetFilesAsync(List<string> ids, CancellationToken cancellationToken = default);
+		Task<List<FileDto>> GetPermissionFilesAsync(List<string> ids, CancellationToken cancellationToken = default);
 		Task<List<WorkflowTraceDto>> WorkflowTraceRecursion(List<WorkflowTraceDto> dto, CancellationToken cancellationToken);
 		Task<List<Hotline.File.File>> GetByKeyAsync(string key, CancellationToken cancellationToken);
         Task<List<FileJson>> AddFileAsync(IList<SnapshotFileInDto> files, string id, CancellationToken requestAborted);

Některé soubory nejsou zobrazeny, neboť je v těchto rozdílových datech změněno mnoho souborů