田爽 6 月之前
父节点
当前提交
cd63bc5869

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

@@ -64,6 +64,7 @@ using XF.Domain.Entities;
 using XF.Domain.Exceptions;
 using XF.Domain.Repository;
 using XF.Utility.EnumExtensions;
+using Newtonsoft.Json;
 
 namespace Hotline.Api.Controllers;
 
@@ -135,10 +136,10 @@ public class OrderController : BaseController
     private readonly IOrderAnalysisApplication _orderAnalysisApplication;
     private readonly ICalcExpireTime _expireTime;
     private readonly IRepository<OrderPushType> _orderPushTypeRepository;
-    private readonly IOptions<CityBaseConfiguration> _cityBaseConfiguration;
     private readonly IRepository<OrderRevoke> _orderRevokeRepository;
+    private readonly IOrderTerminateRepository _orderTerminateRepository;
 
-    public OrderController(
+	public OrderController(
         IOrderDomainService orderDomainService,
         IOrderRepository orderRepository,
         IWorkflowApplication workflowApplication,
@@ -197,11 +198,12 @@ public class OrderController : BaseController
         Publisher publisher,
         IOrderAnalysisApplication orderAnalysisApplication,
         ICalcExpireTime expireTime,
-        IOptions<CityBaseConfiguration> cityBaseConfiguration,
         IRepository<OrderPushType> orderPushTypeRepository,
         ICallNativeRepository callNativeRepository,
         ICallNativeApplication callNativeApplication,
-        IRepository<OrderRevoke> orderRevokeRepository)
+        IRepository<OrderRevoke> orderRevokeRepository,
+        BaseDataApplication baseDataApplication,
+        IOrderTerminateRepository orderTerminateRepository)
     {
         _orderDomainService = orderDomainService;
         _orderRepository = orderRepository;
@@ -264,6 +266,8 @@ public class OrderController : BaseController
         _orderPushTypeRepository = orderPushTypeRepository;
         _callNativeRepository = callNativeRepository;
         _callNativeApplication = callNativeApplication;
+        _baseDataApplication = baseDataApplication;
+		_orderTerminateRepository = orderTerminateRepository;
         _orderRevokeRepository = orderRevokeRepository;
     }
     #endregion 
@@ -2379,9 +2383,10 @@ public class OrderController : BaseController
                 {
                 }
             }
-
-            //推省上
-            if (!string.IsNullOrEmpty(model.Id) && (_cityBaseConfiguration.Value.CityProvince.OrgId.Equals(model.OrgId) || _cityBaseConfiguration.Value.CityProvinceAssign.OrgId.Equals(model.OrgId)))
+            var setting = _systemSettingCacheManager.GetSetting(SettingConstants.CityBaseConfiguration)?.SettingValue[0];
+            CityBaseConfiguration cityBase = JsonConvert.DeserializeObject<CityBaseConfiguration>(setting);
+			//推省上
+			if (!string.IsNullOrEmpty(model.Id) && (cityBase.CityProvince.OrgId.Equals(model.OrgId) || cityBase.CityProvinceAssign.OrgId.Equals(model.OrgId)))
             {
                 var orderDto = _mapper.Map<OrderDto>(order);
                 var supervise = await _orderSuperviseRepository.GetAsync(x => x.Id == model.Id);
@@ -2624,9 +2629,10 @@ public class OrderController : BaseController
                 {
                 }
             }
-
-            //推省上
-            if (!string.IsNullOrEmpty(model.Id) && (_cityBaseConfiguration.Value.CityProvince.OrgId.Equals(model.OrgId) || _cityBaseConfiguration.Value.CityProvinceAssign.OrgId.Equals(model.OrgId)))
+            var setting = _systemSettingCacheManager.GetSetting(SettingConstants.CityBaseConfiguration)?.SettingValue[0];
+            CityBaseConfiguration cityBase = JsonConvert.DeserializeObject<CityBaseConfiguration>(setting);
+			//推省上
+			if (!string.IsNullOrEmpty(model.Id) && (cityBase.CityProvince.OrgId.Equals(model.OrgId) || cityBase.CityProvinceAssign.OrgId.Equals(model.OrgId)))
             {
                 var orderDto = _mapper.Map<OrderDto>(order);
                 var urge = await _orderUrgeRepository.GetAsync(x => x.Id == model.Id);
@@ -3391,7 +3397,9 @@ public class OrderController : BaseController
             throw new UserFriendlyException($"该工单已开启办理流程, No:{order.No}", "该工单已开启办理流程");
 
         ExpiredTimeWithConfig expiredTimeConfig;
-        if (dto.Workflow.NextHandlers.Any(d => d.Key == _cityBaseConfiguration.Value.CityProvince.OrgId || d.Key == _cityBaseConfiguration.Value.CityProvinceAssign.OrgId))
+        var setting = _systemSettingCacheManager.GetSetting(SettingConstants.CityBaseConfiguration)?.SettingValue[0];
+        CityBaseConfiguration cityBase = JsonConvert.DeserializeObject<CityBaseConfiguration>(setting);
+		if (dto.Workflow.NextHandlers.Any(d => d.Key == cityBase.CityProvince.OrgId || d.Key == cityBase.CityProvinceAssign.OrgId))
         {
             var timeResult = await _expireTime.CalcEndTime(DateTime.Now, ETimeType.WorkDay, 45, 80, 50);
             expiredTimeConfig = new ExpiredTimeWithConfig
@@ -4654,9 +4662,11 @@ public class OrderController : BaseController
                 }
             }
 
-            if (order != null && (_cityBaseConfiguration.Value.CityProvince.OrgId.Equals(model.OrgId) ||
-                                  _cityBaseConfiguration.Value.CityProvinceAssign.OrgId.Equals(model.OrgId) || _cityBaseConfiguration.Value.CityEnterprise.OrgId.Equals(model.OrgId) ||
-                                  _cityBaseConfiguration.Value.PublicSecurity.OrgId.Equals(model.OrgId)))
+            var setting = _systemSettingCacheManager.GetSetting(SettingConstants.CityBaseConfiguration)?.SettingValue[0];
+            CityBaseConfiguration cityBase = JsonConvert.DeserializeObject<CityBaseConfiguration>(setting);
+			if (order != null && (cityBase.CityProvince.OrgId.Equals(model.OrgId) ||
+			                      cityBase.CityProvinceAssign.OrgId.Equals(model.OrgId) || cityBase.CityEnterprise.OrgId.Equals(model.OrgId) ||
+			                      cityBase.PublicSecurity.OrgId.Equals(model.OrgId)))
             {
                 await _capPublisher.PublishAsync(Hotline.Share.Mq.EventNames.HotlineOrderFlowRecalled,
                     new PublishSpecialDto { Order = _mapper.Map<OrderDto>(order), Special = _mapper.Map<OrderSpecialDto>(model) },
@@ -4907,9 +4917,11 @@ public class OrderController : BaseController
                 }
             }
 
-            if (order != null && (_cityBaseConfiguration.Value.CityProvince.OrgId.Equals(special.OrgId) ||
-                                  _cityBaseConfiguration.Value.CityProvinceAssign.OrgId.Equals(special.OrgId) || _cityBaseConfiguration.Value.CityEnterprise.OrgId.Equals(special.OrgId) ||
-                                  _cityBaseConfiguration.Value.PublicSecurity.OrgId.Equals(special.OrgId)))
+            var setting = _systemSettingCacheManager.GetSetting(SettingConstants.CityBaseConfiguration)?.SettingValue[0];
+            CityBaseConfiguration cityBase = JsonConvert.DeserializeObject<CityBaseConfiguration>(setting);
+			if (order != null && (cityBase.CityProvince.OrgId.Equals(special.OrgId) ||
+			                      cityBase.CityProvinceAssign.OrgId.Equals(special.OrgId) || cityBase.CityEnterprise.OrgId.Equals(special.OrgId) ||
+			                      cityBase.PublicSecurity.OrgId.Equals(special.OrgId)))
             {
                 await _capPublisher.PublishAsync(Hotline.Share.Mq.EventNames.HotlineOrderFlowRecalled,
                     new PublishSpecialDto { Order = _mapper.Map<OrderDto>(order), Special = _mapper.Map<OrderSpecialDto>(special) },
@@ -5019,10 +5031,11 @@ public class OrderController : BaseController
                         await _orderVisitRepository.UpdateAsync(visit, HttpContext.RequestAborted);
                     }
                 }
