Преглед на файлове

Merge branch 'master' of http://110.188.24.182:10023/Fengwo/hotline

田爽 преди 1 година
родител
ревизия
6b6ea3caf0

+ 5 - 5
src/Hotline.Api/Controllers/EnforcementOrderController.cs

@@ -256,7 +256,7 @@ namespace Hotline.Api.Controllers
                 .Where(d => d.Order.Id != null)
                 .Where(d => d.Order.CreationTime >= StartDate && d.Order.CreationTime <= EndDate)
                 .GroupBy(d => d.Id)
-                 .Select(d => new
+                 .Select(d => new EventClassificationOrderCountDto
                  {
                      TheClueIsTrue = SqlFunc.AggregateSum(SqlFunc.IIF(d.IsTheClueTrue.HasValue && d.IsTheClueTrue.Value == true, 1, 0)),
                      TheClueIsNotTrue = SqlFunc.AggregateSum(SqlFunc.IIF(d.IsTheClueTrue.HasValue && d.IsTheClueTrue.Value == false, 1, 0)),
@@ -267,10 +267,10 @@ namespace Hotline.Api.Controllers
 
             var orderCount = new
             {
-                TheClueIsTrue = data?[0].TheClueIsTrue,
-                TheClueIsNotTrue = data?[0].TheClueIsNotTrue,
-                EnforcementOrder = data?[0].EnforcementOrder,
-                PassTheBuckOrder = data?[0].PassTheBuckOrder
+                TheClueIsTrue = data.Sum(x => x.TheClueIsTrue),
+                TheClueIsNotTrue = data.Sum(x => x.TheClueIsNotTrue),
+                EnforcementOrder = data.Sum(x => x.EnforcementOrder),
+                PassTheBuckOrder = data.Sum(x => x.PassTheBuckOrder),
             };
 
             return new { List = items, Total = total, OrderCount = orderCount };

+ 50 - 85
src/Hotline.Application/FlowEngine/WorkflowApplication.cs

@@ -98,7 +98,7 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
         DateTime? expiredTime, CancellationToken cancellationToken = default)
     {
         var validator = new StartWorkflowDtoValidator();
-        var validResult = validator.Validate(dto);
+        var validResult = await validator.ValidateAsync(dto, cancellationToken);
         if (!validResult.IsValid)
             throw new UserFriendlyException(
                 $"非法参数, {string.Join(',', validResult.Errors.Select(d => d.ErrorMessage))}");
@@ -144,12 +144,19 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
             _sessionContext.RequiredUserId, _sessionContext.RequiredOrgId,
             externalId, cancellationToken);
 
+        var startStepHandles = new List<WorkflowStepHandler>{WorkflowStepHandler.Create(workflow.Id, workflow.ExternalId,
+            EFlowAssignType.User, _sessionContext.RequiredUserId, _sessionContext.UserName,
+            _sessionContext.RequiredOrgId, _sessionContext.OrgName)};
+
         var startStep = _workflowDomainService.CreateStartStep(workflow, startStepDefine, dto,
-            new List<Kv> { new(_sessionContext.RequiredUserId, _sessionContext.UserName) }, expiredTime);
+            new List<Kv> { new(_sessionContext.RequiredUserId, _sessionContext.UserName) },
+            startStepHandles, expiredTime);
 
         var flowAssignInfo =
             await GetNextStepFlowAssignInfoAsync(workflow, startStep, dto, firstStepDefine, isNextDynamic, cancellationToken);
 
+        var firstStepHandlers = await GetNextStepHandlersAsync(workflow, firstStepDefine, dto, cancellationToken);
+
         var counterSignType = _workflowDomainService.GetCounterSignType(startStep.BusinessType);
 
         //办理开始节点
@@ -184,7 +191,7 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
             _sessionContext.OrgName);
 
         await _workflowDomainService.StartAsync(workflow, startStep, dto, firstStepDefine, isNextDynamic,
-            flowAssignInfo, counterSignType, expiredTime, cancellationToken);
+            flowAssignInfo, counterSignType, expiredTime, firstStepHandlers, cancellationToken);
 
         return workflow.Id;
     }
@@ -252,8 +259,10 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
         var flowAssignInfo =
             await GetNextStepFlowAssignInfoAsync(workflow, currentStep, dto, nextStepDefine, isNextDynamic, cancellationToken);
 
+        var nextStepHandlers = await GetNextStepHandlersAsync(workflow, nextStepDefine, dto, cancellationToken);
+
         await _workflowDomainService.NextAsync(workflow, currentStep, dto, nextStepDefine, isNextDynamic,
-            flowAssignInfo, expiredTime, cancellationToken);
+            flowAssignInfo, expiredTime, nextStepHandlers, cancellationToken);
 
         return workflow;
     }
@@ -302,7 +311,9 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
             dto.NextHandlers,
             cancellationToken);
 
-        await _workflowDomainService.RecallAsync(workflow, dto, targetStepDefine, flowAssignInfo, expiredTime, cancellationToken);
+        var stepHandlers = await GetNextStepHandlersAsync(workflow, targetStepDefine, dto, cancellationToken);
+
+        await _workflowDomainService.RecallAsync(workflow, dto, targetStepDefine, flowAssignInfo, stepHandlers, expiredTime, cancellationToken);
     }
 
     ///// <summary>
@@ -339,10 +350,10 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
             throw new UserFriendlyException("未找到实际办理节点");
 
         await _workflowDomainService.EndAsync(workflow, new BasicWorkflowDto
-            {
-                Opinion = opinion,
-                Files = files
-            }, endStepDefine, currentStep,
+        {
+            Opinion = opinion,
+            Files = files
+        }, endStepDefine, currentStep,
             reviewResult, cancellationToken);
     }
 
