Kaynağa Gözat

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

田爽 5 ay önce
ebeveyn
işleme
a09c6ccb43

+ 29 - 7
src/Hotline.Api/Controllers/AiController.cs

@@ -1,5 +1,6 @@
 
 using Consul;
+using DocumentFormat.OpenXml.Wordprocessing;
 using DotNetCore.CAP;
 using Hotline.Ai.CallOut;
 using Hotline.Ai.Jths;
@@ -661,7 +662,7 @@ namespace Hotline.Api.Controllers
                                     x.VisitContent = seatVisitContent;
                                 });
 
-                                await _orderVisitDetailRepository.UpdateRangeAsync(seatDetail, HttpContext.RequestAborted);
+                               
 
                                 //处理结果
                                 orgDetail.ForEach(x =>
@@ -678,8 +679,26 @@ namespace Hotline.Api.Controllers
                                     {
                                         //x.OrgNoSatisfiedReason = new List<Kv>() { new Kv() { Key = "7", Value = "未回复" } };
                                         //TODO 记录不满意原因到内容中供人工回访甄别选择不满意原因
-                                        aiOrderVisitDetail.OrderVisit.VisitState = Share.Enums.Order.EVisitState.WaitForVisit;
-                                        aiOrderVisitDetail.OrderVisit.IsEffectiveAiVisit = false;
+                                        if (dto.CallTimes.Value>=1)
+                                        {
+                                            aiOrderVisitDetail.OrderVisit.VisitState = EVisitState.Visited;
+                                            aiOrderVisitDetail.OrderVisit.NowEvaluate = new Kv() { Key = "6", Value = "未接通" };
+                                            x.VisitContent = "智能回访两次未接默认已回访";
+                                            x.Volved = true;
+                                            x.IsContact = true;
+                                            seatDetail.ForEach(x =>
+                                            {
+                                                x.VoiceEvaluate = Share.Enums.Order.EVoiceEvaluate.Satisfied;
+                                                x.SeatEvaluate =  ESeatEvaluate.DefaultSatisfied;
+                                                x.VisitContent = "智能回访两次未接默认已回访";
+                                            });
+                                        }
+                                        else
+                                        {
+                                            aiOrderVisitDetail.OrderVisit.VisitState = Share.Enums.Order.EVisitState.WaitForVisit;
+                                            aiOrderVisitDetail.OrderVisit.IsEffectiveAiVisit = false;
+                                        }
+                                        
                                     }
                                     else
                                     {
@@ -687,6 +706,7 @@ namespace Hotline.Api.Controllers
                                         aiOrderVisitDetail.OrderVisit.IsEffectiveAiVisit = true;
                                     }
                                 });
+                                await _orderVisitDetailRepository.UpdateRangeAsync(seatDetail, HttpContext.RequestAborted);
                                 await _orderVisitDetailRepository.UpdateRangeAsync(orgDetail, HttpContext.RequestAborted);
 
                                 //var first = orgProcessingResults; //aiOrderVisitDetail.OrderVisit.OrderVisitDetails.FirstOrDefault(x => x.VisitTarget == EVisitTarget.Org);
@@ -1065,10 +1085,12 @@ namespace Hotline.Api.Controllers
         public async Task AddAiVisit([FromBody]AddAiVisitDto dto)
         {
             //验证是否有重复电话
-            if (dto.AiOrderVisitDetails.DistinctBy(x=>x.OuterNo.Trim()).Count() != dto.AiOrderVisitDetails.Count)
-            {
-                throw UserFriendlyException.SameMessage("任务中存在重复外呼号码,请检查后重新提交");
-            }
+            //有重复电话自动排除
+            dto.AiOrderVisitDetails = dto.AiOrderVisitDetails.DistinctBy(x => x.OuterNo.Trim()).ToList() ;
+            //if (dto.AiOrderVisitDetails.DistinctBy(x=>x.OuterNo.Trim()).Count() != dto.AiOrderVisitDetails.Count)
+            //{
+            //    throw UserFriendlyException.SameMessage("任务中存在重复外呼号码,请检查后重新提交");
+            //}
 
             var model = _mapper.Map<AiOrderVisit>(dto);
 

+ 59 - 5
src/Hotline.Api/Controllers/OrderController.cs

@@ -69,6 +69,8 @@ using Microsoft.AspNetCore.Components;
 using Quartz.Simpl;
 using static Lucene.Net.Util.Fst.Util;
 using DocumentFormat.OpenXml.Spreadsheet;
+using System.Threading;
+using Hotline.Caching.Services;
 
 namespace Hotline.Api.Controllers;
 
@@ -418,6 +420,22 @@ public class OrderController : BaseController
             await _publisher.PublishAsync(new ContingencyManagementNotify(order, dto.ArrangeTitle, dto.ArrangeContent, dto.ArrangeOpinion),
                 PublishStrategy.ParallelWhenAll, HttpContext.RequestAborted);
 
+        // 取消发布功能开关
+        var cancelPublishOrderEnabled = _systemSettingCacheManager.CancelPublishOrderEnabled;
+        // 取消发布的工单数量
+        var orderPublishDeletedCount = await _orderPublishRepository.Queryable(includeDeleted: true)
+            .Where(m => m.OrderId == order.Id && m.IsDeleted == true)
+            .CountAsync(HttpContext.RequestAborted);
+        var orderVisitVisitedCount = await _orderVisitRepository.Queryable()
+            .Where(m => m.OrderId == order.Id && m.VisitState == EVisitState.Visited)
+            .CountAsync(HttpContext.RequestAborted);
+
+        // 若取消发布的工单,已经被回访过了,没有经过重新办理,再次发布后,自动跳过回访环节,展示取消发布前的回访结果
+        if (orderPublishDeletedCount != 0 && orderVisitVisitedCount != 0 && cancelPublishOrderEnabled == true)
+        {
+            return;
+        }
+
         var orderVisit = new OrderVisit();
         orderVisit.No = order.No;
         orderVisit.OrderId = order.Id;
@@ -557,7 +575,6 @@ public class OrderController : BaseController
         return res;
     }
 
-
     /// <summary>
     /// 已发布列表
     /// </summary>
@@ -744,6 +761,22 @@ public class OrderController : BaseController
         return rsp;
     }
 
+    /// <summary>
+    /// 取消发布
+    /// </summary>
+    /// <param name="dto"></param>
+    /// <returns></returns>
+    [HttpPut("publish/cancel")]
+    public async Task<string> PublishCancelAsync([FromBody]PublishCancelInDto dto)
+    {
+        var enabled = _systemSettingCacheManager.CancelPublishOrderEnabled;
+        if (enabled == false) return "取消发布功能已被关闭";
+        var publish = await _orderPublishRepository.GetAsync(dto.OrderPublishId)
+            ?? throw UserFriendlyException.SameMessage("发布单不存在");
+        publish.IsDeleted = true;
+        await _orderPublishRepository.UpdateAsync(publish);
+        return "取消成功";
+    }
     #endregion
 
     #region 工单回访