-
-                if (order != null && (_cityBaseConfiguration.Value.CityProvince.OrgId.Equals(special.OrgId) ||
-                                      _cityBaseConfiguration.Value.CityProvinceAssign.OrgId.Equals(special.OrgId) || _cityBaseConfiguration.Value.CityEnterprise.OrgId.Equals(special.OrgId) ||
-                                      _cityBaseConfiguration.Value.PublicSecurity.OrgId.Equals(special.OrgId)))
+                var setting = _systemSettingCacheManager.GetSetting(SettingConstants.CityBaseConfiguration)?.SettingValue[0];
+                CityBaseConfiguration cityBase = JsonConvert.DeserializeObject<CityBaseConfiguration>(setting);
+				if (order != null && (cityBase.CityProvince.OrgId.Equals(special.OrgId) ||
+				                      cityBase.CityProvinceAssign.OrgId.Equals(special.OrgId) || cityBase.CityEnterprise.OrgId.Equals(special.OrgId) ||
+				                      cityBase.PublicSecurity.OrgId.Equals(special.OrgId)))
                 {
                     await _capPublisher.PublishAsync(Hotline.Share.Mq.EventNames.HotlineOrderFlowRecalled,
                         new PublishSpecialDto { Order = _mapper.Map<OrderDto>(order), Special = _mapper.Map<OrderSpecialDto>(special) },
@@ -5203,14 +5216,16 @@ public class OrderController : BaseController
         var step = await _workflowApplication.GetRecallStepsAsync(id, HttpContext.RequestAborted);
         var specialSeats = bool.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.SpecialSeats).SettingValue[0]);
         var specialSendOrder = bool.Parse(_systemSettingCacheManager.GetSetting(SettingConstants.SpecialSendOrder).SettingValue[0]);
+        var order = await _orderRepository.Queryable().Where(d => d.WorkflowId == id).FirstAsync(HttpContext.RequestAborted);
+        if (order == null) throw UserFriendlyException.SameMessage("无效工单信息!");
 
-        var baseTypeId = string.Empty;
-        if (step != null && step.Steps.Any() && _sessionContext.Roles.Contains("zuoxi") && specialSeats &&
+		var baseTypeId = string.Empty;
+		if (step != null && step.Steps.Any() && _sessionContext.Roles.Contains("zuoxi") && specialSeats &&
             !_sessionContext.Roles.Contains("paidanyuan"))
         {
             step.Steps = step.Steps.Where(x => x.Key.ToLower() == "start").ToList();
-            if (step.Steps.Any()) baseTypeId = step.Steps[0].Key;
-        }
+            if (step.Steps.Any()) baseTypeId = step.Steps[0].Key; 
+		}
 
         if (step != null && step.Steps.Any() && _sessionContext.Roles.Contains("paidanyuan") && specialSendOrder &&
             !_sessionContext.Roles.Contains("zuoxi"))
@@ -5225,7 +5240,8 @@ public class OrderController : BaseController
             SpecialReason = _sysDicDataCacheManager.GetSysDicDataCache(SysDicTypeConsts.SpecialReason),
             InstaShotSpecialReason = _sysDicDataCacheManager.GetSysDicDataCache(SysDicTypeConsts.InstaShotSpecialReason),
             Step = step,
-            BaseTypeId = baseTypeId
+			IsTerminate = _orderTerminateRepository.Queryable().Where(d=>d.OrderId == order.Id && d.Status == ETerminateStatus.End).AnyAsync(),
+			BaseTypeId = baseTypeId
         };
         return rsp;
     }
@@ -5251,7 +5267,8 @@ public class OrderController : BaseController
             SpecialTimeType = EnumExts.GetDescriptions<ETimeType>(),
             SpecialReason = isInstaShot ? _sysDicDataCacheManager.GetSysDicDataCache(SysDicTypeConsts.InstaShotSpecialReason) : _sysDicDataCacheManager.GetSysDicDataCache(SysDicTypeConsts.SpecialReason),
             ReTransactErrorType = _sysDicDataCacheManager.GetSysDicDataCache(SysDicTypeConsts.ReTransactErrorType),
-            Step = step,
+            IsTerminate = _orderTerminateRepository.Queryable().Where(d => d.OrderId == order.Id && d.Status == ETerminateStatus.End).AnyAsync(),
+			Step = step,
             Orgs = orgs,
         };
         return rsp;
@@ -5698,7 +5715,7 @@ public class OrderController : BaseController
 
     #endregion
 
-    #region 工单观察
+    #region 工单观察、关注
 
     /// <summary>
     /// 新增工单观察

+ 285 - 0
src/Hotline.Api/Controllers/OrderTerminateController.cs