@@ -623,13 +634,13 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
             throw new UserFriendlyException("无效当前节点编号");
         var quer = workflow.Steps.Where(d =>
             d.StepType != EStepType.End &&
-            d.IsOrigin );
+            d.IsOrigin);
         if (!isEnd)
         {
             quer = quer.Where(d => d.Id != currentStep.Id);
         }
         var originSteps = quer.ToList();
-		var stepCodes = originSteps.Select(d => d.Code).ToList();
+        var stepCodes = originSteps.Select(d => d.Code).ToList();
         var stepDefines = workflow.WorkflowDefinition.FindStepDefines(stepCodes);
 
         var dto = new NextStepsDto<RecallStepOption>
@@ -1065,80 +1076,6 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
         };
     }
 
-    //private async Task<IReadOnlyList<NextStepOption>> GetConfigStepsAsync(List<StepDefine> stepDefines,
-    //    CancellationToken cancellationToken)
-    //{
-    //    foreach (var stepDefine in stepDefines)
-    //    {
-    //        if (stepDefine.StepType == EStepType.End)
-    //            throw new UserFriendlyException("结束节点无待选项");
-
-    //        var handlers = new List<Kv>();
-    //        switch (stepDefine.HandlerType)
-    //        {
-    //            case EHandlerType.AssignedUser:
-    //            case EHandlerType.AssignedOrg:
-    //                handlers = stepDefine.HandlerTypeItems;
-    //                break;
-    //            case EHandlerType.Role:
-    //                //当前操作人所属部门的下级部门并且属于配置包含角色
-    //                var roles = await _roleRepository.Queryable()
-    //                    .Includes(d => d.Accounts.Where(x => !x.IsDeleted && x.Status == EAccountStatus.Normal).ToList(),
-    //                        x => x.User)
-    //                    .Where(d => stepDefine.HandlerTypeItems.Select(x => x.Key).Contains(d.Name))
-    //                    .ToListAsync(cancellationToken);
-    //                var users1 = roles.SelectMany(d => d.Accounts).Select(d => d.User);
-    //                handlers = users1.Select(d => new Kv(d.Id, d.Name)).ToList();
-    //                break;
-    //            case EHandlerType.OrgLevel:
-    //                //当前操作人所属部门的下级部门并且属于配置orgLevel的部门
-    //                var levels = stepDefine.HandlerTypeItems.Select(d => d.Key).Select(d => int.Parse(d));
-    //                var levelOneOrg = _sessionContext.RequiredOrgId.GetHigherOrgCode();
-    //                //var orgs1 = await _organizeRepository.QueryAsync(d =>
-    //                //    d.IsEnable && d.OrgCode.StartsWith(levelOneOrg) &&
-    //                //    levels.Contains(d.OrgLevel));
-
-    //                var orgs1 = await _organizeRepository.Queryable()
-    //                    .Where(d => d.IsEnable && levels.Contains(d.Level))
-    //                    .WhereIF(!levelOneOrg.IsCenter(), d => d.Id.StartsWith(levelOneOrg))
-    //                    .ToListAsync(cancellationToken);
-
-    //                handlers = orgs1.Select(d => new Kv(d.Id, d.Name)).ToList();
-    //                break;
-    //            case EHandlerType.OrgType:
-    //                var types = stepDefine.HandlerTypeItems.Select(d => d.Key)
-    //                    .Select(d => Enum.Parse<EOrgType>(d));
-    //                var levelOneOrg1 = _sessionContext.RequiredOrgId.GetHigherOrgCode();
-    //                //var org2 = await _organizeRepository.QueryAsync(d =>
-    //                //    d.IsEnable && d.OrgCode.StartsWith(levelOneOrg1) &&
-    //                //    types.Contains(d.OrgType));
-
-    //                var orgs2 = await _organizeRepository.Queryable()
-    //                    .Where(d => d.IsEnable && types.Contains(d.OrgType))
-    //                    .WhereIF(!levelOneOrg1.IsCenter(), d => d.Id.StartsWith(levelOneOrg1))
-    //                    .ToListAsync(cancellationToken);
-
-    //                handlers = orgs2.Select(d => new Kv(d.Id, d.Name)).ToList();
-    //                break;
-    //            default:
-    //                throw new ArgumentOutOfRangeException();
-    //        }
-
-    //        var dto = new NextStepOptionDto { Handlers = handlers };
-
-    //        if (stepDefine.Components.Contains(SysDicTypeConsts.OrderRedoReason))
-    //        {
-    //            var orderRedoReasons =
-    //                await _systemDomainService.GetSysDicDataByCodeAsync(SysDicTypeConsts.OrderRedoReason,
-    //                    cancellationToken);
-    //            dto.OrderRedoReasonOptions = orderRedoReasons.Select(d => new Kv(d.DicDataValue, d.DicDataName)).ToList();
-    //        }
-
-    //        return dto;
-    //    }
-    //    throw new NotImplementedException();
-    //}
-
     /// <summary>
     /// 查询下一节点办理对象类型(user or org)及实际办理对象
     /// </summary>
@@ -1203,6 +1140,34 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
             cancellationToken);
     }
 