@@ -1558,7 +1591,7 @@ public class OrderController : BaseController
 
         //验证延期次数
         var setting = _systemSettingCacheManager.GetSetting(SettingConstants.DelayNum);
-        if (int.Parse(setting?.SettingValue[0]) != 0)
+        if (int.Parse(setting?.SettingValue[0]) != 0 && !_sessionContext.OrgIsCenter)
         {
             int count = await _orderDelayRepository.CountAsync(x =>
                 x.OrderId == delaydto.OrderId && x.ApplyOrgCode == _sessionContext.RequiredOrgId && x.DelayState == EDelayState.Pass);
@@ -3917,17 +3950,31 @@ public class OrderController : BaseController
     /// <summary>
     /// 批量归档
     /// </summary>
+    /// <returns>成功总量</returns>
     [HttpPost("batch-file")]
-    public async Task BatchFile([FromBody]OrderBatchFileDto dto)
+    public async Task<string> BatchFile([FromBody]OrderBatchFileDto dto)
     {
         var orders = await _orderRepository.Queryable()
+            .Includes(w=>w.Workflow,d => d.WorkflowDefinition)
+            .Includes(w=>w.Workflow,d => d.Steps)
+            .Includes(w=>w.Workflow,d => d.Traces)
             .Where(d => dto.OrderIds.Contains(d.Id))
             .ToListAsync(HttpContext.RequestAborted);
+        var success = 0;
         foreach (var order in orders)
         {
-            await _workflowDomainService.JumpToEndAsync(_sessionContext, order.WorkflowId,dto.Opinion,dto.Files,
+            var startStep = order.Workflow.Steps.Where(d => d.StepType == EStepType.Start && d.IsOrigin)
+                .MaxBy(d => d.CreationTime);
+            if (startStep?.Status is not EWorkflowStepStatus.Handled || string.IsNullOrEmpty(startStep.Opinion))
+                continue;
+            var opinion = startStep.Opinion;
+
+            await _workflowDomainService.JumpToEndAsync(_sessionContext, order.Workflow, opinion, dto.Files,
                 order.ExpiredTime, cancellationToken: HttpContext.RequestAborted);
+            success++;
         }
+
+        return $"批量办理{dto.OrderIds.Count}件,成功{success}件";
     }
 
     #endregion
@@ -4726,6 +4773,11 @@ public class OrderController : BaseController
         if (currentStep is null)
 	        currentStep = workflow.Steps.OrderByDescending(x => x.CreationTime).FirstOrDefault(x => x.StepType == EStepType.End);
 
+		if (string.IsNullOrEmpty(dto.Cause))
+		{
+			dto.Cause = dto.Reason;
+		}
+
 		var model = _mapper.Map<OrderSpecial>(dto);
         model.OrgId = currentStep is null ? _sessionContext.RequiredOrgId: currentStep.HandlerOrgId;
         model.OrgName = currentStep is null ? _sessionContext.OrgName : currentStep.HandlerOrgName;
@@ -4903,7 +4955,7 @@ public class OrderController : BaseController
 	/// <summary>
 	/// 工单重办信息
 	/// </summary>
-	/// <param name="dtos"></param>
+	/// <param name="dto"></param>
 	/// <returns></returns>
 	[HttpPost("re_transact")]
     [LogFilter("工单重办")]
@@ -5047,6 +5099,8 @@ public class OrderController : BaseController
                 .Where(o => o.Id == order.Id)
                 .ExecuteCommandAsync(HttpContext.RequestAborted);
 
+            // 重办清空已取消发布的工单的回访信息
+            await _orderDomainService.VisitNoneByCancelPublishAsync(order.Id, HttpContext.RequestAborted);
             //var visit = await _orderVisitRepository.GetAsync(x => x.OrderId == dto.OrderId && x.VisitState != EVisitState.None);
             //if (visit != null)
             //{

+ 11 - 0
src/Hotline.Api/Controllers/SysController.cs

@@ -595,5 +595,16 @@ namespace Hotline.Api.Controllers
         {
             return await _systemMobilAreaApplication.VaildMobile(mobile);
         }
+
+        /// <summary>
+        /// 删除缓存
+        /// </summary>
+        /// <param name="code"></param>
+        /// <returns></returns>
+        [HttpPut("cache/clear")]
+        public async Task<string> ClearCache([FromQuery] string code)
+        {
+            return "待实现";
+        }
     }
 }

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

@@ -1,4 +1,5 @@
-using Hotline.Application.Users;
+using Hotline.Api.Filter;
+using Hotline.Application.Users;
 using Hotline.Caching.Interfaces;
 using Hotline.CallCenter.Tels;
 using Hotline.Identity.Accounts;
@@ -254,6 +255,21 @@ public class UserController : BaseController
         await _accountRepository.SetAccountRolesAsync(user.Id, dto.RoleIds, HttpContext.RequestAborted);
     }
 
+    /// <summary>
+    /// 解锁用户
+    /// </summary>
+    /// <returns></returns>
+    [HttpPut("unlock")]
+    [LogFilter("解锁用户")]
+    public async Task<string> UnlockUserAsync([FromQuery]string id)
+    {
+        var user = await _accountRepository.GetAsync(id) ??
+            throw UserFriendlyException.SameMessage("用户不存在");
+        user.LockoutEnd = null;
+        await _accountRepository.UpdateNullAsync(user, HttpContext.RequestAborted);
+        return "解锁成功";
+    }
+
     /// <summary>
     /// 新增用户
     /// </summary>

+ 45 - 12
src/Hotline.Api/Controllers/WebPortalController.cs