@@ -0,0 +1,285 @@
+using Hotline.Orders;
+using Hotline.Permissions;
+using Hotline.Quality;
+using Hotline.Share.Dtos.Quality;
+using Hotline.Share.Dtos;
+using Microsoft.AspNetCore.Mvc;
+using XF.Domain.Authentications;
+using XF.Domain.Exceptions;
+using XF.Domain.Repository;
+using Hotline.Api.Filter;
+using Hotline.Caching.Services;
+using Hotline.FlowEngine.WorkflowModules;
+using Hotline.Repository.SqlSugar.Orders;
+using Hotline.Settings;
+using Hotline.Share.Dtos.FlowEngine;
+using Hotline.Share.Dtos.Order;
+using Hotline.Share.Enums.FlowEngine;
+using Hotline.Share.Enums.Order;
+using Hotline.Share.Enums.Settings;
+using SqlSugar;
+using XF.Utility.EnumExtensions;
+using MapsterMapper;
+using Hotline.File;
+using Hotline.Application.FlowEngine;
+using Hotline.FlowEngine.Workflows;
+using Hotline.Repository.SqlSugar.Extensions;
+using Hotline.Application.Orders;
+using Hotline.Share.Requests;
+using Hotline.Tools;
+
+namespace Hotline.Api.Controllers
+{
+	/// <summary>
+	/// 终止管理
+	/// </summary>
+	public class OrderTerminateController : BaseController
+	{
+		private readonly ISessionContext _sessionContext;
+		private readonly IMapper _mapper;
+		private readonly IFileRepository _fileRepository;
+		private readonly IWorkflowApplication _workflowApplication;
+		private readonly IRepository<Workflow> _workflowRepository;
+		private readonly IWorkflowDomainService _workflowDomainService;
+		private readonly IOrderRepository _orderRepository;
+		private readonly IOrderDomainService _orderDomainService;
+		private readonly IOrderTerminateRepository _orderTerminateRepository;
+		private readonly IOrderApplication _orderApplication;
+
+		public OrderTerminateController(
+			ISessionContext sessionContext,
+			IMapper mapper,
+			IFileRepository fileRepository,
+			IWorkflowApplication workflowApplication,
+			IRepository<Workflow> workflowRepository,
+			IWorkflowDomainService workflowDomainService,
+			IOrderRepository orderRepository,
+			IOrderDomainService orderDomainService,
+			IOrderTerminateRepository orderTerminateRepository,
+			IOrderApplication orderApplication
+			) 
+		{ 
+			_sessionContext = sessionContext;
+			_mapper = mapper;
+			_fileRepository = fileRepository;
+			_workflowApplication = workflowApplication;
+			_workflowRepository = workflowRepository;
+			_workflowDomainService = workflowDomainService;
+			_orderRepository = orderRepository;
+			_orderDomainService = orderDomainService;
+			_orderTerminateRepository = orderTerminateRepository;
+			_orderApplication= orderApplication;
+		}
+
+		/// <summary>
+		/// 工单终止待申请列表
+		/// </summary>
+		/// <param name="dto"></param>
+		/// <returns></returns>
+		[Permission(EPermission.CanOrderTerminate)]
+		[HttpGet("may-terminate")]
+		public async Task<PagedDto<OrderDto>> MayOrderTerminateList([FromQuery] OrderTerminateListDto dto)
+		{
+			var isAdmin = _orderDomainService.IsCheckAdmin();
+			var (total, items) =await _orderRepository.Queryable(isAdmin:isAdmin)
+				.Includes(d=>d.OrderTerminates)
+				.Where(d=> SqlFunc.Subqueryable<OrderTerminate>().Where(t=>t.Status == ETerminateStatus.End || t.Status == ETerminateStatus.Refuse).NotAny())
+				.Where(d => d.Status >= EOrderStatus.Filed && d.ActualHandleOrgCode.StartsWith(_sessionContext.OrgId))
+				.WhereIF(!string.IsNullOrEmpty(dto.No),d=>d.No!.Contains(dto.No!))
+				.WhereIF(!string.IsNullOrEmpty(dto.Title), d => d.Title!.Contains(dto.Title!))
+				.WhereIF(dto.ApplyStartTime.HasValue && dto.ApplyEndTime.HasValue,
+					d => d.OrderTerminates.Any(t=>t.CreationTime >= dto.ApplyStartTime && t.CreationTime<= dto.ApplyEndTime))
+				.WhereIF(dto.StartTime.HasValue && dto.EndTime.HasValue,d=>d.StartTime >= dto.StartTime && d.StartTime <= dto.EndTime)
+				.OrderByDescending(d => d.StartTime)
+				.ToPagedListAsync(dto.PageIndex, dto.PageSize, HttpContext.RequestAborted);
+			return new PagedDto<OrderDto>(total, _mapper.Map<IReadOnlyList<OrderDto>>(items));
+		}
+
+
+		/// <summary>
+		/// 工单终止列表
+		/// </summary>
+		/// <param name="dto"></param>
+		/// <returns></returns>
+		[HttpGet]
+		public async Task<PagedDto<OrderTerminateEntityDto>> OrderTerminateList([FromQuery] OrderTerminateListDto dto)
+		{
+			var (total, items) = await _orderApplication.OrderTerminateList(dto)
+				.ToPagedListAsync(dto.PageIndex, dto.PageSize, HttpContext.RequestAborted);
+			return new PagedDto<OrderTerminateEntityDto>(total, _mapper.Map<IReadOnlyList<OrderTerminateEntityDto>>(items));
+		}
+
+		/// <summary>
+		/// 工单终止列表导出
+		/// </summary>
+		/// <param name="dto"></param>
+		/// <returns></returns>
+
+		[HttpPost("export")]
+		public async Task<FileStreamResult> OrderTerminateListExport([FromBody] ExportExcelDto<OrderTerminateListDto> dto)
+		{
+			var query = _orderApplication.OrderTerminateList(dto.QueryDto);
+			List<OrderTerminate> data;
+			if (dto.IsExportAll)
+			{
+				data = await query.ToListAsync(HttpContext.RequestAborted);
+			}
+			else
+			{
+				var (_, items) = await query.ToPagedListAsync(dto.QueryDto, HttpContext.RequestAborted);
+				data = items;
+			}
+
+			var dataDtos = _mapper.Map<ICollection<OrderTerminateEntityDto>>(data);
+
+			dynamic? dynamicClass = DynamicClassHelper.CreateDynamicClass(dto.ColumnInfos);
+
+			var dtos = dataDtos
+				.Select(stu => _mapper.Map(stu, typeof(OrderTerminateEntityDto), dynamicClass))
+				.Cast<object>()
+				.ToList();
+
+			var stream = ExcelHelper.CreateStream(dtos);
+
+			return ExcelStreamResult(stream, "工单终止列表");
+		}
+
+
+		/// <summary>
+		/// 开始工单终止流程
+		/// </summary>
+		[Permission(EPermission.ApplyTerminate)]
+		[HttpPost("startflow")]
+		[LogFilter("开始工单终止流程")]
+		public async Task StartFlow([FromBody] TerminateStartFlowDto dto)
+		{
+			var screenAny = await _orderTerminateRepository.AnyAsync(x =>
+				x.OrderId == dto.Data.OrderId && x.Status == ETerminateStatus.Approval);
+			if (screenAny)
+				throw UserFriendlyException.SameMessage("该工单已提起终止申请,正在审批过程中,不能申请");
+
+			var isNoPass = await _orderTerminateRepository.AnyAsync(x => x.Status ==  ETerminateStatus.Refuse && x.OrderId == dto.Data.OrderId);
+			if (isNoPass)
+				throw UserFriendlyException.SameMessage("该工单已被拒绝过甄别申请,不能再次申请");
+
+			var model = _mapper.Map<OrderTerminate>(dto.Data);
+			model.Status = ETerminateStatus.Approval ;
+			model.InitId();
+			if (dto.Data.Files.Any())
+				model.FileJson = await _fileRepository.AddFileAsync(dto.Data.Files, model.Id, "", HttpContext.RequestAborted);
+			else
+				model.FileJson = new List<Share.Dtos.File.FileJson>();
+			await _orderTerminateRepository.AddAsync(model, HttpContext.RequestAborted);
+			try
+			{
+				var startDto = _mapper.Map<StartWorkflowDto>(dto.Workflow);
+				startDto.DefinitionModuleCode = WorkflowModuleConsts.OrderTerminate;
+				startDto.Opinion = dto.Data.Content;
+				startDto.Title = "申请终止流程";
+				await _workflowApplication.StartWorkflowAsync(startDto, _sessionContext, model.Id,	
+					cancellationToken: HttpContext.RequestAborted);
+			}
+			catch (Exception e)
+			{
+				await _orderTerminateRepository.RemoveAsync(model.Id);
+				model.Id = string.Empty;
+				throw new UserFriendlyException($"工单开启终止流程失败!, {e.Message}", "工单开启终止流程失败");
+			}
+		}
+
+
+		/// <summary>
+		/// 工单终止修改后下一步流程
+		/// </summary>
+		[HttpPost("initial_nextFlow")]
+		[LogFilter("办理工单终止流程")]
+		public async Task InitialNextFlow([FromBody] TerminateNextFlowDto dto)
+		{
+			var model = await _orderTerminateRepository.GetAsync(dto.Data.Id);
+
+			if (dto.Data.Files.Any())
+				model.FileJson = await _fileRepository.AddFileAsync(dto.Data.Files, model.Id, "", HttpContext.RequestAborted);
+			else
+				model.FileJson = new List<Share.Dtos.File.FileJson>();
+
+			model.Content = dto.Data.Content;
+			model.IsRecommit = true;
+			await _orderTerminateRepository.UpdateAsync(model, HttpContext.RequestAborted);
+			try
+			{
+				dto.NextWorkflow.WorkflowId = model.WorkflowId;
+				await _workflowApplication.NextAsync(dto.NextWorkflow, _sessionContext,
+					cancellationToken: HttpContext.RequestAborted);
+			}
+			catch (Exception e)
+			{
+				throw new UserFriendlyException($"工单终止下一步流程失败!, {e.Message}", "工单终止下一步流程失败");
+			}
+		}
+
+		/// <summary>
+		/// 查询工单终止流程开启参数
+		/// </summary>
+		/// <returns></returns>
+		[HttpGet("screen/startflow")]
+		public async Task<NextStepsDto> GetTerminateFlowStartOptionsAsync()
+		{
+			return await _workflowApplication.GetStartStepsAsync(WorkflowModuleConsts.OrderTerminate,
+				HttpContext.RequestAborted);
+		}
+
+		/// <summary>
+		/// 查询工单终止流程参数
+		/// </summary>
+		/// <returns></returns>
+		[HttpGet("workflow/{id}")]
+		public async Task<string> GetTerminateWorkFlowAsync(string id)
+		{
+			var workflow = await _workflowRepository.Queryable().FirstAsync(x => x.ExternalId == id);
+			return workflow.Id;
+		}
+
+		/// <summary>
+		///  甄别查询流程办理下一步可选节点
+		/// </summary>
+		/// <param name="workflowId"></param>
+		/// <returns></returns>
+		[HttpGet("{workflowId}/nextsteps")]
+		public async Task<NextStepsDto> OrderTerminateNextsteps(string workflowId)
+		{
+			return await _workflowApplication.GetNextStepsAsync(workflowId, HttpContext.RequestAborted);
+		}
+
+		/// <summary>
+		/// 终止详情
+		/// </summary>
+		/// <param name="id"></param>
+		/// <returns></returns>
+		[HttpGet("{id}")]
+		public async Task<OrderTerminateEntityDto> Entity(string id)
+		{
+			var model = await _orderTerminateRepository.Queryable()
+				.Includes(x => x.Order) 
+				.Includes(x => x.Workflow, d => d.Steps)
+				.FirstAsync(x => x.Id == id);
+			var rspModel = _mapper.Map<OrderTerminateEntityDto>(model);
+			rspModel.IsCanHandle = model.Workflow?.IsCanHandle(
+				_sessionContext.RequiredUserId, _sessionContext.RequiredOrgId, _sessionContext.Roles) ?? false;
+			if (model.Status ==  ETerminateStatus.SendBack)
+				rspModel.IsCanHandle = false;
+			rspModel.Handle = false;
+			if (!string.IsNullOrEmpty(rspModel.WorkflowId))
+			{
+				rspModel.Handle = await _workflowDomainService.CheckCurrentIsStartStepAsync(rspModel.WorkflowId, _sessionContext.RequiredUserId,
+					_sessionContext.RequiredOrgId, HttpContext.RequestAborted);
+			}
+
+			if (rspModel.FileJson != null && rspModel.FileJson.Any())
+			{
+				var ids = rspModel.FileJson.Select(x => x.Id).ToList();
+				rspModel.Files = await _fileRepository.GetFilesAsync(ids, HttpContext.RequestAborted);
+			}
+			return rspModel;
+		}
+	}
+}