+    private async ValueTask<List<WorkflowStepHandler>> GetNextStepHandlersAsync(Workflow workflow,
+        StepDefine nextStepDefine, BasicWorkflowDto dto, CancellationToken cancellationToken)
+    {
+        var assignType = FlowAssignInfo.GetAssignType(nextStepDefine.HandlerType);
+        //var assignType = AssignInfo.GetAssignType(nextStepDefine.HandlerType, dto.NextHandlers.Any());
+        switch (assignType)
+        {
+            case EFlowAssignType.Org:
+                return dto.NextHandlers.Select(d => WorkflowStepHandler.Create(workflow.Id, workflow.ExternalId,
+                    assignType, orgId: d.Key, orgName: d.Value)).ToList();
+            case EFlowAssignType.User:
+                var userIds = dto.NextHandlers.Select(d => d.Key).ToList();
+                var users = await _userRepository.Queryable()
+                    .Includes(d => d.Organization)
+                    .Where(d => userIds.Contains(d.Id))
+                    .ToListAsync(cancellationToken);
+                return users.Select(d => WorkflowStepHandler.Create(workflow.Id, workflow.ExternalId,
+                        assignType, d.Id, d.Name, d.OrgId, d.Organization.Name))
+                    .ToList();
+            //case EFlowAssignType.Role:
+            //    handlers = dto.NextHandlers.Select(d => WorkflowStepHandler.Create(workflow.Id, workflow.ExternalId,
+            //        assignType, roleId: d.Key, roleName: d.Value)).ToList();
+            //    break;
+            default:
+                throw new ArgumentOutOfRangeException();
+        }
+    }
+
     /// <summary>
     /// 按流程模板配置创建下一步办理对象
     /// </summary>

+ 2 - 0
src/Hotline.Application/Mappers/WorkflowMapperConfigs.cs

@@ -84,5 +84,7 @@ public class WorkflowMapperConfigs : IRegister
             .IgnoreNonMapped(true)
             ;
 
+        config.ForType<BasicWorkflowDto, WorkflowStep>()
+            .Ignore(d => d.BusinessType);
     }
 }

+ 27 - 0
src/Hotline.Share/Dtos/JudicialManagement/EventClassificationOrderCountDto.cs

@@ -0,0 +1,27 @@
+namespace Hotline.Share.Dtos.JudicialManagement
+{
+    public class EventClassificationOrderCountDto
+    {
+        /// <summary>
+        /// 线索属实
+        /// </summary>
+        public int TheClueIsTrue { get; set; }
+        /// <summary>
+        /// 线索不属实
+        /// </summary>
+        public int TheClueIsNotTrue { get; set; }
+
+        /// <summary>
+        /// 行政执法类工单
+        /// </summary>
+        public int EnforcementOrder { get; set; }
+
+        /// <summary>
+        /// 推诿工单
+        /// </summary>
+        public int PassTheBuckOrder { get; set; }
+
+
+
+    }
+}

+ 8 - 4
src/Hotline/FlowEngine/Workflows/IWorkflowDomainService.cs

@@ -23,7 +23,7 @@ namespace Hotline.FlowEngine.Workflows
         /// </summary>
         Task StartAsync(Workflow workflow, WorkflowStep startStep, BasicWorkflowDto dto, StepDefine firstStepDefine,
             bool isNextDynamic, FlowAssignInfo flowAssignInfo, ECounterSignType? counterSignType, DateTime? expiredTime,
-            CancellationToken cancellationToken);
+            List<WorkflowStepHandler> stepHandlers, CancellationToken cancellationToken);
 
         /// <summary>
         /// 查询工作流
@@ -48,7 +48,7 @@ namespace Hotline.FlowEngine.Workflows
         /// 办理(流转至下一节点)
         /// </summary>
         Task NextAsync(Workflow workflow, WorkflowStep currentStep, NextWorkflowDto dto, StepDefine nextStepDefine,
-            bool isNextDynamic, FlowAssignInfo flowAssignInfo, DateTime? expiredTime,
+            bool isNextDynamic, FlowAssignInfo flowAssignInfo, DateTime? expiredTime, List<WorkflowStepHandler> stepHandlers,
             CancellationToken cancellationToken);
 
         /// <summary>
@@ -62,7 +62,7 @@ namespace Hotline.FlowEngine.Workflows
         /// 撤回(返回到之前任意节点)
         /// </summary>
         Task RecallAsync(Workflow workflow, RecallDto dto, StepDefine targetStepDefine, FlowAssignInfo flowAssignInfo,
-            DateTime? expiredTime, CancellationToken cancellationToken);
+            List<WorkflowStepHandler> stepHandlers, DateTime? expiredTime, CancellationToken cancellationToken);
 
         /// <summary>
         /// 撤回至开始节点
@@ -147,7 +147,7 @@ namespace Hotline.FlowEngine.Workflows
         /// 创建开始节点
         /// </summary>
         WorkflowStep CreateStartStep(Workflow workflow, StepDefine startStepDefine, BasicWorkflowDto dto,
-            List<Kv> handlers, DateTime? expiredTime);
+            List<Kv> handlers, List<WorkflowStepHandler> stepHandlers, DateTime? expiredTime);
 
         /// <summary>
         /// 查询未完成节点
@@ -220,5 +220,9 @@ namespace Hotline.FlowEngine.Workflows
             IReadOnlyList<(string userId, string username, IReadOnlyList<string> stepIds)> handlers,
             CancellationToken cancellationToken);
 
+        /// <summary>
+        /// 查询工单办理中的一级部门
+        /// </summary>
+        Task<ICollection<Kv>> GetLevelOneOrgsAsync(string workflowId, CancellationToken cancellation);
     }
 }

+ 80 - 35
src/Hotline/FlowEngine/Workflows/WorkflowDomainService.cs