@@ -565,13 +565,14 @@ namespace Hotline.Api.Controllers
         public async Task<OpenResponse> GetOrderListByNum([FromBody] QueryOrderListByNumDto dto)
         {
             var queryNew = _orderRepository.Queryable()
+                .LeftJoin<OrderPublish>((p, op) => p.Id == op.OrderId)
                 .Where(p => p.IsPublicity == true)
-                .OrderByDescending(p => p.CreationTime)
-               .Select(p => new DataListTopDto
+                .OrderByDescending((p, op) => p.CreationTime)
+               .Select((p, op) => new DataListTopDto
                {
                    DataID = p.Id,
                    Title = p.Title,
-                   CreateDate = p.CreationTime
+                   CreateDate = op.CreationTime
                });
 
             var queryold = _oldPublicDataRepository.Queryable()
@@ -580,7 +581,7 @@ namespace Hotline.Api.Controllers
                        {
                            DataID = p.OrderId,
                            Title = p.Title,
-                           CreateDate = p.PubDate
+                           CreateDate = p.CreationTime
                        });
             var items = await _orderRepository.UnionAll(queryNew, queryold)
               .OrderByDescending(p => p.CreateDate)
@@ -922,7 +923,7 @@ namespace Hotline.Api.Controllers
 
             //数据查询
             var listType = await _orderRepository.Queryable()
-                .Where(p => p.StartTime >= startDate && p.StartTime <= endDate && p.Status > EOrderStatus.WaitForAccept)
+                .Where(p => p.CreationTime >= startDate && p.CreationTime <= endDate && p.Status > EOrderStatus.WaitForAccept)
                 .Select(it => new
                 {
                     it.AcceptType,
@@ -938,12 +939,13 @@ namespace Hotline.Api.Controllers
                  .ToListAsync();
 
             var listHot = await _orderRepository.Queryable()
-                .Where(it => it.StartTime >= startDate && it.StartTime <= endDate && it.Status > EOrderStatus.WaitForAccept)
+                .Where(it => it.CreationTime >= startDate && it.CreationTime <= endDate && it.Status > EOrderStatus.WaitForAccept)
                  .Select(it => new
                  {
                      HotspotId = it.HotspotId.Substring(SqlFunc.MappingColumn<int>("0"), SqlFunc.MappingColumn<int>("2")),
                  })
                  .MergeTable()
+                 .Where(it => it.HotspotId != "18")
                  .GroupBy(it => it.HotspotId)//对新表进行分组
                  .Select(it => new
                  {
@@ -960,8 +962,6 @@ namespace Hotline.Api.Controllers
                      num = it.num
                  }).ToListAsync();
 
-
-
             ////数据查询-查询总数前5的数据
             //var listHot = await _orderRepository.Queryable()
             //    .Where(p => p.StartTime >= startDate && p.StartTime <= endDate && p.Status > EOrderStatus.WaitForAccept)
@@ -1012,8 +1012,8 @@ namespace Hotline.Api.Controllers
           {
               AllCount = SqlFunc.AggregateSum(SqlFunc.IIF(p.Status > EOrderStatus.WaitForAccept, 1, 0)) + 7079457,
               AllTrandCount = SqlFunc.AggregateSum(SqlFunc.IIF(p.Status >= EOrderStatus.Filed, 1, 0)) + 7079214,
-              DayCount = SqlFunc.AggregateSum(SqlFunc.IIF(p.StartTime >= startDate && p.StartTime <= endDate && p.Status > EOrderStatus.WaitForAccept, 1, 0)),
-              DayTrandCount = SqlFunc.AggregateSum(SqlFunc.IIF(p.FiledTime >= startDate && p.FiledTime <= endDate && p.Status >= EOrderStatus.Filed, 1, 0)),
+              DayCount = SqlFunc.AggregateSum(SqlFunc.IIF(p.CreationTime >= startDate && p.CreationTime <= endDate && p.Status > EOrderStatus.WaitForAccept, 1, 0)),
+              DayTrandCount = SqlFunc.AggregateSum(SqlFunc.IIF(p.CreationTime >= startDate && p.CreationTime <= endDate && p.FiledTime >= startDate && p.FiledTime <= endDate && p.Status >= EOrderStatus.Filed, 1, 0)),
           })
           .FirstAsync();
             return OpenResponse.Ok(WebPortalDeResponse<GetStatistDto>.Success(getStatistDto));
@@ -1127,7 +1127,7 @@ namespace Hotline.Api.Controllers
             var list = _systemDicDataCacheManager.GetSysDicDataCache(SysDicTypeConsts.KnowledgeBaseTags).Select(p => new
             {
                 SDICT_Name = p.DicDataName,
-                SDICT_ID = p.DicDataValue
+                SDICT_ID = p.DicDataName
             }).ToList();
 
             var rsp = new
@@ -1154,9 +1154,16 @@ namespace Hotline.Api.Controllers
                 typeSpliceName = type?.SpliceName;
             }
 
+            var typeSpliceNameTags = string.Empty;
+            if (!string.IsNullOrEmpty(dto.KnowledgeBaseTags))
+            {
+                var type = await _knowledgeTypeRepository.GetAsync(x => x.Name == dto.KnowledgeBaseTags);
+                typeSpliceNameTags = type?.SpliceName;
+            }
+
             var (total, items) = await _knowledgeRepository.Queryable()
                 .WhereIF(!string.IsNullOrEmpty(dto.Title), p => p.Title.Contains(dto.Title))
-                .WhereIF(!string.IsNullOrEmpty(dto.KnowledgeBaseTags), p => p.KnowledgeType.Any(t => t.KnowledgeTypeSpliceName.EndsWith(dto.KnowledgeBaseTags)))
+                .WhereIF(!string.IsNullOrEmpty(typeSpliceNameTags), p => p.KnowledgeType.Any(t => t.KnowledgeTypeSpliceName.EndsWith(typeSpliceNameTags)))
                 .WhereIF(!string.IsNullOrEmpty(typeSpliceName), x => x.KnowledgeType.Any(t => t.KnowledgeTypeSpliceName.EndsWith(typeSpliceName)))
                 .OrderByDescending(p => p.CreationTime)
                 .ToPagedListAsync(dto.PageIndex, dto.PageSize, HttpContext.RequestAborted);
@@ -1164,5 +1171,31 @@ namespace Hotline.Api.Controllers
             var data = new PagedDto<KnowledgeInfoDto>(total, _mapper.Map<IReadOnlyList<KnowledgeInfoDto>>(items));
             return OpenResponse.Ok(WebPortalDeResponse<PagedDto<KnowledgeInfoDto>>.Success(data));
         }
+
+        /// <summary>
+        /// 获取知识库详情
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        [HttpPost("getknowledgeinfo")]
+        [AllowAnonymous]
+        public async Task<OpenResponse> GetKnowledgeInfo([FromBody] QueryKnowledgeInfo dto)
+        {
+            var data = await _knowledgeRepository.GetAsync(p => p.Id == dto.Id, HttpContext.RequestAborted);
+            KnowledgeInfoDto detailsDto = null;
+            if (data != null)
+            {
+                detailsDto = _mapper.Map<KnowledgeInfoDto>(data);
+
+                if (detailsDto != null && !string.IsNullOrEmpty(detailsDto.Content))
+                    data.Content = _bulletinApplication.GetSiteUrls(data.Content);
+            }
+            else
+            {
+                detailsDto = new();
+            }
+            List<KnowledgeInfoDto> dataDto = [detailsDto];
+            return OpenResponse.Ok(WebPortalDeResponse<IReadOnlyList<KnowledgeInfoDto>>.Success(dataDto));
+        }
     }
 }

+ 0 - 1
src/Hotline.Application.Contracts/Validators/Order/OrderBatchFileDtoValidator.cs

@@ -8,6 +8,5 @@ public class OrderBatchFileDtoValidator:AbstractValidator<OrderBatchFileDto>
     public OrderBatchFileDtoValidator()
     {
         RuleFor(d => d.OrderIds).NotEmpty();
-        RuleFor(d=>d.Opinion).NotEmpty();
     }
 }