+ 8 - 8
src/Hotline.Api/Controllers/TestController.cs

@@ -49,6 +49,7 @@ using Microsoft.AspNetCore.Builder.Extensions;
 using Microsoft.AspNetCore.Mvc;
 using Microsoft.Extensions.Options;
 using MiniExcelLibs;
+using Newtonsoft.Json;
 using SqlSugar;
 using XC.RSAUtil;
 using XF.Domain.Authentications;
@@ -125,7 +126,6 @@ public class TestController : BaseController
     //private readonly ITypedCache<List<User>> _cache;
     //private readonly ICacheManager<User> _cache;
     private readonly ICalcExpireTime _expireTime;
-    private readonly IOptions<CityBaseConfiguration> _cityBaseConfiguration;
 
 
 	public TestController(
@@ -177,8 +177,7 @@ IOrderDomainService orderDomainService,
 ICallApplication callApplication,
         IOptionsSnapshot<AppConfiguration> appOptions,
         ISystemSettingCacheManager systemSettingCacheManager,
-        ICalcExpireTime expireTime,
-        IOptions<CityBaseConfiguration> cityBaseConfiguration
+        ICalcExpireTime expireTime
 		)
     {
         _logger = logger;
@@ -228,7 +227,6 @@ ICallApplication callApplication,
         _appOptions = appOptions;
         _systemSettingCacheManager = systemSettingCacheManager;
         _expireTime = expireTime;
-        _cityBaseConfiguration = cityBaseConfiguration;
 	}
 
 
@@ -692,10 +690,12 @@ ICallApplication callApplication,
     [HttpGet("t5")]
     public async Task<string> GetUserAllowAnonymous()
     {
-        //var users = await _userRepository.Queryable()
-        //    .FirstAsync(d => d.Name == "xf", HttpContext.RequestAborted);
-        //return users.Id;
-        var b = _cityBaseConfiguration.Value;
+		//var users = await _userRepository.Queryable()
+		//    .FirstAsync(d => d.Name == "xf", HttpContext.RequestAborted);
+		//return users.Id;
+		var setting = _systemSettingCacheManager.GetSetting(SettingConstants.CityBaseConfiguration)?.SettingValue[0];
+		CityBaseConfiguration cityBase = JsonConvert.DeserializeObject<CityBaseConfiguration>(setting);
+		var b = cityBase;
 
 		var a = await _expireTime.GetWorkDay(DateTime.Now);
         return string.Empty;

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

@@ -32,6 +32,7 @@ using System.Text;
 using System.Diagnostics;
 using Hotline.Configurations;
 using Microsoft.Extensions.Options;
+using Newtonsoft.Json;
 using NPOI.SS.Formula.Functions;
 
 namespace Hotline.Application.FlowEngine;
@@ -59,7 +60,6 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
     private readonly IFileRepository _fileRepository;
     private readonly ILogger<WorkflowApplication> _logger;
     private readonly ISystemSettingCacheManager _systemSettingCacheManager;
-    private readonly IOptions<CityBaseConfiguration> _cityBaseConfiguration;
 
 	public WorkflowApplication(
         IDefinitionDomainService definitionDomainService,
@@ -80,8 +80,7 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
         IMapper mapper,
         IFileRepository fileRepository,
         ISystemSettingCacheManager systemSettingCacheManager,
-		ILogger<WorkflowApplication> logger,
-        IOptions<CityBaseConfiguration> cityBaseConfiguration)
+		ILogger<WorkflowApplication> logger)
     {
         _definitionDomainService = definitionDomainService;
         _workflowDomainService = workflowDomainService;
@@ -102,7 +101,6 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
         _fileRepository = fileRepository;
         _logger = logger;
         _systemSettingCacheManager = systemSettingCacheManager;
-        _cityBaseConfiguration = cityBaseConfiguration;
 	}
 
     public async Task<string> StartWorkflowAsync(StartWorkflowDto dto, ISessionContext current, string externalId,
@@ -1761,11 +1759,13 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
     /// </summary>
     public async Task UpdateProvinceHandleResultFilesAsync(string workflowId, List<FileDto> files, CancellationToken cancellationToken)
     {
-        var workflow = await _workflowDomainService.GetWorkflowAsync(workflowId, withSteps: true, withTraces: true,
+	    var setting = _systemSettingCacheManager.GetSetting(SettingConstants.CityBaseConfiguration)?.SettingValue[0];
+	    CityBaseConfiguration cityBase = JsonConvert.DeserializeObject<CityBaseConfiguration>(setting);
+		var workflow = await _workflowDomainService.GetWorkflowAsync(workflowId, withSteps: true, withTraces: true,
             cancellationToken: cancellationToken);
         var step = workflow.Steps.FirstOrDefault(d =>
 			//d.StepHandlers.Any(d => d.OrgId == _cityBaseConfiguration.Value.CityProvince.OrgId || d.OrgId == _cityBaseConfiguration.Value.CityProvinceAssign.OrgId));
-			d.HandlerOrgId == _cityBaseConfiguration.Value.CityProvince.OrgId || d.HandlerOrgId == _cityBaseConfiguration.Value.CityProvinceAssign.OrgId);
+			d.HandlerOrgId == cityBase.CityProvince.OrgId || d.HandlerOrgId == cityBase.CityProvinceAssign.OrgId);
         if (step is not null)
         {
             step.FileJson = await _fileRepository.AddFileAsync(files, workflow.ExternalId, step.Id, cancellationToken);

+ 15 - 4
src/Hotline.Application/Handlers/FlowEngine/WorkflowEndHandler.cs

@@ -45,8 +45,9 @@ public class WorkflowEndHandler : INotificationHandler<EndWorkflowNotify>
     private readonly ISystemSettingCacheManager _systemSettingCacheManager;
     private readonly Publisher _publisher;
     private readonly ICalcExpireTime _expireTime;
+    private readonly IRepository<OrderTerminate> _orderTerminateRepository;
 
-    public WorkflowEndHandler(
+	public WorkflowEndHandler(
         IMapper mapper,
         IKnowledgeDomainService knowledgeDomainService,
         IOrderDomainService orderDomainService,
@@ -61,9 +62,9 @@ public class WorkflowEndHandler : INotificationHandler<EndWorkflowNotify>
         IOptionsSnapshot<AppConfiguration> appOptions,
         ISystemSettingCacheManager systemSettingCacheManager,
         Publisher publisher,
-        ILogger<WorkflowEndHandler> logger
-,
-        ICalcExpireTime expireTime)
+        ILogger<WorkflowEndHandler> logger,
+        ICalcExpireTime expireTime,
+        IRepository<OrderTerminate> orderTerminateRepository)
     {
         _mapper = mapper;
         _knowledgeDomainService = knowledgeDomainService;
@@ -81,6 +82,7 @@ public class WorkflowEndHandler : INotificationHandler<EndWorkflowNotify>
         _publisher = publisher;
         _logger = logger;
         _expireTime = expireTime;
+        _orderTerminateRepository = orderTerminateRepository;
     }
 
     /// <summary>Handles a notification</summary>
@@ -331,6 +333,15 @@ public class WorkflowEndHandler : INotificationHandler<EndWorkflowNotify>
                         }
                     }
                     break;
+                case WorkflowModuleConsts.OrderTerminate:
+                    var orderTerminate = await _orderTerminateRepository.Queryable()
+                        .Where(x => x.Id == workflow.ExternalId).FirstAsync(cancellationToken);
+                    if (orderTerminate != null)
+                    {
+                        orderTerminate.Status = isReviewPass ? ETerminateStatus.End : ETerminateStatus.Refuse;
+                        await _orderTerminateRepository.UpdateAsync(orderTerminate, cancellationToken);
+                    }
+                    break;
             }
 
         }