@@ -98,7 +98,8 @@ namespace Hotline.FlowEngine.Workflows
         /// </summary>
         public async Task StartAsync(Workflow workflow, WorkflowStep startStep, BasicWorkflowDto dto,
             StepDefine firstStepDefine, bool isNextDynamic, FlowAssignInfo flowAssignInfo,
-            ECounterSignType? counterSignType, DateTime? expiredTime, CancellationToken cancellationToken)
+            ECounterSignType? counterSignType, DateTime? expiredTime, List<WorkflowStepHandler> stepHandlers,
+            CancellationToken cancellationToken)
         {
             //1. 创建first节点 (和trace)2.办理开始节点 
 
@@ -121,7 +122,7 @@ namespace Hotline.FlowEngine.Workflows
 
             //firststeps
             var firstSteps = await CreateNextStepsAsync(workflow, startStep, dto, firstStepDefine,
-                isNextDynamic, flowAssignInfo, expiredTime, cancellationToken);
+                isNextDynamic, flowAssignInfo, expiredTime, stepHandlers, cancellationToken);
             if (firstSteps.Any())
                 workflow.Steps.AddRange(firstSteps);
 
@@ -193,7 +194,7 @@ namespace Hotline.FlowEngine.Workflows
             if (withCountersigns)
                 query = query.Includes(d => d.Countersigns, x => x.Members);
             if (withSteps)
-                query = query.Includes(d => d.Steps);
+                query = query.Includes(d => d.Steps, x => x.StepHandlers);
             //if (withTraces)
             //    query = query.Includes(d => d.Traces);
 
@@ -313,7 +314,7 @@ namespace Hotline.FlowEngine.Workflows
         /// </summary>
         public async Task NextAsync(Workflow workflow, WorkflowStep currentStep, NextWorkflowDto dto,
             StepDefine nextStepDefine, bool isNextDynamic, FlowAssignInfo flowAssignInfo,
-            DateTime? expiredTime, CancellationToken cancellationToken)
+            DateTime? expiredTime, List<WorkflowStepHandler> stepHandlers, CancellationToken cancellationToken)
         {
             ValidatePermission(workflow, _sessionContext.RequiredOrgId, _sessionContext.RequiredUserId);
             //CheckWhetherRunnable(workflow.Status);
@@ -405,7 +406,10 @@ namespace Hotline.FlowEngine.Workflows
                     .ExecuteCommandAsync();
             }
 
-            await _workflowStepRepository.UpdateRangeAsync(updateSteps, cancellationToken);
+            //await _workflowStepRepository.UpdateRangeAsync(updateSteps, cancellationToken);
+            await _workflowStepRepository.UpdateNav(updateSteps)
+                .Include(d => d.StepHandlers)
+                .ExecuteCommandAsync();
 
             await NextTraceAsync(workflow, dto, currentStep, cancellationToken);
 
@@ -454,7 +458,7 @@ namespace Hotline.FlowEngine.Workflows
 
             //创建下一/N个节点(会签汇总节点:会签未全部办理时不创建,最后一个会签办理节点创建会签汇总节点)
             var nextSteps = await CreateNextStepsAsync(workflow, currentStep, dto, nextStepDefine, isNextDynamic,
-                flowAssignInfo, expiredTime, cancellationToken);
+                flowAssignInfo, expiredTime, stepHandlers, cancellationToken);
 
             //赋值当前节点的下级办理节点
             if (dto.IsStartCountersign
@@ -693,6 +697,20 @@ namespace Hotline.FlowEngine.Workflows
             return steps.Select(d => d.WorkflowId).ToList();
         }
 
+        /// <summary>
+        /// 查询工单办理中的一级部门
+        /// </summary>
+        public async Task<ICollection<Kv>> GetLevelOneOrgsAsync(string workflowId, CancellationToken cancellation)
+        {
+            var workflow = await GetWorkflowAsync(workflowId, withSteps: true, cancellationToken: cancellation);
+            return workflow.Steps.Where(d => d.BusinessType == EBusinessType.Department &&
+                                      d.HandlerType == EHandlerType.OrgLevel &&
+                                      d.StepHandlers.Any(d =>
+                                          !string.IsNullOrEmpty(d.OrgId) && d.OrgId.CheckIfOrgLevelIs(1)))
+                .Select(d => new Kv(d.StepHandlers.First().OrgId, d.StepHandlers.First().OrgName))
+                .ToList();
+        }
+
         /// <summary>
         /// 查找当前会签内所有节点(含start,end)
         /// </summary>
@@ -717,7 +735,8 @@ namespace Hotline.FlowEngine.Workflows
         /// 撤回(返回到之前任意节点)
         /// </summary>
         public async Task RecallAsync(Workflow workflow, RecallDto dto, StepDefine targetStepDefine,
-            FlowAssignInfo flowAssignInfo, DateTime? expiredTime, CancellationToken cancellationToken)
+            FlowAssignInfo flowAssignInfo, List<WorkflowStepHandler> stepHandlers, DateTime? expiredTime,
+            CancellationToken cancellationToken)
         {
             var targetStep = workflow.Steps.FirstOrDefault(d => d.Code == dto.NextStepCode && d.IsOrigin);
             if (targetStep is null)
@@ -727,7 +746,7 @@ namespace Hotline.FlowEngine.Workflows
             await RecallTraceAsync(workflow.Id, dto.Opinion, cancellationToken);
 
             var isOrgToCenter = await RecallAsync(workflow, dto, flowAssignInfo, targetStepDefine, targetStep,
-                EWorkflowTraceStatus.Recall, expiredTime, cancellationToken);
+                EWorkflowTraceStatus.Recall, stepHandlers, expiredTime, cancellationToken);
 
             workflow.ResetHandlers(flowAssignInfo.FlowAssignType, flowAssignInfo.HandlerObjects);
 