+ 26 - 0
src/Hotline.Application.Tests/Application/SystemSettingCacheManagerTest.cs

@@ -0,0 +1,26 @@
+using Hotline.Caching.Interfaces;
+using Hotline.Caching.Services;
+using Shouldly;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Hotline.Application.Tests.Application;
+public class SystemSettingCacheManagerTest
+{
+    private readonly ISystemSettingCacheManager _systemSettingCacheManager;
+
+    public SystemSettingCacheManagerTest(ISystemSettingCacheManager systemSettingCacheManager)
+    {
+        _systemSettingCacheManager = systemSettingCacheManager;
+    }
+
+    [Fact]
+    public void CancelPublishOrderEnabled_Test()
+    {
+        var result = _systemSettingCacheManager.CancelPublishOrderEnabled;
+        result.ShouldBeTrue();
+    }
+}

+ 52 - 2
src/Hotline.Application.Tests/Controller/OrderControllerTest.cs

@@ -3,17 +3,21 @@ using Castle.DynamicProxy;
 using Hotline.Api.Controllers;
 using Hotline.Application.Tests.Infrastructure;
 using Hotline.Application.Tests.Mock;
+using Hotline.Caching.Interfaces;
+using Hotline.Caching.Services;
 using Hotline.FlowEngine.Notifications;
 using Hotline.FlowEngine.WorkflowModules;
 using Hotline.Identity.Accounts;
 using Hotline.Identity.Roles;
 using Hotline.Orders;
+using Hotline.Settings;
 using Hotline.Settings.Hotspots;
 using Hotline.Share.Dtos.File;
 using Hotline.Share.Dtos.FlowEngine;
 using Hotline.Share.Dtos.Order;
 using Hotline.Share.Dtos.Users;
 using Hotline.Share.Enums.FlowEngine;
+using Hotline.Share.Enums.Order;
 using Hotline.Share.Enums.Settings;
 using Hotline.Share.Tools;
 using Hotline.Users;
@@ -41,8 +45,11 @@ public class OrderControllerTest : TestBase
     private readonly OrderServiceMock _orderServiceMock;
     private readonly IRepository<OrderPublish> _orderPublishRepository;
     private readonly INotificationHandler<EndWorkflowNotify> _orderPublishEndWorkflowHandler;
+    private readonly IOrderVisitRepository _orderVisitRepository;
+    private readonly IRepository<SystemSetting> _systemSettingRepository;
+    private readonly ISystemSettingCacheManager _systemSettingCacheManager;
 
-    public OrderControllerTest(IAccountRepository accountRepository, IRepository<Role> roleRepository, UserController userController, IRepository<Hotspot> hotspotRepository, OrderController orderController, IOrderRepository orderRepository, IServiceScopeFactory scopeFactory, IRepository<User> userRepository, OrderServiceMock orderServiceMock, IRepository<OrderPublish> orderPublishRepository, INotificationHandler<EndWorkflowNotify> orderPublishEndWorkflowHandler) : base(accountRepository, roleRepository, userController, scopeFactory, userRepository)
+    public OrderControllerTest(IAccountRepository accountRepository, IRepository<Role> roleRepository, UserController userController, IRepository<Hotspot> hotspotRepository, OrderController orderController, IOrderRepository orderRepository, IServiceScopeFactory scopeFactory, IRepository<User> userRepository, OrderServiceMock orderServiceMock, IRepository<OrderPublish> orderPublishRepository, INotificationHandler<EndWorkflowNotify> orderPublishEndWorkflowHandler, IOrderVisitRepository orderVisitRepository, IRepository<SystemSetting> systemSettingRepository, ISystemSettingCacheManager systemSettingCacheManager) : base(accountRepository, roleRepository, userController, scopeFactory, userRepository)
     {
         _hotspotRepository = hotspotRepository;
         _orderController = orderController;
@@ -54,6 +61,9 @@ public class OrderControllerTest : TestBase
         _orderServiceMock = orderServiceMock;
         _orderPublishRepository = orderPublishRepository;
         _orderPublishEndWorkflowHandler = orderPublishEndWorkflowHandler;
+        _orderVisitRepository = orderVisitRepository;
+        _systemSettingRepository = systemSettingRepository;
+        _systemSettingCacheManager = systemSettingCacheManager;
     }
 
 
@@ -82,7 +92,7 @@ public class OrderControllerTest : TestBase
         SetZuoXi();
         var order = _orderServiceMock.CreateOrder()
             .办理到派单员()
-            .办理到归档(SetPaiDanYuan, data => 
+            .办理到归档(SetPaiDanYuan, data =>
             {
                 data.IsEvasive = true;
                 data.IsInactively = true;
@@ -91,6 +101,7 @@ public class OrderControllerTest : TestBase
         var publish = await _orderPublishRepository.GetAsync(m => m.No == order.No);
         publish.ShouldNotBeNull(order.No);
         var orderEntity = await _orderRepository.GetAsync(order.Id);
+        orderEntity.ShouldNotBeNull();
         orderEntity.IsEvasive.ShouldBeTrue();
         orderEntity.IsInactively.ShouldBeTrue();
     }
@@ -159,4 +170,43 @@ public class OrderControllerTest : TestBase
         result.TimeText.ShouldBe("1个工作日");
         result.TimeType.ShouldBe(ETimeType.WorkDay);
     }
+
+
+    /// <summary>
+    /// 测试取消发布功能
+    /// </summary>
+    /// <returns></returns>
+    [Fact]
+    public async Task CancelOrderPublish_Test()
+    {
+        var system = await _systemSettingRepository.Queryable()
+            .Where(m => m.Code == SettingConstants.CancelPublishOrderEnabled)
+            .FirstAsync();
+        system.SettingValue[0] = "true";
+        await _systemSettingRepository.UpdateAsync(system);
+        _systemSettingCacheManager.DelSystemSetting(SettingConstants.CancelPublishOrderEnabled);
+
+        SetZuoXi();
+        var order = _orderServiceMock.CreateOrder()
+            .办理到派单员()
+            .办理到归档(SetPaiDanYuan, data =>
+            {
+                data.IsEvasive = true;
+                data.IsInactively = true;
+            })
+            .GetCreateResult();
+        var publish = await _orderPublishRepository.GetAsync(m => m.No == order.No);
+        publish.ShouldNotBeNull(order.No);
+        var orderEntity = await _orderRepository.GetAsync(order.Id);
+        orderEntity.ShouldNotBeNull();
+        orderEntity.IsEvasive.ShouldBeTrue();
+        orderEntity.IsInactively.ShouldBeTrue();
+
+        await _orderController.PublishCancelAsync(new PublishCancelInDto { OrderPublishId = publish.Id });
+        var visit = await _orderVisitRepository.GetAsync(m => m.OrderId == order.Id);
+        var publishCount = await _orderPublishRepository.Queryable().Where(m => m.OrderId == order.Id && m.IsDeleted == false).CountAsync();
+        publishCount.ShouldBe(0);
+        visit.ShouldNotBeNull();
+        visit.VisitState.ShouldBe(EVisitState.Visited);
+    }
 }

