Browse Source

Merge branch 'test' into lib/test

libin 2 weeks ago
parent
commit
dc771031ba
49 changed files with 612 additions and 193 deletions
  1. 1 0
      .gitignore
  2. 20 0
      src/Hotline.Api/Controllers/Bi/BiOrderController.cs
  3. 2 0
      src/Hotline.Api/Controllers/Bigscreen/DataScreenController.cs
  4. 3 1
      src/Hotline.Api/Controllers/Bigscreen/EnforcementScreenController.cs
  5. 3 1
      src/Hotline.Api/Controllers/Bigscreen/JudicialManagementScreenController.cs
  6. 3 1
      src/Hotline.Api/Controllers/Bigscreen/SeatController.cs
  7. 5 2
      src/Hotline.Api/Controllers/ExportWordController.cs
  8. 76 45
      src/Hotline.Api/Controllers/OrderController.cs
  9. 15 13
      src/Hotline.Api/Controllers/PushMessageController.cs
  10. 15 5
      src/Hotline.Api/Controllers/SysController.cs
  11. 2 1
      src/Hotline.Api/Controllers/TestController.cs
  12. BIN
      src/Hotline.Api/Documents/luzhou/泸州12345平台工单查询接口(V1.0-20250312) .docx
  13. BIN
      src/Hotline.Api/Documents/zigong/丰窝12345平台工单接入接口(V1.1-20250225) .docx
  14. 53 0
      src/Hotline.Api/Filter/ClientIpFilterAttribute.cs
  15. 23 14
      src/Hotline.Api/Properties/launchSettings.json
  16. 24 12
      src/Hotline.Api/StartupExtensions.cs
  17. 3 0
      src/Hotline.Api/StartupHelper.cs
  18. 12 0
      src/Hotline.Api/config/appsettings.Test.json
  19. 51 50
      src/Hotline.Api/config/appsettings.json
  20. 0 3
      src/Hotline.Application/Mappers/SnapshotMapperConfigs.cs
  21. 7 0
      src/Hotline.Application/OrderApp/OrderApplication.cs
  22. 5 1
      src/Hotline.Application/Snapshot/IndustryApplication.cs
  23. 9 7
      src/Hotline.Application/Snapshot/RedPackApplication.cs
  24. 4 1
      src/Hotline.Application/Snapshot/SnapshotOrderApplication.cs
  25. 1 0
      src/Hotline.Application/Snapshot/SnapshotThirdAccountSupplier.cs
  26. 23 11
      src/Hotline.Application/StatisticalReport/OrderReportApplication.cs
  27. 4 0
      src/Hotline.Share/Dtos/FlowEngine/Workflow/PreviousWorkflowDto.cs
  28. 12 5
      src/Hotline.Share/Dtos/Order/OrderDto.cs
  29. 37 1
      src/Hotline.Share/Dtos/Order/OrderVisitDto.cs
  30. 5 0
      src/Hotline.Share/Dtos/Order/QueryOrderDto.cs
  31. 2 2
      src/Hotline.Share/Dtos/Push/MessagePagedDto.cs
  32. 5 0
      src/Hotline.Share/Dtos/Snapshot/ThirdTokenDto.cs
  33. 42 0
      src/Hotline.Share/Dtos/StatisticalReport/OrderDelayStatisicalReturnDto.cs
  34. 22 0
      src/Hotline.Share/Enums/Order/EOrderUpdateSource.cs
  35. 14 0
      src/Hotline.Share/Requests/PagedKeywordRequest.cs
  36. 9 0
      src/Hotline.WeChat/WeChatService.cs
  37. 5 0
      src/Hotline/Caching/Interfaces/ISystemSettingCacheManager.cs
  38. 7 0
      src/Hotline/Caching/Services/SystemSettingCacheManager.cs
  39. 4 1
      src/Hotline/FlowEngine/Workflows/IWorkflowDomainService.cs
  40. 10 4
      src/Hotline/FlowEngine/Workflows/WorkflowDomainService.cs
  41. 21 4
      src/Hotline/Orders/Order.cs
  42. 7 1
      src/Hotline/Orders/OrderCopy.cs
  43. 17 0
      src/Hotline/SeedData/SystemDicDataSeedData.cs
  44. 10 0
      src/Hotline/Settings/SettingConstants.cs
  45. 0 1
      src/Hotline/Settings/SysDicTypeConsts.cs
  46. 3 1
      src/Hotline/Snapshot/Services/SnapshotPointsDomainService.cs
  47. 9 0
      test/Hotline.Tests/Application/IndustryApplicationTest.cs
  48. 4 3
      test/Hotline.Tests/Infrastructure/WeiXinTest.cs
  49. 3 2
      test/Hotline.Tests/Startup.cs

+ 1 - 0
.gitignore

@@ -348,3 +348,4 @@ healthchecksdb
 /merge_245_test.ps1
 /merge_dev.ps1
 /merge_snapshot_test.ps1
+/src/Hotline.Api/Properties/launchSettings.json

+ 20 - 0
src/Hotline.Api/Controllers/Bi/BiOrderController.cs

@@ -1324,6 +1324,26 @@ namespace Hotline.Api.Controllers.Bi
             return returnModel;
         }
 
+        /// <summary>
+        /// 智能回访不满意明细
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [HttpGet("aivisit-nosatisfied-paeglist")]
+        public async Task<PagedDto<AiVisitNoSatisfiedPageListRep>> AiVisitNoSatisfiedPageList([FromQuery] AiVisitNoSatisfiedPageListRequest dto)
+        {
+            var (total, items) = await _aiOrderVisitDetailRepository.Queryable()
+                .Includes(x => x.OrderVisit)
+                .Includes(x => x.OrderVisit.Order)
+                .Includes(x=>x.OrderVisit.OrderVisitDetails)
+                .Includes(x => x.OrderVisit.Employee)
+                .Where(x => x.AiVisitTime >= dto.StartTime && x.AiVisitTime <= dto.EndTime && x.IsSuccess == true && SqlFunc.JsonField(x.AiOrgProcessingResults, "Key") == "2")
+                .WhereIF(!string.IsNullOrEmpty(dto.VisitName), x => x.OrderVisit.Employee.Name.Contains(dto.VisitName))
+                .ToPagedListAsync(dto.PageIndex, dto.PageSize, HttpContext.RequestAborted);
+
+            return new PagedDto<AiVisitNoSatisfiedPageListRep>(total, _mapper.Map<IReadOnlyList<AiVisitNoSatisfiedPageListRep>>(items));
+        }
+
         /// <summary>
         /// 热点类型小类统计
         /// </summary>

+ 2 - 0
src/Hotline.Api/Controllers/Bigscreen/DataScreenController.cs

@@ -1,4 +1,5 @@
 using DocumentFormat.OpenXml.Drawing;
+using Hotline.Api.Filter;
 using Hotline.Caching.Interfaces;
 using Hotline.Configurations;
 using Hotline.KnowledgeBase;