+ 16 - 4
src/Hotline.Application/Handlers/FlowEngine/WorkflowPreviousHandler.cs

@@ -35,8 +35,9 @@ namespace Hotline.Application.Handlers.FlowEngine
         private readonly IMediator _mediator;
         private readonly ISessionContext _sessionContext;
         private readonly IRepository<OrderScreenDetail> _orderScreenDetailRepository;
+        private readonly IRepository<OrderTerminate> _orderTerminateRepository;
 
-        public WorkflowPreviousHandler(
+		public WorkflowPreviousHandler(
             IOrderDomainService orderDomainService,
             IOrderRepository orderRepository,
             IOrderScreenRepository orderScreenRepository,
@@ -49,8 +50,9 @@ namespace Hotline.Application.Handlers.FlowEngine
             IRepository<User> userRepository,
             IMediator mediator,
             ISessionContext sessionContext,
-            IRepository<OrderScreenDetail> orderScreenDetailRepository
-            )
+            IRepository<OrderScreenDetail> orderScreenDetailRepository,
+            IRepository<OrderTerminate> orderTerminateRepository
+			)
         {
             _orderDomainService = orderDomainService;
             _orderRepository = orderRepository;
@@ -65,6 +67,7 @@ namespace Hotline.Application.Handlers.FlowEngine
             _orderDelayRepository = orderDelayRepository;
             _sessionContext = sessionContext;
             _orderScreenDetailRepository = orderScreenDetailRepository;
+            _orderTerminateRepository = orderTerminateRepository;
         }
 
         /// <summary>Handles a notification</summary>
@@ -190,7 +193,16 @@ namespace Hotline.Application.Handlers.FlowEngine
                     case WorkflowModuleConsts.KnowledgeDelete:
                     case WorkflowModuleConsts.TelRestApply:
                         break;
-                }
+                    case WorkflowModuleConsts.OrderTerminate:
+	                    var orderTerminate = await _orderTerminateRepository.Queryable()
+		                    .Where(x => x.Id == workflow.ExternalId).FirstAsync(cancellationToken);
+	                    if (orderTerminate != null)
+	                    {
+		                    orderTerminate.Status = ETerminateStatus.SendBack;
+		                    await _orderTerminateRepository.UpdateAsync(orderTerminate, cancellationToken);
+	                    }
+	                    break;
+				}
 
             }
             catch (Exception e)

+ 17 - 5
src/Hotline.Application/Handlers/FlowEngine/WorkflowStartHandler.cs

@@ -42,8 +42,9 @@ namespace Hotline.Application.Handlers.FlowEngine
         private readonly IOptionsSnapshot<AppConfiguration> _appOptions;
         private readonly IMediator _mediator;
         private readonly ISystemSettingCacheManager _systemSettingCacheManager;
+        private readonly IRepository<OrderTerminate> _orderTerminateRepository;
 
-        public WorkflowStartHandler(
+		public WorkflowStartHandler(
             IOrderDomainService orderDomainService,
             IKnowledgeDomainService knowledgeDomainService,
             IOrderRepository orderRepository,
@@ -56,8 +57,9 @@ namespace Hotline.Application.Handlers.FlowEngine
             ICallApplication callApplication,
             IOptionsSnapshot<AppConfiguration> appOptions,
             IMediator mediator,
-            ISystemSettingCacheManager systemSettingCacheManager
-        )
+            ISystemSettingCacheManager systemSettingCacheManager,
+            IRepository<OrderTerminate> orderTerminateRepository
+		)
         {
             _orderDomainService = orderDomainService;
             _knowledgeDomainService = knowledgeDomainService;
@@ -73,7 +75,8 @@ namespace Hotline.Application.Handlers.FlowEngine
             _appOptions = appOptions;
             _mediator = mediator;
             _systemSettingCacheManager = systemSettingCacheManager;
-        }
+            _orderTerminateRepository = orderTerminateRepository;
+		}
 
         /// <summary>Handles a notification</summary>
         /// <param name="notification">The notification</param>
@@ -239,7 +242,16 @@ namespace Hotline.Application.Handlers.FlowEngine
                         }
 
                         break;
-                }
+                    case WorkflowModuleConsts.OrderTerminate:
+                        var orderTerminate = await _orderTerminateRepository.Queryable()
+	                        .Where(x => x.Id == workflow.ExternalId).FirstAsync(cancellationToken);
+                        if (orderTerminate != null)
+                        {
+                            orderTerminate.Status = ETerminateStatus.Approval;
+                            await _orderTerminateRepository.UpdateAsync(orderTerminate, cancellationToken);
+						}
+                        break;
+				}
 
             }
             catch (Exception e)

+ 7 - 0
src/Hotline.Application/Orders/IOrderApplication.cs

@@ -307,5 +307,12 @@ namespace Hotline.Application.Orders
         ISugarQueryable<OrderObserve> OrderObserveList(OrderObserveListDto dto);
 
 
+        /// <summary>
+        /// 终止列表查询
+        /// </summary>
+        /// <param name="dto"></param>
+        /// <returns></returns>
+        ISugarQueryable<OrderTerminate> OrderTerminateList(OrderTerminateListDto dto);
+
 	}
 }

+ 30 - 11
src/Hotline.Application/Orders/OrderApplication.cs

@@ -64,6 +64,7 @@ using Hotline.Share.Tools;
 using Hotline.EventBus;
 using Hotline.Orders.Notifications;
 using Hotline.OrderTranspond;
+using Newtonsoft.Json;
 
 namespace Hotline.Application.Orders;
 
@@ -102,7 +103,7 @@ public class OrderApplication : IOrderApplication, IScopeDependency
     private readonly IRepository<OrderScreen> _orderScreenRepository;
     private readonly IRepository<OrderSendBackAudit> _orderSendBackAuditRepository;
     private readonly ICalcExpireTime _expireTime;
-    private readonly IOptions<CityBaseConfiguration> _cityBaseConfiguration;
+    private readonly IOrderTerminateRepository _orderTerminateRepository;
     private readonly IRepository<OrderObserve> _orderObserveRepository;
 
 
@@ -136,10 +137,10 @@ public class OrderApplication : IOrderApplication, IScopeDependency
         IMediator mediator,
         IRepository<OrderVisitDetail> orderVisitedDetailRepository,
         IOptionsSnapshot<AppConfiguration> appOptions,