+ 44 - 0
src/Hotline.Application.Tests/Controller/UserControllerTest.cs

@@ -0,0 +1,44 @@
+using Hotline.Api.Controllers;
+using Hotline.Identity.Accounts;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using Shouldly;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using XF.Domain.Repository;
+
+namespace Hotline.Application.Tests.Controller;
+public class UserControllerTest
+{
+    private readonly UserController _userController;
+    private readonly IRepository<Account> _accountRepository;
+
+    public UserControllerTest(UserController userController, IRepository<Account> accountRepository)
+    {
+        _userController = userController;
+        _userController.ControllerContext = new ControllerContext
+        {
+            HttpContext = new DefaultHttpContext()
+        };
+        _accountRepository = accountRepository;
+    }
+
+    [Fact]
+    public async Task UnlockUser_Test()
+    {
+        var user = await _accountRepository.Queryable()
+            .Where(m => m.LockoutEnd == null).FirstAsync();
+        user.ShouldNotBeNull();
+        user.LockoutEnd = DateTime.Now.AddDays(1);
+        await _accountRepository.UpdateAsync(user);
+        user = await _accountRepository.GetAsync(user.Id);
+        user.LockoutEnd.ShouldNotBeNull();
+
+        await _userController.UnlockUserAsync(user.Id);
+        user = await _accountRepository.GetAsync(user.Id);
+        user.LockoutEnd.ShouldBeNull();
+    }
+}

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

@@ -957,7 +957,7 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
                         UserId = d.Id,
                         Username = d.Name,
                         OrgId = d.OrgId,
-                        OrgName = d.Organization.Name,
+                        OrgName = d.Organization?.Name,
                         RoleId = defineTypeItem.Key,
                         RoleName = defineTypeItem.Value
                     })

+ 2 - 8
src/Hotline.Repository.SqlSugar/Orders/OrderVisitRepository.cs

@@ -8,6 +8,7 @@ using Hotline.Settings;
 using Hotline.Share.Dtos.Push;
 using Hotline.Share.Enums.Order;
 using Hotline.Share.Tools;
+using Microsoft.AspNetCore.Http;
 using Microsoft.Extensions.Logging;
 using SqlSugar;
 using XF.Domain.Dependency;
@@ -18,17 +19,10 @@ using static System.Runtime.InteropServices.JavaScript.JSType;
 namespace Hotline.Repository.SqlSugar.Orders;
 public class OrderVisitRepository : BaseRepository<OrderVisit>, IOrderVisitRepository, IScopeDependency
 {
-    private readonly IRepository<OrderVisitDetail> _orderVisitDetailRepository;
     private readonly ILogger<OrderVisitRepository> _logger;
-    private readonly IRepository<Order> _orderRepository;
-    private readonly ISystemDicDataCacheManager _systemDicDataCacheManager;
 
-    public OrderVisitRepository(ISugarUnitOfWork<HotlineDbContext> uow, IDataPermissionFilterBuilder dataPermissionFilterBuilder, IRepository<OrderVisitDetail> orderVisitDetailRepository, ILogger<OrderVisitRepository> logger, IRepository<Order> orderRepository, ISystemDicDataCacheManager systemDicDataCacheManager) : base(uow, dataPermissionFilterBuilder)
+    public OrderVisitRepository(ISugarUnitOfWork<HotlineDbContext> uow, IDataPermissionFilterBuilder dataPermissionFilterBuilder,  ILogger<OrderVisitRepository> logger) : base(uow, dataPermissionFilterBuilder)
     {
-        _orderVisitDetailRepository = orderVisitDetailRepository;
         _logger = logger;
-        _orderRepository = orderRepository;
-        _systemDicDataCacheManager = systemDicDataCacheManager;
     }
-
 }

+ 12 - 11
src/Hotline.Share/Dtos/Article/BulletinDto.cs

@@ -117,9 +117,10 @@ namespace Hotline.Share.Dtos.Article
 
         public string BulletinStateText => BulletinState.GetDescription();
 
-        public string PushRangesText => PushRanges.Any() ? string.Join("、", PushRanges.Select(x => x.Value)) : "-";
+        public string PushRangesText => PushRanges != null && PushRanges.Any() ? string.Join("、", PushRanges.Select(x => x.Value)) : "-";
 
-	}
+        public string DisplayLocationText => DisplayLocation != null && DisplayLocation.Any() ? string.Join("、", DisplayLocation.Select(x => x.Value)) : "-";
+    }
 
     public record CircularDto
     {
@@ -229,7 +230,7 @@ namespace Hotline.Share.Dtos.Article
         /// 审核时间
         /// </summary>
         public DateTime? ExaminTime { get; set; }
-	}
+    }
 
     public record CircularReadGroupDto
     {
@@ -295,7 +296,7 @@ namespace Hotline.Share.Dtos.Article
         public List<CircularReadGroupDto> CircularReadGroups { get; set; }
     }
 
-    public record UpdateCircularDto:AddCircularDto
+    public record UpdateCircularDto : AddCircularDto
     {
         public string Id { get; set; }
     }
@@ -376,10 +377,10 @@ namespace Hotline.Share.Dtos.Article
         /// </summary>
         public DateTime LoseEfficacyTime { get; set; }
 
-		/// <summary>
-		/// 发布范围  EPushRange
-		/// </summary>
-		public List<Kv> PushRanges { get; set; }
+        /// <summary>
+        /// 发布范围  EPushRange
+        /// </summary>
+        public List<Kv> PushRanges { get; set; }
 
         /// <summary>
         /// 显示位置(多选) 位枚举  BulletinDisplayLocation
@@ -407,13 +408,13 @@ namespace Hotline.Share.Dtos.Article
         public bool IsArrive { get; set; }
 
     }
-    public record UpdateBulletinDto:AddBulletinDto
+    public record UpdateBulletinDto : AddBulletinDto
     {
         public string Id { get; set; }
     }
 
 
-    public record QueryCircularListRequestDto:PagedRequest
+    public record QueryCircularListRequestDto : PagedRequest
     {
         /// <summary>
         /// 标题
@@ -455,7 +456,7 @@ namespace Hotline.Share.Dtos.Article
         public string Reason { get; set; }
     }
 
-    public record QueryBulletinListRequestDto:PagedRequest
+    public record QueryBulletinListRequestDto : PagedRequest
     {
         /// <summary>
         /// 标题

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

@@ -1024,6 +1024,8 @@ namespace Hotline.Share.Dtos.Order
         /// </summary>
         public bool IsSecret { get; set; }
 
+        public string IsSecretText => IsSecret ? "保密" : "";
+
         /// <summary>
         /// 工作单位(当“来电/信人身份”为“企业”时必填,其他情况非必填)
         /// </summary>
@@ -1370,6 +1372,13 @@ namespace Hotline.Share.Dtos.Order
         public string OrderId { get; set; }
     }
 
