Эх сурвалжийг харах

feature: 工单详情增加退回意见

xfe 9 сар өмнө
parent
commit
508e9989bd

+ 1 - 0
src/Hotline.Api/Controllers/OrderController.cs

@@ -2426,6 +2426,7 @@ public class OrderController : BaseController
             dto.CountersignId = result.CountersignId;
             dto.CanHandle = result.CanHandle;
             dto.CanPrevious = result.CanPrevious;
+            dto.PreviousOpinion = result.previousOpinion;
 
             await _mediator.Publish(new GetOrderDetailNotify(result.Workflow,
                 _sessionContext.RequiredUserId, _sessionContext.UserName,

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

@@ -353,6 +353,11 @@ namespace Hotline.Share.Dtos.Order
         /// 是否可办理
         /// </summary>
         public bool CanHandle { get; set; }
+        
+        /// <summary>
+        /// 退回意见
+        /// </summary>
+        public string? PreviousOpinion { get; set; }
 
         /// <summary>
         /// 延期申请数量

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

@@ -36,7 +36,7 @@ namespace Hotline.FlowEngine.Workflows
         /// <summary>
         /// 查询工作流包含当前用户办理权限(是否可办理)
         /// </summary>
-        Task<(Workflow Workflow, string? CountersignId, bool CanHandle, bool CanPrevious)> GetWorkflowHandlePermissionAsync(
+        Task<(Workflow Workflow, string? CountersignId, bool CanHandle, bool CanPrevious,string? previousOpinion)> GetWorkflowHandlePermissionAsync(
             string workflowId, string userId, string orgId, string[] roleIds, CancellationToken cancellationToken = default);
 
         /// <summary>

+ 84 - 45
src/Hotline/FlowEngine/Workflows/WorkflowDomainService.cs

@@ -29,7 +29,9 @@ namespace Hotline.FlowEngine.Workflows
         private readonly IRepository<WorkflowStep> _workflowStepRepository;
         private readonly IRepository<WorkflowTrace> _workflowTraceRepository;
         private readonly IRepository<WorkflowSupplement> _workflowSupplementRepository;
+
         private readonly IRepository<WorkflowCountersign> _workflowCountersignRepository;
+
         //private readonly IRepository<WorkflowStepHandler> _workflowStepHandlerRepository;
         private readonly ISessionContext _sessionContext;
         private readonly IMapper _mapper;
@@ -104,7 +106,8 @@ namespace Hotline.FlowEngine.Workflows
 
             if (firstStepDefine.StepType is EStepType.End)
             {
-                await _mediator.Publish(new StartWorkflowNotify(workflow, dto, flowAssignInfo, startStep.WorkflowTrace), cancellationToken);
+                await _mediator.Publish(new StartWorkflowNotify(workflow, dto, flowAssignInfo, startStep.WorkflowTrace),
+                    cancellationToken);
 
                 //firstStep是否为end,t: 实际办理节点为startStep, 并且handlerId赋值 f: 实际办理节点为firstStep, handlerId未赋值
                 workflow.UpdateActualStepWhenHandle(startStep,
@@ -113,7 +116,8 @@ namespace Hotline.FlowEngine.Workflows
                     current.OrgAreaCode, current.OrgAreaName,
                     current.OrgLevel);
 
-                var endTrace = await EndAsync(workflow, dto, firstStepDefine, startStep, current, expiredTime, cancellationToken);
+                var endTrace = await EndAsync(workflow, dto, firstStepDefine, startStep, current, expiredTime,
+                    cancellationToken);
                 return;
             }
 
@@ -230,10 +234,12 @@ namespace Hotline.FlowEngine.Workflows
         /// <summary>
         /// 查询工作流包含当前用户结束会签权限(是否可结束)
         /// </summary>
-        public async Task<(Workflow Workflow, string? CountersignId, bool CanHandle, bool CanPrevious)> GetWorkflowHandlePermissionAsync(
-            string workflowId, string userId, string orgId, string[] roleIds, CancellationToken cancellationToken = default)
+        public async Task<(Workflow Workflow, string? CountersignId, bool CanHandle, bool CanPrevious, string? previousOpinion)>
+            GetWorkflowHandlePermissionAsync(
+                string workflowId, string userId, string orgId, string[] roleIds,
+                CancellationToken cancellationToken = default)
         {
-            var workflow = await GetWorkflowAsync(workflowId, withSteps: true, withCountersigns: true,
+            var workflow = await GetWorkflowAsync(workflowId, withSteps: true, withTraces: true, withCountersigns: true,
                 cancellationToken: cancellationToken);
 
             var canHandle = workflow.IsCanHandle(userId, orgId, roleIds);
@@ -255,7 +261,13 @@ namespace Hotline.FlowEngine.Workflows
             //var existCountersignEndStep = workflow.Steps.Exists(d =>
             //    d.IsCountersignEndStep && d.CountersignStartStepId == unCompletedCountersign.StartStepId);
             //return (workflow, existCountersignEndStep ? null : unCompletedCountersign.Id, canPrevious);
-            return (workflow, unCompletedCountersign.Id, canHandle, canPrevious);
+
+            var unhandlePreviousTrace = workflow.Traces.FirstOrDefault(d =>
+                d.Status is not EWorkflowStepStatus.Handled &&
+                d.TraceType is EWorkflowTraceType.Previous);
+            var previousOpinion = unhandlePreviousTrace?.Opinion ?? null;
+            
+            return (workflow, unCompletedCountersign.Id, canHandle, canPrevious, previousOpinion);
         }
 
         /// <summary>
@@ -267,7 +279,8 @@ namespace Hotline.FlowEngine.Workflows
             string? orgAreaCode, string? orgAreaName,
             CancellationToken cancellationToken)
         {
-            if (!workflow.IsCanHandle(_sessionContext.RequiredUserId, _sessionContext.RequiredOrgId, _sessionContext.Roles)) return;
+            if (!workflow.IsCanHandle(_sessionContext.RequiredUserId, _sessionContext.RequiredOrgId,
+                    _sessionContext.Roles)) return;
             //工单完成以后查看的场景
             if (workflow.Status != EWorkflowStatus.Runnable) return;
 
@@ -415,7 +428,8 @@ namespace Hotline.FlowEngine.Workflows
                 {
                     //会签中正常办理节点,更新会签members办理状态
                     var countersign =
-                        workflow.Countersigns.FirstOrDefault(d => !d.IsCompleted() && d.Id == currentStep.CountersignId);
+                        workflow.Countersigns.FirstOrDefault(d =>
+                            !d.IsCompleted() && d.Id == currentStep.CountersignId);
                     if (countersign is not null)
                     {
                         //throw new UserFriendlyException(
@@ -463,7 +477,8 @@ namespace Hotline.FlowEngine.Workflows
             //检查是否流转到流程终点
             if (nextStepDefine.StepType is EStepType.End)
             {
-                var endTrace = await EndAsync(workflow, dto, nextStepDefine, currentStep, current, expiredTime, cancellationToken);
+                var endTrace = await EndAsync(workflow, dto, nextStepDefine, currentStep, current, expiredTime,
+                    cancellationToken);
                 return;
             }
 
@@ -531,7 +546,8 @@ namespace Hotline.FlowEngine.Workflows
         {
             //ValidatePermission(workflow, operater.OrgId, operater.Id);
 
-            var (currentStep, prevStep, countersignStartStep) = GetPreviousStep(workflow, applicantId, applicantOrgId, applicantRoleIds);
+            var (currentStep, prevStep, countersignStartStep) =
+                GetPreviousStep(workflow, applicantId, applicantOrgId, applicantRoleIds);
 
             //保存附件
             if (dto.Files.Any())
@@ -595,7 +611,8 @@ namespace Hotline.FlowEngine.Workflows
             //await _workflowTraceRepository.UpdateAsync(trace, cancellationToken);
 
             //复制上一个节点为待接办
-            var newPrevStep = await DuplicateStepWithTraceAsync(workflow, prevStep, EWorkflowTraceType.Previous, cancellationToken);
+            var newPrevStep =
+                await DuplicateStepWithTraceAsync(workflow, prevStep, EWorkflowTraceType.Previous, cancellationToken);
 
             //remove workflow.steps
             await _workflowStepRepository.RemoveRangeAsync(removeSteps, cancellationToken);
@@ -626,7 +643,8 @@ namespace Hotline.FlowEngine.Workflows
             return GetFlowDirection(currentStep.BusinessType, prevStep.BusinessType);
         }
 
-        private async Task UpdateTracesStateAsync(List<WorkflowTrace> traces, EWorkflowTraceState traceState, CancellationToken cancellationToken)
+        private async Task UpdateTracesStateAsync(List<WorkflowTrace> traces, EWorkflowTraceState traceState,
+            CancellationToken cancellationToken)
         {
             foreach (var trace in traces)
             {
@@ -648,7 +666,8 @@ namespace Hotline.FlowEngine.Workflows
             if (currentStep.IsInCountersign() && !isCurrentTopCountersignEndStep)
                 throw UserFriendlyException.SameMessage("会签节点不支持退回");
 
-            if (workflow.FlowType is EFlowType.Review && currentStep.StepType is EStepType.Start && currentStep.IsOrigin)
+            if (workflow.FlowType is EFlowType.Review && currentStep.StepType is EStepType.Start &&
+                currentStep.IsOrigin)
                 throw UserFriendlyException.SameMessage("当前流程已退回到开始节点");
 
             //当退回操作遇到会签时,删除所有会签节点直达topCsStep
@@ -684,7 +703,8 @@ namespace Hotline.FlowEngine.Workflows
         /// <summary>
         /// 查询派单池中流程节点id
         /// </summary>
-        public async Task<IReadOnlyList<string>> GetUnhandleStepIdsFromSendPoolAsync(string sendPoolId, CancellationToken cancellationToken)
+        public async Task<IReadOnlyList<string>> GetUnhandleStepIdsFromSendPoolAsync(string sendPoolId,
+            CancellationToken cancellationToken)
         {
             return await _workflowStepRepository.Queryable()
                 .Where(d => SqlFunc.JsonListObjectAny(d.Handlers, "Key", sendPoolId))
@@ -766,7 +786,8 @@ namespace Hotline.FlowEngine.Workflows
         /// 批量修改工单办理对象
         /// </summary>
         public async Task ChangeHandlerBatchAsync(
-            IReadOnlyList<(string userId, string username, string orgId, string orgName, ICollection<WorkflowStep> steps)> handlers,
+            IReadOnlyList<(string userId, string username, string orgId, string orgName, ICollection<WorkflowStep> steps
+                )> handlers,
             CancellationToken cancellationToken)
         {
             foreach (var handler in handlers)
@@ -796,11 +817,11 @@ namespace Hotline.FlowEngine.Workflows
         public async Task<ICollection<Kv>> GetLevelOneOrgsAsync(string workflowId, CancellationToken cancellation)
         {
             var traces = await _workflowTraceRepository.Queryable()
-                 .LeftJoin<SystemOrganize>((t, o) => t.HandlerOrgId == o.Id)
-                 .Where((t, o) => t.WorkflowId == workflowId &&
-                                  !string.IsNullOrEmpty(t.HandlerOrgId) &&
-                                  o.Level == 1)
-                 .ToListAsync(cancellation);
+                .LeftJoin<SystemOrganize>((t, o) => t.HandlerOrgId == o.Id)
+                .Where((t, o) => t.WorkflowId == workflowId &&
+                                 !string.IsNullOrEmpty(t.HandlerOrgId) &&
+                                 o.Level == 1)
+                .ToListAsync(cancellation);
 
             //var handlers = await _workflowStepHandlerRepository.Queryable()
             //    .InnerJoin<WorkflowTrace>((wsh, wt) => wsh.WorkflowStepId == wt.StepId)
@@ -836,7 +857,8 @@ namespace Hotline.FlowEngine.Workflows
         /// <summary>
         /// 更新未办理节点的期满时间
         /// </summary>
-        public async Task UpdateUnhandleExpiredTimeAsync(string workflowId, DateTime expiredTime, CancellationToken cancellation)
+        public async Task UpdateUnhandleExpiredTimeAsync(string workflowId, DateTime expiredTime,
+            CancellationToken cancellation)
         {
             var steps = await _workflowStepRepository.Queryable()
                 .Includes(d => d.WorkflowTrace)
@@ -858,7 +880,8 @@ namespace Hotline.FlowEngine.Workflows
         /// 查询该部门最后办理节点
         /// </summary>
         /// <returns></returns>
-        public async Task<WorkflowStep> FindLastHandleStepAsync(string workflowId, string orgId, CancellationToken cancellation)
+        public async Task<WorkflowStep> FindLastHandleStepAsync(string workflowId, string orgId,
+            CancellationToken cancellation)
         {
             return await _workflowStepRepository.Queryable()
                 .Where(d => d.WorkflowId == workflowId && d.HandlerOrgId == orgId && d.StepType != EStepType.End)
@@ -870,7 +893,8 @@ namespace Hotline.FlowEngine.Workflows
         /// <summary>
         /// 查询流转方向
         /// </summary>
-        public EFlowDirection GetFlowDirection(EBusinessType sourceStepBusinessType, EBusinessType directionStepBusinessType)
+        public EFlowDirection GetFlowDirection(EBusinessType sourceStepBusinessType,
+            EBusinessType directionStepBusinessType)
         {
             switch (sourceStepBusinessType)
             {
@@ -904,9 +928,11 @@ namespace Hotline.FlowEngine.Workflows
         /// <summary>
         /// 流程被签收至某个用户(更新流转对象,办理对象,节点办理对象以及stepHandlers)
         /// </summary>
-        public async Task<Workflow> SignToSomebodyAsync(string workflowId, string userId, string username, string orgId, string orgName, CancellationToken cancellationToken)
+        public async Task<Workflow> SignToSomebodyAsync(string workflowId, string userId, string username, string orgId,
+            string orgName, CancellationToken cancellationToken)
         {
-            var workflow = await GetWorkflowAsync(workflowId, withSteps: true, withTraces: true, cancellationToken: cancellationToken);
+            var workflow = await GetWorkflowAsync(workflowId, withSteps: true, withTraces: true,
+                cancellationToken: cancellationToken);
             workflow.Assign(EFlowAssignType.User, _sessionContext.RequiredUserId);
 
             workflow.HandlerOrgs = new();
@@ -954,7 +980,8 @@ namespace Hotline.FlowEngine.Workflows
         /// <summary>
         /// 查找当前会签内所有节点(含start,end)
         /// </summary>
-        private void SearchCountersignSteps(WorkflowStep startStep, List<WorkflowStep> steps, ref List<WorkflowStep> csSteps)
+        private void SearchCountersignSteps(WorkflowStep startStep, List<WorkflowStep> steps,
+            ref List<WorkflowStep> csSteps)
         {
             if (startStep.IsStartCountersign)
             {
@@ -996,7 +1023,8 @@ namespace Hotline.FlowEngine.Workflows
         /// <summary>
         /// 撤回至开始节点
         /// </summary>
-        public async Task RecallToStartStepAsync(string workflowId, string opinion, ISessionContext current, CancellationToken cancellationToken)
+        public async Task RecallToStartStepAsync(string workflowId, string opinion, ISessionContext current,
+            CancellationToken cancellationToken)
         {
             //todo 1.当前待办节点删掉 2.当前待办trace更新(status, opinion) 3.复制startStep为待办 4.更新workflow(status, csStatus, handlers) 5.publish event
             var workflow = await GetWorkflowAsync(workflowId, withDefine: true, withSteps: true, withTraces: true,
@@ -1216,7 +1244,8 @@ namespace Hotline.FlowEngine.Workflows
         /// <summary>
         /// 查询当前待办理节点
         /// </summary>
-        public WorkflowStep FindCurrentStepWaitForHandle(Workflow workflow, string userId, string orgId, string[] roleIds) =>
+        public WorkflowStep FindCurrentStepWaitForHandle(Workflow workflow, string userId, string orgId,
+            string[] roleIds) =>
             GetUnHandleStep(workflow.Steps, orgId, userId, roleIds);
 
         /// <summary>
@@ -1232,7 +1261,8 @@ namespace Hotline.FlowEngine.Workflows
         /// 查询所有办理部门及实际办理部门
         /// </summary>
         /// <returns></returns>
-        public async Task<(Kv, IReadOnlyList<Kv>)> GetHandleOrgsAsync(string workflowId, CancellationToken cancellationToken)
+        public async Task<(Kv, IReadOnlyList<Kv>)> GetHandleOrgsAsync(string workflowId,
+            CancellationToken cancellationToken)
         {
             var workflow = await GetWorkflowAsync(workflowId, withTraces: true, cancellationToken: cancellationToken);
             var steps = workflow.Traces
@@ -1278,7 +1308,8 @@ namespace Hotline.FlowEngine.Workflows
         /// <summary>
         /// 撤销流程
         /// </summary>
-        public async Task CancelAsync(CancelDto dto, DateTime? expiredTime, ISessionContext current, CancellationToken cancellationToken)
+        public async Task CancelAsync(CancelDto dto, DateTime? expiredTime, ISessionContext current,
+            CancellationToken cancellationToken)
         {
             var workflow = await GetWorkflowAsync(dto.WorkflowId, withDefine: true, withSteps: true,
                 cancellationToken: cancellationToken);
@@ -1291,7 +1322,7 @@ namespace Hotline.FlowEngine.Workflows
 
             var basicDto = _mapper.Map<BasicWorkflowDto>(dto);
             var endTrace = await EndAsync(workflow, basicDto, endStepDefine, currentStep, current,
-               expiredTime, cancellationToken: cancellationToken);
+                expiredTime, cancellationToken: cancellationToken);
 
             await _mediator.Publish(new CancelWorkflowNotify(workflow), cancellationToken);
         }
@@ -1364,7 +1395,8 @@ namespace Hotline.FlowEngine.Workflows
             //};
 
             //create endStep
-            var endStep = await CreateEndStepAsync(current, workflow, endStepDefine, currentStep, expiredTime, cancellationToken);
+            var endStep = await CreateEndStepAsync(current, workflow, endStepDefine, currentStep, expiredTime,
+                cancellationToken);
             workflow.Steps.Add(endStep);
 
             //update endTrace
@@ -1533,11 +1565,13 @@ namespace Hotline.FlowEngine.Workflows
                         if (dto.BackToCountersignEnd)
                         {
                             // csStartStep.prev
-                            var csStartStep = workflow.Steps.FirstOrDefault(d => d.Id == currentStep.CountersignStartStepId);
+                            var csStartStep =
+                                workflow.Steps.FirstOrDefault(d => d.Id == currentStep.CountersignStartStepId);
                             if (csStartStep is null)
                                 throw new UserFriendlyException("未查询到会签节点");
 
-                            nextSteps = await CreateCsEndStepsByTargetPrevAsync(workflow, csStartStep, dto, expiredTime, cancellationToken);
+                            nextSteps = await CreateCsEndStepsByTargetPrevAsync(workflow, csStartStep, dto, expiredTime,
+                                cancellationToken);
                         }
                         else
                         {
@@ -1656,7 +1690,8 @@ namespace Hotline.FlowEngine.Workflows
             //会签未全部办理则不创建汇总节点
 
             var csInnerSteps = workflow.Steps.Where(d => d.PrevStepId == countersignStartStep.Id).ToList();
-            if (csInnerSteps.Any(d => d.Status != EWorkflowStepStatus.Handled || (d.IsStartCountersign && !d.IsStartedCountersignEnd)))
+            if (csInnerSteps.Any(d =>
+                    d.Status != EWorkflowStepStatus.Handled || (d.IsStartCountersign && !d.IsStartedCountersignEnd)))
                 return nextSteps;
             //if (csInnerSteps.All(d => d.Status == EWorkflowStepStatus.Handled))
             //{
@@ -1731,9 +1766,9 @@ namespace Hotline.FlowEngine.Workflows
         private void HandleStep(ISessionContext current, WorkflowStep step, string opinion, string nextStepCode)
         {
             step.Handle(current.RequiredUserId, current.UserName,
-                 current.RequiredOrgId, current.OrgName,
-                 current.OrgAreaCode, current.OrgAreaName,
-                 current.OrgIsCenter, opinion, nextStepCode);
+                current.RequiredOrgId, current.OrgName,
+                current.OrgAreaCode, current.OrgAreaName,
+                current.OrgIsCenter, opinion, nextStepCode);
 
             //var handler = step.FindActualHandler(current.Roles, current.RequiredUserId, current.RequiredOrgId);
             //if (handler is not null)
@@ -1744,7 +1779,8 @@ namespace Hotline.FlowEngine.Workflows
         /// <summary>
         /// 开始会签(创建会签数据,更新currentStep会签数据)
         /// </summary>
-        private async Task StartCountersignAsync(ISessionContext current, Workflow workflow, WorkflowStep startStep, BasicWorkflowDto dto,
+        private async Task StartCountersignAsync(ISessionContext current, Workflow workflow, WorkflowStep startStep,
+            BasicWorkflowDto dto,
             EFlowAssignType? flowAssignType, ECounterSignType? counterSignType, DateTime? expiredTime,
             CancellationToken cancellationToken)
         {
@@ -1846,7 +1882,6 @@ namespace Hotline.FlowEngine.Workflows
             //    .ExecuteCommandAsync();
 
 
-
             await CreateTraceAsync(workflow, newStep, traceType, cancellationToken);
 
             return newStep;
@@ -1907,7 +1942,8 @@ namespace Hotline.FlowEngine.Workflows
         //    await _workflowTraceRepository.UpdateRangeAsync(uncompleteTraces, cancellationToken);
         //}
 
-        private async Task RecallTraceAsync(List<WorkflowTrace> traces, string opinion, ISessionContext current, CancellationToken cancellationToken)
+        private async Task RecallTraceAsync(List<WorkflowTrace> traces, string opinion, ISessionContext current,
+            CancellationToken cancellationToken)
         {
             //未办理的traces
             //var uncompleteTraces =
@@ -2085,6 +2121,7 @@ namespace Hotline.FlowEngine.Workflows
                 {
                     trace.TraceState = EWorkflowTraceState.StepRemoveByRecall;
                 }
+
                 updateTraces.AddRange(traces);
             }
 
@@ -2170,7 +2207,7 @@ namespace Hotline.FlowEngine.Workflows
             };
 
             var step = CreateStep(workflow, endStepDefine, prevStep, EFlowAssignType.User, handler,
-                 null, null, EWorkflowStepStatus.WaitForAccept,
+                null, null, EWorkflowStepStatus.WaitForAccept,
                 ECountersignPosition.None, expiredTime, endStepDefine.Name, true);
 
             //step.Accept(_sessionContext.RequiredUserId, _sessionContext.UserName,
@@ -2212,7 +2249,7 @@ namespace Hotline.FlowEngine.Workflows
                 var handler = stepDefine.HandlerTypeItems.First();
                 handlers = new List<FlowStepHandler>
                 {
-                    new(){Key = handler.Key, Value = handler.Value, RoleId = handler.Key, RoleName = handler.Value}
+                    new() { Key = handler.Key, Value = handler.Value, RoleId = handler.Key, RoleName = handler.Value }
                 }; //flowAssignInfo.GetHandlers();
             }
             else
@@ -2379,7 +2416,8 @@ namespace Hotline.FlowEngine.Workflows
                 var updateSteps = new List<WorkflowStep>();
                 var updateTraces = new List<WorkflowTrace>();
 
-                HandleStepsByTerminalCs(startCountersignStep, workflow.Steps, workflow.Traces, ref updateSteps, ref updateTraces);
+                HandleStepsByTerminalCs(startCountersignStep, workflow.Steps, workflow.Traces, ref updateSteps,
+                    ref updateTraces);
                 if (updateSteps.Any())
                     await _workflowStepRepository.RemoveRangeAsync(updateSteps, cancellationToken);
                 //await _workflowStepRepository.RemoveNav(updateSteps)
@@ -2392,7 +2430,8 @@ namespace Hotline.FlowEngine.Workflows
 
                 //cp会签发起节点变为待办节点
                 //1. create terminal trace 2. 撤回至startStep
-                var newStep = await DuplicateStepWithTraceAsync(workflow, startCountersignStep, EWorkflowTraceType.Normal,
+                var newStep = await DuplicateStepWithTraceAsync(workflow, startCountersignStep,
+                    EWorkflowTraceType.Normal,
                     cancellationToken);
 
                 //当topcsStep结束cs时,实际办理节点应该更新为newStep