@@ -22,6 +23,7 @@ using XF.Domain.Repository;
 
 namespace Hotline.Api.Controllers.Bigscreen
 {
+    [ServiceFilter(typeof(ClientIpFilterAttribute))]
     public class DataScreenController : BaseController
     {
         private readonly IOrderRepository _orderRepository;

+ 3 - 1
src/Hotline.Api/Controllers/Bigscreen/EnforcementScreenController.cs

@@ -1,4 +1,5 @@
-using Hotline.JudicialManagement;
+using Hotline.Api.Filter;
+using Hotline.JudicialManagement;
 using Hotline.Orders;
 using Hotline.Settings;
 using Hotline.Share.Dtos.JudicialManagement;
@@ -14,6 +15,7 @@ namespace Hotline.Api.Controllers.Bigscreen
     /// <summary>
     /// 司法行政监督管理大屏  
     /// </summary>
+    [ServiceFilter(typeof(ClientIpFilterAttribute))]
     public class EnforcementScreenController : BaseController
     {
         private readonly IMapper _mapper;

+ 3 - 1
src/Hotline.Api/Controllers/Bigscreen/JudicialManagementScreenController.cs

@@ -1,4 +1,5 @@
-using Hotline.JudicialManagement;
+using Hotline.Api.Filter;
+using Hotline.JudicialManagement;
 using Hotline.Orders;
 using Hotline.Settings;
 using Hotline.Share.Dtos.JudicialManagement;
@@ -14,6 +15,7 @@ namespace Hotline.Api.Controllers.Bigscreen
     /// <summary>
     /// 司法行政监督管理大屏
     /// </summary>
+    [ServiceFilter(typeof(ClientIpFilterAttribute))]
     public class JudicialManagementScreenController : BaseController
     {
         private readonly IMapper _mapper;

+ 3 - 1
src/Hotline.Api/Controllers/Bigscreen/SeatController.cs

@@ -1,4 +1,5 @@
-using Hotline.Application.Bigscreen;
+using Hotline.Api.Filter;
+using Hotline.Application.Bigscreen;
 using Hotline.Caching.Interfaces;
 using Hotline.CallCenter.Tels;
 using Hotline.CallCenter.Tels.CallTelDomain;
@@ -11,6 +12,7 @@ using Microsoft.AspNetCore.Mvc;
 
 namespace Hotline.Api.Controllers.Bigscreen
 {
+    [ServiceFilter(typeof(ClientIpFilterAttribute))]
     public class SeatController : BaseController
     {
         private readonly ISeatStateDataService _seatStateDataService;

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

@@ -1,4 +1,5 @@
-using Hotline.Application.ExportWord;
+using Hotline.Api.Filter;
+using Hotline.Application.ExportWord;
 using Hotline.Caching.Interfaces;
 using Hotline.Configurations;
 using Hotline.Orders;
@@ -56,7 +57,8 @@ namespace Hotline.Api.Controllers
         /// </summary>
         /// <returns></returns>
         [HttpPost("order_submission_form")]
-        public async Task<IActionResult> OrderSubmissionForm([FromBody] List<string> Ids)
+		[LogFilterAlpha("导出日志")]
+		public async Task<IActionResult> OrderSubmissionForm([FromBody] List<string> Ids)
         {
             var streams = new Dictionary<string, Stream>();
             var path = $"{Directory.GetCurrentDirectory()}/Template/AssignmentForm.doc";
@@ -136,6 +138,7 @@ namespace Hotline.Api.Controllers
         /// <param name="Ids"></param>
         /// <returns></returns>
 		[HttpPost("quality_certificate")]
+		[LogFilterAlpha("导出日志")]
 		public async Task<IActionResult> QualityCertificate([FromBody] List<string> Ids) {
 
 			var streams = new Dictionary<string, Stream>();

+ 76 - 45
src/Hotline.Api/Controllers/OrderController.cs

@@ -59,6 +59,7 @@ using Hotline.Share.Requests;
 using Hotline.Share.Tools;
 using Hotline.Snapshot;
 using Hotline.Snapshot.IRepository;
+using Hotline.Special;
 using Hotline.Tools;
 using Hotline.Users;
 using Hotline.Validators.FlowEngine;
@@ -1561,6 +1562,10 @@ public class OrderController : BaseController
         dto.CallId = await _callApplication.PublishVisitRelevanceCallIdAsync(dto.Adapt<OrderRelevanceCallIdDto>(), HttpContext.RequestAborted);
         dto.CallId = await _callApplication.GetOrSetCallIdAsync(dto.CallId, HttpContext.RequestAborted);
         await _orderApplication.SaveOrderVisit(dto, HttpContext.RequestAborted);
+        if (dto.IsTransact.HasValue && dto.IsTransact.Value)
+        {
+            await Add(dto.OrderReTransact);
+        }
     }
 
     /// <summary>
@@ -3410,29 +3415,38 @@ public class OrderController : BaseController
 
         if (_appOptions.Value.IsZiGong)
         {
-            await _workflowDomainService.PreviousAsync(dto,
-                newStepConfig: (workflow, currentStep, prevStepDefine, prevStep, newPrevStep) =>
-                {
-                    if (prevStepDefine.BusinessType is EBusinessType.Department)
-                    {
-                        newPrevStep.FlowAssignType = EFlowAssignType.Org;
-                    }
-                    else if (prevStepDefine.HandlerType is EHandlerType.Role)
-                    {
-                        newPrevStep.Assign(
-                            new StepAssignInfo
-                            {
-                                FlowAssignType = EFlowAssignType.Role,
-                                RoleId = prevStepDefine.HandlerTypeItems[0].Key,
-                                RoleName = prevStepDefine.HandlerTypeItems[0].Value
-                            });
-                    }
-                    else
+            //退回至申请人
+            if (dto.IsRecallToStartStep)
+            {
+                await _workflowDomainService.RecallToStartStepAsync(dto.WorkflowId, dto.Opinion, null, false, EHandleMode.Previous, EFlowAssignType.User,
+                    cancellationToken: HttpContext.RequestAborted);
+            }
+            else
+            {
+                await _workflowDomainService.PreviousAsync(dto,
+                    newStepConfig: (workflow, currentStep, prevStepDefine, prevStep, newPrevStep) =>
                     {
-                        newPrevStep.FlowAssignType = prevStep.FlowAssignType;
-                    }
-                },
-                cancellationToken: HttpContext.RequestAborted);
+                        if (prevStepDefine.BusinessType is EBusinessType.Department)
+                        {
+                            newPrevStep.FlowAssignType = EFlowAssignType.Org;
+                        }
+                        else if (prevStepDefine.HandlerType is EHandlerType.Role)
+                        {
+                            newPrevStep.Assign(
+                                new StepAssignInfo
+                                {
+                                    FlowAssignType = EFlowAssignType.Role,
+                                    RoleId = prevStepDefine.HandlerTypeItems[0].Key,
+                                    RoleName = prevStepDefine.HandlerTypeItems[0].Value
+                                });
+                        }
+                        else
+                        {
+                            newPrevStep.FlowAssignType = prevStep.FlowAssignType;
+                        }
+                    },
+                    cancellationToken: HttpContext.RequestAborted);
+            }
         }
         else
         {
@@ -4323,9 +4337,19 @@ public class OrderController : BaseController
             .WhereIF(!string.IsNullOrEmpty(dto.Keyword), d => d.Title.Contains(dto.Keyword!) || d.No.Contains(dto.Keyword!))
             .Select(d => new OrderHistoryOutDto
             {
+                Id = d.Id,
+                Title = d.Title,
+                No = d.No,
+                CurrentStepName = d.CurrentStepName,
+                CreationTime = d.CreationTime,
+                ActualHandleOrgName = d.ActualHandleOrgName,
+                Status = d.Status,
                 IsScreen = d.OrderScreens.Any(x => x.Status == EScreenStatus.End),
-                TerminateStatus = d.OrderTerminates.OrderByDescending(p => p.CreationTime).Select(p => p.Status).FirstOrDefault()
-            }, true)
+                IsTerminate = d.OrderTerminates.Any(p => p.Status == ETerminateStatus.End),
+                //TerminateStatus = SqlFunc.Subqueryable<OrderTerminate>().Where(s => s.OrderId == d.Id).OrderByDesc(s => s.CreationTime).Select(s => s.Status)
+            })
+            .MergeTable()
+            .OrderByIF(_appOptions.Value.IsZiGong, d => d.IsTerminate, OrderByType.Desc)
             .OrderByDescending(d => d.CreationTime)
             .ToPagedListAsync(dto.PageIndex, dto.PageSize, HttpContext.RequestAborted);
         return new PagedDto<OrderHistoryOutDto>(total, _mapper.Map<IReadOnlyList<OrderHistoryOutDto>>(items));
@@ -4749,16 +4773,14 @@ public class OrderController : BaseController
         //工单保存特殊身份验证
         var specialIdentityVerification =
             bool.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.SpecialIdentityVerification).SettingValue[0]);
+        var specialNumber = await _specialNumberRepository.GetAsync(p => p.PhoneNumber == dto.FromPhone, HttpContext.RequestAborted);
         if (specialIdentityVerification)
         {
-            var specialNumber = await _specialNumberRepository.GetAsync(p => p.PhoneNumber == dto.FromPhone, HttpContext.RequestAborted);
             if (specialNumber != null && specialNumber.PoliticalIdentityValue == "1")
             {
-                if (dto.OrderPushTypes == null || dto.OrderPushTypes.Count() == 0)
+                if (dto.OrderPushTypes == null || dto.OrderPushTypes.Count() == 0 || dto.OrderPushTypes.Exists(p => p.PushTypeCode == "9") == false)
                     throw UserFriendlyException.SameMessage("人大代表来电,请选择推送分类人大代表意见快办!");
 
-                if (dto.OrderPushTypes.Exists(p => p.PushTypeCode == "9") == false)
-                    throw UserFriendlyException.SameMessage("人大代表来电,请选择推送分类人大代表意见快办!");
             }
         }
 
@@ -4801,6 +4823,11 @@ public class OrderController : BaseController
         var order = _mapper.Map<Orders.Order>(dto);
         order.SignerId = _sessionContext.UserId;
         order.SignerName = _sessionContext.UserName;
+        if (specialNumber != null)
+        {
+            order.PoliticalIdentityValue = specialNumber.PoliticalIdentityValue;
+            order.PoliticalIdentityName = specialNumber.PoliticalIdentityName;
+        }
         order.InitId();
         if (dto.Files.Any())
             order.FileJson = await _fileRepository.AddFileAsync(dto.Files, order.Id, "", HttpContext.RequestAborted);
@@ -5009,15 +5036,12 @@ public class OrderController : BaseController
         //工单保存特殊身份验证
         var specialIdentityVerification =
             bool.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.SpecialIdentityVerification).SettingValue[0]);
+        var specialNumber = await _specialNumberRepository.GetAsync(p => p.PhoneNumber == dto.FromPhone, HttpContext.RequestAborted);
         if (specialIdentityVerification)
         {
-            var specialNumber = await _specialNumberRepository.GetAsync(p => p.PhoneNumber == dto.FromPhone, HttpContext.RequestAborted);
             if (specialNumber != null && specialNumber.PoliticalIdentityValue == "1")
             {
-                if (dto.OrderPushTypes == null || dto.OrderPushTypes.Count() == 0)
-                    throw UserFriendlyException.SameMessage("人大代表来电,请选择推送分类人大代表意见快办!");
-
-                if (dto.OrderPushTypes.Exists(p => p.PushTypeCode == "9") == false)
+                if (dto.OrderPushTypes == null || dto.OrderPushTypes.Count() == 0 || dto.OrderPushTypes.Exists(p => p.PushTypeCode == "9") == false)
                     throw UserFriendlyException.SameMessage("人大代表来电,请选择推送分类人大代表意见快办!");
             }
         }
@@ -5068,6 +5092,7 @@ public class OrderController : BaseController
         copy.AuditTime = DateTime.Now;
         copy.AuditUserId = _sessionContext.UserId;
         copy.AuditUserName = _sessionContext.UserName;
+        copy.AuditSource = dto.IsEdit ? EOrderUpdateSource.Alter : EOrderUpdateSource.Accepted;
         copy.InitId();
         await _orderCopyRepository.AddAsync(copy, HttpContext.RequestAborted);
 
@@ -5093,6 +5118,12 @@ public class OrderController : BaseController
         if (order.SourceChannelCode != AppDefaults.SourceChannel.DianHua)
             order.CallId = null;
 
+        if (specialNumber != null)
+        {
+            order.PoliticalIdentityValue = specialNumber.PoliticalIdentityValue;
+            order.PoliticalIdentityName = specialNumber.PoliticalIdentityName;
+        }
+
         if (dto.IsEdit && !string.IsNullOrEmpty(dto.CenterOpinion) && order.CenterToOrgOpinion != dto.CenterOpinion)
             order.CenterToOrgOpinion = dto.CenterOpinion;
         if (dto.IsEdit && !string.IsNullOrEmpty(dto.FileOpinion) && order.FileOpinion != dto.FileOpinion)
@@ -5545,14 +5576,12 @@ public class OrderController : BaseController
                     // 宜宾需求: 1.是否是派单节点  2.是否存在历史派单节点  3.存在获取上个派单节点  4.不存在走平均派单   过滤历史派单节点不为派单池
                     if (_appOptions.Value.IsYiBin || _appOptions.Value.IsZiGong)
                     {
-                        var sendOrderTraces =
-                            workflow.Traces.Where(x => x.BusinessType == EBusinessType.Send && x.HandlerId != AppDefaults.SendPoolId);
-                        if (sendOrderTraces.Any())
+                        var sendOrderTrace = workflow.Traces
+                            .Where(x => x.BusinessType == EBusinessType.Send && x.HandlerId != AppDefaults.SendPoolId)
+                            .MaxBy(x => x.CreationTime);
+                        if (sendOrderTrace is not null)
                         {
-                            var sendOrderTrace = workflow.Traces
-                                .Where(x => x.BusinessType == EBusinessType.Send && x.HandlerId != AppDefaults.SendPoolId)
-                                .OrderByDescending(x => x.CreationTime)
-                                .FirstOrDefault();
+                            //todo
                             nextDto.NextHandlers = new List<StepAssignInfo>
                             {
                                 new()
@@ -5763,11 +5792,11 @@ public class OrderController : BaseController
 
     private async Task AverageSendOrderAsync(NextWorkflowDto nextDto, CancellationToken cancellationToken)
     {
-        // 平均派单
-        var averageSendOrder = bool.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.AverageSendOrder).SettingValue[0]);
-        if (averageSendOrder)
+        if (!nextDto.NextHandlers.Any())
         {
-            if (!nextDto.NextHandlers.Any())
+            // 平均派单
+            var averageSendOrder = bool.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.AverageSendOrder).SettingValue[0]);
+            if (averageSendOrder)
             {
                 var handler = await _orderDomainService.AverageOrder(cancellationToken);
                 nextDto.NextHandlers = new List<StepAssignInfo> { handler };
@@ -5926,7 +5955,8 @@ public class OrderController : BaseController
             CurrentStepOptions = definition?.Steps.Select(x => new Kv(x.Code, x.Name)),
             IdentityTypeOptions = EnumExts.GetDescriptions<EIdentityType>(),
             OrderTags = _sysDicDataCacheManager.GetSysDicDataCache(SysDicTypeConsts.OrderTag),
-            FocusOnEvents = _sysDicDataCacheManager.GetSysDicDataCache(SysDicTypeConsts.FocusOnEvent)
+            FocusOnEvents = _sysDicDataCacheManager.GetSysDicDataCache(SysDicTypeConsts.FocusOnEvent),
+            PoliticalIdentity = _sysDicDataCacheManager.GetSysDicDataCache(SysDicTypeConsts.PoliticalIdentity)
         };
         return rsp;
     }
@@ -9498,6 +9528,7 @@ public class OrderController : BaseController
     {
         var (total, items) = await _orderCopyRepository.Queryable()
             .WhereIF(!string.IsNullOrEmpty(dto.Keyword), x => x.No.Contains(dto.Keyword!))
+            .WhereIF(_appOptions.Value.IsLuZhou, x => x.AuditSource == EOrderUpdateSource.Alter)
             .OrderByDescending(x => x.CreationTime)
             .ToPagedListAsync(dto.PageIndex, dto.PageSize, HttpContext.RequestAborted);
         return new PagedDto<OrderDto>(total, _mapper.Map<IReadOnlyList<OrderDto>>(items));

+ 15 - 13
src/Hotline.Api/Controllers/PushMessageController.cs

@@ -186,7 +186,7 @@ namespace Hotline.Api.Controllers
         public async Task<dynamic> SendMessageBaseDataAsync()
         {
             var users = await _userRepository.Queryable()
-                .Select(m => new {m.Id,  m.PhoneNo, m.Name }).ToListAsync();
+                .Select(m => new { m.Id, m.PhoneNo, m.Name }).ToListAsync();
             return new Dictionary<string, dynamic>() { { "contacts", users } };
         }
 
@@ -211,11 +211,13 @@ namespace Hotline.Api.Controllers
                     d => d.Name.Contains(pagedDto.Keyword!) || d.Content.Contains(pagedDto.Keyword!) ||
                          d.TelNumber.Contains(pagedDto.Keyword!) || d.CreatorName.Contains(pagedDto.Keyword!) ||
                          d.CreatorOrgName.Contains(pagedDto.Keyword!))
-                .WhereIF(pagedDto.StartTime.HasValue, d => d.SendTime >= pagedDto.StartTime)
-                .WhereIF(pagedDto.EndTime.HasValue, d => d.SendTime <= pagedDto.EndTime)
+                .WhereIF(pagedDto.StartTime.HasValue, d => d.SendTime >= pagedDto.StartTime)//发送时间开始
+                .WhereIF(pagedDto.EndTime.HasValue, d => d.SendTime <= pagedDto.EndTime)//发送时间结束
+                 .WhereIF(pagedDto.CreationStartTime.HasValue, d => d.CreationTime >= pagedDto.CreationStartTime)//添加时间开始
+                .WhereIF(pagedDto.CreationEndTime.HasValue, d => d.CreationTime <= pagedDto.CreationEndTime)//添加时间结束
                 .WhereIF(!string.IsNullOrEmpty(pagedDto.UserName), d => d.Name.Contains(pagedDto.UserName))
                  .WhereIF(!string.IsNullOrEmpty(pagedDto.TelNumber), d => d.TelNumber.Contains(pagedDto.TelNumber))
-                .WhereIF(!string.IsNullOrEmpty(pagedDto.SendName),d=>d.CreatorName.Contains(pagedDto.SendName))
+                .WhereIF(!string.IsNullOrEmpty(pagedDto.SendName), d => d.CreatorName.Contains(pagedDto.SendName))
                 .WhereIF(!string.IsNullOrEmpty(pagedDto.SendOrg), d => d.CreatorOrgName.Contains(pagedDto.SendOrg))
                 .WhereIF(!string.IsNullOrEmpty(pagedDto.SendContent), d => d.Content.Contains(pagedDto.SendContent))
                 .OrderByDescending(it => it.CreationTime)
@@ -250,7 +252,7 @@ namespace Hotline.Api.Controllers
         /// <param name="dto"></param>
         /// <returns></returns>
         [HttpPost("add-batchsmstask")]
-        public async Task AddBatchSmsTask([FromBody]AddBatchSmsTaskDto dto)
+        public async Task AddBatchSmsTask([FromBody] AddBatchSmsTaskDto dto)
         {
             //验证是否有重复电话
             if (dto.BatchSmsTaskDetail.DistinctBy(x => x.PhoneNum).Count() != dto.BatchSmsTaskDetail.Count)
@@ -267,7 +269,7 @@ namespace Hotline.Api.Controllers
             {
                 x.TaskId = task.Id;
             });
-            await _batchSmsTaskDetailRepository.AddRangeAsync(detail,HttpContext.RequestAborted);
+            await _batchSmsTaskDetailRepository.AddRangeAsync(detail, HttpContext.RequestAborted);
             //推送任务延迟消息
             //_capPublisher.PublishDelay(expiredTimeConfig.NearlyExpiredTime - DateTime.Now, EventNames.HotlineOrderNearlyExpiredTimeSms, new PublishNearlyExpiredTimeSmsDto() { OrderId = order.Id });
             _capPublisher.PublishDelay(task.PlanSendTime - DateTime.Now, EventNames.HotlineBatchSmsTask, new PublishBatchSmsTaskDto() { TaskId = task.Id });
@@ -279,7 +281,7 @@ namespace Hotline.Api.Controllers
         /// <param name="id"></param>
         /// <returns></returns>
         [HttpGet("stop-batchsmstask")]
-        public async Task StopBatchSmsTask([FromQuery]string id)
+        public async Task StopBatchSmsTask([FromQuery] string id)
         {
             var task = await _batchSmsTaskRepository.GetAsync(id, HttpContext.RequestAborted);
             if (task == null)
@@ -287,7 +289,7 @@ namespace Hotline.Api.Controllers
                 throw UserFriendlyException.SameMessage("无效任务");
             }
 
-            if (task.SmsTaskState!= ESmsTaskState.WaitDo)
+            if (task.SmsTaskState != ESmsTaskState.WaitDo)
             {
                 throw UserFriendlyException.SameMessage("当前任务状态不能终止");
             }
@@ -302,9 +304,9 @@ namespace Hotline.Api.Controllers
         /// <param name="dto"></param>
         /// <returns></returns>
         [HttpGet("batchsmstask-query")]
-        public async Task<PagedDto<BatchSmsTaskRep>> BatchSmsTaskList([FromQuery]BatchSmsTaskReq dto)
+        public async Task<PagedDto<BatchSmsTaskRep>> BatchSmsTaskList([FromQuery] BatchSmsTaskReq dto)
         {
-            var (total,items) = await _batchSmsTaskRepository.Queryable()
+            var (total, items) = await _batchSmsTaskRepository.Queryable()
                 .WhereIF(string.IsNullOrEmpty(dto.TaskName) == false, x => x.TaskName.Contains(dto.TaskName))
                 .WhereIF(dto.State.HasValue, x => x.SmsTaskState == dto.State)
                 .WhereIF(dto.StateTime.HasValue, x => x.CreationTime >= dto.StateTime)
@@ -333,10 +335,10 @@ namespace Hotline.Api.Controllers
         /// <param name="id"></param>
         /// <returns></returns>
         [HttpGet("batchsmstask-entity")]
-        public async Task<BatchSmsTaskRep> BatchSmsTaskEntity([FromQuery]string id)
+        public async Task<BatchSmsTaskRep> BatchSmsTaskEntity([FromQuery] string id)
         {
             var model = await _batchSmsTaskRepository.GetAsync(id, HttpContext.RequestAborted);
-            if(model == null)
+            if (model == null)
             {
                 throw UserFriendlyException.SameMessage("无效任务");
             }
@@ -351,7 +353,7 @@ namespace Hotline.Api.Controllers
         [HttpGet("batchsmsmtaskdetail-query")]
         public async Task<PagedDto<AddBatchSmsTaskDetailDto>> BatchSmsTaskDetailList([FromQuery] BatchSmsTaskDetailListReq dto)
         {
-            var (total,items) = await _batchSmsTaskDetailRepository.Queryable()
+            var (total, items) = await _batchSmsTaskDetailRepository.Queryable()
                 .Where(x => x.TaskId == dto.Id)
                 .WhereIF(string.IsNullOrEmpty(dto.Name) == false, x => x.Name.Contains(dto.Name))
                 .WhereIF(string.IsNullOrEmpty(dto.PhoneNum) == false, x => x.PhoneNum.Contains(dto.PhoneNum))

+ 15 - 5
src/Hotline.Api/Controllers/SysController.cs

@@ -417,11 +417,21 @@ namespace Hotline.Api.Controllers
             return await _systemAreaDomainService.GetAreaTree(_appOptions.Value.IsZiGong ? 6 : 0);
         }
 
-        /// <summary>
-        /// 获取省市区树形
-        /// </summary>
-        /// <returns></returns>
-        [HttpPost("area/tree/export")]
+		/// <summary>
+		/// 获取省市区树形 来电弹单界面获取
+		/// </summary>
+		/// <returns></returns>
+		[HttpGet("area/tree_accepted")]
+		public async Task<List<SystemArea>> GetAreaTree_Accepted()
+		{
+			return await _systemAreaDomainService.GetAreaTree(_appOptions.Value.IsZiGong ? 6 : 0, _appOptions.Value.IsLuZhou ? "510500" : "510000");
+		}
+
+		/// <summary>
+		/// 获取省市区树形
+		/// </summary>
+		/// <returns></returns>
+		[HttpPost("area/tree/export")]
 		[LogFilterAlpha("导出日志")]
 		public async Task<FileStreamResult> GetAreaTreeExport([FromBody]ExportExcelDto<string> dto)
         {

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

@@ -254,7 +254,8 @@ public class TestController : BaseController
         var inDto = new ThirdTokenDto
         {
             AppId = _systemSettingCacheManager.WxOpenAppId,
-            Secret = _systemSettingCacheManager.WxOpenAppSecret
+            Secret = _systemSettingCacheManager.WxOpenAppSecret,
+            WebApiHost = "http://10.56.131.9:50200/wxapi/"
         };
         return await _thirdIdentiyService.GetPhoneNumberAsync(inDto, CancellationToken.None);
     }

BIN
src/Hotline.Api/Documents/luzhou/泸州12345平台工单查询接口(V1.0-20250312) .docx


BIN
src/Hotline.Api/Documents/zigong/丰窝12345平台工单接入接口(V1.1-20250225) .docx


+ 53 - 0
src/Hotline.Api/Filter/ClientIpFilterAttribute.cs

@@ -0,0 +1,53 @@
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.Filters;
+
+namespace Hotline.Api.Filter
+{
+    public class ClientIpFilterAttribute : ActionFilterAttribute
+    {
+        private readonly List<string> _whiteIps;
+        private readonly ILogger _logger;
+
+        public ClientIpFilterAttribute(List<string> whiteIps, ILogger logger)
+        {
+            _whiteIps = whiteIps;
+            _logger = logger;
+        }
+
+        public override void OnActionExecuting(ActionExecutingContext context)
+        {
+            if (!_whiteIps.Any()) return;
+
+            var ip = context.HttpContext.Request.Headers["X-Forwarded-For"].FirstOrDefault();
+            if (string.IsNullOrEmpty(ip))
+                ip = context.HttpContext.Request.Headers["X-Real-IP"].FirstOrDefault();
+
+            if (string.IsNullOrEmpty(ip))
+            {
+                var remoteIp = context.HttpContext.Connection.RemoteIpAddress;
+                if (remoteIp?.IsIPv4MappedToIPv6 ?? false)
+                    remoteIp = remoteIp.MapToIPv4();
+                ip = remoteIp?.ToString();
+            }
+
+            _logger.LogWarning("Request from IP: {RemoteIp}", ip);
+
+            if (ip.StartsWith(':') || ip.StartsWith("::"))
+                ip = ip.Replace(':', ' ').Trim();
+
+            if (string.IsNullOrEmpty(ip))
+            {
+                _logger.LogWarning("Forbidden Request from IP: {RemoteIp}", ip);
+                context.Result = new StatusCodeResult(StatusCodes.Status403Forbidden);
+            }
+
+            if (!_whiteIps.Contains(ip))
+            {
+                _logger.LogWarning("Forbidden Request from IP: {RemoteIp}", ip);
+                context.Result = new StatusCodeResult(StatusCodes.Status403Forbidden);
+            }
+
+            base.OnActionExecuting(context);
+        }
+    }
+}

+ 23 - 14
src/Hotline.Api/Properties/launchSettings.json

@@ -1,15 +1,24 @@
-{
-  "$schema": "https://json.schemastore.org/launchsettings.json",
-  "profiles": {
-    "Hotline.Api": {
-      "commandName": "Project",
-      "dotnetRunMessages": true,
-      "launchBrowser": true,
-      "launchUrl": "swagger",
-      "applicationUrl": "http://localhost:50100",
-      "environmentVariables": {
-        "ASPNETCORE_ENVIRONMENT": "Development"
-      }
+{
+    "profiles": {
+        "Hotline.Api": {
+            "commandName": "Project",
+            "dotnetRunMessages": true,
+            "launchBrowser": true,
+            "launchUrl": "swagger",
+            "applicationUrl": "http://localhost:50100",
+            "environmentVariables": {
+                "ASPNETCORE_ENVIRONMENT": "Development"
+            }
+        },
+        "Hotline.Api.Test": {
+            "commandName": "Project",
+            "dotnetRunMessages": true,
+            "launchBrowser": true,
+            "launchUrl": "swagger",
+            "applicationUrl": "http://localhost:50100",
+            "environmentVariables": {
+                "ASPNETCORE_ENVIRONMENT": "Test"
+            }
+        }
     }
-  }
-}
+}

+ 24 - 12
src/Hotline.Api/StartupExtensions.cs

@@ -3,7 +3,6 @@ using FluentValidation;
 using FluentValidation.AspNetCore;
 using Hotline.Api.Realtimes;
 using Hotline.Application;
-using Hotline.Application.Contracts;
 using Hotline.NewRock;
 using Hotline.Permissions;
 using Hotline.Repository.SqlSugar.Extensions;
@@ -37,8 +36,11 @@ using Hotline.Orders.DatabaseEventHandler;
 using Hotline.Ai.XingTang;
 using Hotline.Pdf;
 using Hotline.XingTang;
-using Hotline.ThirdAccountDomainServices.Interfaces;
 using Hotline.Snapshot.IRepository;
+using Hotline.Validators;
+using Hotline.Api.Filter;
+using Hotline.Caching.Interfaces;
+using Hotline.Settings;
 
 
 namespace Hotline.Api;
@@ -56,7 +58,7 @@ internal static class StartupExtensions
 #if DEBUG
         builder.WebHost.UseUrls("http://*:50100");
 #endif
-        
+
         services.Configure<IdentityConfiguration>(d => configuration.GetSection(nameof(IdentityConfiguration)).Bind(d));
 
         var appConfigurationSection = configuration.GetRequiredSection(nameof(AppConfiguration));
@@ -69,10 +71,10 @@ internal static class StartupExtensions
         services.Configure<CallCenterConfiguration>(d => callCenterConfigurationSection.Bind(d));
 
         services.Configure<CityBaseConfiguration>(d => configuration.GetSection(nameof(CityBaseConfiguration)).Bind(d));
-		//services.Configure<SendSmsConfiguration>(d => configuration.GetSection("SendSms").Bind(d));
+        //services.Configure<SendSmsConfiguration>(d => configuration.GetSection("SendSms").Bind(d));
 
-		// Add services to the container.
-		services
+        // Add services to the container.
+        services
             .BatchInjectServices(d =>
             {
                 var attr = d.GetCustomAttribute(typeof(InjectionAttribute)) as InjectionAttribute;
@@ -85,11 +87,11 @@ internal static class StartupExtensions
             .AddScoped(typeof(IPasswordHasher<>), typeof(PasswordHasher<>))
             .AddHttpClient()
             ;
-        
+
         services.AddKeyedScoped<ISessionContext, ProvinceSessionContext>(ProvinceSessionContext.Key)
             .AddKeyedScoped<ISessionContext, Police110SessionContext>(Police110SessionContext.Key)
             ;
-        
+
         //cache
         services.AddCache(d =>
         {
@@ -144,7 +146,7 @@ internal static class StartupExtensions
                     .AddKeyedScoped<ISessionContext, ZzptSessionContext>(ZzptSessionContext.Key);
                 break;
             case AppDefaults.AppScope.ZiGong:
-				services.AddAiXingTang(appConfiguration.ZiGong.AiQuality.Url);
+                services.AddAiXingTang(appConfiguration.ZiGong.AiQuality.Url);
                 break;
             case AppDefaults.AppScope.LuZhou:
                 break;
@@ -195,7 +197,7 @@ internal static class StartupExtensions
         {
             config.DisableDataAnnotationsValidation = true;
         })
-            .AddValidatorsFromAssembly(typeof(AppContractsStartupExtensions).Assembly);
+            .AddValidatorsFromAssembly(typeof(ValidatorExtensions).Assembly);
 
         //mq
         services.AddMq(configuration);
@@ -222,8 +224,18 @@ internal static class StartupExtensions
 
         services.AddScoped<IGuiderSystemService, TiqnQueService>();
 
-        //services.AddScoped<LogFilterAttribute>();
-        //ServiceLocator.Instance = services.BuildServiceProvider();
+        services.AddScoped<ClientIpFilterAttribute>(sp =>
+        {
+            var loggerFactory = sp.GetService<ILoggerFactory>();
+            var logger = loggerFactory.CreateLogger<ClientIpFilterAttribute>();
+            var cacheManager = sp.GetService<ISystemSettingCacheManager>();
+            var whiteIps = cacheManager.GetSetting(SettingConstants.WhiteIp).SettingValue
+                .Where(d => !string.IsNullOrEmpty(d))
+                .ToList();
+
+            return new ClientIpFilterAttribute(whiteIps, logger);
+        });
+
         return builder.Build();
     }
 

+ 3 - 0
src/Hotline.Api/StartupHelper.cs

@@ -2,8 +2,10 @@
 using System.IO.Compression;
 using System.Reflection;
 using System.Text;
+using Hotline.Api.Filter;
 using Hotline.Application;
 using Hotline.Application.Jobs;
+using Hotline.Caching.Interfaces;
 using Hotline.CallCenter.Configs;
 using Hotline.Configurations;
 using Hotline.DI;
@@ -11,6 +13,7 @@ using Hotline.EventBus;
 using Hotline.Identity;
 using Hotline.Repository.SqlSugar;
 using Hotline.Repository.SqlSugar.Ts;
+using Hotline.Settings;
 using Mapster;
 using MapsterMapper;
 using MediatR.Pipeline;

+ 12 - 0
src/Hotline.Api/config/appsettings.Test.json

@@ -0,0 +1,12 @@
+{
+  "ConnectionStrings": {
+    "Hotline": "PORT=5432;DATABASE=hotline_test;HOST=110.188.24.182;PASSWORD=fengwo11!!;USER ID=dev;",
+    //"Hotline1": "PORT=5432;DATABASE=hotline_dev;HOST=110.188.24.182;PASSWORD=fengwo11!!;USER ID=dev;"
+  },
+  "Cache": {
+    "Host": "110.188.24.182",
+    "Port": 50179,
+    "Password": "fengwo123!$!$",
+    "Database": 2 //hotline:3, dev:5, test:2, demo:4
+  }
+}

+ 51 - 50
src/Hotline.Api/config/appsettings.json

@@ -1,7 +1,48 @@
 {
   "AllowedHosts": "*",
+  "AppConfiguration": {
+    "AppScope": "ZiGong",
+    "YiBin": {
+      "AreaCode": "511500",
+      "CallCenterType": "TianRun", //XunShi、WeiErXin、TianRun、XingTang
+      //智能回访
+      "AiVisit": {
+        "Url": "http://118.122.73.80:19061",
+        "Appkey": "MTAwMDAx",
+        "ServiceVersion": "V1.0.0" //接口版本号
+      },
+      //智能质检
+      "AiQuality": {
+        "Url": "http://118.122.73.80:19072/" // 正式
+        //"Url": "http://118.122.73.80:19072/", // 测试
+      },
+      //企业服务
+      "Enterprise": {
+        "AddressUrl": "http://10.12.185.227:8834/",
+        "ClientId": "1462598736",
+        "ClientSecret": "6nZtVK4rKfnsncGymUHB",
+        "TenantId": "000000"
+      }
+    },
+    "ZiGong": {
+      //智能质检
+      "AiQuality": {
+        "Url": "http://175.10.86.234:10095" //  测试
+        //"Url": "http://10.56.131.13:10095/", // 正式
+      },
+      "AreaCode": "510300",
+      "CallCenterType": "XingTang"
+    },
+    "LuZhou": {
+      "AreaCode": "510500",
+      "CallCenterType": "XingTang"
+    },
+    "FileUpload": {
+      "Url": "http://110.188.24.28:50120/"
+    }
+  },
   "CallCenterConfiguration": {
-    "CallCenterType": "XingTang", //XunShi、WeiErXin、TianRun、XingTang
+    //"CallCenterType": "TianRun", //XunShi、WeiErXin、TianRun、XingTang
     "NewRock": {
       "Address": "http://192.168.100.100/xml",
       "Authorize": true,
@@ -25,24 +66,24 @@
     },
     "XingTang": {
       //"DbConnectionString": "server=123.56.10.71;Database=callcenter_db;Uid=root;Pwd=Lhw1981!(*!"
-      "DbConnectionString": "server=110.188.24.182;Database=callcenter_db;Uid=dev;Pwd=fengwo123!@#"
+      "DbConnectionString": "server=110.188.24.182;Database=callcenter_xingtang;Uid=dev;Pwd=fengwo11!!"
+      //"DbConnectionString": "PORT=50143;server=110.188.24.182;Database=callcenter_db;Uid=dev;Pwd=fengwo123!@#;"
     }
   },
   "ConnectionStrings": {
-    "Hotline": "PORT=5432;DATABASE=hotline_dev;HOST=110.188.24.182;PASSWORD=fengwo11!!;USER ID=dev;"
-    //"Redis": "110.188.24.182:50179,password=fengwo22@@",
-    //"MongoDB": "mongodb://192.168.100.121:27017",
-    //"Wex": "server=222.212.82.225;Port=4509;Database=fs_kft;Uid=root;Pwd=Wex@12345;"
+    "Hotline": "PORT=5432;DATABASE=hotline;HOST=110.188.24.182;PASSWORD=fengwo11!!;USER ID=dev;",
+    //"Hotline1": "PORT=5432;DATABASE=hotline_dev;HOST=110.188.24.182;PASSWORD=fengwo11!!;USER ID=dev;"
   },
   "Cache": {
     "Host": "110.188.24.182",
     "Port": 50179,
     "Password": "fengwo123!$!$",
-    "Database": 5 
+    "Database": 3 //hotline:3, dev:5, test:2, demo:4
   },
   "Swagger": true,
+  "AccLog": false,
   "Cors": {
-    "Origins": [ "http://10.210.252.122:8888", "http://localhost:8888", "http://localhost:8113", "http://hotline.12345lm.cn", "http://110.188.24.28:50101", "http://110.188.24.28:50102", "http://110.188.24.28:50301", "http://bs.hotline.12345lm.cn" ]
+    "Origins": [ "http://localhost:8888", "http://admin.hotline.fw.com", "http://localhost:80", "http://localhost:8113" ]
   },
   "IdentityConfiguration": {
     "Password": {
@@ -59,7 +100,7 @@
     },
     "Lockout": {
       "MaxFailedAccessAttempts": 5,
-      "DefaultLockoutTimeSpan": "00:01:00"
+      "DefaultLockoutTimeSpan": "00:10:00"
     },
     "Account": {
       "DefaultPassword": "Fwkj@789"
@@ -86,49 +127,9 @@
       "VirtualHost": "fwt-master"
     }
   },
-  "SmsAccountInfo": {
-    "MessageServerUrl": "http://webservice.fway.com.cn:1432/FWebService.asmx/FWay_Service", //短信发送地址
-    "AccountUser": "CS12345", //短信系统账号
-    "AccountPwd": "9EE3899305A8FC97D6146CAC6B802E6F", //短信系统密码
-    "ReturnAccountUser": "fwkj", //短信回传账号
-    "ReturnAccountPwd": "fwkj12" //短信回传密码
-  },
   "FwClient": {
     "ClientId": "hotline",
     "ClientSecret": "08db29cc-0da0-4adf-850c-1b2689bd535d"
-  },
-  "ConfigCenter": {
-    "ServerAddresses": [ "http://110.188.24.28:8848" ],
-    "Namespace": "f5017bc5-af0a-4f85-8e38-6718accc8f36", //dev
-    "ServiceName": "hotline"
-  },
-  "Tr": {
-    //"Address": "http://internal.ttf-cti.com:8080",
-    //"Username": "yscs",
-    //"Password": "123456"
-    "Address": "http://222.213.23.229:29003/",
-    "Username": "root",
-    "Password": "12345678aa",
-    "Ip": "222.213.23.229"
-  },
-  //智能质检
-  "AiQuality": {
-    "Url": "http://118.121.201.140:19072/"
-  },
-  //智能回访
-  "AiVisit": {
-    "Url": "http://118.122.73.80:19061",
-    "Appkey": "MTAwMDAx",
-    "ServiceVersion": "V1.0.0" //接口版本号
-  },
-  //企业服务
-  "Enterprise": {
-    "AddressUrl": "http://172.15.28.11:8835",
-    "ClientId": "1476116956",
-    "ClientSecret": "6NVnvmsFSC8d3qwgBZmN",
-    "TenantId": "000000"
-  },
-  "SendSms": {
-    "Url": "http://110.188.24.28:50108/api/v1/PushMessage/addwaitmsg"
   }
+
 }

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

@@ -56,9 +56,6 @@ public class SnapshotMapperConfigs : IRegister
         config.ForType<Hotline.File.File, IndustryFileDto>()
             .Map(m => m.AdditionId, n => n.Additions);
 
-        config.ForType<IndustryFileDto, Hotline.File.File>()
-            .Map(m => m.Additions, n => n.AdditionId);
-
         config.ForType<SnapshotFileInDto, File.File>()
             .Map(m => m.Additions, n => n.AdditionId);
 

+ 7 - 0
src/Hotline.Application/OrderApp/OrderApplication.cs

@@ -1521,6 +1521,10 @@ public class OrderApplication : IOrderApplication, IScopeDependency
                 visit.OrderVisitDetails[i].ScreenByEndTime = await _orderDomainService.GetScreenByEndTime();
             }
         }
+        if (dto.IsTransact.HasValue && dto.IsTransact.Value)
+        {
+            visit.Order.VisitReTransactNum = visit.Order.VisitReTransactNum is null ? 1 : visit.Order.VisitReTransactNum + 1;
+        }
 
         await _orderVisitRepository.UpdateAsync(visit, cancellationToken);
         await _orderVisitedDetailRepository.UpdateRangeAsync(visit.OrderVisitDetails, cancellationToken);
@@ -1812,6 +1816,7 @@ public class OrderApplication : IOrderApplication, IScopeDependency
         //}
 
         query = query
+                .WhereIF(!string.IsNullOrEmpty(dto.PoliticalIdentityValue), d => d.PoliticalIdentityValue == dto.PoliticalIdentityValue)//政治身份查询
                 .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)) //省编号
@@ -3994,6 +3999,8 @@ public class OrderApplication : IOrderApplication, IScopeDependency
                     .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)
+            .WhereIF(dto.IsEmployeeNameNull.HasValue && dto.IsEmployeeNameNull == true, d => d.EmployeeId != null && d.EmployeeId != "")
+            .WhereIF(dto.IsEmployeeNameNull.HasValue && dto.IsEmployeeNameNull == false, d => d.EmployeeId == null || d.EmployeeId == "")
 
             .OrderByIF(_appOptions.Value.IsYiBin && dto.VisitStateQuery != EVisitStateQuery.Visited, d => d.Order.IsUrgent, OrderByType.Desc)
             .OrderByIF(_appOptions.Value.IsZiGong == false, d => d.PublishTime, OrderByType.Desc)

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

@@ -125,6 +125,10 @@ public class IndustryApplication : IIndustryApplication, IScopeDependency
             await _fileRepository.Removeable().Where(m => m.Key == entity.Id).ExecuteCommandAsync(requestAborted);
             await _fileRepository.AddRangeAsync(fileEntities, requestAborted);
         }
