瀏覽代碼

终止管理

田爽 6 月之前
父節點
當前提交
7eb200dd53

+ 16 - 9
src/Hotline.Api/Controllers/OrderController.cs

@@ -143,8 +143,9 @@ public class OrderController : BaseController
     private readonly ICalcExpireTime _expireTime;
     private readonly IRepository<OrderPushType> _orderPushTypeRepository;
     private readonly IOptions<CityBaseConfiguration> _cityBaseConfiguration;
+    private readonly IOrderTerminateRepository _orderTerminateRepository;
 
-    public OrderController(
+	public OrderController(
         IOrderDomainService orderDomainService,
         IOrderRepository orderRepository,
         IWorkflowApplication workflowApplication,
@@ -207,7 +208,8 @@ public class OrderController : BaseController
         IRepository<OrderPushType> orderPushTypeRepository,
         ICallNativeRepository callNativeRepository,
         ICallNativeApplication callNativeApplication,
-        BaseDataApplication baseDataApplication)
+        BaseDataApplication baseDataApplication,
+        IOrderTerminateRepository orderTerminateRepository)
     {
         _orderDomainService = orderDomainService;
         _orderRepository = orderRepository;
@@ -271,7 +273,8 @@ public class OrderController : BaseController
         _callNativeRepository = callNativeRepository;
         _callNativeApplication = callNativeApplication;
         _baseDataApplication = baseDataApplication;
-    }
+		_orderTerminateRepository = orderTerminateRepository;
+	}
     #endregion 
 
     #region 工单发布
@@ -5206,14 +5209,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"))
@@ -5228,7 +5233,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(HttpContext.RequestAborted),
+			BaseTypeId = baseTypeId
         };
         return rsp;
     }
@@ -5254,7 +5260,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(HttpContext.RequestAborted),
+			Step = step,
             Orgs = orgs,
         };
         return rsp;

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

@@ -0,0 +1,252 @@
+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;
+
+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;
+
+		public OrderTerminateController(
+			ISessionContext sessionContext,
+			IMapper mapper,
+			IFileRepository fileRepository,
+			IWorkflowApplication workflowApplication,
+			IRepository<Workflow> workflowRepository,
+			IWorkflowDomainService workflowDomainService,
+			IOrderRepository orderRepository,
+			IOrderDomainService orderDomainService,
+			IOrderTerminateRepository orderTerminateRepository
+			) 
+		{ 
+			_sessionContext = sessionContext;
+			_mapper = mapper;
+			_fileRepository = fileRepository;
+			_workflowApplication = workflowApplication;
+			_workflowRepository = workflowRepository;
+			_workflowDomainService = workflowDomainService;
+			_orderRepository = orderRepository;
+			_orderDomainService = orderDomainService;
+			_orderTerminateRepository = orderTerminateRepository;
+		}
+
+		/// <summary>
+		/// 工单终止待申请列表
+		/// </summary>
+		/// <param name="dto"></param>
+		/// <returns></returns>
+		[Permission(EPermission.CanOrderScreen)]
+		[HttpGet("may-terminate")]
+		public async Task<PagedDto<OrderDto>> MayScreenList([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>> ScreenList([FromQuery] OrderTerminateListDto dto)
+		{
+			var isAdmin = _orderDomainService.IsCheckAdmin();
+			var (total, items) = await _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)
+				.ToPagedListAsync(dto.PageIndex, dto.PageSize, HttpContext.RequestAborted);
+			return new PagedDto<OrderTerminateEntityDto>(total, _mapper.Map<IReadOnlyList<OrderTerminateEntityDto>>(items));
+		}
+
+
+		/// <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;
+		}
+	}
+}

+ 16 - 5
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 IOrderTerminateRepository _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,
+        IOrderTerminateRepository 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>
@@ -329,7 +331,16 @@ 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;
+			}
 
         }
         catch (Exception e)

+ 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 IOrderTerminateRepository _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,
+            IOrderTerminateRepository 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 IOrderTerminateRepository _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,
+            IOrderTerminateRepository 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)

+ 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; }
 	}
 }

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

@@ -44,7 +44,13 @@ public class WorkflowModuleConsts
     /// </summary>
     public const string TelRestApply = "TelRestApply";
 
-    public static List<WorkflowModule> AllModules =>
+    /// <summary>
+    /// 工单终止
+    /// </summary>
+    public const string OrderTerminate = "OrderTerminate";
+
+
+	public static List<WorkflowModule> AllModules =>
         new()
         {
             new(OrderHandle, "工单办理"),
@@ -55,5 +61,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

@@ -1074,8 +1074,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 - 8
src/Hotline/Permissions/EPermission.cs

@@ -849,15 +849,74 @@ namespace Hotline.Permissions
         [Display(GroupName = "二次办理", Name = "二次办理查询", Description = "二次办理查询")]
         SecondHandleQuery = 201507,
 
-        #endregion
-        #endregion
-        #endregion
+		#endregion
+		#endregion
 
-        #region 质检管理(40,00,00)
-        /// <summary>
-        /// 质检管理
-        /// </summary>
-        [Display(GroupName = "质检管理",Name ="质检管理",Description ="质检管理")]
+		#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
+
+		#region 质检管理(40,00,00)
+		/// <summary>
+		/// 质检管理
+		/// </summary>
+		[Display(GroupName = "质检管理",Name ="质检管理",Description ="质检管理")]
         QualityManage = 400000,
 
         #region 质检中心