+    public class PublishCancelInDto
+    {
+        /// <summary>
+        /// Id
+        /// </summary>
+        public string OrderPublishId { get; set; }
+    }
 
     public class OrderListOutDto
     {

+ 5 - 0
src/Hotline.Share/Dtos/WebPortal/QueryKnowledgeList.cs

@@ -21,6 +21,11 @@ namespace Hotline.Share.Dtos.WebPortal
         public string? KnowledgeBaseTags { get; set; }
     }
 
+    public class QueryKnowledgeInfo
+    {
+        public string Id { get; set; }
+    }
+
     /// <summary>
     /// 知识实体
     /// </summary>

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

@@ -13,7 +13,7 @@ namespace Hotline.Article
     {
         public string Title { get; set; }
 
-        [SugarColumn(ColumnDataType = "varchar(8000)")]
+        [SugarColumn(ColumnDataType = "text")]
         public string Content { get; set; }
 
         public string BulletinTypeId { get; set; }

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

@@ -25,5 +25,6 @@ namespace Hotline.Caching.Interfaces
         /// 自动发布中心直办归档工单
         /// </summary>
         bool AutomaticPublishOrder { get; }
+        bool CancelPublishOrderEnabled { get; }
     }
 }

+ 38 - 19
src/Hotline/Caching/Services/SystemSettingCacheManager.cs

@@ -36,6 +36,30 @@ namespace Hotline.Caching.Services
             _cacheSystemSetting.Remove(code);
         }
 
+        public string GetOrDefault(string code, string name, string defaultValue, string remark)
+        {
+            try
+            {
+                var value = GetSetting(code)?.SettingValue[0];
+                if (value == null) return defaultValue;
+                return value.Trim();
+            }
+            catch (UserFriendlyException e)
+            {
+                if (e.Message.Contains("无效系统设置"))
+                {
+                    _systemSettingRepository.AddAsync(new SystemSetting
+                    {
+                        Code = code,
+                        SettingName = name,
+                        SettingValue = [defaultValue],
+                        Remark = remark
+                    }).GetAwaiter().GetResult();
+                }
+            }
+            return defaultValue;
+        }
+
         public int EffectiveTimes
             => int.Parse(GetSetting(SettingConstants.EffectiveTimes)?.SettingValue[0]);
 
@@ -60,28 +84,23 @@ namespace Hotline.Caching.Services
         {
             get
             {
-
-                try
+                var value = GetOrDefault(SettingConstants.AutomaticPublishOrder, "自动发布中心直办归档工单", "false", "用于中心直办件归档后默认自动发布, 开启就自动发布.true 或者 false");
+                if (value == "true")
                 {
-                    var value = GetSetting(SettingConstants.AutomaticPublishOrder)?.SettingValue[0];
-                    if (value == null) return false;
-                    if (value.Trim() == "true")
-                    {
-                        return true;
-                    }
+                    return true;
                 }
-                catch (UserFriendlyException e)
+                return false;
+            }
+        }
+
+        public bool CancelPublishOrderEnabled
+        {
+            get
+            {
+                var value = GetOrDefault(SettingConstants.CancelPublishOrderEnabled, "取消发布功能开关", "false", "取消发布功能总开关, 关闭后取消发布功能的所有代码都不会执行.true 或者 false");
+                if (value == "true")
                 {
-                    if (e.Message.Contains("无效系统设置"))
-                    {
-                        _systemSettingRepository.AddAsync(new SystemSetting 
-                        {
-                            Code = SettingConstants.AutomaticPublishOrder,
-                            SettingName = "自动发布中心直办归档工单",
-                            SettingValue = ["false"],
-                            Remark = "用于中心直办件归档后默认自动发布, 开启就自动发布.true 或者 false"
-                        });
-                    }
+                    return true;
                 }
                 return false;
             }

+ 3 - 0
src/Hotline/FlowEngine/Workflows/IWorkflowDomainService.cs

@@ -292,5 +292,8 @@ namespace Hotline.FlowEngine.Workflows
         /// </summary>
         Task JumpToEndAsync(ISessionContext current,string workflowId, string opinion, List<FileDto> files, DateTime? expiredTime,
             EReviewResult reviewResult = EReviewResult.Unknown, CancellationToken cancellationToken = default);
+
+        Task JumpToEndAsync(ISessionContext current, Workflow workflow, string opinion, List<FileDto> files, DateTime? expiredTime,
+            EReviewResult reviewResult = EReviewResult.Unknown, CancellationToken cancellationToken = default);
     }
 }

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

@@ -436,10 +436,14 @@ namespace Hotline.FlowEngine.Workflows
             //检查是否流转到流程终点
             if (nextStepDefine.StepType is EStepType.End)
             {
-                //更新实际办理节点信息
-                workflow.UpdateActualStepWhenHandle(currentStep, _sessionContextProvider.SessionContext.OrgAreaCode,
-                    _sessionContextProvider.SessionContext.OrgAreaName, _sessionContextProvider.SessionContext.OrgLevel);
-                workflow.ActualHandleStepAcceptTime = currentStep.AcceptTime;
+                if (string.IsNullOrEmpty(workflow.ActualHandlerId)
+                    || string.IsNullOrEmpty(workflow.ActualHandleOrgCode))//开始流程直接归档
+                {
+                    //更新实际办理节点信息
+                    workflow.UpdateActualStepWhenHandle(currentStep, _sessionContextProvider.SessionContext.OrgAreaCode,
+                        _sessionContextProvider.SessionContext.OrgAreaName, _sessionContextProvider.SessionContext.OrgLevel);
+                    workflow.ActualHandleStepAcceptTime = currentStep.AcceptTime;
+                }
 
                 var endTrace = await EndAsync(current, workflow, dto, nextStepDefine, currentStep, expiredTime, cancellationToken);
                 return new List<WorkflowStep>();
@@ -1444,6 +1448,15 @@ namespace Hotline.FlowEngine.Workflows
         {
             var workflow = await GetWorkflowAsync(workflowId, withDefine: true, withSteps: true, withTraces: true,
                 withCountersigns: true, cancellationToken: cancellationToken);
+            await JumpToEndAsync(current, workflow, opinion, files, expiredTime, reviewResult, cancellationToken);
+        }
+
+        /// <summary>
+        /// 跳转至结束节点(无视流程模板配置以及当前办理对象,直接跳至结束节点)
+        /// </summary>
+        public async Task JumpToEndAsync(ISessionContext current, Workflow workflow, string opinion, List<FileDto> files, DateTime? expiredTime,
+            EReviewResult reviewResult = EReviewResult.Unknown, CancellationToken cancellationToken = default)
+        {
             var endStepDefine = workflow.WorkflowDefinition.FindEndStepDefine();
             if (endStepDefine is null)
                 throw new UserFriendlyException("未正确配置结束节点");

+ 8 - 0
src/Hotline/Orders/IOrderDomainService.cs

@@ -104,5 +104,13 @@ namespace Hotline.Orders
         Task SendOverTimeSms(CancellationToken cancellationToken);
         Task OrderPublishAsync(Order order, CancellationToken cancellationToken);
         Task OrderAutomaticPublishAsync(Order order, CancellationToken cancellationToken);
+
+        /// <summary>
+        /// 重办和退回工单时如果有取消发布的工单, 清除回访待办和回访列表中的数据
+        /// </summary>
+        /// <param name="orderId"></param>
+        /// <param name="cancellationToken"></param>
+        /// <returns></returns>
+        Task VisitNoneByCancelPublishAsync(string orderId, CancellationToken cancellationToken);
     }
 }