+        else
+        {
+            await _fileRepository.Removeable().Where(m => m.Key == entity.Id).ExecuteCommandAsync(requestAborted);
+        }
         dto.Adapt(entity);
         await _industryRepository.UpdateAsync(entity, requestAborted);
     }
@@ -400,7 +404,7 @@ public class IndustryApplication : IIndustryApplication, IScopeDependency
                 PhoneNumber = volunteer.DeclarePhoneNumber,
                 FullAddress = volunteer.Address + volunteer.FullAddress,
             }, true)
-            .Mapper((item, cache)=> 
+            .Mapper((item, cache) =>
             {
                 item.Files = _fileRepository.Queryable()
                 .Where(file => file.Key == item.Id)

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

@@ -19,6 +19,7 @@ using Mapster;
 using Microsoft.AspNetCore.Http;
 using Microsoft.EntityFrameworkCore;
 using Microsoft.Extensions.Logging;
+using Microsoft.Identity.Client;
 using SqlSugar;
 using SqlSugar.Extensions;
 using System.ComponentModel;
@@ -357,16 +358,17 @@ public class RedPackApplication : IRedPackApplication, IScopeDependency
 
     public async Task RevocationRedPackSpecialAuditAsync(IList<string> ids)
     {
+        var specialRedPackAuditIds = await _specialRedPackAuditRepository.Queryable()
+            .LeftJoin<Order>((special, order) => special.OrderId == order.Id)
+            .LeftJoin<RedPackAudit>((special, order, audit) => order.Id == audit.OrderId)
+            .Where((special, order, audit) => ids.Contains(audit.Id))
+            .Select((special, order, audit) => special.Id).ToListAsync();
         var has = await _specialRedPackAuditRepository.Queryable()
-            .Where(m => m.IsSend == true && ids.Contains(m.Id))
+            .Where(m => m.IsSend == true && specialRedPackAuditIds.Contains(m.Id))
             .AnyAsync();
         if (has) throw new UserFriendlyException("该工单已发放红包,不能撤销审批");
-        await _specialRedPackAuditRepository.Updateable()
-            .SetColumns(m => m.Status, ERedPackAuditStatus.Pending)
-            .SetColumns(m => m.AuditType, null)
-            .SetColumns(m => m.AuditTypeCode, null)
-            .SetColumns(m => m.AuditRemark, null)
-            .Where(m => ids.Contains(m.Id) && m.IsSend == false)
+        await _specialRedPackAuditRepository.Removeable()
+            .Where(m => specialRedPackAuditIds.Contains(m.Id))
             .ExecuteCommandAsync();
     }
 

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

@@ -368,7 +368,10 @@ public class SnapshotOrderApplication : IOrderSnapshotApplication, IScopeDepende
             snapshot.VerifyType = dto.Data.VerifyType;
             snapshot.IsCheckList = dto.Data.IsCheckList;
             snapshot.CompliantType = dto.Data.CompliantType;
-            dto.Workflow.Opinion += $" 核实方式: {dto.Data.VerifyType}  是否按清单检查: {(dto.Data.IsCheckList.Value ? "是" : "否")}";
+            if (dto.Data.VerifyType != null)
+                dto.Workflow.Opinion += $" 核实方式: {dto.Data.VerifyType}";
+            if (dto.Data.IsCheckList.HasValue)
+                dto.Workflow.Opinion += $" 是否按清单检查: {(dto.Data.IsCheckList.Value ? "是" : "否")}";
             await _orderSnapshotRepository.UpdateAsync(snapshot);
             return;
         }

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

@@ -75,6 +75,7 @@ public class SnapshotThirdAccountSupplier : IThirdAccountSupplier, IScopeDepende
         {
             thirdDto.AppId = _systemSettingCacheManager.WxOpenAppId;
             thirdDto.Secret = _systemSettingCacheManager.WxOpenAppSecret;
+            thirdDto.WebApiHost = _systemSettingCacheManager.WxApiHost;
         }
         return await Task.FromResult(thirdDto);
     }

+ 23 - 11
src/Hotline.Application/StatisticalReport/OrderReportApplication.cs

@@ -5,6 +5,7 @@ using Hotline.FlowEngine.WorkflowModules;
 using Hotline.FlowEngine.Workflows;
 using Hotline.Identity.Accounts;
 using Hotline.Orders;
+using Hotline.Repository.SqlSugar.Extensions;
 using Hotline.SeedData;
 using Hotline.Settings;
 using Hotline.Settings.Hotspots;
@@ -3018,7 +3019,7 @@ namespace Hotline.Application.StatisticalReport
             var IsCenter = _sessionContext.OrgIsCenter;
             DataTable data = new DataTable();
 
-            data = await _hotspotTypeRepository.Queryable()
+            var  list = 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)
@@ -3026,21 +3027,32 @@ namespace Hotline.Application.StatisticalReport
             .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
