Просмотр исходного кода

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

田爽 6 месяцев назад
Родитель
Сommit
03febd1302

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

@@ -71,6 +71,7 @@ using XF.Domain.Extensions;
 using Order = Hotline.Orders.Order;
 using WorkflowStep = Hotline.FlowEngine.Workflows.WorkflowStep;
 using System.Threading;
+using Hotline.Application.Contracts.Validators.FlowEngine;
 
 namespace Hotline.Api.Controllers;
 
@@ -3481,6 +3482,11 @@ public class OrderController : BaseController
     [HttpPost("startflow")]
     public async Task StartFlow([FromBody] StartWorkflowDto<OrderHandleFlowDto> dto)
     {
+        var validator = new BasicWorkflowDtoValidator();
+        var validResult = await validator.ValidateAsync(dto.Workflow, HttpContext.RequestAborted);
+        if (!validResult.IsValid)
+            throw new UserFriendlyException(
+                $"非法参数, {string.Join(',', validResult.Errors.Select(d => d.ErrorMessage))}");
         var order = await _orderDomainService.GetOrderAsync(dto.Data.OrderId, cancellationToken: HttpContext.RequestAborted);
         if (!string.IsNullOrEmpty(order.WorkflowId))
             throw new UserFriendlyException($"该工单已开启办理流程, No:{order.No}", "该工单已开启办理流程");
@@ -3579,6 +3585,11 @@ public class OrderController : BaseController
     [HttpPost("handle")]
     public async Task Handle([FromBody] NextWorkflowDto<OrderHandleFlowDto> dto)
     {
+        var validator = new BasicWorkflowDtoValidator();
+        var validResult = await validator.ValidateAsync(dto.WorkflowDto, HttpContext.RequestAborted);
+        if (!validResult.IsValid)
+            throw new UserFriendlyException(
+                $"非法参数, {string.Join(',', validResult.Errors.Select(d => d.ErrorMessage))}");
         var order = await _orderApplication.SaveOrderWorkflowInfo(dto, HttpContext.RequestAborted);
 
         var workflow = await _workflowDomainService.GetWorkflowAsync(dto.WorkflowDto.WorkflowId, withSteps: true, withTraces: true,
@@ -3725,6 +3736,7 @@ public class OrderController : BaseController
     /// 跨级指派查询下一步可选节点及办理对象参数
     /// </summary>
     /// <returns></returns>
+    [HttpGet("nextsteps/cross")]
     public async Task<NextStepsDto<NextStepOption>> GetCrossLevelStepsOptions([FromQuery] GetCrossLevelStepsDto dto)
     {
         return await _workflowApplication.GetCrossLevelStepsAsync(dto, HttpContext.RequestAborted);

+ 4 - 8
src/Hotline.Application.Contracts/Validators/FlowEngine/BasicWorkflowDtoValidator.cs

@@ -15,14 +15,10 @@ namespace Hotline.Application.Contracts.Validators.FlowEngine
         {
             RuleFor(d => d.BusinessType).NotNull();
             RuleFor(d => d.HandlerType).NotNull();
-        }
-    }
-
-    public class RecallDtoValidator : AbstractValidator<RecallDto>
-    {
-        public RecallDtoValidator()
-        {
-            Include(new BasicWorkflowDtoValidator());
+            RuleFor(d => d.Opinion)
+                .Cascade(CascadeMode.Stop)
+                .NotEmpty()
+                .MaximumLength(2000);
         }
     }
 }

+ 1 - 0
src/Hotline.Application.Contracts/Validators/FlowEngine/NextWorkflowDtoValidator.cs

@@ -12,6 +12,7 @@ namespace Hotline.Application.Contracts.Validators.FlowEngine
     {
         public NextWorkflowDtoValidator()
         {
+            Include(new BasicWorkflowDtoValidator());
             RuleFor(d => d.WorkflowId).NotEmpty();
             //RuleFor(d=>d.ExpiredTime).NotEmpty();
             //RuleFor(d=>d.NextStepCode).NotEmpty();

+ 12 - 0
src/Hotline.Application.Contracts/Validators/FlowEngine/RecallDtoValidator.cs

@@ -0,0 +1,12 @@
+using FluentValidation;
+using Hotline.Share.Dtos.FlowEngine;
+
+namespace Hotline.Application.Contracts.Validators.FlowEngine;
+
+public class RecallDtoValidator : AbstractValidator<RecallDto>
+{
+    public RecallDtoValidator()
+    {
+        Include(new BasicWorkflowDtoValidator());
+    }
+}

+ 17 - 21
src/Hotline.Application.Contracts/Validators/FlowEngine/StartWorkflowDtoValidator.cs

@@ -8,28 +8,24 @@ using Hotline.Share.Dtos.FlowEngine;
 using Hotline.Application.Contracts.Validators;
 using Hotline.Share.Enums.FlowEngine;
 
-namespace Hotline.Application.Contracts.Validators.FlowEngine
+namespace Hotline.Application.Contracts.Validators.FlowEngine;
+
+public class StartWorkflowDtoValidator : AbstractValidator<StartWorkflowDto>
 {
-    public class StartWorkflowDtoValidator : AbstractValidator<StartWorkflowDto>
+    public StartWorkflowDtoValidator()
     {
-        public StartWorkflowDtoValidator()
-        {
-            //RuleFor(d => d.DefinitionCode)
-            //    .NotEmpty().When(d => string.IsNullOrEmpty(d.DefinitionModuleCode));
-            //RuleFor(d => d.DefinitionModuleCode)
-            //    .NotEmpty().When(d => string.IsNullOrEmpty(d.DefinitionCode));
+        //RuleFor(d => d.DefinitionCode)
+        //    .NotEmpty().When(d => string.IsNullOrEmpty(d.DefinitionModuleCode));
+        //RuleFor(d => d.DefinitionModuleCode)
+        //    .NotEmpty().When(d => string.IsNullOrEmpty(d.DefinitionCode));
 
-            //RuleFor(d => d.DefinitionModuleCode).NotEmpty();
-            //RuleFor(d => d.Title).NotEmpty();
-            RuleFor(d => d.NextStepCode).NotEmpty();
-            RuleFor(d => d.IsSms).NotNull();
-            RuleFor(d => d.IsStartCountersign).NotNull();
-            RuleFor(d => d.NextHandlers.Count).LessThanOrEqualTo(1)
-                .Unless(d => d.IsStartCountersign);
-            RuleFor(d => d.Opinion)
-                .Cascade(CascadeMode.Stop)
-                .NotEmpty()
-                .MaximumLength(2000);
-        }
+        //RuleFor(d => d.DefinitionModuleCode).NotEmpty();
+        //RuleFor(d => d.Title).NotEmpty();
+        Include(new BasicWorkflowDtoValidator());
+        RuleFor(d => d.NextStepCode).NotEmpty();
+        RuleFor(d => d.IsSms).NotNull();
+        RuleFor(d => d.IsStartCountersign).NotNull();
+        RuleFor(d => d.NextHandlers.Count).LessThanOrEqualTo(1)
+            .Unless(d => d.IsStartCountersign);
     }
-}
+}

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

@@ -109,11 +109,11 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
     public async Task<string> StartWorkflowAsync(StartWorkflowDto dto, string externalId,
         DateTime? expiredTime, CancellationToken cancellationToken = default)
     {
-        var validator = new StartWorkflowDtoValidator();
-        var validResult = await validator.ValidateAsync(dto, cancellationToken);
-        if (!validResult.IsValid)
-            throw new UserFriendlyException(
-                $"非法参数, {string.Join(',', validResult.Errors.Select(d => d.ErrorMessage))}");
+        //var validator = new StartWorkflowDtoValidator();
+        //var validResult = await validator.ValidateAsync(dto, cancellationToken);
+        //if (!validResult.IsValid)
+        //    throw new UserFriendlyException(
+        //        $"非法参数, {string.Join(',', validResult.Errors.Select(d => d.ErrorMessage))}");
 
         var wfModule = await GetWorkflowModuleAsync(dto.DefinitionModuleCode, cancellationToken);
         var definition = wfModule.Definition;

+ 109 - 109
src/Hotline/FlowEngine/Workflows/WorkflowDomainService.cs

@@ -253,58 +253,58 @@ namespace Hotline.FlowEngine.Workflows
             withTraces: true, withCountersigns: true, cancellationToken: cancellationToken);
             CheckWhetherRunnable(workflow.Status);
 
-        //var currentStep = _workflowDomainService.FindCurrentStepWaitForHandle(workflow,
-        //    current.RequiredUserId, current.RequiredOrgId, current.Roles);
-        var currentStep = workflow.Steps.FirstOrDefault(d => d.Id == dto.StepId);
-        if (currentStep == null)
-            throw new UserFriendlyException(
-                $"未找到对应节点, workflowId: {dto.WorkflowId}, stepId: {dto.StepId}", "未找到对应节点");
-        if (currentStep.Status is EWorkflowStepStatus.Handled)
-            throw new UserFriendlyException("该状态不支持继续办理");
+            //var currentStep = _workflowDomainService.FindCurrentStepWaitForHandle(workflow,
+            //    current.RequiredUserId, current.RequiredOrgId, current.Roles);
+            var currentStep = workflow.Steps.FirstOrDefault(d => d.Id == dto.StepId);
+            if (currentStep == null)
+                throw new UserFriendlyException(
+                    $"未找到对应节点, workflowId: {dto.WorkflowId}, stepId: {dto.StepId}", "未找到对应节点");
+            if (currentStep.Status is EWorkflowStepStatus.Handled)
+                throw new UserFriendlyException("该状态不支持继续办理");
 
-        var currentStepDefine = GetStepDefine(workflow.WorkflowDefinition, currentStep.Code);
+            var currentStepDefine = GetStepDefine(workflow.WorkflowDefinition, currentStep.Code);
 
-        //下一节点是否为动态节点
-        var isNextDynamic = currentStepDefine.InstanceMode is EInstanceMode.Dynamic &&
-                            !DynamicShouldTerminal(currentStepDefine, _sessionContext.OrgLevel);
+            //下一节点是否为动态节点
+            var isNextDynamic = currentStepDefine.InstanceMode is EInstanceMode.Dynamic &&
+                                !DynamicShouldTerminal(currentStepDefine, _sessionContext.OrgLevel);
 
-        StepDefine nextStepDefine;
-        if (isNextDynamic
-            || (currentStep.IsInCountersign() && !currentStep.IsTopCountersignEndStep(workflow.TopCountersignStepId))
-            || dto.IsStartCountersign)
-        {
-            //下一步配置为当前节点配置
-            nextStepDefine = currentStepDefine;
-        }
-        else
-        {
-            //下一步配置为下一步节点配置
-            nextStepDefine = GetStepDefine(workflow.WorkflowDefinition, dto.NextStepCode);
-        }
+            StepDefine nextStepDefine;
+            if (isNextDynamic
+                || (currentStep.IsInCountersign() && !currentStep.IsTopCountersignEndStep(workflow.TopCountersignStepId))
+                || dto.IsStartCountersign)
+            {
+                //下一步配置为当前节点配置
+                nextStepDefine = currentStepDefine;
+            }
+            else
+            {
+                //下一步配置为下一步节点配置
+                nextStepDefine = GetStepDefine(workflow.WorkflowDefinition, dto.NextStepCode);
+            }
 
-        //需求:按角色选择办理人可以不选,表示该角色下所有人都可以办理,同时依据配置:是否本部门人办理显示待选办理人。角色下只要一人办理即可(即:角色下不发起会签)
-        if (nextStepDefine.HandlerType != EHandlerType.Role && !dto.NextHandlers.Any())
-            throw new UserFriendlyException("未指定节点处理者");
+            //需求:按角色选择办理人可以不选,表示该角色下所有人都可以办理,同时依据配置:是否本部门人办理显示待选办理人。角色下只要一人办理即可(即:角色下不发起会签)
+            if (nextStepDefine.HandlerType != EHandlerType.Role && !dto.NextHandlers.Any())
+                throw new UserFriendlyException("未指定节点处理者");
 
-        if (dto.IsStartCountersign)
-        {
-            if (!currentStepDefine.CanStartCountersign)
-                throw new UserFriendlyException("当前节点不支持发起会签");
-            //if (currentStepDefine.HandlerType is EHandlerType.Role)
-            //    throw new UserFriendlyException("当前节点不支持发起会签");
-            //即使当前节点支持发起会签,但下一节点为信息汇总节点、结束节点时也不可发起会签
-            if (nextStepDefine.StepType is EStepType.Summary or EStepType.End)
-                throw new UserFriendlyException("下一节点不允许发起会签");
-            //下一节点是会签汇总节点也不允许发起会签
-            if (dto.BackToCountersignEnd)
-                throw new UserFriendlyException("下一节点不允许发起会签");
-        }
+            if (dto.IsStartCountersign)
+            {
+                if (!currentStepDefine.CanStartCountersign)
+                    throw new UserFriendlyException("当前节点不支持发起会签");
+                //if (currentStepDefine.HandlerType is EHandlerType.Role)
+                //    throw new UserFriendlyException("当前节点不支持发起会签");
+                //即使当前节点支持发起会签,但下一节点为信息汇总节点、结束节点时也不可发起会签
+                if (nextStepDefine.StepType is EStepType.Summary or EStepType.End)
+                    throw new UserFriendlyException("下一节点不允许发起会签");
+                //下一节点是会签汇总节点也不允许发起会签
+                if (dto.BackToCountersignEnd)
+                    throw new UserFriendlyException("下一节点不允许发起会签");
+            }
+
+            var flowAssignInfo =
+                await GetNextStepFlowAssignInfoAsync(workflow, currentStep, dto, nextStepDefine, isNextDynamic, cancellationToken);
 
-        var flowAssignInfo =
-            await GetNextStepFlowAssignInfoAsync(workflow, currentStep, dto, nextStepDefine, isNextDynamic, cancellationToken);
-        
 
-         #region 办理当前节点
+            #region 办理当前节点
 
             if (dto.Files != null && dto.Files.Any())
                 currentStep.FileJson = await _fileRepository.AddFileAsync(dto.Files, workflow.ExternalId,
@@ -372,7 +372,7 @@ namespace Hotline.FlowEngine.Workflows
             currentStep.IsActualHandled = CheckIsActualHandle(workflow, currentStep, nextStepDefine, dto);
 
             _mapper.Map(dto, workflow);
-            
+
             //会签办理节点办理时更新会签members字段
             if (currentStep.CountersignPosition is ECountersignPosition.Multi or ECountersignPosition.Single)
             {
@@ -495,7 +495,7 @@ namespace Hotline.FlowEngine.Workflows
                 new NextStepNotify(workflow, dto, flowAssignInfo, currentTrace, nextStepDefine,
                     _sessionContext.RequiredOrgId, expiredTime.HasValue), PublishStrategy.ParallelWhenAll,
                 cancellationToken);
-            
+
             return nextSteps;
         }
 
@@ -579,7 +579,7 @@ namespace Hotline.FlowEngine.Workflows
 
             var unhandlePreviousTrace = workflow.Traces.FirstOrDefault(d =>
                     d.Status is not EWorkflowStepStatus.Handled
-                //&& d.TraceType is EWorkflowTraceType.Previous
+            //&& d.TraceType is EWorkflowTraceType.Previous
             );
             //var previousOpinion = unhandlePreviousTrace?.Opinion ?? null;
 
@@ -3260,86 +3260,86 @@ namespace Hotline.FlowEngine.Workflows
         // }
 
         /// <summary>
-    /// 查询下一节点办理对象类型(user or org)及实际办理对象
-    /// </summary>
-    private async Task<FlowAssignInfo> GetNextStepFlowAssignInfoAsync(Workflow workflow, WorkflowStep currentStep,
-        BasicWorkflowDto dto, StepDefine nextStepDefine, bool isNextDynamic, CancellationToken cancellationToken)
-    {
-        if (nextStepDefine.StepType is EStepType.End) return new();
+        /// 查询下一节点办理对象类型(user or org)及实际办理对象
+        /// </summary>
+        private async Task<FlowAssignInfo> GetNextStepFlowAssignInfoAsync(Workflow workflow, WorkflowStep currentStep,
+            BasicWorkflowDto dto, StepDefine nextStepDefine, bool isNextDynamic, CancellationToken cancellationToken)
+        {
+            if (nextStepDefine.StepType is EStepType.End) return new();
 
-        var isStartCountersign = dto.IsStartCountersign;
-        var handlers = dto.NextHandlers.Select(d => new Kv(d.Key, d.Value)).ToList();
+            var isStartCountersign = dto.IsStartCountersign;
+            var handlers = dto.NextHandlers.Select(d => new Kv(d.Key, d.Value)).ToList();
 
-        if (isStartCountersign)
-        {
-            var assignType = FlowAssignInfo.GetAssignType(dto.HandlerType, dto.NextHandlers.Any());
-            //按会签策略判断,目前所有策略为org
-            return FlowAssignInfo.Create(assignType, handlers, isStartCountersign);
-        }
+            if (isStartCountersign)
+            {
+                var assignType = FlowAssignInfo.GetAssignType(dto.HandlerType, dto.NextHandlers.Any());
+                //按会签策略判断,目前所有策略为org
+                return FlowAssignInfo.Create(assignType, handlers, isStartCountersign);
+            }
 
-        //if (currentStep.IsInCountersign() && !currentStep.IsTopCountersignEndStep(workflow.TopCountersignStepId))
-        //    return FlowAssignInfo.Create(EFlowAssignType.Org, handlers, isStartCountersign);
+            //if (currentStep.IsInCountersign() && !currentStep.IsTopCountersignEndStep(workflow.TopCountersignStepId))
+            //    return FlowAssignInfo.Create(EFlowAssignType.Org, handlers, isStartCountersign);
 
-        if (currentStep.IsInCountersign())
-        {
-            if (currentStep.IsCountersignEndStep)
+            if (currentStep.IsInCountersign())
             {
-                //汇总节点(非顶级)
-                if (!currentStep.IsTopCountersignEndStep(workflow.TopCountersignStepId))
+                if (currentStep.IsCountersignEndStep)
+                {
+                    //汇总节点(非顶级)
+                    if (!currentStep.IsTopCountersignEndStep(workflow.TopCountersignStepId))
+                    {
+                        if (dto.BackToCountersignEnd)
+                        {
+                            var csStartStep = GetCsLoopStartStep(workflow, currentStep);
+                            var prevStep = workflow.Steps.FirstOrDefault(d => d.Id == csStartStep.PrevStepId);
+                            if (prevStep is null)
+                                throw new UserFriendlyException("未查询到目标节点的前一节点");
+                            return FlowAssignInfo.Create(prevStep.FlowAssignType.Value, prevStep.Handlers, isStartCountersign);
+                        }
+                    }
+                }
+                else
                 {
                     if (dto.BackToCountersignEnd)
                     {
-                        var csStartStep = GetCsLoopStartStep(workflow, currentStep);
-                        var prevStep = workflow.Steps.FirstOrDefault(d => d.Id == csStartStep.PrevStepId);
+                        var prevStep = workflow.Steps.FirstOrDefault(d => d.Id == currentStep.PrevStepId);
                         if (prevStep is null)
-                            throw new UserFriendlyException("未查询到目标节点的前一节点");
+                            throw new UserFriendlyException($"未查询到当前节点的上级节点");
                         return FlowAssignInfo.Create(prevStep.FlowAssignType.Value, prevStep.Handlers, isStartCountersign);
                     }
+                    else
+                    {
+                        var assignType = FlowAssignInfo.GetAssignType(dto.HandlerType, dto.NextHandlers.Any());
+                        //按会签策略判断,目前所有策略为org
+                        return FlowAssignInfo.Create(assignType, handlers, isStartCountersign);
+                    }
                 }
             }
-            else
+
+            if (isNextDynamic)
             {
-                if (dto.BackToCountersignEnd)
+                switch (currentStep.InstancePolicy)
                 {
-                    var prevStep = workflow.Steps.FirstOrDefault(d => d.Id == currentStep.PrevStepId);
-                    if (prevStep is null)
-                        throw new UserFriendlyException($"未查询到当前节点的上级节点");
-                    return FlowAssignInfo.Create(prevStep.FlowAssignType.Value, prevStep.Handlers, isStartCountersign);
-                }
-                else
-                {
-                    var assignType = FlowAssignInfo.GetAssignType(dto.HandlerType, dto.NextHandlers.Any());
-                    //按会签策略判断,目前所有策略为org
-                    return FlowAssignInfo.Create(assignType, handlers, isStartCountersign);
+                    case EDynamicPolicy.OrgUpCenterTop:
+                    case EDynamicPolicy.OrgUp:
+                    case EDynamicPolicy.OrgDownCenterTop:
+                    case EDynamicPolicy.OrgDown:
+                    case EDynamicPolicy.ArriveCenter:
+                    case EDynamicPolicy.ArriveOneOrg:
+                        return FlowAssignInfo.Create(EFlowAssignType.Org, handlers, isStartCountersign);
+                    case EDynamicPolicy.OrgUpHandleCenterTop:
+                    case EDynamicPolicy.OrgUpHandle:
+                    case EDynamicPolicy.OrgUpLeadCenterTop:
+                    case EDynamicPolicy.OrgUpLead:
+                        return FlowAssignInfo.Create(EFlowAssignType.OrgAndRole, handlers, isStartCountersign);
+                    default:
+                        throw new ArgumentOutOfRangeException();
                 }
             }
-        }
 
-        if (isNextDynamic)
-        {
-            switch (currentStep.InstancePolicy)
-            {
-                case EDynamicPolicy.OrgUpCenterTop:
-                case EDynamicPolicy.OrgUp:
-                case EDynamicPolicy.OrgDownCenterTop:
-                case EDynamicPolicy.OrgDown:
-                case EDynamicPolicy.ArriveCenter:
-                case EDynamicPolicy.ArriveOneOrg:
-                    return FlowAssignInfo.Create(EFlowAssignType.Org, handlers, isStartCountersign);
-                case EDynamicPolicy.OrgUpHandleCenterTop:
-                case EDynamicPolicy.OrgUpHandle:
-                case EDynamicPolicy.OrgUpLeadCenterTop:
-                case EDynamicPolicy.OrgUpLead:
-                    return FlowAssignInfo.Create(EFlowAssignType.OrgAndRole, handlers, isStartCountersign);
-                default:
-                    throw new ArgumentOutOfRangeException();
-            }
+            return await GetNextStepFlowAssignInfoByDefineAsync(nextStepDefine, dto.HandlerType, isStartCountersign, handlers,
+                cancellationToken);
         }
 
-        return await GetNextStepFlowAssignInfoByDefineAsync(nextStepDefine, dto.HandlerType, isStartCountersign, handlers,
-            cancellationToken);
-    }
-        
         private WorkflowStep GetCsLoopStartStep(Workflow workflow, WorkflowStep currentStep)
         {
             var startCountersignStep =
@@ -3352,7 +3352,7 @@ namespace Hotline.FlowEngine.Workflows
                 return startCountersignStep;
             return GetCsLoopStartStep(workflow, startCountersignStep);
         }
-        
+
         #endregion
     }
 }