-        IOptions<CityBaseConfiguration> cityBaseConfiguration,
         ISystemDicDataCacheManager sysDicDataCacheManager,
         Publisher publisher,
         IRepository<TranspondCityRawData> transpondCityRawDataRepository,
+        IOrderTerminateRepository orderTerminateRepository,
         IRepository<OrderObserve> orderObserveRepository)
     {
         _orderDomainService = orderDomainService;
@@ -174,7 +175,7 @@ public class OrderApplication : IOrderApplication, IScopeDependency
         _sysDicDataCacheManager = sysDicDataCacheManager;
         _publisher = publisher;
         _transpondCityRawDataRepository = transpondCityRawDataRepository;
-        _cityBaseConfiguration = cityBaseConfiguration;
+        _orderTerminateRepository = orderTerminateRepository;
         _orderObserveRepository = orderObserveRepository;
 
 	}
@@ -2117,18 +2118,36 @@ public class OrderApplication : IOrderApplication, IScopeDependency
         return query;
     }
 
+	/// <summary>
+	/// 终止列表查询
+	/// </summary>
+	/// <param name="dto"></param>
+	/// <returns></returns>
 
+	public ISugarQueryable<OrderTerminate> OrderTerminateList(OrderTerminateListDto dto) {
+		var isAdmin = _orderDomainService.IsCheckAdmin();
+		var query = _orderTerminateRepository.Queryable(hasHandled: false, isAdmin: isAdmin)
+            .Includes(d => d.Order)
+            .WhereIF(!string.IsNullOrEmpty(dto.No), d => d.Order.No!.Contains(dto.No!))
+            .WhereIF(!string.IsNullOrEmpty(dto.Title), d => d.Order.Title!.Contains(dto.Title!))
+            .WhereIF(dto.ApplyStartTime.HasValue && dto.ApplyEndTime.HasValue,
+                d => d.CreationTime >= dto.ApplyStartTime && d.CreationTime <= dto.ApplyEndTime)
+            .WhereIF(dto.StartTime.HasValue && dto.EndTime.HasValue, d => d.Order.StartTime >= dto.StartTime && d.Order.StartTime <= dto.EndTime)
+            .OrderByDescending(d => d.CreationTime);
+		return query;
+	}
 
 
-    #region private
 
-    /// <summary>
-    /// 接受外部工单(除省平台)
-    /// </summary>
-    /// <param name="dto"></param>
-    /// <param name="cancellationToken"></param>
-    /// <returns></returns>
-    private async Task<AddOrderResponse> ReceiveOrderFromOtherPlatformAsync(AddOrderDto dto, List<FileDto> files,
+	#region private
+
+	/// <summary>
+	/// 接受外部工单(除省平台)
+	/// </summary>
+	/// <param name="dto"></param>
+	/// <param name="cancellationToken"></param>
+	/// <returns></returns>
+	private async Task<AddOrderResponse> ReceiveOrderFromOtherPlatformAsync(AddOrderDto dto, List<FileDto> files,
         ISessionContext current, CancellationToken cancellationToken)
     {
         if (string.IsNullOrEmpty(dto.ExternalId))

+ 23 - 16
src/Hotline.Application/Subscribers/DatasharingSubscriber.cs

@@ -27,6 +27,7 @@ using Hotline.Share.Mq;
 using MapsterMapper;
 using Microsoft.AspNetCore.Http;
 using Microsoft.Extensions.Options;
+using Newtonsoft.Json;
 using XF.Domain.Authentications;
 using XF.Domain.Dependency;
 using XF.Domain.Exceptions;
@@ -68,7 +69,6 @@ namespace Hotline.Application.Subscribers
         private readonly ICallApplication _callApplication;
         private readonly IRepository<OrderPublish> _orderPublishRepository;
         private readonly ISystemDicDataCacheManager _sysDicDataCacheManager;
-        private readonly IOptions<CityBaseConfiguration> _cityBaseConfiguration;
         private readonly ISessionContext _sessionContext;
 
         public DataSharingSubscriber(
@@ -100,9 +100,8 @@ namespace Hotline.Application.Subscribers
             IOptionsSnapshot<AppConfiguration> appOptions,
             ICallApplication callApplication,
             IRepository<OrderPublish> orderPublishRepository,
-            ISystemDicDataCacheManager sysDicDataCacheManager,
-            IOptions<CityBaseConfiguration> cityBaseConfiguration,
-            ISessionContext sessionContext)
+            ISessionContext sessionContext,
+            ISystemDicDataCacheManager sysDicDataCacheManager)
         {
             _orderSendBackRepository = orderSendBackRepository;
             _workflowApplication = workflowApplication;
@@ -133,7 +132,6 @@ namespace Hotline.Application.Subscribers
             _callApplication = callApplication;
             _orderPublishRepository = orderPublishRepository;
             _sysDicDataCacheManager = sysDicDataCacheManager;
-            _cityBaseConfiguration = cityBaseConfiguration;
             _sessionContext = sessionContext;
         }
 
@@ -258,9 +256,11 @@ namespace Hotline.Application.Subscribers
                 }
             };
             await _orderRevokeRepository.AddAsync(orderRevoke, cancellationToken);
+            var setting = _systemSettingCacheManager.GetSetting(SettingConstants.CityBaseConfiguration)?.SettingValue[0];
+            CityBaseConfiguration cityBase = JsonConvert.DeserializeObject<CityBaseConfiguration>(setting);
 
-            //宜宾需求:特提至中心(优先派单组无派单组节点就特提至坐席),由派单员归档
-            var current = SessionContextCreator.CreateSessionContext(_sessionContext, dto.Source, _cityBaseConfiguration.Value);
+			//宜宾需求:特提至中心(优先派单组无派单组节点就特提至坐席),由派单员归档
+			var current = SessionContextCreator.CreateSessionContext(_sessionContext, dto.Source, cityBase);
             if (string.IsNullOrEmpty(order?.WorkflowId))
             {
                 var startDto = new StartWorkflowDto
@@ -433,7 +433,9 @@ namespace Hotline.Application.Subscribers
                             x.Status == EScreenStatus.Approval)
                 .FirstAsync(cancellationToken);
 
-            var current = SessionContextCreator.CreateSessionContext(_sessionContext, dto.Source, _cityBaseConfiguration.Value);
+            var setting = _systemSettingCacheManager.GetSetting(SettingConstants.CityBaseConfiguration)?.SettingValue[0];
+            CityBaseConfiguration cityBase = JsonConvert.DeserializeObject<CityBaseConfiguration>(setting);
+			var current = SessionContextCreator.CreateSessionContext(_sessionContext, dto.Source, cityBase);
             await _workflowApplication.HandleToEndAsync(current,
                 orderScreen.WorkflowId, "省上推送甄别结果", null,
                 dto.ProvinceScreenResult.AuditResult
@@ -679,7 +681,10 @@ namespace Hotline.Application.Subscribers
                         orderDelay.FileJson = await _fileRepository.AddFileAsync(dto.Files, orderDelay.Id, orderDelay.WorkflowId, cancellationToken);
                     await _orderDelayRepository.UpdateAsync(orderDelay, cancellationToken);
 
-                    var current = SessionContextCreator.CreateSessionContext(_sessionContext, dto.Source, _cityBaseConfiguration.Value);
+                    var setting = _systemSettingCacheManager.GetSetting(SettingConstants.CityBaseConfiguration)?.SettingValue[0];
+                    CityBaseConfiguration cityBase = JsonConvert.DeserializeObject<CityBaseConfiguration>(setting);
+
+					var current = SessionContextCreator.CreateSessionContext(_sessionContext, dto.Source, cityBase);
                     await _workflowApplication.HandleToEndAsync(current, orderDelay.WorkflowId, dto.Opinion, dto.Files,
                         dto.IsPass ? EReviewResult.Approval : EReviewResult.Failed, cancellationToken);
                 }
@@ -698,14 +703,16 @@ namespace Hotline.Application.Subscribers
             if (string.IsNullOrEmpty(order.WorkflowId))
                 throw new UserFriendlyException($"该工单未开启流程,orderId: {dto.OrderId}");
 