@@ -1075,7 +1094,7 @@ namespace Hotline.FlowEngine.Workflows
         /// 创建开始节点
         /// </summary>
         public WorkflowStep CreateStartStep(Workflow workflow, StepDefine startStepDefine,
-            BasicWorkflowDto dto, List<Kv> handles, DateTime? expiredTime)
+            BasicWorkflowDto dto, List<Kv> handles, List<WorkflowStepHandler> stepHandlers, DateTime? expiredTime)
         {
             //startstep
             var nextSteps = _mapper.Map<List<StepSimple>>(startStepDefine.NextSteps);
@@ -1090,6 +1109,7 @@ namespace Hotline.FlowEngine.Workflows
             _mapper.Map(dto, startStep);
             _mapper.Map(workflow, startStep);
             startStep.Handlers = handles;
+            startStep.StepHandlers = stepHandlers;
             startStep.NextSteps = nextSteps;
             startStep.IsMain = true;
             startStep.IsOrigin = true;
@@ -1115,8 +1135,12 @@ namespace Hotline.FlowEngine.Workflows
             WorkflowStep currentStep, EReviewResult? reviewResult = EReviewResult.Unknown,
             CancellationToken cancellationToken = default)
         {
+            var endStepHandles = new List<WorkflowStepHandler>{WorkflowStepHandler.Create(workflow.Id, workflow.ExternalId,
+                EFlowAssignType.User, _sessionContext.RequiredUserId, _sessionContext.UserName,
+                _sessionContext.RequiredOrgId, _sessionContext.OrgName)};
+
             //create endStep
-            var endStep = await CreateEndStepAsync(workflow, endStepDefine, currentStep, cancellationToken);
+            var endStep = await CreateEndStepAsync(workflow, endStepDefine, currentStep, endStepHandles, cancellationToken);
             workflow.Steps.Add(endStep);
 
             //update endTrace
@@ -1222,11 +1246,14 @@ namespace Hotline.FlowEngine.Workflows
         }
 
         private async Task<WorkflowStep> CreateStartStepAsync(Workflow workflow, StepDefine startStepDefine,
-            BasicWorkflowDto dto, List<Kv> handles, EWorkflowTraceStatus traceStatus, DateTime? expiredTime,
-            CancellationToken cancellationToken)
+            BasicWorkflowDto dto, List<Kv> handles, List<WorkflowStepHandler> stepHandlers, EWorkflowTraceStatus traceStatus,
+            DateTime? expiredTime, CancellationToken cancellationToken)
         {
-            var startStep = CreateStartStep(workflow, startStepDefine, dto, handles, expiredTime);
-            await _workflowStepRepository.AddAsync(startStep, cancellationToken);
+            var startStep = CreateStartStep(workflow, startStepDefine, dto, handles, stepHandlers, expiredTime);
+            //await _workflowStepRepository.AddAsync(startStep, cancellationToken);
+            await _workflowStepRepository.AddNav(startStep)
+                .Include(d => d.StepHandlers)
+                .ExecuteCommandAsync();
             await CreateTraceAsync(workflow, startStep, traceStatus, cancellationToken);
             return startStep;
         }
@@ -1250,7 +1277,7 @@ namespace Hotline.FlowEngine.Workflows
         /// </summary>
         private async Task<List<WorkflowStep>> CreateNextStepsAsync(Workflow workflow, WorkflowStep currentStep,
             BasicWorkflowDto dto, StepDefine nextStepDefine, bool isNextDynamic, FlowAssignInfo flowAssignInfo,
-            DateTime? expiredTime, CancellationToken cancellationToken)
+            DateTime? expiredTime, List<WorkflowStepHandler> stepHandlers, CancellationToken cancellationToken)
         {
             List<WorkflowStep> nextSteps = new();
             if (currentStep.IsInCountersign())
@@ -1265,13 +1292,13 @@ namespace Hotline.FlowEngine.Workflows
                         {
                             //依据会签策略创建会签下一级节点
                             nextSteps = await CreateCountersignStepsAsync(workflow, nextStepDefine, currentStep, dto,
-                                flowAssignInfo.FlowAssignType, expiredTime, cancellationToken);
+                                flowAssignInfo.FlowAssignType, expiredTime, stepHandlers, cancellationToken);
                         }
                         else
                         {
                             //创建普通节点(根据配置)
                             nextSteps = await CreateConfigStepsAsync(workflow, nextStepDefine, currentStep, dto,
-                                flowAssignInfo, EWorkflowTraceStatus.Normal, expiredTime, cancellationToken);
+                                flowAssignInfo, EWorkflowTraceStatus.Normal, expiredTime, stepHandlers, cancellationToken);
                         }
                     }
                     else
@@ -1298,7 +1325,7 @@ namespace Hotline.FlowEngine.Workflows
                     {
                         //依据会签策略创建会签下一级节点
                         nextSteps = await CreateCountersignStepsAsync(workflow, nextStepDefine, currentStep, dto,
-                            flowAssignInfo.FlowAssignType, expiredTime, cancellationToken);
+                            flowAssignInfo.FlowAssignType, expiredTime, stepHandlers, cancellationToken);
                     }
                 }
             }
@@ -1306,19 +1333,19 @@ namespace Hotline.FlowEngine.Workflows
             {
                 //依据会签策略创建会签下一级节点
                 nextSteps = await CreateCountersignStepsAsync(workflow, nextStepDefine, currentStep, dto,
-                    flowAssignInfo.FlowAssignType, expiredTime, cancellationToken);
+                    flowAssignInfo.FlowAssignType, expiredTime, stepHandlers, cancellationToken);
             }
             else if (isNextDynamic)
             {
                 //创建动态下一级节点
                 nextSteps = await CreateDynamicStepsAsync(workflow, nextStepDefine, currentStep, dto, flowAssignInfo,
-                    expiredTime, cancellationToken);
+                    expiredTime, stepHandlers, cancellationToken);
             }
             else
             {
                 //创建普通节点(根据配置)
                 nextSteps = await CreateConfigStepsAsync(workflow, nextStepDefine, currentStep, dto, flowAssignInfo,
-                    EWorkflowTraceStatus.Normal, expiredTime, cancellationToken);
+                    EWorkflowTraceStatus.Normal, expiredTime, stepHandlers, cancellationToken);
             }
 
             return nextSteps;