+ 75 - 36
src/Hotline/Orders/OrderDomainService.cs

@@ -170,7 +170,7 @@ public class OrderDomainService : IOrderDomainService, IScopeDependency
         var publishPublishOrder = orderPublish.Adapt<PublishPublishOrderDto>();
         publishPublishOrder.Order = order.Adapt<OrderDto>();
         //查询实际办理附件
-        if (!string.IsNullOrEmpty(order.ActualHandleStepId)) 
+        if (!string.IsNullOrEmpty(order.ActualHandleStepId))
         {
             var actualHandleStep = await _workflowStepRepository.GetAsync(order.ActualHandleStepId, cancellationToken);
             publishPublishOrder.FileJsons = actualHandleStep?.FileJson;
@@ -186,6 +186,19 @@ public class OrderDomainService : IOrderDomainService, IScopeDependency
             await _publisher.PublishAsync(new ContingencyManagementNotify(order, order.Title, order.Content, order.ActualOpinion),
                 PublishStrategy.ParallelWhenAll, cancellationToken);
 
+        // 取消发布的工单数量
+        var orderPublishDeletedCount = await _orderPublishRepository.Queryable(includeDeleted: true)
+            .Where(m => m.OrderId == order.Id && m.IsDeleted == true)
+            .CountAsync(cancellationToken);
+        var orderVisitVisitedCount = await _orderVisitRepository.Queryable()
+            .Where(m => m.OrderId == order.Id && m.VisitState == EVisitState.Visited)
+            .CountAsync(cancellationToken);
+
+        // 若取消发布的工单,已经被回访过了,没有经过重新办理,再次发布后,自动跳过回访环节,展示取消发布前的回访结果
+        if (orderPublishDeletedCount != 0 && orderVisitVisitedCount !=0)
+        {
+            return;
+        }
         var orderVisit = new OrderVisit
         {
             No = order.No,
@@ -260,6 +273,7 @@ public class OrderDomainService : IOrderDomainService, IScopeDependency
         visitedDetail.Add(seatDetail);
         await _orderVisitDetailRepository.AddRangeAsync(visitedDetail, cancellationToken);
 
+
         if (order.IsProvince == false && orderVisit.VisitState == EVisitState.Visited)
         {
             //推省上
@@ -293,6 +307,31 @@ public class OrderDomainService : IOrderDomainService, IScopeDependency
         }, cancellationToken: cancellationToken);
     }
 
+    /// <summary>
+    /// 重办和退回工单时如果有取消发布的工单, 清除回访待办和回访列表中的数据
+    /// </summary>
+    /// <param name="orderId"></param>
+    /// <param name="cancellationToken"></param>
+    /// <returns></returns>
+    public async Task VisitNoneByCancelPublishAsync(string orderId, CancellationToken cancellationToken)
+    {
+        var cancelPublishOrderEnabled = _systemSettingCacheManager.CancelPublishOrderEnabled;
+
+        // 取消发布的工单数量
+        var orderPublishDeletedCount = await _orderPublishRepository.Queryable(includeDeleted: true)
+            .Where(m => m.OrderId == orderId && m.IsDeleted == true)
+            .CountAsync(cancellationToken);
+
+        if (orderPublishDeletedCount == 0 || cancelPublishOrderEnabled == false) return;
+
+        var visit = await _orderVisitRepository.GetAsync(x => x.OrderId == orderId && x.VisitState != EVisitState.None, cancellationToken);
+        if (visit != null)
+        {
+            visit.VisitState = EVisitState.None;
+            await _orderVisitRepository.UpdateAsync(visit, cancellationToken);
+        }
+    }
+
     public async Task<Order> GetOrderAsync(string? orderId, bool withHotspot = false, bool withAcceptor = false,
         bool withExtension = false, CancellationToken cancellationToken = default)
     {
@@ -323,8 +362,8 @@ public class OrderDomainService : IOrderDomainService, IScopeDependency
         order.Init();
         order.No = GenerateNewOrderNo();
         order.Password = Random.Shared.Next(100000, 1000000).ToString();
-		order.ProvinceNo = string.IsNullOrEmpty(order.ProvinceNo) ? GenerateNewProvinceNo(order.No, order.SourceChannelCode) : order.ProvinceNo;
-		return await _orderRepository.AddOrderNavAsync(order, cancellationToken);
+        order.ProvinceNo = string.IsNullOrEmpty(order.ProvinceNo) ? GenerateNewProvinceNo(order.No, order.SourceChannelCode) : order.ProvinceNo;
+        return await _orderRepository.AddOrderNavAsync(order, cancellationToken);
     }
 
     /// <summary>
@@ -407,7 +446,7 @@ public class OrderDomainService : IOrderDomainService, IScopeDependency
             return false;
     }
 
-   
+
 
     #region 平均派单
     /// <summary>