-            {
+            .Select((it, o) => new HotspotStatisticsDto
+			{
                 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 = "分类统计";
+            .ToListAsync();
+            data = list.ToDataTable("HotspotStatistics");
+
+			data.Columns["HotspotName"].SetOrdinal(0);
+            data.Columns["OneHotspotName"].SetOrdinal(1);
+			data.Columns["TwoHotspotName"].SetOrdinal(2);
+			data.Columns["ThreeHotspotName"].SetOrdinal(3);
+			data.Columns["FourHotspotName"].SetOrdinal(4);
+			data.Columns["FiveHotspotName"].SetOrdinal(5);
+			data.Columns["SumCount"].ColumnName = "分类统计";
             data.Columns["HotspotName"].ColumnName = "热点名称";
-            data.Columns["HotSpotFullName"].ColumnName = "热点分级";
-            //合计
-            DataRow sumRow = data.NewRow();
+			data.Columns["OneHotspotName"].ColumnName = "一级热点";
+			data.Columns["TwoHotspotName"].ColumnName = "二级热点";
+			data.Columns["ThreeHotspotName"].ColumnName = "三级热点";
+			data.Columns["FourHotspotName"].ColumnName = "四级热点";
+			data.Columns["FiveHotspotName"].ColumnName = "五级热点";
+			data.Columns.Remove("HotSpotFullName");
+			//data.Columns["HotSpotFullName"].ColumnName = "热点分级";
+			//合计
+			DataRow sumRow = data.NewRow();
             sumRow["热点名称"] = "合计";
             decimal totalAmount = 0;
             foreach (DataRow row in data.Rows)

+ 4 - 0
src/Hotline.Share/Dtos/FlowEngine/Workflow/PreviousWorkflowDto.cs

@@ -18,6 +18,10 @@ public class PreviousWorkflowDto : EndWorkflowIdDto
     //public FlowStepHandler? Handler { get; set; }
     public StepAssignInfo? Handler { get; set; }
 
+    /// <summary>
+    /// 是否退回至申请人
+    /// </summary>
+    public bool IsRecallToStartStep { get; set; }
     // /// <summary>
     // /// 逆向流程,节点指派方式
     // /// 退回、特提

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

@@ -41,12 +41,9 @@ namespace Hotline.Share.Dtos.Order
 
         public string StatusText => Status.GetDescription();
 
-        /// <summary>
-		/// 终止状态
-		/// </summary>
-        public ETerminateStatus? TerminateStatus { get; set; }
+        public string? TerminateStatusText => IsTerminate == true ? "终止同意" : "";
 
-        public string? TerminateStatusText => TerminateStatus?.GetDescription();
+        public bool IsTerminate { get; set; }
     }
 
     public class OrderDto : UpdateOrderDto
@@ -1198,6 +1195,7 @@ namespace Hotline.Share.Dtos.Order
         /// 能否编辑,true:任何节点可以编辑;false:未发起流程可以编辑
         /// </summary>
         public bool IsEdit { get; set; }
+
     }
 
     public class OrderUploadFiles
@@ -1217,6 +1215,15 @@ namespace Hotline.Share.Dtos.Order
     public class AddOrderDto : Position
     {
         #region 来电信息
+        /// <summary>
+        /// 政治身份
+        /// </summary>
+        public string? PoliticalIdentityValue { get; set; }
+
+        /// <summary>
+        /// 政治身份
+        /// </summary>
+        public string? PoliticalIdentityName { get; set; }
 
         /// <summary>
         /// 来源渠道

+ 37 - 1
src/Hotline.Share/Dtos/Order/OrderVisitDto.cs

@@ -153,6 +153,11 @@ namespace Hotline.Share.Dtos.Order
         /// </summary>
         public bool? IsUpdate { get; set; }
 
+        /// <summary>
+        /// 回访人是否为空
+        /// </summary>
+        public bool? IsEmployeeNameNull { get; set;}
+
     }
 
     public record QueryOrderPublishStatisticsAllDto : PagedRequest
@@ -476,7 +481,18 @@ namespace Hotline.Share.Dtos.Order
         public bool? IsUpdate { get; set; }
 
         public List<VisitDetailDto> VisitDetails { get; set; }
-    }
+
+        /// <summary>
+        /// 是否重办
+        /// </summary>
+        public bool? IsTransact { get; set; }
+
+        /// <summary>
+        /// 重办对象
+        /// </summary>
+        public OrderReTransactDto? OrderReTransact { get; set; }
+
+	}
 
     public record VisitDetailDto
     {
@@ -813,6 +829,26 @@ namespace Hotline.Share.Dtos.Order
         public List<OrderVisitDetailDto> OrderVisitDetails { get; set; }
 
 
+        /// <summary>
+        /// 坐席满意度
+        /// </summary>
+        public ESeatEvaluate? SeatEvaluate => OrderVisitDetails.Where(x => x.VisitTarget == EVisitTarget.Seat).FirstOrDefault()?.SeatEvaluate;
+
+        /// <summary>
+        /// 坐席满意度
+        /// </summary>
+        public string? SeatEvaluateText => SeatEvaluate?.GetDescription() ?? string.Empty;
+
+        /// <summary>
+        /// 部门满意度
+        /// </summary>
+        public Kv? OrgProcessingResults => OrderVisitDetails.Where(x => x.VisitTarget == EVisitTarget.Org).FirstOrDefault()?.OrgProcessingResults;
+
+        /// <summary>
+        /// 回复内容
+        /// </summary>
+        public string? VisitContent => OrderVisitDetails.Where(x => x.VisitTarget == EVisitTarget.Org).FirstOrDefault()?.VisitContent ?? string.Empty;
+
         /// <summary>
         /// 可直接访问的通话录音地址
         /// </summary>

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

@@ -10,6 +10,11 @@ namespace Hotline.Share.Dtos.Order
 {
     public record QueryOrderDto : PagedKeywordRequest
     {
+        /// <summary>
+        /// 政治身份
+        /// </summary>
+        public string? PoliticalIdentityValue {  get; set; }
+
         /// <summary>
         /// 省本地编号(√)
         /// </summary>

+ 2 - 2
src/Hotline.Share/Dtos/Push/MessagePagedDto.cs

@@ -23,12 +23,12 @@ namespace Hotline.Share.Dtos.Push
         /// <summary>
         /// 开始时间
         /// </summary>
-        public DateTime? StartTime { get; set; }
+        public DateTime? CreationStartTime { get; set; }
 
         /// <summary>
         /// 结束时间
         /// </summary>
-        public DateTime? EndTime { get; set; }
+        public DateTime? CreationEndTime { get; set; }
 
         /// <summary>
         /// 电话号码

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

@@ -31,6 +31,11 @@ public class ThirdTokenInDto
     /// 3: 市民办件app
     /// </summary>
     public EAppType AppType { get; set; } = EAppType.Snapshot;
+
+    /// <summary>
+    /// 接口地址前缀
+    /// </summary>
+    public string? WebApiHost { get; set; }
 }
 
 public class ThirdPhoneInDto : ThirdTokenInDto { }

+ 42 - 0
src/Hotline.Share/Dtos/StatisticalReport/OrderDelayStatisicalReturnDto.cs

@@ -44,4 +44,46 @@ namespace Hotline.Share.Dtos.StatisticalReport
         /// </summary>
         public string NodeCode { get; set; }
     }
+
+    public class HotspotStatisticsDto { 
+    
+        /// <summary>
+        /// 分类名称
+        /// </summary>
+        public string HotspotName { get; set; }
+
+        /// <summary>
+        /// 分类全称
+        /// </summary>
+        public string HotSpotFullName { get; set; }
+
+        /// <summary>
+        /// 一级热点
+        /// </summary>
+        public string OneHotspotName => !string.IsNullOrEmpty(HotSpotFullName) && HotSpotFullName.Split("-").Length >= 1 && HotSpotFullName.Split("-")[0] == HotspotName ? HotSpotFullName.Split("-")[0] : string.Empty;
+
+        /// <summary>
+        /// 二级热点
+        /// </summary>
+        public string TwoHotspotName => !string.IsNullOrEmpty(HotSpotFullName) && HotSpotFullName.Split("-").Length >= 2 && HotSpotFullName.Split("-")[1] == HotspotName ? HotSpotFullName.Split("-")[1] : string.Empty;
+
+        /// <summary>
+        /// 三级热点
+        /// </summary>
+        public string ThreeHotspotName => !string.IsNullOrEmpty(HotSpotFullName) && HotSpotFullName.Split("-").Length >= 3 && HotSpotFullName.Split("-")[2] == HotspotName ? HotSpotFullName.Split("-")[2] : string.Empty;
+
+        /// <summary>
+        /// 四级热点
+        /// </summary>
+        public string FourHotspotName => !string.IsNullOrEmpty(HotSpotFullName) && HotSpotFullName.Split("-").Length >= 4 && HotSpotFullName.Split("-")[3] == HotspotName ? HotSpotFullName.Split("-")[3] : string.Empty;
+
+        /// <summary>
+        /// 五级热点
+        /// </summary>
+        public string FiveHotspotName => !string.IsNullOrEmpty(HotSpotFullName) && HotSpotFullName.Split("-").Length >= 5 && HotSpotFullName.Split("-")[4] == HotspotName ? HotSpotFullName.Split("-")[4] : string.Empty;
+
+
+        public int SumCount { get; set; }
+
+	}
 }