@@ -1331,6 +1358,7 @@ namespace Hotline.FlowEngine.Workflows
             BasicWorkflowDto dto,
             FlowAssignInfo flowAssignInfo,
             DateTime? expiredTime,
+            List<WorkflowStepHandler> stepHandlers,
             CancellationToken cancellationToken)
         {
             var handlerType = nextStepDefine.InstancePolicy switch
@@ -1344,7 +1372,7 @@ namespace Hotline.FlowEngine.Workflows
             };
 
             return await CreateStepsAsync(workflow, nextStepDefine, prevStep, dto,
-                flowAssignInfo.FlowAssignType, dto.NextHandlers, null, EWorkflowStepStatus.WaitForAccept,
+                flowAssignInfo.FlowAssignType, dto.NextHandlers, stepHandlers, null, EWorkflowStepStatus.WaitForAccept,
                 ECountersignPosition.None, false, EWorkflowTraceStatus.Normal, handlerType, expiredTime, cancellationToken);
         }
 
@@ -1355,6 +1383,7 @@ namespace Hotline.FlowEngine.Workflows
             BasicWorkflowDto dto,
             EFlowAssignType flowAssignType,
             DateTime? expiredTime,
+            List<WorkflowStepHandler> stepHandlers,
             CancellationToken cancellationToken
         )
         {
@@ -1370,7 +1399,7 @@ namespace Hotline.FlowEngine.Workflows
                 _ => throw new ArgumentOutOfRangeException()
             };
 
-            return CreateStepsAsync(workflow, stepDefine, prevStep, dto, flowAssignType, dto.NextHandlers,
+            return CreateStepsAsync(workflow, stepDefine, prevStep, dto, flowAssignType, dto.NextHandlers, stepHandlers,
                 countersignId, EWorkflowStepStatus.WaitForAccept, prevStep.GetNextStepCountersignPosition(),
                 false, EWorkflowTraceStatus.Normal, handlerType, expiredTime, cancellationToken);
         }
@@ -1388,7 +1417,7 @@ namespace Hotline.FlowEngine.Workflows
             //会签未全部办理则不创建汇总节点
             if (prevStep.StartedCountersignHasAllHandled())
             {
-                //todo 创建会签汇总节点
+                // 创建会签汇总节点
                 var countersignEndStep =
                     await CreateCountersignEndStepAsync(prevStep, dto, expiredTime, cancellationToken);
                 nextSteps = new List<WorkflowStep> { countersignEndStep };
@@ -1456,6 +1485,10 @@ namespace Hotline.FlowEngine.Workflows
                 _sessionContext.RequiredOrgId, _sessionContext.OrgName,
                 _sessionContext.OrgAreaCode, _sessionContext.OrgAreaName,
                 _sessionContext.OrgIsCenter, opinion, nextStepCode);
+
+            var handler = step.FindActualHandler(_sessionContext.Roles, _sessionContext.RequiredUserId, _sessionContext.RequiredOrgId);
+            if (handler is not null)
+                handler.IsActualHandler = true;
         }
 
 