-            //if (dto.Files != null && dto.Files.Any())
-            //{
-            //    order.FileJson = await _fileRepository.AddFileAsync(dto.Files, order.Id, order.WorkflowId,
-            //        cancellationToken);
-            //    await _orderRepository.FileAsync(order, cancellationToken);
-            //}
+			//if (dto.Files != null && dto.Files.Any())
+			//{
+			//    order.FileJson = await _fileRepository.AddFileAsync(dto.Files, order.Id, order.WorkflowId,
+			//        cancellationToken);
+			//    await _orderRepository.FileAsync(order, cancellationToken);
+			//}
+			var setting = _systemSettingCacheManager.GetSetting(SettingConstants.CityBaseConfiguration)?.SettingValue[0];
+			CityBaseConfiguration cityBase = JsonConvert.DeserializeObject<CityBaseConfiguration>(setting);
 
-            var current = SessionContextCreator.CreateSessionContext(_sessionContext, dto.Source, _cityBaseConfiguration.Value);
+			var current = SessionContextCreator.CreateSessionContext(_sessionContext, dto.Source, cityBase);
             switch (dto.FinishType)
             {
                 case "0":

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

@@ -10,6 +10,7 @@ using SqlSugar;
 using System.Threading;
 using XF.Domain.Authentications;
 using XF.Domain.Dependency;
+using XF.Domain.Exceptions;
 using XF.Domain.Repository;
 
 namespace Hotline.Repository.SqlSugar.File
@@ -34,6 +35,11 @@ namespace Hotline.Repository.SqlSugar.File
 			await Db.Deleteable<Hotline.File.File>().In(x=>x.Id , deleteFilesId).ExecuteCommandAsync(cancellationToken);
 			foreach (FileDto file in files)
 			{
+				if (string.IsNullOrEmpty(file.Path) || string.IsNullOrEmpty(file.AllPath))
+					throw UserFriendlyException.SameMessage("附件信息错误,请检查后重新上传附件!");
+				var names = file.FileName.Split(".");
+				file.Name = names[0];
+				file.Type = names[1];
 				file.OrgName = _sessionContext.OrgName;
 				file.OrgId = _sessionContext.OrgId;
 				file.UserId = _sessionContext.UserId;

+ 12 - 2
src/Hotline.Share/Dtos/File/FileDto.cs

@@ -9,7 +9,12 @@ namespace Hotline.Share.Dtos.File
 {
 	public class FileDto
 	{
-        /// <summary>
+		/// <summary>
+		/// 附件全称
+		/// </summary>
+		public string FileName { get; set; }
+
+		/// <summary>
 		/// 附件名称
 		/// </summary>
 		public string? Name { get; set; }
@@ -73,7 +78,12 @@ namespace Hotline.Share.Dtos.File
 		/// </summary>
 		public string? Path { get; set; }
 
-}
+		/// <summary>
+		/// 完整附件路径
+		/// </summary>
+		public string? AllPath { get; set; }
+
+	}
 	public class UpdateFileDto: FileDto
 	{
 		public string Id { get; set; }

+ 13 - 1
src/Hotline.Share/Dtos/Order/OrderStartFlowDto.cs

@@ -22,7 +22,19 @@ namespace Hotline.Share.Dtos.Order
 
     }
 
-    public class ScreenNextFlowDto
+    public class TerminateStartFlowDto : StartWorkflowDto<OrderTerminateDto>
+    {
+
+    }
+
+    public class TerminateNextFlowDto
+	{
+	    public OrderTerminateDto Data { get; set; }
+
+	    public NextWorkflowDto NextWorkflow { get; set; }
+    }
+
+	public class ScreenNextFlowDto
     {
         public OrderScreenDto Data { get; set; }
 

+ 182 - 0
src/Hotline.Share/Dtos/Order/OrderTerminateDto.cs

@@ -0,0 +1,182 @@
+using Hotline.Share.Dtos.File;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Hotline.Share.Requests;
+using System.ComponentModel;
+using XF.Utility.EnumExtensions;
+
+namespace Hotline.Share.Dtos.Order
+{
+	public class OrderTerminateDto
+	{
+
+		/// <summary>
+		/// 终止Id
+		/// </summary>
+		public string Id { get; set; }
+
+		/// <summary>
+		/// 工单id
+		/// </summary>
+		public string OrderId { get; set; }
+
+
+		/// <summary>
+		/// 终止状态
+		/// </summary>
+		public ETerminateStatus? Status { get; set; }
+
+		public string StatusText => Status.GetDescription();
+
+		/// <summary>
+		/// 工单编号
+		/// </summary>
+		public string No { get; set; }
+
+		/// <summary>
+		/// 终止理由
+		/// </summary>
+		public string? Content { get; set; }
+
+		/// <summary>
+		/// 附件
+		/// </summary>
+		public List<FileJson>? FileJson { get; set; }
+
+		/// <summary>
+		/// 附件列表
+		/// </summary>
+		public List<FileDto> Files { get; set; } = new();
+	}
+
+	public class OrderTerminateBaseDto
+	{
+		public DateTime? LastModificationTime { get; set; }
+
+		public bool IsDeleted { get; set; }
+
+		/// <summary>
+		/// 删除时间
+		/// </summary>
+		public DateTime? DeletionTime { get; set; }
+
+
+		/// <summary>
+		/// 创建时间
+		/// </summary>
+		public DateTime CreationTime { get; set; }
+
+		public string Id { get; set; }
+
+		/// <summary>
+		/// 组织Id
+		/// </summary>
+		public string? CreatorOrgId { get; set; }
+
+
+		public string? CreatorOrgName { get; set; }
+
+		/// <summary>
+		/// 创建人
+		/// </summary>
+		public string? CreatorId { get; set; }
+
+		public string? CreatorName { get; set; }
+
+		public string? WorkflowId { get; set; }
+	}
+
+	public class OrderTerminateEntityDto : OrderTerminateBaseDto 
+	{
+		/// <summary>
+		/// 工单
+		/// </summary>
+		public OrderDto Order { get; set; }
+
+		/// <summary>
+		/// 是否可办理
+		/// </summary>
+		public bool IsCanHandle { get; set; }
+
+
+		/// <summary>
+		/// 办理 true  审批 false 
+		/// </summary>
+		public bool Handle { get; set; }
+
+		/// <summary>
+		/// 附件列表
+		/// </summary>
+		public List<FileDto> Files { get; set; } = new();
+
+		public List<FileJson>? FileJson { get; set; }
+	}
+
+	public record OrderTerminateListDto : PagedRequest
+	{
+		/// <summary>
+		/// 工单编号
+		/// </summary>
+		public string? No { get; set; }
+
+		/// <summary>
+		/// 工单标题
+		/// </summary>
+		public string? Title { get; set; }
+
+		/// <summary>
+		/// 审批状态  0全部  1待审批 2已审批 
+		/// </summary>
+		public int AuditStatus { get; set; }
+
+		/// <summary>
+		/// 申请开始时间
+		/// </summary>
+		public DateTime? ApplyStartTime { get; set; }
+
+		/// <summary>
+		/// 申请结束时间
+		/// </summary>
+		public DateTime? ApplyEndTime { get; set; }
+
+		/// <summary>
+		/// 工单受理开始时间
+		/// </summary>
+		public DateTime? StartTime { get; set; }
+
+		/// <summary>
+		/// 工单受理结束时间
+		/// </summary>
+		public DateTime? EndTime { get; set; }
+	}
+
+	public enum ETerminateStatus
+	{
+		/// <summary>
+		/// 审批中
+		/// </summary>
+		[Description("审批中")]
+		Approval = 1,
+
+		/// <summary>
+		/// 终止完成
+		/// </summary>
+		[Description("终止同意")]
+		End = 2,
+
+		/// <summary>
+		/// 终止拒绝
+		/// </summary>
+		[Description("终止不同意")]
+		Refuse = 3,
+
+		/// <summary>
+		/// 退回
+		/// </summary>
+		[Description("终止退回")]
+		SendBack = 4,
+	}
+}

+ 6 - 0
src/Hotline/File/File.cs

@@ -81,5 +81,11 @@ namespace Hotline.File
 		/// </summary>
 		[SugarColumn(ColumnDescription = "附件路径")]
 		public string? Path { get; set; }
+
+		/// <summary>
+		/// 完整附件路径
+		/// </summary>
+		[SugarColumn(ColumnDescription = "完整附件路径")]
+		public string? AllPath { get; set; }
 	}
 }

+ 13 - 1
src/Hotline/FlowEngine/WorkflowModules/WorkflowModuleConsts.cs

@@ -44,7 +44,18 @@ public class WorkflowModuleConsts
     /// </summary>
     public const string TelRestApply = "TelRestApply";
 
-    public static List<WorkflowModule> AllModules =>
+    /// <summary>
+    /// 工单终止
+    /// </summary>
+    public const string OrderTerminate = "OrderTerminate";
+
+    /// <summary>
+    /// 二次办理
+    /// </summary>
+    public const string OrderSecondaryHandling = "OrderSecondaryHandling";
+
+
+	public static List<WorkflowModule> AllModules =>
         new()
         {
             new(OrderHandle, "工单办理"),
@@ -55,5 +66,6 @@ public class WorkflowModuleConsts
             new(OrderDelay,"工单延期"),
             new(OrderPrevious,"工单退回"),
             new(OrderScreen,"工单甄别"),
+            new(OrderTerminate,"工单终止"),
         };
 }