+ 22 - 0
src/Hotline.Share/Enums/Order/EOrderUpdateSource.cs

@@ -0,0 +1,22 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Hotline.Share.Enums.Order
+{
+	public enum EOrderUpdateSource
+	{
+		/// <summary>
+		/// 受理列表
+		/// </summary>
+		Accepted = 0,
+
+		/// <summary>
+		/// 工单修改
+		/// </summary>
+		Alter =1
+
+	}
+}

+ 14 - 0
src/Hotline.Share/Requests/PagedKeywordRequest.cs

@@ -1,4 +1,5 @@
 using Hotline.Share.Dtos;
+using Hotline.Share.Dtos.Order;
 using Hotline.Share.Enums.Order;
 using System.ComponentModel.DataAnnotations;
 using XF.Utility.EnumExtensions;
@@ -371,6 +372,19 @@ public record VisitMeasureStatisticsRequest
     public string? VisitName { get; set; }
 }
 
+public record AiVisitNoSatisfiedPageListRequest : PagedRequest
+{
+    public DateTime StartTime { get; set; }
+    public DateTime EndTime { get; set; }
+
+    public string? VisitName { get; set; }
+}
+
+public class AiVisitNoSatisfiedPageListRep
+{
+    public OrderVisitDto OrderVisit { get; set; }
+}
+
 public record HotspotReportPagedRequest : ReportPagedRequest
 {
 

+ 9 - 0
src/Hotline.WeChat/WeChatService.cs

@@ -1,5 +1,6 @@
 using Hotline.Settings;
 using Hotline.Share.Dtos.Snapshot;
+using Hotline.Share.Tools;
 using Hotline.ThirdAccountDomainServices.Interfaces;
 using Microsoft.Extensions.Logging;
 using Senparc.CO2NET.Extensions;
@@ -27,6 +28,7 @@ public class WeChatService : IThirdIdentiyService, IScopeDependency
     {
         try
         {
+            GetWebApiHost(dto.WebApiHost);
             var result = await SnsApi.JsCode2JsonAsync(dto.AppId, dto.Secret, dto.LoginCode);
             _systemLog.Add("微信获取Token", dto, status: 1, executeResult: result.ToJson());
             if (result.errcode != ReturnCode.请求成功) throw UserFriendlyException.SameMessage("获取微信用户信息失败");
@@ -47,6 +49,7 @@ public class WeChatService : IThirdIdentiyService, IScopeDependency
     /// <returns></returns>
     public async Task<ThirdPhoneOutDto> GetPhoneNumberAsync(ThirdTokenDto dto, CancellationToken token)
     {
+        GetWebApiHost(dto.WebApiHost);
         await AccessTokenContainer.RegisterAsync(dto.AppId, dto.Secret);
         _logger.LogInformation($"GetPhoneNumberAsync: {dto.ToJson()}");
         var result = await BusinessApi.GetUserPhoneNumberAsync(dto.AppId, dto.TelCode);
@@ -61,4 +64,10 @@ public class WeChatService : IThirdIdentiyService, IScopeDependency
             PhoneNumber = result.phone_info?.phoneNumber,
         };
     }
+
+    private void GetWebApiHost(string? webApiHost)
+    {
+        if (webApiHost.NotNullOrEmpty())
+            Config.ApiMpHost = webApiHost;
+    }
 }

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

@@ -68,6 +68,11 @@ namespace Hotline.Caching.Interfaces
         /// </summary>
         string WxOpenAppId { get; }
 
+        /// <summary>
+        /// 微信ApiHost
+        /// </summary>
+        string WxApiHost { get; }
+
         /// <summary>
         /// 文件服务器地址
         /// </summary>

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

@@ -160,6 +160,12 @@ namespace Hotline.Caching.Services
         public string WxOpenAppId =>
             GetOrDefault("08dccbbc-090a-4446-842f-be95fc74c95e", SettingConstants.WxOpenAppId, "微信小程序OpenId", "wx22e6092dd504b567", "微信小程序OpenId");
 
+        /// <summary>
+        /// 微信小程序WebApi地址
+        /// </summary>
+        public string WxApiHost =>
+            GetOrDefault("fafcd1b0-72fb-cfe0-7c82-3a1926c8a6cb", SettingConstants.WxApiHost, "微信小程序WebApi", "https://api.weixin.qq.com", "微信小程序WebApi");
+
         /// <summary>
         /// 文件服务器地址
         /// </summary>
@@ -211,5 +217,6 @@ namespace Hotline.Caching.Services
         public bool IsNoPushCallNativeOutNoFile => GetOrDefault("08dd23d1-5c5b-4262-8c99-8c83710723ea", SettingConstants.IsNoPushCallNativeOutNoFile, "是否不推送呼出的无文件的通话记录(true: 不推送, false: 推送)", true, "是否不推送呼出的无文件的通话记录(true: 不推送, false: 推送)");
 
         public bool IsSendDissatisfiedScreenSms => GetOrDefault("08db29ad-9b54-44ca-8d4d-35c032748040", SettingConstants.IsSendDissatisfiedScreenSms, "回访不满意是否发送甄别短信", true, "true:发送;false:不发送");
+
     }
 }