@@ -1723,7 +1756,7 @@ namespace Hotline.FlowEngine.Workflows
 
         private async Task<bool> RecallAsync(Workflow workflow, BasicWorkflowDto dto, FlowAssignInfo flowAssignInfo,
             StepDefine targetStepDefine, WorkflowStep targetStep, EWorkflowTraceStatus traceStatus,
-            DateTime? expiredTime, CancellationToken cancellationToken)
+            List<WorkflowStepHandler> stepHandlers, DateTime? expiredTime, CancellationToken cancellationToken)
         {
             var targetIsStartStep = targetStepDefine.StepType is EStepType.Start;
 
@@ -1750,10 +1783,10 @@ namespace Hotline.FlowEngine.Workflows
                 workflow.SetStatusRunnable();
 
             var targetStepNew = targetIsStartStep
-                ? await CreateStartStepAsync(workflow, targetStepDefine, dto, dto.NextHandlers, traceStatus, expiredTime,
+                ? await CreateStartStepAsync(workflow, targetStepDefine, dto, dto.NextHandlers, stepHandlers, traceStatus, expiredTime,
                     cancellationToken)
                 : (await CreateStepsAsync(workflow, targetStepDefine, targetPrevStep, dto,
-                    flowAssignInfo.FlowAssignType, dto.NextHandlers,
+                    flowAssignInfo.FlowAssignType, dto.NextHandlers, stepHandlers,
                     null, EWorkflowStepStatus.WaitForAccept, ECountersignPosition.None, true, traceStatus,
                     null, expiredTime, cancellationToken)).First();
 
@@ -1804,6 +1837,7 @@ namespace Hotline.FlowEngine.Workflows
             Workflow workflow,
             StepDefine endStepDefine,
             WorkflowStep prevStep,
+            List<WorkflowStepHandler> stepHandlers,
             CancellationToken cancellationToken)
         {
             if (workflow.Steps.Any(d => d.StepType == EStepType.End))
@@ -1812,8 +1846,8 @@ namespace Hotline.FlowEngine.Workflows
             var handler = new Kv { Key = _sessionContext.RequiredUserId, Value = _sessionContext.UserName };
 
             var step = CreateStep(workflow, endStepDefine, prevStep, null, new List<Kv> { handler },
-                null, null, null, EWorkflowStepStatus.WaitForAccept, ECountersignPosition.None, DateTime.Now,
-                endStepDefine.Name, true);
+              stepHandlers, null, null, null, EWorkflowStepStatus.WaitForAccept,
+              ECountersignPosition.None, DateTime.Now, endStepDefine.Name, true);
 
             //step.Accept(_sessionContext.RequiredUserId, _sessionContext.UserName,
             //    _sessionContext.RequiredOrgId, _sessionContext.OrgName,
@@ -1837,6 +1871,7 @@ namespace Hotline.FlowEngine.Workflows
             FlowAssignInfo flowAssignInfo,
             EWorkflowTraceStatus traceStatus,
             DateTime? expiredTime,
+            List<WorkflowStepHandler> stepHandlers,
             CancellationToken cancellationToken)
         {
             List<Kv> handlers;
@@ -1859,8 +1894,8 @@ namespace Hotline.FlowEngine.Workflows
             }
 
             return await CreateStepsAsync(workflow, stepDefine, prevStep, dto, flowAssignInfo.FlowAssignType, handlers,
-                null, EWorkflowStepStatus.WaitForAccept, ECountersignPosition.None, true, traceStatus,
-                null, expiredTime, cancellationToken);
+                stepHandlers, null, EWorkflowStepStatus.WaitForAccept, ECountersignPosition.None,
+                true, traceStatus, null, expiredTime, cancellationToken);
         }
 
         private async Task<List<WorkflowStep>> CreateStepsAsync(
@@ -1870,6 +1905,7 @@ namespace Hotline.FlowEngine.Workflows
             BasicWorkflowDto dto,
             EFlowAssignType? flowAssignType,
             List<Kv> handlers,
+            List<WorkflowStepHandler> stepHandlers,
             string? countersignId,
             EWorkflowStepStatus stepStatus,
             ECountersignPosition csPosition,
@@ -1890,7 +1926,7 @@ namespace Hotline.FlowEngine.Workflows
                 foreach (var handler in handlers)
                 {
                     var step = CreateStep(workflow, stepDefine, prevStep, flowAssignType, new List<Kv> { handler },
-                        dto.NextStepCode, dto.NextMainHandler, countersignId,
+                        stepHandlers, dto.NextStepCode, dto.NextMainHandler, countersignId,
                         stepStatus, csPosition, expiredTime, dto.NextStepName, isOrigin, handlerType);
 
                     steps.Add(step);
@@ -1898,14 +1934,17 @@ namespace Hotline.FlowEngine.Workflows
             }
             else
             {
-                var step = CreateStep(workflow, stepDefine, prevStep, flowAssignType, handlers,
+                var step = CreateStep(workflow, stepDefine, prevStep, flowAssignType, handlers, stepHandlers,
                     dto.NextStepCode, dto.NextMainHandler, countersignId,
                     stepStatus, csPosition, expiredTime, dto.NextStepName, isOrigin, handlerType);
 
                 steps.Add(step);
             }
 
-            await _workflowStepRepository.AddRangeAsync(steps, cancellationToken);
+            //await _workflowStepRepository.AddRangeAsync(steps, cancellationToken);
+            await _workflowStepRepository.AddNav(steps)
+                .Include(d => d.StepHandlers)
+                .ExecuteCommandAsync();
 
             //create traces todo add range traces
             foreach (var step in steps)
@@ -2158,6 +2197,7 @@ namespace Hotline.FlowEngine.Workflows
             WorkflowStep prevStep,
             EFlowAssignType? flowAssignType,
             List<Kv> handlers,
+            List<WorkflowStepHandler> stepHandlers,
             string nextStepCode,
             string? nextMainHandler,
             string? countersignId,
@@ -2166,7 +2206,8 @@ namespace Hotline.FlowEngine.Workflows
             DateTime? expiredTime,
             string stepName,
             bool isOrigin,
-            EHandlerType? handlerType = null //动态节点依据动态策略判断
+            EHandlerType? handlerType = null, //动态节点依据动态策略判断
+            string? opinion = null//汇总以及会签汇总节点需要提前为option赋值之前办理节点内容
         )
         {
             if (!handlers.Any())
@@ -2178,6 +2219,7 @@ namespace Hotline.FlowEngine.Workflows
 
             step.FlowAssignType = flowAssignType;
             step.Handlers = handlers;
+            step.StepHandlers = stepHandlers;
             step.NextStepCode = step.StepType is EStepType.End ? string.Empty : nextStepCode;
             step.IsMain = isMain;
             step.PrevStepId = prevStep.Id;
@@ -2193,6 +2235,9 @@ namespace Hotline.FlowEngine.Workflows
             if (handlerType.HasValue)
                 step.HandlerType = handlerType.Value;
 
+            if(!string.IsNullOrEmpty(opinion))
+                step.Opinion = opinion;
+
             return step;
         }
 

+ 12 - 2
src/Hotline/FlowEngine/Workflows/WorkflowStep.cs

@@ -38,7 +38,6 @@ public class WorkflowStep : StepBasicEntity
     /// </summary>
     public bool IsOrigin { get; set; }
 
-
     #region 会签
 
     /// <summary>
@@ -92,10 +91,14 @@ public class WorkflowStep : StepBasicEntity
     [Navigate(NavigateType.ManyToOne, nameof(WorkflowId))]
     public Workflow Workflow { get; set; }
 
-    [Navigate(NavigateType.OneToOne, nameof(Id), 
+    [Navigate(NavigateType.OneToOne, nameof(Id),
         nameof(Workflows.WorkflowTrace.StepId))]
     public WorkflowTrace WorkflowTrace { get; set; }
 
+    [Navigate(NavigateType.OneToMany, nameof(Id),
+        nameof(WorkflowStepHandler.WorkflowStepId))]
+    public List<WorkflowStepHandler> StepHandlers { get; set; }
+
     #region Method
 
     /// <summary>
@@ -256,6 +259,13 @@ public class WorkflowStep : StepBasicEntity
         return IsCountersignEndStep && CountersignStartStepId == topCountersignStepId;
     }
 
+    public WorkflowStepHandler?
+        FindActualHandler(ICollection<string> roles, string? userId = null, string? orgId = null) =>
+        StepHandlers.FirstOrDefault(d => d.IsHandler(roles, userId, orgId));
+
+    public WorkflowStepHandler? GetActualHandler() =>
+        StepHandlers.FirstOrDefault(d => d.IsActualHandler);
+
     #endregion
 }
 

+ 114 - 0
src/Hotline/FlowEngine/Workflows/WorkflowStepHandler.cs

@@ -0,0 +1,114 @@
+using Hotline.Share.Dtos;
+using SqlSugar;
+using XF.Domain.Entities;
+using XF.Domain.Exceptions;
+using XF.Domain.Repository;
+
+namespace Hotline.FlowEngine.Workflows;
+
+public class WorkflowStepHandler : CreationEntity
+{
+    public string WorkflowId { get; set; }
+    public string ExternalId { get; set; }
+    public string WorkflowStepId { get; set; }
+
+    /// <summary>
+    /// 流程指派类型
+    /// </summary>
+    public EFlowAssignType FlowAssignType { get; set; }
+
+    public string? UserId { get; set; }
+    public string? Username { get; set; }
+    public string? OrgId { get; set; }
+    public string? OrgName { get; set; }
+    public string? RoleId { get; set; }
+    public string? RoleName { get; set; }
+
+    /// <summary>
+    /// 是否为实际办理者
+    /// </summary>
+    public bool IsActualHandler { get; set; }
+
+    [Navigate(NavigateType.ManyToOne, nameof(WorkflowStepId))]
+    public WorkflowStep WorkflowStep { get; set; }
+
+    #region method
+
+    public static WorkflowStepHandler Create(string workflowId, string externalId, EFlowAssignType flowAssignType, string? userId = null, string? username = null,
+        string? orgId = null, string? orgName = null, string? roleId = null, string? roleName = null)
+    {
+        var handler = new WorkflowStepHandler
+        {
+            WorkflowId = workflowId,
+            ExternalId = externalId,
+        };
+        handler.Assign(flowAssignType, userId, username, orgId, orgName, roleId, roleName);
+        return handler;
+    }
+
+    public void Assign(EFlowAssignType assignType, string? userId, string? username,
+        string? orgId, string? orgName, string? roleId, string? roleName)
+    {
+        FlowAssignType = assignType;
+        switch (assignType)
+        {
+            case EFlowAssignType.Org:
+                if (string.IsNullOrEmpty(orgId) || string.IsNullOrEmpty(orgName))
+                    throw new ArgumentNullException(nameof(orgId));
+                OrgId = orgId;
+                OrgName = orgName;
+                break;
+            case EFlowAssignType.User:
+                if (string.IsNullOrEmpty(userId) || string.IsNullOrEmpty(username) ||
+                   string.IsNullOrEmpty(orgId) || string.IsNullOrEmpty(orgName))
+                    throw new ArgumentNullException(nameof(userId));
+                UserId = userId;
+                Username = username;
+                OrgId = orgId;
+                OrgName = orgName;
+                break;
+            //case EFlowAssignType.Role:
+            //    if (string.IsNullOrEmpty(roleId) || string.IsNullOrEmpty(roleName))
+            //        throw new ArgumentNullException(nameof(roleId));
+            //    RoleId = roleId;
+            //    RoleName = roleName;
+            //    break;
+            default:
+                throw new ArgumentOutOfRangeException(nameof(assignType), assignType, null);
+        }
+    }
+
+    public bool IsHandler(ICollection<string> roleIds, string? userId = null, string? orgId = null)
+    {
+        switch (FlowAssignType)
+        {
+            case EFlowAssignType.Org:
+                if (string.IsNullOrEmpty(OrgId))
+                    throw new UserFriendlyException($"数据异常, {nameof(OrgId)}");
+                return OrgId.Equals(orgId, StringComparison.OrdinalIgnoreCase);
+            case EFlowAssignType.User:
+                if (string.IsNullOrEmpty(UserId))
+                    throw new UserFriendlyException($"数据异常, {nameof(UserId)}");
+                return UserId.Equals(userId, StringComparison.OrdinalIgnoreCase);
+            //case EFlowAssignType.Role:
+            //    if (string.IsNullOrEmpty(RoleId))
+            //        throw new UserFriendlyException($"数据异常, {nameof(RoleId)}");
+            //    return roleIds.Any() && roleIds.Contains(RoleId, StringComparer.OrdinalIgnoreCase);
+            default:
+                throw new ArgumentOutOfRangeException();
+        }
+    }
+
+    public Kv GetHandler()
+    {
+        return FlowAssignType switch
+        {
+            EFlowAssignType.Org => new Kv(OrgId, OrgName),
+            EFlowAssignType.User => new Kv(UserId, Username),
+            //EFlowAssignType.Role => new Kv(RoleId, RoleName),
+            _ => throw new ArgumentOutOfRangeException()
+        };
+    }
+
+    #endregion
+}