+ 6 - 1
src/Hotline/Orders/IOrderRepository.cs

@@ -185,7 +185,12 @@ namespace Hotline.Orders
 
     }
 
-    public interface IOrderDelayRepository : IRepositoryWorkflow<OrderDelay>
+    public interface IOrderTerminateRepository : IRepositoryWorkflow<OrderTerminate>
+    {
+
+    }
+
+	public interface IOrderDelayRepository : IRepositoryWorkflow<OrderDelay>
     {
 
     }

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

@@ -1080,8 +1080,14 @@ namespace Hotline.Orders
         public List<OrderScreen> OrderScreens { get; set; }
 
         /// <summary>
-        /// 
+        /// 终止
         /// </summary>
+        [Navigate(NavigateType.OneToMany, nameof(OrderTerminate.OrderId))]
+        public List<OrderTerminate> OrderTerminates { get; set; }
+
+		/// <summary>
+		/// 
+		/// </summary>
 		[Navigate(NavigateType.OneToMany, nameof(OrderSpecial.OrderId))]
         public List<OrderSpecial> OrderSpecials { get; set; }
 

+ 66 - 0
src/Hotline/Orders/OrderTerminate.cs

@@ -0,0 +1,66 @@
+using Hotline.FlowEngine.Workflows;
+using Hotline.Share.Dtos.File;
+using SqlSugar;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Hotline.Share.Dtos.Order;
+using XF.Domain.Repository;
+
+namespace Hotline.Orders
+{
+	public class OrderTerminate : WorkflowEntity
+	{
+		/// <summary>
+		/// 工单id
+		/// </summary>
+		[SugarColumn(ColumnDescription = "工单id")]
+		public string OrderId { get; set; }
+
+		/// <summary>
+		/// 工单编号
+		/// </summary>
+		[SugarColumn(ColumnDescription = "工单编号")]
+		public string No { get; set; }
+
+		/// <summary>
+		/// 终止状态
+		/// </summary>
+		[SugarColumn(ColumnDescription = "终止状态")]
+		public ETerminateStatus? Status { get; set; }
+
+		/// <summary>
+		/// 重提终止
+		/// </summary>
+		[SugarColumn(ColumnDescription = "重提终止",DefaultValue ="f")]
+		public bool IsRecommit { get; set; }
+
+		/// <summary>
+		/// 终止理由
+		/// </summary>
+		[SugarColumn(ColumnDescription = "终止理由", ColumnDataType = "varchar(2000)")]
+		public string? Content { get; set; }
+
+		/// <summary>
+		/// 附件
+		/// </summary>
+		[SugarColumn(ColumnDescription = "附件", ColumnDataType = "json", IsJson = true, IsNullable = true)]
+		public List<FileJson>? FileJson { get; set; }
+
+		/// <summary>
+		/// 工单
+		/// </summary>
+		[Navigate(NavigateType.OneToOne, nameof(OrderId))]
+		public Order Order { get; set; }
+
+		/// <summary>
+		/// 流程
+		/// </summary>
+		[Navigate(NavigateType.OneToOne, nameof(WorkflowId))]
+		public Workflow? Workflow { get; set; }
+	}
+
+}

+ 67 - 5
src/Hotline/Permissions/EPermission.cs

@@ -855,6 +855,68 @@ namespace Hotline.Permissions
         [Display(GroupName = "二次办理", Name = "二次办理查询", Description = "二次办理查询")]
         SecondHandleQuery = 201507,
 
+		#endregion
+		#endregion
+
+		#region 终止管理
+
+		/// <summary>
+		/// 终止管理
+		/// </summary>
+		[Display(GroupName = "业务管理", Name = "终止管理", Description = "终止管理")]
+		OrderTerminateManage = 201600,
+
+		#region 终止待办
+		/// <summary>
+		/// 终止待办
+		/// </summary>
+		[Display(GroupName = "终止待办", Name = "终止待办", Description = "终止待办")]
+		TerminateOrder = 201601,
+		/// <summary>
+		/// 终止审批
+		/// </summary>
+		[Display(GroupName = "终止待办", Name = "终止审批", Description = "终止审批")]
+		TerminateOrderAudit = 201602,
+		/// <summary>
+		/// 终止退回
+		/// </summary>
+		[Display(GroupName = "终止待办", Name = "终止退回", Description = "终止退回")]
+		TerminateOrderReturn = 201603,
+		#endregion
+
+		#region 终止待申请
+		/// <summary>
+		/// 待终止列表
+		/// </summary>
+		[Display(GroupName = "业务管理", Name = "待终止列表", Description = "待终止列表")]
+		CanOrderTerminate = 201604,
+		/// <summary>
+		/// 申请终止
+		/// </summary>
+		[Display(GroupName = "业务管理", Name = "申请终止", Description = "申请终止")]
+		ApplyTerminate = 201605,
+		#endregion
+
+		#region 终止列表
+		/// <summary>
+		/// 终止列表
+		/// </summary>
+		[Display(GroupName = "业务管理", Name = "终止列表", Description = "终止列表")]
+		OrderTerminate = 201606,
+		/// <summary>
+		/// 终止审批
+		/// </summary>
+		[Display(GroupName = "业务管理", Name = "终止审批", Description = "终止审批")]
+		OrderTerminateAudit = 201607,
+		/// <summary>
+		/// 终止退回
+		/// </summary>
+		[Display(GroupName = "业务管理", Name = "终止退回", Description = "终止退回")]
+		OrderTerminateReturn = 201608,
+		#endregion
+
+		#endregion
+		#endregion
         #endregion
         #endregion
 
@@ -878,11 +940,11 @@ namespace Hotline.Permissions
         #endregion
         #endregion
 
-        #region 质检管理(40,00,00)
-        /// <summary>
-        /// 质检管理
-        /// </summary>
-        [Display(GroupName = "质检管理",Name ="质检管理",Description ="质检管理")]
+		#region 质检管理(40,00,00)
+		/// <summary>
+		/// 质检管理
+		/// </summary>
+		[Display(GroupName = "质检管理",Name ="质检管理",Description ="质检管理")]
         QualityManage = 400000,
 
         #region 质检中心

+ 6 - 1
src/Hotline/Settings/SettingConstants.cs

@@ -535,5 +535,10 @@ namespace Hotline.Settings
         /// 旧数据通知公告知识库附件地址	
         /// </summary>
         public const string OldFilesUrls = "OldFilesUrls";
-    }
+
+		/// <summary>
+		/// 市州基本信息配置
+		/// </summary>
+		public const string CityBaseConfiguration = "CityBaseConfiguration";
+	}
 }