+ 4 - 1
src/Hotline/FlowEngine/Workflows/IWorkflowDomainService.cs

@@ -103,7 +103,7 @@ namespace Hotline.FlowEngine.Workflows
         /// </summary>
         Task<(Workflow workflow, WorkflowStep currentStep, StepDefine prevDefine,
                 WorkflowStep prevStep, WorkflowStep newStep, EFlowDirection flowDirection)> PreviousAsync
-            (Workflow workflow, PreviousWorkflowDto dto, OperatorInfo operatorInfo, 
+            (Workflow workflow, PreviousWorkflowDto dto, OperatorInfo operatorInfo,
                 EHandleMode handleMode = EHandleMode.Previous,
                 Action<Workflow, WorkflowStep, StepDefine, WorkflowStep, WorkflowStep>? newStepConfig = null,
                 CancellationToken cancellationToken = default);
@@ -143,6 +143,9 @@ namespace Hotline.FlowEngine.Workflows
         /// <summary>
         /// 撤回至开始节点
         /// </summary>
+        Task RecallToStartStepAsync(string workflowId, string opinion, DateTime? expiredTime, bool isOrderFiled, EHandleMode handleMode,
+    EFlowAssignType? flowAssignType = EFlowAssignType.Role, CancellationToken cancellationToken = default);
+
         Task RecallToStartStepAsync(Workflow workflow, string opinion, DateTime? expiredTime, bool isOrderFiled, EHandleMode handleMode,
             EFlowAssignType? flowAssignType = EFlowAssignType.Role, CancellationToken cancellationToken = default);
 

+ 10 - 4
src/Hotline/FlowEngine/Workflows/WorkflowDomainService.cs

@@ -16,17 +16,13 @@ using SqlSugar;
 using Hotline.EventBus;
 using XF.Domain.Authentications;
 using XF.Domain.Dependency;
-using XF.Domain.Entities;
 using XF.Domain.Exceptions;
 using XF.Domain.Repository;
 using System.Text;
-using FluentValidation;
 using Hotline.Configurations;
 using Hotline.Share.Dtos.File;
 using Hotline.Share.Dtos.FlowEngine.Workflow;
-using Hotline.Validators.FlowEngine;
 using Microsoft.Extensions.Options;
-using Microsoft.AspNetCore.Http;
 
 namespace Hotline.FlowEngine.Workflows
 {
@@ -1822,6 +1818,15 @@ namespace Hotline.FlowEngine.Workflows
         /// <summary>
         /// 撤回至开始节点
         /// </summary>
+        public async Task RecallToStartStepAsync(string workflowId, string opinion, DateTime? expiredTime, bool isOrderFiled,
+            EHandleMode handleMode, EFlowAssignType? flowAssignType = EFlowAssignType.Role, CancellationToken cancellationToken = default)
+        {
+            var workflow = await GetWorkflowAsync(workflowId, withDefine: true, withSteps: true, withTraces: true, withCountersigns: true,
+                cancellationToken: cancellationToken);
+
+            await RecallToStartStepAsync(workflow, opinion, expiredTime, isOrderFiled, handleMode, flowAssignType, cancellationToken);
+        }
+
         public async Task RecallToStartStepAsync(Workflow workflow, string opinion,
             DateTime? expiredTime, bool isOrderFiled, EHandleMode handleMode, EFlowAssignType? flowAssignType,
             CancellationToken cancellationToken = default)
@@ -4163,6 +4168,7 @@ namespace Hotline.FlowEngine.Workflows
             };
         }
 