@@ -501,30 +540,30 @@ public class OrderDomainService : IOrderDomainService, IScopeDependency
                 cancellationToken);
             if (steps.Any())
             {
-	            List<(string userId, string username, string orgId, string orgName, string? roleId, string? roleName, ICollection<WorkflowStep> steps)> handlers = new();
-				var avg = steps.Count / schedulings.Count;
+                List<(string userId, string username, string orgId, string orgName, string? roleId, string? roleName, ICollection<WorkflowStep> steps)> handlers = new();
+                var avg = steps.Count / schedulings.Count;
                 var remaining = steps.Count % schedulings.Count;
                 var skip = 0;
-                for (var i = 0; i < schedulings.Count; i++)
+                for (var i = 0;i < schedulings.Count;i++)
                 {
-					var scheduling = schedulings[i];
+                    var scheduling = schedulings[i];
                     var size = avg + (i < remaining ? 1 : 0);
                     if (size > 0)
                     {
-						handlers.Add(new(
-							scheduling.SchedulingUser.UserId,
-							scheduling.SchedulingUser.UserName,
-							scheduling.SchedulingUser.OrgId,
-							scheduling.SchedulingUser.OrgIdName,
-							null, null,
-							steps.Skip(skip).Take(size).ToList()));
+                        handlers.Add(new(
+                            scheduling.SchedulingUser.UserId,
+                            scheduling.SchedulingUser.UserName,
+                            scheduling.SchedulingUser.OrgId,
+                            scheduling.SchedulingUser.OrgIdName,
+                            null, null,
+                            steps.Skip(skip).Take(size).ToList()));
                         skip += size;
-						scheduling.SendOrderNum += size;
-						await _schedulingRepository.Updateable()
-							.SetColumns(s => new Scheduling() { SendOrderNum = scheduling.SendOrderNum })
-							.Where(s => s.Id == scheduling.Id).ExecuteCommandAsync(cancellationToken);
-					}
-				}
+                        scheduling.SendOrderNum += size;
+                        await _schedulingRepository.Updateable()
+                            .SetColumns(s => new Scheduling() { SendOrderNum = scheduling.SendOrderNum })
+                            .Where(s => s.Id == scheduling.Id).ExecuteCommandAsync(cancellationToken);
+                    }
+                }
                 if (handlers.Any())
                     await _workflowDomainService.ChangeHandlerBatchAsync(handlers, cancellationToken);
             }
@@ -549,7 +588,7 @@ public class OrderDomainService : IOrderDomainService, IScopeDependency
                 //投诉举报
                 case "30":
                 case "35":
-                    valid.Validation = dto.Title.Contains("意见") || dto.Title.Contains("建议")  || dto.Title.Contains("咨询") 
+                    valid.Validation = dto.Title.Contains("意见") || dto.Title.Contains("建议") || dto.Title.Contains("咨询")
                         || dto.Content.Contains("意见") || dto.Content.Contains("建议") || dto.Content.Contains("咨询");
                     if (dto.Content.Length < 25)
                     {
@@ -559,7 +598,7 @@ public class OrderDomainService : IOrderDomainService, IScopeDependency
                     break;
                 // 意见
                 case "1":
-                    valid.Validation = dto.Title.Contains("投诉") || dto.Title.Contains("举报")|| dto.Title.Contains("咨询") 
+                    valid.Validation = dto.Title.Contains("投诉") || dto.Title.Contains("举报") || dto.Title.Contains("咨询")
                         || dto.Content.Contains("投诉") || dto.Content.Contains("举报") || dto.Content.Contains("咨询");
                     if (dto.Content.Length < 5)
                     {
@@ -570,7 +609,7 @@ public class OrderDomainService : IOrderDomainService, IScopeDependency
                 //建议求助
                 case "15":
                 case "20":
-                    valid.Validation = dto.Title.Contains("投诉") || dto.Title.Contains("举报")  || dto.Title.Contains("咨询") 
+                    valid.Validation = dto.Title.Contains("投诉") || dto.Title.Contains("举报") || dto.Title.Contains("咨询")
                         || dto.Content.Contains("投诉") || dto.Content.Contains("举报") || dto.Content.Contains("咨询");
                     if (dto.Content.Length < 25)
                     {
@@ -580,7 +619,7 @@ public class OrderDomainService : IOrderDomainService, IScopeDependency
                     break;
                 // 咨询
                 case "10":
-                    valid.Validation = dto.Title.Contains("投诉") || dto.Title.Contains("举报") || dto.Title.Contains("意见") 
+                    valid.Validation = dto.Title.Contains("投诉") || dto.Title.Contains("举报") || dto.Title.Contains("意见")
                         || dto.Title.Contains("建议") || dto.Content.Contains("投诉") || dto.Content.Contains("举报") || dto.Content.Contains("意见") || dto.Content.Contains("建议");
                     if (dto.Content.Length < 5)
                     {
@@ -629,19 +668,19 @@ public class OrderDomainService : IOrderDomainService, IScopeDependency
         }
         //查询即将超期和超期工单
         var orderList = await _orderRepository.Queryable()
-            .Where(x=> x.Status< EOrderStatus.Filed && !string.IsNullOrEmpty(x.CurrentHandleOrgId))
-            .GroupBy(x=>x.CurrentHandleOrgId)
+            .Where(x => x.Status < EOrderStatus.Filed && !string.IsNullOrEmpty(x.CurrentHandleOrgId))
+            .GroupBy(x => x.CurrentHandleOrgId)
             .Select(x => new OverTimeOrderDto
-            { 
-                 OrgId = x.CurrentHandleOrgId,
-                 NearlyOrderCount = SqlFunc.AggregateSum(SqlFunc.IIF(now >= x.NearlyExpiredTime  && now < x.ExpiredTime ,1,0)),
-                 ExpiredTimeOrderCount = SqlFunc.AggregateSum(SqlFunc.IIF(x.ExpiredTime<now,1,0))
+            {
+                OrgId = x.CurrentHandleOrgId,
+                NearlyOrderCount = SqlFunc.AggregateSum(SqlFunc.IIF(now >= x.NearlyExpiredTime && now < x.ExpiredTime, 1, 0)),
+                ExpiredTimeOrderCount = SqlFunc.AggregateSum(SqlFunc.IIF(x.ExpiredTime < now, 1, 0))
             })
             .ToListAsync(cancellationToken);
 
         foreach (var item in orderList)
         {
-            if (item.NearlyOrderCount==0 && item.ExpiredTimeOrderCount==0)
+            if (item.NearlyOrderCount == 0 && item.ExpiredTimeOrderCount == 0)
             {
                 continue;
             }
@@ -724,12 +763,12 @@ public class OrderDomainService : IOrderDomainService, IScopeDependency
         //成员单位标识  99
         //宜宾市 511500  市级
         var today = DateTime.Today;
-		//var count = no.Substring(no.Length - 5);
-		var setting = _systemSettingCacheManager.GetSetting(SettingConstants.VersionsAreaCode);
+        //var count = no.Substring(no.Length - 5);
+        var setting = _systemSettingCacheManager.GetSetting(SettingConstants.VersionsAreaCode);
         var versionsAreaCode = setting?.SettingValue[0];
 
-		//todo 双系统并行暂时执行此方案
-		var count = no.Substring(no.Length - 4);
+        //todo 双系统并行暂时执行此方案
+        var count = no.Substring(no.Length - 4);
         count = (Convert.ToInt32(count) + 50000).ToString();
 
         var provinceCodes = new[] { "RGDH", "WX", "WB", "AP", "WZ", "YJ", "SCZWFWW", "XCX", "QT" };

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

@@ -580,5 +580,10 @@ namespace Hotline.Settings
         /// 国家政务平台办理结果字数限制
         /// </summary>
         public const string NationalPlatformWordLimit = "NationalPlatformWordLimit";
+
+        /// <summary>
+        /// 取消发布功能总开关
+        /// </summary>
+        public const string CancelPublishOrderEnabled = "CancelPublishOrderEnabled";
     }
 }