+
         #endregion
     }
 }

+ 21 - 4
src/Hotline/Orders/Order.cs

@@ -45,6 +45,17 @@ namespace Hotline.Orders
         public string? FirstVisitResultCode { get; set; }
 
         #region 来电信息
+        /// <summary>
+        /// 政治身份
+        /// </summary>
+        [SugarColumn(ColumnDescription = "政治身份")]
+        public string? PoliticalIdentityValue { get; set; }
+
+        /// <summary>
+        /// 政治身份
+        /// </summary>
+        [SugarColumn(ColumnDescription = "政治身份")]
+        public string? PoliticalIdentityName { get; set; }
 
         /// <summary>
         /// 来源渠道(电话、网站、APP等)
@@ -951,10 +962,16 @@ namespace Hotline.Orders
         [SugarColumn(ColumnDescription = "重办次数")]
         public int? ReTransactNum { get; set; }
 
-        /// <summary>
-        /// 派单退回次数
-        /// </summary>
-        [SugarColumn(ColumnDescription = "派单退回次数")]
+		/// <summary>
+		/// 回访重办次数
+		/// </summary>
+		[SugarColumn(ColumnDescription = "重办次数")]
+		public int? VisitReTransactNum { get; set; }
+
+		/// <summary>
+		/// 派单退回次数
+		/// </summary>
+		[SugarColumn(ColumnDescription = "派单退回次数")]
         public int? SendBackNum { get; set; }
 
         /// <summary>

+ 7 - 1
src/Hotline/Orders/OrderCopy.cs

@@ -1,4 +1,5 @@
-using SqlSugar;
+using Hotline.Share.Enums.Order;
+using SqlSugar;
 using System;
 using System.Collections.Generic;
 using System.ComponentModel;
@@ -30,5 +31,10 @@ namespace Hotline.Orders
 		///  修改时间
 		/// </summary>
 		public DateTime AuditTime { get; set; }
+
+		/// <summary>
+		/// 工单修改来源
+		/// </summary>
+		public EOrderUpdateSource? AuditSource { get; set; }
 	}
 }

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

@@ -190,6 +190,19 @@ public class SystemDicDataSeedData : ISeedData<SystemDicData>
                 new () { Id = "08dd6060-7913-4b4b-83cf-475f692e93ae", DicDataValue = "default", DicDataName = "LocalHeadImageDefault", Sort = 2}
                 ];
         }
+        if (dicTypeCode == SysDicTypeConsts.InstaShotSpecialReason)
+        {
+            return [
+                new() { Id= "08dcd790-af29-4815-83b2-35df6e9e3400", DicDataValue = "工单内容不规范", DicDataName = "工单内容不规范", Sort = 1},
+                new () { Id = "08dcd790-b7e2-4905-895d-05b04c5a522b", DicDataValue = "回访不满意", DicDataName = "回访不满意", Sort = 2},
+                new () { Id = "08dcd790-f209-440c-8639-3c8312fe00ae", DicDataValue = "随手拍无办理结果", DicDataName = "随手拍无办理结果", Sort = 3},
+                new () { Id = "08dcd790-fbe5-4aea-8036-d227ff3bc09f", DicDataValue = "随手拍乱回复", DicDataName = "随手拍乱回复", Sort = 4},
+                new () { Id = "08dcd791-0af7-4d24-8b7f-4712fb719fb0", DicDataValue = "未上传已整改图片", DicDataName = "未上传已整改图片", Sort = 5},
+                new () { Id = "08dcd790-c6f2-406c-85cb-63a5417218de", DicDataValue = "部门要求退回重办", DicDataName = "部门要求退回重办", Sort = 6},
+                new () { Id = "08dcd790-d67d-4617-8ec3-7b5d2be5e7bb", DicDataValue = "随手拍没有整改完成时间", DicDataName = "随手拍没有整改完成时间", Sort = 7},
+                new () { Id = "08dcd790-e54b-4da2-8a1a-bba70490b08c", DicDataValue = "随手拍整改时间过长", DicDataName = "随手拍整改时间过长", Sort = 8}
+                ];
+        }
 
         throw new NotImplementedException();
     }
@@ -197,6 +210,10 @@ public class SystemDicDataSeedData : ISeedData<SystemDicData>
     public SystemDicType GetType(string dicTypeCode)
     {
         var dicType = new string[2];
+        if (dicTypeCode == SysDicTypeConsts.InstaShotSpecialReason)
+        {
+            dicType = ["81c202b7-5c50-45e5-bbde-fb1904957f85", "随手拍特提原因"];
+        }
         if (dicTypeCode == SysDicTypeConsts.HeaderImages)
         {
             dicType = ["08dd6ac1-024b-49cb-8ce0-723246d62767", "用户默认头像集合"];

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

@@ -660,6 +660,11 @@ namespace Hotline.Settings
         /// </summary>
         public const string WxOpenAppId = "WxOpenAppId";
 
+        /// <summary>
+        /// 微信小程序ApiHost
+        /// </summary>
+        public const string WxApiHost = "WxApiHost";
+
         /// <summary>
         /// 微信小程序Secret
         /// </summary>
@@ -785,5 +790,10 @@ namespace Hotline.Settings
         /// 高频预警许排除的工单标题
         /// </summary>
         public const string HighMatterWarningFilterTitle = "HighMatterWarningFilterTitle";
+
+        /// <summary>
+        /// ip白名单
+        /// </summary>
+        public const string WhiteIp = "WhiteIp";
     }
 }

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

@@ -211,7 +211,6 @@ public class SysDicTypeConsts
     /// </summary>
     public const string InstaShotSpecialReason = "InstaShotSpecialReason";
 
-
     /// <summary>
     /// 网民评价类型
     /// </summary>

+ 3 - 1
src/Hotline/Snapshot/Services/SnapshotPointsDomainService.cs

@@ -30,7 +30,9 @@ public class SnapshotPointsDomainService : ISnapshotPointsDomainService, IScopeD
             .Where((snapshot, industry) => snapshot.Id == orderId)
             .Select((snapshot, industry) => new { snapshot.Id, industry.ReportPoints , industry.ArgeePoints , industry.RefusePoints,  industry.Name,
                 snapshot.CreatorId})
-            .FirstAsync() ?? throw new UserFriendlyException($"{orderId} 工单不存在");
+            .FirstAsync();
+        if (order == null) return;
+        
         if (order.ReportPoints.HasValue == false)
             throw new UserFriendlyException($"{order.Name} 行业未配置积分");
 

+ 9 - 0
test/Hotline.Tests/Application/IndustryApplicationTest.cs

@@ -18,6 +18,7 @@ using Hotline.ThirdAccountDomainServices;
 using Hotline.Repository.SqlSugar.Extensions;
 using Hotline.Snapshot.IRepository;
 using Hotline.Application.Snapshot.Contracts;
+using Hotline.Application.Snapshot;
 
 namespace Hotline.Tests.Application;
 public class IndustryApplicationTest : TestBase
@@ -41,6 +42,14 @@ public class IndustryApplicationTest : TestBase
         items.ShouldNotBeNull();
     }
 
+    [Fact]
+    public async Task GetIndustryDetail_Test()
+    {
+        var industry = await _industryApplication.GetIndustres(new IndustryListInDto("电气焊", null)).FirstAsync();
+        var detail = await _industryApplication.GetIndustryDetailAsync(industry.Id, CancellationToken.None);
+        detail.Name.ShouldNotBeNull();
+    }
+
     [Fact]
     public async Task UpdateIndustry_Test()
     {

+ 4 - 3
test/Hotline.Tests/Infrastructure/WeiXinTest.cs

@@ -1,17 +1,18 @@
 using Hotline.Share.Dtos.Snapshot;
+using Hotline.ThirdAccountDomainServices.Interfaces;
 using Hotline.WeChat;
 
 namespace Hotline.Tests.Infrastructure;
 public class WeiXinTest
 {
-    private readonly WeChatService _weChatService;
+    private readonly IThirdIdentiyService _weChatService;
 
-    public WeiXinTest(WeChatService weChatService)
+    public WeiXinTest(IThirdIdentiyService weChatService)
     {
         _weChatService = weChatService;
     }
 
-    // [Fact]
+    [Fact]
     public async Task GetPhoneNumber_Test()
     {
         var result = await _weChatService.GetPhoneNumberAsync(new ThirdTokenDto { AppId = "12333", Secret = "4444444" }, CancellationToken.None);

+ 3 - 2
test/Hotline.Tests/Startup.cs

@@ -53,6 +53,7 @@ using Xunit.DependencyInjection.AspNetCoreTesting;
 using Hotline.Pdf;
 using Hotline.ThirdAccountDomainServices.Interfaces;
 using Hotline.Snapshot.IRepository;
+using Hotline.WeChat;
 
 namespace Hotline.Tests;
 public class Startup
@@ -146,8 +147,8 @@ public class Startup
                 .ToList()
                 .ForEach(d => ServiceRegister.Register(services, d));
 
-            // services.AddScoped<IThirdIdentiyService, WeChatService>();
-            services.AddScoped<IThirdIdentiyService, ThirdTestService>();
+            services.AddScoped<IThirdIdentiyService, WeChatService>();
+            //services.AddScoped<IThirdIdentiyService, ThirdTestService>();
 
             services.AddScoped<IUpdateDatabaseEvent<OrderVisitDetail>, OrderVisitDetailEventHandler>();