xfe 1 anno fa
parent
commit
ebed33a2f4
34 ha cambiato i file con 612 aggiunte e 435 eliminazioni
  1. 3 3
      src/Hotline.Api/Controllers/OrderController.cs
  2. 4 4
      src/Hotline.Api/Controllers/WorkflowController.cs
  3. 2 2
      src/Hotline.Application/FlowEngine/IWorkflowApplication.cs
  4. 136 209
      src/Hotline.Application/FlowEngine/WorkflowApplication.cs
  5. 15 15
      src/Hotline.Application/FlowEngine/WorkflowApplication1.cs
  6. 2 2
      src/Hotline.Repository.SqlSugar/DataPermissions/DataPermissionFilterBuilder.cs
  7. 6 1
      src/Hotline.Share/Dtos/FlowEngine/BasicWorkflowDto.cs
  8. 1 1
      src/Hotline.Share/Dtos/FlowEngine/Definition/StepDefineBasic.cs
  9. 2 2
      src/Hotline.Share/Dtos/FlowEngine/NextStepOptionDto.cs
  10. 1 1
      src/Hotline.Share/Dtos/FlowEngine/NextStepsDto.cs
  11. 1 1
      src/Hotline.Share/Dtos/FlowEngine/StepBasicDto.cs
  12. 3 3
      src/Hotline.Share/Dtos/FlowEngine/WorkflowStepDto.cs
  13. 20 0
      src/Hotline.Share/Dtos/IdName.cs
  14. 0 20
      src/Hotline.Share/Dtos/Kv.cs
  15. 1 1
      src/Hotline.Share/Dtos/Order/OrderComplainDto.cs
  16. 7 7
      src/Hotline.Share/Dtos/Order/OrderDto.cs
  17. 3 3
      src/Hotline.Share/Dtos/Order/QueryOrderDto.cs
  18. 1 1
      src/Hotline/CallCenter/Tels/TelDomainService.cs
  19. 12 8
      src/Hotline/FlowEngine/FlowAssignInfo.cs
  20. 2 2
      src/Hotline/FlowEngine/Workflows/IWorkflowDomainService.cs
  21. 12 16
      src/Hotline/FlowEngine/Workflows/StepBasicEntity.cs
  22. 7 7
      src/Hotline/FlowEngine/Workflows/Workflow.cs
  23. 79 13
      src/Hotline/FlowEngine/Workflows/WorkflowCountersign.cs
  24. 220 55
      src/Hotline/FlowEngine/Workflows/WorkflowDomainService.cs
  25. 17 17
      src/Hotline/FlowEngine/Workflows/WorkflowDomainService1.cs
  26. 15 2
      src/Hotline/FlowEngine/Workflows/WorkflowStep.cs
  27. 1 0
      src/Hotline/FlowEngine/Workflows/WorkflowTrace.cs
  28. 4 4
      src/Hotline/KnowledgeBase/KnowledgeDomainService.cs
  29. 1 1
      src/Hotline/Orders/OrderComplain.cs
  30. 1 1
      src/Hotline/Orders/OrderVisit.cs
  31. 3 3
      src/Hotline/Orders/OrderVisitDetail.cs
  32. 6 6
      src/Hotline/SeedData/Codes/ICode.cs
  33. 20 20
      src/XF.Domain.Repository/Entity.cs
  34. 4 4
      src/XF.Domain/Entities/IWorkflow.cs

+ 3 - 3
src/Hotline.Api/Controllers/OrderController.cs

@@ -301,8 +301,8 @@ public class OrderController : BaseController
             var details = visit.OrderVisitDetails.Where(x => x.VisitTarget == EVisitTarget.Org).ToList();
             if (details != null && details.Count > 0)
             {
-                pubentity.IdNames = new List<Kv>();
-                details.ForEach(f => pubentity.IdNames.Add(new Kv() { Id = f.VisitOrgCode, Name = f.VisitOrgName }));
+                pubentity.IdNames = new List<IdName>();
+                details.ForEach(f => pubentity.IdNames.Add(new IdName() { Id = f.VisitOrgCode, Name = f.VisitOrgName }));
             }
         }
 
@@ -1351,7 +1351,7 @@ public class OrderController : BaseController
             PatentTypeOptions = await _systemDomainService.GetSysDicDataByCodeAsync(SysDicTypeConsts.PatentType),
             AffairTargetOptions = await _systemDomainService.GetSysDicDataByCodeAsync(SysDicTypeConsts.AffairTarget),
             ComplainTypeOptions =
-                EnumExts.GetDescriptions<EComplainType>().Select(d => new Kv(d.Key.ToString(), d.Value)),
+                EnumExts.GetDescriptions<EComplainType>().Select(d => new IdName(d.Key.ToString(), d.Value)),
             SalesChannelOptions = await _systemDomainService.GetSysDicDataByCodeAsync(SysDicTypeConsts.SalesChannel),
         };
         return rsp;

+ 4 - 4
src/Hotline.Api/Controllers/WorkflowController.cs

@@ -468,13 +468,13 @@ public class WorkflowController : BaseController
     /// <param name="workflowId"></param>
     /// <returns></returns>
     [HttpGet("{workflowId}/urge")]
-    public async Task<IReadOnlyList<Kv>> GetUrgeOrgs(string workflowId)
+    public async Task<IReadOnlyList<IdName>> GetUrgeOrgs(string workflowId)
     {
         var workflow = await _workflowDomainService.GetWorkflowAsync(workflowId, true, true, cancellationToken: HttpContext.RequestAborted);
         var stepBoxes = workflow.Steps.Where(d => d.StepType != EStepType.Start && d.StepType != EStepType.End).ToList();
         var steps = stepBoxes.SelectMany(d => d.Steps);
 
-        var orgs = new List<Kv>();
+        var orgs = new List<IdName>();
         foreach (var step in steps)
         {
             var orgCode = step.HandlerOrgId ?? step.AcceptorOrgId;
@@ -490,7 +490,7 @@ public class WorkflowController : BaseController
                             .Includes(d => d.Organization)
                             .Where(d => step.Handlers.Select(x => x.Id).Contains(d.Id))
                             .ToListAsync(HttpContext.RequestAborted);
-                        orgs.AddRange(users.Select(d => new Kv(d.OrgId, d.Organization.Name)));
+                        orgs.AddRange(users.Select(d => new IdName(d.OrgId, d.Organization.Name)));
                         break;
                     case EHandlerType.OrgLevel:
                     case EHandlerType.OrgType:
@@ -503,7 +503,7 @@ public class WorkflowController : BaseController
             }
             else
             {
-                orgs.Add(new Kv(orgCode, orgName));
+                orgs.Add(new IdName(orgCode, orgName));
             }
         }
         return orgs.DistinctBy(d => d.Id).ToList();

+ 2 - 2
src/Hotline.Application/FlowEngine/IWorkflowApplication.cs

@@ -39,7 +39,7 @@ namespace Hotline.Application.FlowEngine
         /// <summary>
         /// 查询节点配置可选参数
         /// </summary>
-        Task<IReadOnlyList<Kv>> GetNextStepOptionsAsync(StepDefineBasic stepDefine, CancellationToken cancellationToken);
+        Task<IReadOnlyList<IdName>> GetNextStepOptionsAsync(StepDefineBasic stepDefine, CancellationToken cancellationToken);
         
         ////////
 
@@ -56,6 +56,6 @@ namespace Hotline.Application.FlowEngine
         /// <summary>
         /// 查询下一步所选节点对应待选项
         /// </summary>
-        Task<IReadOnlyList<Kv>> GetNextStepOptionsAsync(GetNextStepItemsDto dto, CancellationToken cancellationToken);
+        Task<IReadOnlyList<IdName>> GetNextStepOptionsAsync(GetNextStepItemsDto dto, CancellationToken cancellationToken);
     }
 }

+ 136 - 209
src/Hotline.Application/FlowEngine/WorkflowApplication.cs

@@ -96,8 +96,8 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
 
         //如果发起会签需检查是否支持发起会签
         var startStepDefine = definition.FindStartStepDefine();
-        if(dto.IsStartCountersign && !startStepDefine.CanStartCountersign)
-            throw UserFriendlyException.SameMessage("发起人节点不支持发起会签");
+        //if (dto.IsStartCountersign && !startStepDefine.CanStartCountersign)
+        //    throw UserFriendlyException.SameMessage("发起人节点不支持发起会签");
 
 
         //var nextStepBoxDefine = _workflowDomainService.GetStepBoxDefine(definition, dto.NextStepCode);
@@ -106,18 +106,36 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
         //     nextStepBoxDefine.CountersignMode is not ECountersignMode.Support))
         //    throw UserFriendlyException.SameMessage("下一节点不支持办理会签");
 
+
+        var firstStepDefine = startStepDefine.InstanceMode is EInstanceMode.Dynamic
+            ? startStepDefine
+            : definition.FindStepDefine(dto.NextStepCode);
+        if (firstStepDefine is null)
+            throw new UserFriendlyException("未查询到下一步节点配置");
+
         //1. 如果不是按角色指派,handlers必填 2. 如果按角色指派,handlers可以不选
-        if (startStepDefine.HandlerType is not EHandlerType.Role && !dto.NextHandlers.Any())
+        if (firstStepDefine.HandlerType is not EHandlerType.Role && !dto.NextHandlers.Any())
             throw UserFriendlyException.SameMessage("未指派办理人");
 
-        //todo
+        if (dto.IsStartCountersign)
+        {
+            if (!startStepDefine.CanStartCountersign)
+                throw new UserFriendlyException("当前节点不支持发起会签");
+            //即使当前节点支持发起会签,但下一节点为信息汇总节点、结束节点时也不可发起会签
+            if (firstStepDefine.StepType is EStepType.Summary or EStepType.End)
+                throw new UserFriendlyException("下一节点不允许发起会签");
+            //下一节点是会签汇总节点也不允许发起会签
+            if (dto.BackToCountersignSummary)
+                throw new UserFriendlyException("下一节点不允许发起会签");
+        }
+
         var workflow = await _workflowDomainService.CreateWorkflowAsync(wfModule, dto.Title,
             _sessionContext.RequiredUserId, _sessionContext.RequiredOrgId, externalId, cancellationToken);
 
-        var flowAssignMode = await GetFlowAssignModeAsync(nextStepBoxDefine, dto.IsStartCountersign, dto.NextHandlers,
+        var flowAssignInfo = await GetFlowAssignInfoAsync(firstStepDefine, dto.IsStartCountersign, dto.NextHandlers,
             cancellationToken);
 
-        await _workflowDomainService.StartAsync(workflow, dto, nextStepBoxDefine, flowAssignMode, cancellationToken);
+        await _workflowDomainService.StartAsync(workflow, dto, startStepDefine, firstStepDefine, flowAssignInfo, cancellationToken);
 
         ////更新接办部门(详情页面展示)
         //await AddOrUpdateAssignAsync(workflow, dto, nextStepBoxDefine, cancellationToken);
@@ -148,7 +166,7 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
              nextStepBoxDefine.CountersignMode is not ECountersignMode.Support))
             throw UserFriendlyException.SameMessage("下一节点不支持办理会签");
 
-        var flowAssignMode = await GetFlowAssignModeAsync(nextStepBoxDefine, dto.IsStartCountersign, dto.NextHandlers,
+        var flowAssignMode = await GetFlowAssignInfoAsync(nextStepBoxDefine, dto.IsStartCountersign, dto.NextHandlers,
             cancellationToken);
 
         await _workflowDomainService.NextAsync(workflow, dto, nextStepBoxDefine, flowAssignMode, dto.ExpiredTime,
@@ -158,10 +176,53 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
         //await AddOrUpdateAssignAsync(workflow, dto, nextStepBoxDefine, cancellationToken);
     }
 
+    //new
+    public async Task NextAsync(NextWorkflowDto dto, CancellationToken cancellationToken)
+    {
+        var workflow = await _workflowDomainService.GetWorkflowAsync(dto.WorkflowId, true, true, withCountersigns: true,
+            cancellationToken: cancellationToken);
+        //await NextAsync(workflow, dto, dto.ExpiredTime, dto.IsStartCountersign, cancellationToken);
+
+        //未超期工单,节点超期时间不能小于当前时间,不能大于流程整体超期时间
+        if (workflow.ExpiredTime > DateTime.Now &&
+            (dto.ExpiredTime <= DateTime.Now || dto.ExpiredTime > workflow.ExpiredTime))
+            throw UserFriendlyException.SameMessage("节点期满时间无效");
+
+        var currentStep = _workflowDomainService.FindCurrentStepWaitForHandle(workflow);
+        if(currentStep.InstanceMode is EInstanceMode.Dynamic)
+        {
+            if (!DynamicShouldTerminal(currentStep))
+            {
+                //下一步配置为当前节点配置
+            }
+        }
+        else
+        {
+            //下一步配置为下一步节点配置
+        }
+
+        var nextStepDefine = _workflowDomainService.GetStepBoxDefine(workflow.WorkflowDefinition, dto.NextStepCode);
+
+        //需求:按角色选择办理人可以不选,表示该角色下所有人都可以办理,同时依据配置:是否本部门人办理显示待选办理人。角色下只要一人办理即可(即:角色下不发起会签)
+        if (nextStepDefine.HandlerType != EHandlerType.Role && !dto.NextHandlers.Any())
+            throw new UserFriendlyException("未指定节点处理者");
+
+        if (dto.IsStartCountersign &&
+            (nextStepDefine.StepType is EStepType.Summary ||
+             nextStepDefine.CountersignMode is not ECountersignMode.Support))
+            throw UserFriendlyException.SameMessage("下一节点不支持办理会签");
+
+        var flowAssignMode = await GetFlowAssignInfoAsync(nextStepDefine, dto.IsStartCountersign, dto.NextHandlers,
+            cancellationToken);
+
+        await _workflowDomainService.NextAsync(workflow, dto, nextStepDefine, flowAssignMode, dto.ExpiredTime,
+            cancellationToken);
+    }
+
     /// <summary>
-    /// 撤回至任意节点
-    /// </summary>
-    public async Task RecallAsync(RecallDto dto, CancellationToken cancellationToken)
+        /// 撤回至任意节点
+        /// </summary>
+        public async Task RecallAsync(RecallDto dto, CancellationToken cancellationToken)
     {
         var workflow =
             await _workflowDomainService.GetWorkflowAsync(dto.WorkflowId, true, true,
@@ -171,7 +232,7 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
 
         var targetStepDefine = _workflowDomainService.GetStepBoxDefine(workflow.WorkflowDefinition, dto.NextStepCode);
         //var isStartCountersign = targetStepDefine.CouldPrevStartCountersign(dto.NextHandlers.Count);
-        var flowAssignMode = await GetFlowAssignModeAsync(targetStepDefine, dto.IsStartCountersign, dto.NextHandlers,
+        var flowAssignMode = await GetFlowAssignInfoAsync(targetStepDefine, dto.IsStartCountersign, dto.NextHandlers,
             cancellationToken);
         await _workflowDomainService.RecallAsync(workflow, dto, targetStepDefine, flowAssignMode, cancellationToken);
     }
@@ -189,7 +250,7 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
 
         var targetStepDefine = _workflowDomainService.GetStepBoxDefine(workflow.WorkflowDefinition, dto.NextStepCode);
         //var isStartCountersign = targetStepDefine.CouldPrevStartCountersign(dto.NextHandlers.Count);
-        var flowAssignMode = await GetFlowAssignModeAsync(targetStepDefine, dto.IsStartCountersign, dto.NextHandlers,
+        var flowAssignMode = await GetFlowAssignInfoAsync(targetStepDefine, dto.IsStartCountersign, dto.NextHandlers,
             cancellationToken);
         await _workflowDomainService.JumpAsync(workflow, dto, targetStepDefine, dto.IsStartCountersign, flowAssignMode,
             cancellationToken);
@@ -204,7 +265,7 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
             await _workflowDomainService.GetWorkflowAsync(dto.WorkflowId, true, true,
                 cancellationToken: cancellationToken);
         var targetStepDefine = _workflowDomainService.GetStepBoxDefine(workflow.WorkflowDefinition, dto.NextStepCode);
-        var flowAssignInfo = await GetFlowAssignModeAsync(targetStepDefine, dto.IsStartCountersign, dto.NextHandlers,
+        var flowAssignInfo = await GetFlowAssignInfoAsync(targetStepDefine, dto.IsStartCountersign, dto.NextHandlers,
             cancellationToken);
         await _workflowDomainService.RedoAsync(workflow, dto, targetStepDefine, flowAssignInfo, cancellationToken);
     }
@@ -247,26 +308,26 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
     /// <param name="cancellationToken"></param>
     /// <returns></returns>
     /// <exception cref="ArgumentOutOfRangeException"></exception>
-    public async Task<IReadOnlyList<Kv>> GetNextStepOptionsAsync(StepDefineBasic stepDefine,
+    public async Task<IReadOnlyList<IdName>> GetNextStepOptionsAsync(StepDefineBasic stepDefine,
         CancellationToken cancellationToken)
     {
         if (stepDefine.StepType == EStepType.End)
             throw new UserFriendlyException("结束节点无待选项");
 
-        var handlers = new List<Kv>();
+        var handlers = new List<IdName>();
         switch (stepDefine.HandlerType)
         {
             // case EHandlerType.AssignedUser:
             //     var users = await _userRepository.Queryable()
-            //         .Where(d => stepDefine.HandlerTypeItems.Select(x => x.Key).Contains(d.Id))
+            //         .Where(d => stepDefine.HandlerTypeItems.Select(x => x.Id).Contains(d.Id))
             //         .ToListAsync(cancellationToken);
-            //     handlers = users.Select(d => new Kv(d.Id, d.Name)).ToList();
+            //     handlers = users.Select(d => new IdName(d.Id, d.Name)).ToList();
             //     break;
             // case EHandlerType.AssignedOrg:
             //     var orgs = await _organizeRepository.QueryAsync(d =>
             //         d.IsEnable &&
-            //         stepDefine.HandlerTypeItems.Select(d => d.Key).Contains(d.Id));
-            //     handlers = orgs.Select(d => new Kv(d.Id, d.OrgName))
+            //         stepDefine.HandlerTypeItems.Select(d => d.Id).Contains(d.Id));
+            //     handlers = orgs.Select(d => new IdName(d.Id, d.OrgName))
             //         .ToList();
             //     break;
             case EHandlerType.AssignedUser:
@@ -278,14 +339,14 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
                 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))
+                    .Where(d => stepDefine.HandlerTypeItems.Select(x => x.Id).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();
+                handlers = users1.Select(d => new IdName(d.Id, d.Name)).ToList();
                 break;
             case EHandlerType.OrgLevel:
                 //当前操作人所属部门的下级部门并且属于配置orgLevel的部门
-                var levels = stepDefine.HandlerTypeItems.Select(d => d.Key).Select(d => int.Parse(d));
+                var levels = stepDefine.HandlerTypeItems.Select(d => d.Id).Select(d => int.Parse(d));
                 var levelOneOrg = _sessionContext.RequiredOrgId.GetHigherOrgCode();
                 //var orgs1 = await _organizeRepository.QueryAsync(d =>
                 //    d.IsEnable && d.OrgCode.StartsWith(levelOneOrg) &&
@@ -296,10 +357,10 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
                     .WhereIF(!levelOneOrg.IsCenter(), d => d.Id.StartsWith(levelOneOrg))
                     .ToListAsync(cancellationToken);
 
-                handlers = orgs1.Select(d => new Kv(d.Id, d.Name)).ToList();
+                handlers = orgs1.Select(d => new IdName(d.Id, d.Name)).ToList();
                 break;
             case EHandlerType.OrgType:
-                var types = stepDefine.HandlerTypeItems.Select(d => d.Key)
+                var types = stepDefine.HandlerTypeItems.Select(d => d.Id)
                     .Select(d => Enum.Parse<EOrgType>(d));
                 var levelOneOrg1 = _sessionContext.RequiredOrgId.GetHigherOrgCode();
                 //var org2 = await _organizeRepository.QueryAsync(d =>
@@ -311,7 +372,7 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
                     .WhereIF(!levelOneOrg1.IsCenter(), d => d.Id.StartsWith(levelOneOrg1))
                     .ToListAsync(cancellationToken);
 
-                handlers = orgs2.Select(d => new Kv(d.Id, d.Name)).ToList();
+                handlers = orgs2.Select(d => new IdName(d.Id, d.Name)).ToList();
                 break;
             default:
                 throw new ArgumentOutOfRangeException();
@@ -324,12 +385,12 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
             var orderRedoReasons =
                 await _systemDomainService.GetSysDicDataByCodeAsync(SysDicTypeConsts.OrderRedoReason,
                     cancellationToken);
-            dto.OrderRedoReasonOptions = orderRedoReasons.Select(d => new Kv(d.DicDataValue, d.DicDataName)).ToList();
+            dto.OrderRedoReasonOptions = orderRedoReasons.Select(d => new IdName(d.DicDataValue, d.DicDataName)).ToList();
         }
 
         return dto;
     }
-    
+
     /// <summary>
     /// 查询开始流程的下一步待选节点
     /// </summary>
@@ -422,9 +483,9 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
     /// <summary>
     /// 查询下一步所选节点对应待选项
     /// </summary>
-    public async Task<IReadOnlyList<Kv>> GetNextStepOptionsAsync(GetNextStepItemsDto dto, CancellationToken cancellationToken)
+    public async Task<IReadOnlyList<IdName>> GetNextStepOptionsAsync(GetNextStepItemsDto dto, CancellationToken cancellationToken)
     {
-        List<Kv> nextStepOptions;
+        List<IdName> nextStepOptions;
         if (dto.InstanceMode is EInstanceMode.Config)
         {
             nextStepOptions = await NextStepOptionsByConfigAsync(dto, cancellationToken);
@@ -437,11 +498,11 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
         return nextStepOptions;
     }
 
-    private async Task<List<Kv>> CreateNextStepOptionsDynamicAsync(GetNextStepItemsDto dto, CancellationToken cancellationToken)
+    private async Task<List<IdName>> CreateNextStepOptionsDynamicAsync(GetNextStepItemsDto dto, CancellationToken cancellationToken)
     {
         if (!int.TryParse(dto.Key, out int orgLevel))
             throw new UserFriendlyException("无效参数");
-        List<Kv> items;
+        List<IdName> items;
         var firstLevelOrgCode = _sessionContext.RequiredOrgId.GetHigherOrgCode();
         switch (dto.DynamicPolicy)
         {
@@ -450,21 +511,21 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
                 {
                     items = await _organizeRepository.Queryable()
                         .Where(d => d.IsCenter)
-                        .Select(d => new Kv(d.Id, d.Name))
+                        .Select(d => new IdName(d.Id, d.Name))
                         .ToListAsync(cancellationToken);
                 }
                 else
                 {
                     items = await _organizeRepository.Queryable()
                         .Where(d => !d.IsCenter && d.Level == orgLevel && d.Id.StartsWith(firstLevelOrgCode))
-                        .Select(d => new Kv(d.Id, d.Name))
+                        .Select(d => new IdName(d.Id, d.Name))
                         .ToListAsync(cancellationToken);
                 }
                 break;
             case EDynamicPolicy.OrgUp:
                 items = await _organizeRepository.Queryable()
                     .Where(d => d.Level == orgLevel && d.Id.StartsWith(firstLevelOrgCode))
-                    .Select(d => new Kv(d.Id, d.Name))
+                    .Select(d => new IdName(d.Id, d.Name))
                     .ToListAsync(cancellationToken);
                 break;
             case EDynamicPolicy.OrgDownCenterTop:
@@ -472,21 +533,21 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
                 {
                     items = await _organizeRepository.Queryable()
                         .Where(d => !d.IsCenter && d.Level == orgLevel)
-                        .Select(d => new Kv(d.Id, d.Name))
+                        .Select(d => new IdName(d.Id, d.Name))
                         .ToListAsync(cancellationToken);
                 }
                 else
                 {
                     items = await _organizeRepository.Queryable()
                         .Where(d => !d.IsCenter && d.Level == orgLevel && d.Id.StartsWith(firstLevelOrgCode))
-                        .Select(d => new Kv(d.Id, d.Name))
+                        .Select(d => new IdName(d.Id, d.Name))
                         .ToListAsync(cancellationToken);
                 }
                 break;
             case EDynamicPolicy.OrgDown:
                 items = await _organizeRepository.Queryable()
                     .Where(d => d.Level == orgLevel && d.Id.StartsWith(firstLevelOrgCode))
-                    .Select(d => new Kv(d.Id, d.Name))
+                    .Select(d => new IdName(d.Id, d.Name))
                     .ToListAsync(cancellationToken);
                 break;
 
@@ -497,7 +558,7 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
         return items;
     }
 
-    private async Task<List<Kv>> NextStepOptionsByConfigAsync(GetNextStepItemsDto dto, CancellationToken cancellationToken)
+    private async Task<List<IdName>> NextStepOptionsByConfigAsync(GetNextStepItemsDto dto, CancellationToken cancellationToken)
     {
         var definition = await _definitionRepository.GetAsync(dto.DefinitionId, cancellationToken);
         if (definition == null)
@@ -510,7 +571,7 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
         if (stepDefine.StepType == EStepType.End)
             throw new UserFriendlyException("结束节点无待选项");
 
-        var handlers = new List<Kv>();
+        var handlers = new List<IdName>();
         var firstLevelOrgCode = _sessionContext.RequiredOrgId.GetHigherOrgCode();
         switch (stepDefine.HandlerType)
         {
@@ -529,7 +590,7 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
                     .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();
+                handlers = users1.Select(d => new IdName(d.Id, d.Name)).ToList();
                 break;
             case EHandlerType.OrgLevel:
                 //当前操作人所属部门的垂直部门并且属于配置orgLevel的部门
@@ -539,7 +600,7 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
                     .WhereIF(!firstLevelOrgCode.IsCenter(), d => d.Id.StartsWith(firstLevelOrgCode))
                     .ToListAsync(cancellationToken);
 
-                handlers = orgs1.Select(d => new Kv(d.Id, d.Name)).ToList();
+                handlers = orgs1.Select(d => new IdName(d.Id, d.Name)).ToList();
                 break;
             case EHandlerType.OrgType:
                 var types = stepDefine.HandlerTypeItems.Select(d => d.Key)
@@ -549,7 +610,7 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
                     .WhereIF(!firstLevelOrgCode.IsCenter(), d => d.Id.StartsWith(firstLevelOrgCode))
                     .ToListAsync(cancellationToken);
 
-                handlers = orgs2.Select(d => new Kv(d.Id, d.Name)).ToList();
+                handlers = orgs2.Select(d => new IdName(d.Id, d.Name)).ToList();
                 break;
             default:
                 throw new ArgumentOutOfRangeException();
@@ -607,184 +668,50 @@ public class WorkflowApplication : IWorkflowApplication, IScopeDependency
         };
     }
 
-    private async Task<NextStepOption> CreateDynamicStepAsync1(EDynamicPolicy policy, CancellationToken cancellationToken)
-    {
-        int orgLevel;
-        List<Kv> items = new();
-        var firstLevelOrgCode = _sessionContext.RequiredOrgId.GetHigherOrgCode();
-        switch (policy)
-        {
-            case EDynamicPolicy.OrgUpCenterTop:
-                orgLevel = _sessionContext.OrgLevel - 1;
-                if (orgLevel < 0) orgLevel = 0;
-
-                if (orgLevel == 0)
-                {
-                    items = await _organizeRepository.Queryable()
-                        .Where(d => d.IsCenter)
-                        .Select(d => new Kv(d.Id, d.Name))
-                        .ToListAsync(cancellationToken);
-                }
-                else
-                {
-                    items = await _organizeRepository.Queryable()
-                        .Where(d => !d.IsCenter && d.Level == orgLevel && d.Id.StartsWith(firstLevelOrgCode))
-                        .Select(d => new Kv(d.Id, d.Name))
-                        .ToListAsync(cancellationToken);
-                }
-                break;
-            case EDynamicPolicy.OrgUp:
-                orgLevel = _sessionContext.OrgLevel - 1;
-                if (orgLevel <= 0) orgLevel = 1;
-                items = await _organizeRepository.Queryable()
-                    .Where(d => d.Level == orgLevel && d.Id.StartsWith(firstLevelOrgCode))
-                    .Select(d => new Kv(d.Id, d.Name))
-                    .ToListAsync(cancellationToken);
-                break;
-            case EDynamicPolicy.OrgDownCenterTop:
-                if (_sessionContext.RequiredOrgId.IsBelongCenter())
-                {
-                    orgLevel = 1;
-                    items = await _organizeRepository.Queryable()
-                        .Where(d => !d.IsCenter && d.Level == orgLevel)
-                        .Select(d => new Kv(d.Id, d.Name))
-                        .ToListAsync(cancellationToken);
-                }
-                else
-                {
-                    orgLevel = _sessionContext.OrgLevel + 1;
-                    items = await _organizeRepository.Queryable()
-                        .Where(d => !d.IsCenter && d.Level == orgLevel && d.Id.StartsWith(firstLevelOrgCode))
-                        .Select(d => new Kv(d.Id, d.Name))
-                        .ToListAsync(cancellationToken);
-                }
-                break;
-            case EDynamicPolicy.OrgDown:
-                orgLevel = _sessionContext.OrgLevel + 1;
-                items = await _organizeRepository.Queryable()
-                    .Where(d => d.Level == orgLevel && d.Id.StartsWith(firstLevelOrgCode))
-                    .Select(d => new Kv(d.Id, d.Name))
-                    .ToListAsync(cancellationToken);
-                break;
-            default:
-                throw new ArgumentOutOfRangeException(nameof(policy), policy, null);
-        }
-
-        return new NextStepOption
-        {
-            Key = orgLevel.ToString(),
-            Value = orgLevel == 0 ? "中心办理" : $"{orgLevel.ToChinese()}级部门办理",
-            Items = items
-        };
-    }
-
-    private async Task<IReadOnlyList<NextStepOption>> CreateConfigStepsAsync(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>
     /// 查询指派办理人的处理方式及实际办理人
     /// </summary>
-    public async Task<FlowAssignInfo> GetFlowAssignModeAsync(StepDefine stepDefine, bool isStartCountersign,
-        List<Kv> handlers, CancellationToken cancellationToken)
+    public async Task<FlowAssignInfo> GetFlowAssignInfoAsync(StepDefine stepDefine, bool isStartCountersign,
+        List<IdName> handlers, CancellationToken cancellationToken)
     {
-        if (stepDefine.StepType is EStepType.Start or EStepType.End) return new();
-        switch (stepDefine.HandlerType)
+        if (stepDefine.StepType is EStepType.End) return new();
+
+        switch (stepDefine.InstanceMode)
         {
-            case EHandlerType.Role:
-                if (!handlers.Any())
+            case EInstanceMode.Config:
+                switch (stepDefine.HandlerType)
                 {
-                    var roles = await _roleRepository.Queryable()
-                        .Includes(d => d.Accounts, x => x.User)
-                        .Where(d => stepDefine.HandlerTypeItems.Select(d => d.Key).Contains(d.Name))
-                        .ToListAsync(cancellationToken);
-                    handlers = roles.SelectMany(d => d.Accounts).Distinct().Select(d => new Kv(d.Id, d.User.Name))
-                        .ToList();
+                    case EHandlerType.Role:
+                        if (!handlers.Any())
+                        {
+                            var roles = await _roleRepository.Queryable()
+                                .Includes(d => d.Accounts, x => x.User)
+                                .Where(d => stepDefine.HandlerTypeItems.Select(d => d.Id).Contains(d.Name))
+                                .ToListAsync(cancellationToken);
+                            handlers = roles.SelectMany(d => d.Accounts).Distinct().Select(d => new IdName(d.Id, d.User.Name))
+                                .ToList();
+                        }
+
+                        return FlowAssignInfo.Create(EFlowAssignType.User, handlers, isStartCountersign);
+
+                    case EHandlerType.OrgLevel:
+                    case EHandlerType.OrgType:
+                    case EHandlerType.AssignedOrg:
+                        return FlowAssignInfo.Create(EFlowAssignType.Org, handlers, isStartCountersign);
+
+                    case EHandlerType.AssignedUser:
+                        return FlowAssignInfo.Create(EFlowAssignType.User, handlers, isStartCountersign);
+
+                    default:
+                        throw new ArgumentOutOfRangeException();
                 }
-
-                return FlowAssignInfo.Create(EFlowAssignType.User, handlers, isStartCountersign);
-
-            case EHandlerType.OrgLevel:
-            case EHandlerType.OrgType:
-            case EHandlerType.AssignedOrg:
+            case EInstanceMode.Dynamic:
                 return FlowAssignInfo.Create(EFlowAssignType.Org, handlers, isStartCountersign);
-
-            case EHandlerType.AssignedUser:
-                return FlowAssignInfo.Create(EFlowAssignType.User, handlers, isStartCountersign);
-
             default:
                 throw new ArgumentOutOfRangeException();
         }
+
+
     }
 
 

+ 15 - 15
src/Hotline.Application/FlowEngine/WorkflowApplication1.cs

@@ -220,33 +220,33 @@ public class WorkflowApplication1 : IWorkflowApplication, IScopeDependency
     {
         if (stepDefine.StepType == EStepType.End) return new();
 
-        var handlers = new List<Kv>();
+        var handlers = new List<IdName>();
         switch (stepDefine.HandlerType)
         {
             case EHandlerType.AssignedUser:
                 var users = await _userRepository.QueryAsync(d =>
                     !d.IsDeleted &&
-                    stepDefine.HandlerTypeItems.Select(d => d.Key).Contains(d.Id));
-                handlers = users.Select(d => new Kv(d.Id, d.Name)).ToList();
+                    stepDefine.HandlerTypeItems.Select(d => d.Id).Contains(d.Id));
+                handlers = users.Select(d => new IdName(d.Id, d.Name)).ToList();
                 break;
             case EHandlerType.AssignedOrg:
                 var orgs = await _organizeRepository.QueryAsync(d =>
                     d.IsEnable &&
-                    stepDefine.HandlerTypeItems.Select(d => d.Key).Contains(d.Id));
-                handlers = orgs.Select(d => new Kv(d.Id, d.Name))
+                    stepDefine.HandlerTypeItems.Select(d => d.Id).Contains(d.Id));
+                handlers = orgs.Select(d => new IdName(d.Id, d.Name))
                     .ToList();
                 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(d => d.Key).Contains(d.Name))
+                    .Where(d => stepDefine.HandlerTypeItems.Select(d => d.Id).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();
+                handlers = users1.Select(d => new IdName(d.Id, d.Name)).ToList();
                 break;
             case EHandlerType.OrgLevel:
                 //当前操作人所属部门的下级部门并且属于配置orgLevel的部门
-                var levels = stepDefine.HandlerTypeItems.Select(d => d.Key).Select(d => int.Parse(d));
+                var levels = stepDefine.HandlerTypeItems.Select(d => d.Id).Select(d => int.Parse(d));
                 var levelOneOrg = _sessionContext.RequiredOrgId.GetHigherOrgCode();
                 //var orgs1 = await _organizeRepository.QueryAsync(d =>
                 //    d.IsEnable && d.OrgCode.StartsWith(levelOneOrg) &&
@@ -257,10 +257,10 @@ public class WorkflowApplication1 : IWorkflowApplication, IScopeDependency
                     .WhereIF(!levelOneOrg.IsCenter(), d => d.Id.StartsWith(levelOneOrg))
                     .ToListAsync(cancellationToken);
 
-                handlers = orgs1.Select(d => new Kv(d.Id, d.Name)).ToList();
+                handlers = orgs1.Select(d => new IdName(d.Id, d.Name)).ToList();
                 break;
             case EHandlerType.OrgType:
-                var types = stepDefine.HandlerTypeItems.Select(d => d.Key)
+                var types = stepDefine.HandlerTypeItems.Select(d => d.Id)
                     .Select(d => Enum.Parse<EOrgType>(d));
                 var levelOneOrg1 = _sessionContext.RequiredOrgId.GetHigherOrgCode();
                 //var org2 = await _organizeRepository.QueryAsync(d =>
@@ -272,7 +272,7 @@ public class WorkflowApplication1 : IWorkflowApplication, IScopeDependency
                     .WhereIF(!levelOneOrg1.IsCenter(), d => d.Id.StartsWith(levelOneOrg1))
                     .ToListAsync(cancellationToken);
 
-                handlers = orgs2.Select(d => new Kv(d.Id, d.Name)).ToList();
+                handlers = orgs2.Select(d => new IdName(d.Id, d.Name)).ToList();
                 break;
             default:
                 throw new ArgumentOutOfRangeException();
@@ -283,7 +283,7 @@ public class WorkflowApplication1 : IWorkflowApplication, IScopeDependency
         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();
+            dto.OrderRedoReasonOptions = orderRedoReasons.Select(d => new IdName(d.DicDataValue, d.DicDataName)).ToList();
         }
 
         return dto;
@@ -292,7 +292,7 @@ public class WorkflowApplication1 : IWorkflowApplication, IScopeDependency
     /// <summary>
     /// 查询指派办理人的处理方式及实际办理人
     /// </summary>
-    public async Task<FlowAssignInfo> GetFlowAssignModeAsync(StepDefine stepDefine, bool isStartCountersign, List<Kv> handlers, CancellationToken cancellationToken)
+    public async Task<FlowAssignInfo> GetFlowAssignModeAsync(StepDefine stepDefine, bool isStartCountersign, List<IdName> handlers, CancellationToken cancellationToken)
     {
         if (stepDefine.StepType is EStepType.Start or EStepType.End) return new();
         switch (stepDefine.HandlerType)
@@ -302,9 +302,9 @@ public class WorkflowApplication1 : IWorkflowApplication, IScopeDependency
                 {
                     var roles = await _roleRepository.Queryable()
                         .Includes(d => d.Accounts, x => x.User)
-                        .Where(d => stepDefine.HandlerTypeItems.Select(d => d.Key).Contains(d.Name))
+                        .Where(d => stepDefine.HandlerTypeItems.Select(d => d.Id).Contains(d.Name))
                         .ToListAsync(cancellationToken);
-                    handlers = roles.SelectMany(d => d.Accounts).Distinct().Select(d => new Kv(d.Id, d.User.Name)).ToList();
+                    handlers = roles.SelectMany(d => d.Accounts).Distinct().Select(d => new IdName(d.Id, d.User.Name)).ToList();
                 }
 
                 return FlowAssignInfo.Create(EFlowAssignType.User, handlers, isStartCountersign);

+ 2 - 2
src/Hotline.Repository.SqlSugar/DataPermissions/DataPermissionFilterBuilder.cs

@@ -50,8 +50,8 @@ public class DataPermissionFilterBuilder : IDataPermissionFilterBuilder, IScopeD
         if (roles != null && roles.Contains(RoleSeedData.AdminRole))
             return d => true;
 
-        return d => SqlFunc.JsonArrayAny(d.AssignUserIds, userId) ||
-                    SqlFunc.JsonArrayAny(d.AssignOrgCodes, orgCode);
+        return d => SqlFunc.JsonArrayAny(d.FlowedUserIds, userId) ||
+                    SqlFunc.JsonArrayAny(d.FlowedOrgIds, orgCode);
     }
 
     public Expression<Func<TEntity, Workflow, bool>> BuildIncludeFlowData1<TEntity>() where TEntity : class, IEntity<string>, IDataPermission, IWorkflow, new()

+ 6 - 1
src/Hotline.Share/Dtos/FlowEngine/BasicWorkflowDto.cs

@@ -10,13 +10,18 @@ public class BasicWorkflowDto : EndWorkflowDto
     /// </summary>
     public string NextStepCode { get; set; } = string.Empty;
 
+    /// <summary>
+    /// 是否回到会签发起节点汇总
+    /// </summary>
+    public bool BackToCountersignSummary { get; set; }
+
     /// <summary>
     /// 根据办理者类型不同,此字段为不同内容
     /// <example>
     /// 部门等级/分类为:depCodes, 角色为:userIds
     /// </example>
     /// </summary>
-    public List<Kv> NextHandlers { get; set; } = new();
+    public List<IdName> NextHandlers { get; set; } = new();
 
     /// <summary>
     /// 是否短信通知

+ 1 - 1
src/Hotline.Share/Dtos/FlowEngine/Definition/StepDefineBasic.cs

@@ -40,7 +40,7 @@ namespace Hotline.Share.Dtos.FlowEngine.Definition
         /// 根据类型可能为:roles, orgLevels, orgTypes, orgCodes, userIds
         /// </example>
         /// </summary>
-        public List<Kv> HandlerTypeItems { get; set; } = new();
+        public List<IdName> HandlerTypeItems { get; set; } = new();
 
         /// <summary>
         /// 是否有否决按钮

+ 2 - 2
src/Hotline.Share/Dtos/FlowEngine/NextStepOptionDto.cs

@@ -14,11 +14,11 @@ namespace Hotline.Share.Dtos.FlowEngine
         /// <summary>
         /// 办理对象:人/部门(待选项)
         /// </summary>
-        public IReadOnlyList<Kv> Handlers { get; set; }
+        public IReadOnlyList<IdName> Handlers { get; set; }
 
         /// <summary>
         /// 工单重办理由待选项,对应code:OrderRedoReason
         /// </summary>
-        public IReadOnlyList<Kv> OrderRedoReasonOptions { get; set; }
+        public IReadOnlyList<IdName> OrderRedoReasonOptions { get; set; }
     }
 }

+ 1 - 1
src/Hotline.Share/Dtos/FlowEngine/NextStepsDto.cs

@@ -22,7 +22,7 @@ public class NextStepOption
     /// </summary>
     public bool BackToCountersignSummary { get; set; }
 
-    //public IReadOnlyList<Kv> Items { get; set; }
+    //public IReadOnlyList<IdName> Items { get; set; }
 }
 
 public class GetNextStepItemsDto

+ 1 - 1
src/Hotline.Share/Dtos/FlowEngine/StepBasicDto.cs

@@ -29,7 +29,7 @@ namespace Hotline.Share.Dtos.FlowEngine
         /// 根据类型可能为:roles, orgLevels, orgTypes, orgCodes, userIds
         /// </example>
         /// </summary>
-        public List<Kv> HandlerClassifies { get; set; } = new();
+        public List<IdName> HandlerClassifies { get; set; } = new();
 
         /// <summary>
         /// 会签模式

+ 3 - 3
src/Hotline.Share/Dtos/FlowEngine/WorkflowStepDto.cs

@@ -30,7 +30,7 @@ public class WorkflowStepDto
     /// 根据类型可能为:roles, orgLevels, orgTypes, orgCodes, userIds
     /// </example>
     /// </summary>
-    public List<Kv> HandlerClassifies { get; set; } = new();
+    public List<IdName> HandlerClassifies { get; set; } = new();
 
     /// <summary>
     /// 会签模式
@@ -43,7 +43,7 @@ public class WorkflowStepDto
     /// 被指派办理对象(依据不同指派方式可能为:orgCode或userId),该字段subStep才会存在,stepBox不存在
     /// 采用list类型,兼容多个办理对象可以办理同一个节点的场景
     /// </summary>
-    public List<Kv> Handlers { get; set; } = new();
+    public List<IdName> Handlers { get; set; } = new();
 
     #region 办理参数
 
@@ -53,7 +53,7 @@ public class WorkflowStepDto
     /// 部门等级/分类为:orgCodes, 角色为:userIds
     /// </example>
     /// </summary>
-    public List<Kv> NextHandlers { get; set; } = new();
+    public List<IdName> NextHandlers { get; set; } = new();
 
     /// <summary>
     /// 下一节点主办,(NextHandlers其中一个, 如果不是会签则只有一个)

+ 20 - 0
src/Hotline.Share/Dtos/IdName.cs

@@ -0,0 +1,20 @@
+namespace Hotline.Share.Dtos;
+
+/// <summary>
+/// id-name:string,string
+/// </summary>
+public struct IdName
+{
+    public IdName()
+    {
+    }
+
+    public IdName(string id, string name)
+    {
+        Id = id;
+        Name = name;
+    }
+
+    public string Id { get; set; }
+    public string Name { get; set; }
+}

+ 0 - 20
src/Hotline.Share/Dtos/Kv.cs

@@ -1,20 +0,0 @@
-namespace Hotline.Share.Dtos;
-
-/// <summary>
-/// key-value:string,string
-/// </summary>
-public struct Kv
-{
-    public Kv()
-    {
-    }
-
-    public Kv(string key, string value)
-    {
-        Key = key;
-        Value = value;
-    }
-
-    public string Key { get; set; }
-    public string Value { get; set; }
-}

+ 1 - 1
src/Hotline.Share/Dtos/Order/OrderComplainDto.cs

@@ -33,7 +33,7 @@ public class OrderComplainDto : OrderExtensionDto
     /// <summary>
     /// 诉求类型,多选
     /// </summary>
-    public List<Kv> ComplainTypes { get; set; } = new();
+    public List<IdName> ComplainTypes { get; set; } = new();
 
     #endregion
 }

+ 7 - 7
src/Hotline.Share/Dtos/Order/OrderDto.cs

@@ -564,7 +564,7 @@ namespace Hotline.Share.Dtos.Order
         /// <summary>
         /// 回访部门
         /// </summary>
-        public List<Kv> IdNames { get; set; }
+        public List<IdName> IdNames { get; set; }
 
     }
 
@@ -638,12 +638,12 @@ namespace Hotline.Share.Dtos.Order
         /// <summary>
         /// 实际办理部门名称
         /// </summary>
-        public Kv ActualHandleOrgName { get; set; }
+        public IdName ActualHandleOrgName { get; set; }
 
         /// <summary>
         /// 需回访部门
         /// </summary>
-        public List<Kv>? idNames { get; set; }
+        public List<IdName>? idNames { get; set; }
     }
 
 
@@ -677,7 +677,7 @@ namespace Hotline.Share.Dtos.Order
         /// <summary>
         /// 需回访部门
         /// </summary>
-        public List<Kv> IdNames { get; set; }
+        public List<IdName> IdNames { get; set; }
 
         #region 省工单使用
 
@@ -805,17 +805,17 @@ namespace Hotline.Share.Dtos.Order
         /// <summary>
         /// 部门办件结果
         /// </summary>
-        public Kv? OrgProcessingResults { get; set; }
+        public IdName? OrgProcessingResults { get; set; }
 
         /// <summary>
         /// 不满意原因
         /// </summary>
-        public List<Kv>? OrgNoSatisfiedReason { get; set; }
+        public List<IdName>? OrgNoSatisfiedReason { get; set; }
 
         /// <summary>
         /// 部门办件态度
         /// </summary>
-        public Kv? OrgHandledAttitude { get; set; }
+        public IdName? OrgHandledAttitude { get; set; }
 
         /// <summary>
         /// 回访内容

+ 3 - 3
src/Hotline.Share/Dtos/Order/QueryOrderDto.cs

@@ -183,17 +183,17 @@ namespace Hotline.Share.Dtos.Order
         /// <summary>
         /// 部门办件结果
         /// </summary>
-        public Kv? OrgProcessingResults { get; set; }
+        public IdName? OrgProcessingResults { get; set; }
 
         /// <summary>
         /// 不满意原因
         /// </summary>
-        public List<Kv>? OrgNoSatisfiedReason { get; set; }
+        public List<IdName>? OrgNoSatisfiedReason { get; set; }
 
         /// <summary>
         /// 部门办件态度
         /// </summary>
-        public Kv? OrgHandledAttitude { get; set; }
+        public IdName? OrgHandledAttitude { get; set; }
 
         /// <summary>
         /// 回访内容

+ 1 - 1
src/Hotline/CallCenter/Tels/TelDomainService.cs

@@ -69,7 +69,7 @@ public class TelDomainService : ITelDomainService, IScopeDependency
         if (telRest == null)
             throw new UserFriendlyException($"无效分机休息编号, telRestId: {telRestId}", "无效分机休息编号");
         telRest.WorkflowId = workflowId;
-        telRest.Assign(assignInfo.FlowAssignType, assignInfo.GetHandlers());
+        telRest.Assign(assignInfo.FlowAssignType, assignInfo.GetHandlerIds());
         await _telRestRepository.UpdateAsync(telRest, cancellationToken);
     }
 

+ 12 - 8
src/Hotline/FlowEngine/FlowAssignInfo.cs

@@ -19,31 +19,30 @@ public class FlowAssignInfo
     /// <summary>
     /// 创建mode对象实例,自动生成一个groupId
     /// </summary>
-    /// <param name="flowAssignType"></param>
-    /// <param name="handlers"></param>
-    /// <returns></returns>
-    public static FlowAssignInfo Create(EFlowAssignType flowAssignType, List<Kv> handlers, bool multiGroups = false)
+    public static FlowAssignInfo Create(EFlowAssignType flowAssignType, List<IdName> handlers, bool isStartCountersign = false)
     {
         if (handlers == null || !handlers.Any())
             return new();
         List<HandlerGroupItem> handlerObjects;
-        if (multiGroups)
+        if (isStartCountersign)
         {
+            //会签
             handlerObjects = handlers.Select(d => new HandlerGroupItem
             {
                 Id = d.Id,
                 Name = d.Name,
-                StepId = Guid.NewGuid().ToString(),
+                GroupId = Guid.NewGuid().ToString(),
             }).ToList();
         }
         else
         {
+            //或签
             var groupId = Guid.NewGuid().ToString();
             handlerObjects = handlers.Select(d => new HandlerGroupItem
             {
                 Id = d.Id,
                 Name = d.Name,
-                StepId = groupId
+                GroupId = groupId
             }).ToList();
         }
 
@@ -58,6 +57,11 @@ public class FlowAssignInfo
     /// 办理人/办理部门(UserIds/OrgCodes)
     /// </summary>
     /// <returns></returns>
-    public List<string> GetHandlers() =>
+    public List<string> GetHandlerIds() =>
         HandlerObjects.Select(d => d.Id).ToList();
+
+    public List<IdName> GetHandlers() =>
+        HandlerObjects.Select(d => new IdName(d.Id, d.Name))
+            .Distinct()
+            .ToList();
 }

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

@@ -17,7 +17,7 @@ namespace Hotline.FlowEngine.Workflows
         /// <summary>
         /// 进行流程的开始节点
         /// </summary>
-        Task StartAsync(Workflow workflow, BasicWorkflowDto dto, StepDefine nextStepBoxDefine, FlowAssignInfo flowAssignInfo, CancellationToken cancellationToken);
+        Task StartAsync(Workflow workflow, BasicWorkflowDto dto, StepDefine startStepDefine, StepDefine firstStepDefine, FlowAssignInfo flowAssignInfo, CancellationToken cancellationToken);
 
         /// <summary>
         /// 查询工作流
@@ -93,7 +93,7 @@ namespace Hotline.FlowEngine.Workflows
         /// <summary>
         /// 查询待回访部门
         /// </summary>
-        Task<(Kv, IReadOnlyList<Kv>)> GetUnvisitOrgsAsync(string workflowId,
+        Task<(IdName, IReadOnlyList<IdName>)> GetUnvisitOrgsAsync(string workflowId,
             CancellationToken cancellationToken);
 
         /// <summary>

+ 12 - 16
src/Hotline/FlowEngine/Workflows/StepBasicEntity.cs

@@ -30,7 +30,7 @@ public abstract class StepBasicEntity : CreationEntity
     /// 采用list类型,兼容多个办理对象可以办理同一个节点的场景
     /// </summary>
     [SugarColumn(ColumnDataType = "json", IsJson = true)]
-    public List<Kv> Handlers { get; set; } = new();
+    public List<IdName> Handlers { get; set; } = new();
 
     // /// <summary>
     // /// 办理时间限制(如:24小时、7个工作日)
@@ -54,6 +54,11 @@ public abstract class StepBasicEntity : CreationEntity
     // /// </summary>
     // [SugarColumn(DefaultValue = "0")]
     // public EPathPolicy PathPolicy { get; set; }
+
+    /// <summary>
+    /// 节点期满时间
+    /// </summary>
+    public DateTime StepExpiredTime { get; set; }
     
     #region 接办
 
@@ -93,34 +98,28 @@ public abstract class StepBasicEntity : CreationEntity
     /// <summary>
     /// 办理人
     /// </summary>
-    [SugarColumn(IsNullable = true)]
     public string? HandlerId { get; set; }
 
-    [SugarColumn(IsNullable = true)]
     public string? HandlerName { get; set; }
 
     /// <summary>
     /// 办理人部门code
     /// </summary>
-    [SugarColumn(IsNullable = true)]
     public string? HandlerOrgId { get; set; }
 
     /// <summary>
     /// 办理人部门名称
     /// </summary>
-    [SugarColumn(IsNullable = true)]
     public string? HandlerOrgName { get; set; }
 
     /// <summary>
     /// 办理人部门行政区划代码
     /// </summary>
-    [SugarColumn(IsNullable = true)]
     public string? HandlerOrgAreaCode { get; set; }
 
     /// <summary>
     /// 办理人部门行政区划名称
     /// </summary>
-    [SugarColumn(IsNullable = true)]
     public string? HandlerOrgAreaName { get; set; }
 
     /// <summary>
@@ -139,7 +138,7 @@ public abstract class StepBasicEntity : CreationEntity
     /// </example>
     /// </summary>
     [SugarColumn(ColumnDataType = "json", IsJson = true)]
-    public List<Kv> NextHandlers { get; set; } = new();
+    public List<IdName> NextHandlers { get; set; } = new();
 
     /// <summary>
     /// 下一节点主办,(NextHandlers其中一个, 如果不是会签则只有一个)
@@ -151,6 +150,11 @@ public abstract class StepBasicEntity : CreationEntity
     /// </summary>
     public string NextStepCode { get; set; }
 
+    /// <summary>
+    /// 是否回到会签发起节点汇总
+    /// </summary>
+    public bool BackToCountersignSummary { get; set; }
+
     /// <summary>
     /// 是否短信通知
     /// </summary>
@@ -174,12 +178,4 @@ public abstract class StepBasicEntity : CreationEntity
     public DateTime ExpiredTime { get; set; }
 
     #endregion
-
-    #region method
-
-    public bool IsCenter() => BusinessType is EBusinessType.Center or EBusinessType.Send;
-
-    public bool IsOrg() => BusinessType is EBusinessType.Department;
-
-    #endregion
 }

+ 7 - 7
src/Hotline/FlowEngine/Workflows/Workflow.cs

@@ -402,15 +402,15 @@ public partial class Workflow
     /// <summary>
     /// 更新workflow中实际办理节点数据(节点,时间,部门)
     /// </summary>
-    public void UpdateWorkflowActualHandleInfo(WorkflowStep currentStepBox, WorkflowStep currentStep,
+    public void UpdateWorkflowActualHandleInfo(WorkflowStep currentStep,
         string actualHandlerId, string? actualHandlerName,
         string actualHandleOrgCode, string? actualHandleOrgName,
         string? actualHandleOrgAreaCode, string? actualHandleOrgAreaName)
     {
         //a.会签中分为1.之前无会签,当前节点发起会签,记录当前节点 2. 之前有会签,不更新  b.非会签中,记录当前节点
-        if (IsInCountersign() && !currentStep.HasStartedCountersign) return;
+        if (IsInCountersign() && !currentStep.HasStartedCountersign()) return;
 
-        if (currentStepBox.StepType is EStepType.Summary or EStepType.End) return;
+        if (currentStep.StepType is EStepType.Summary or EStepType.End) return;
 
         ActualHandle(currentStep,
             actualHandlerId, actualHandlerName,
@@ -533,7 +533,7 @@ public partial class Workflow
         var groupId = Guid.NewGuid().ToString();
         var handlerObjects = prevStep.Handlers.Select(d => new HandlerGroupItem
         {
-            StepId = groupId,
+            GroupId = groupId,
             Id = d.Id,
             Name = d.Name
         });
@@ -570,13 +570,13 @@ public partial class Workflow
         var orgItem = HandlerOrgs.FirstOrDefault(d => d.Id == handlerOrg);
         if (orgItem != null)
         {
-            HandlerOrgs.RemoveAll(d => d.StepId == orgItem.StepId);
+            HandlerOrgs.RemoveAll(d => d.GroupId == orgItem.GroupId);
         }
 
         var userItem = HandlerUsers.FirstOrDefault(d => d.Id == handlerId);
         if (userItem != null)
         {
-            HandlerUsers.RemoveAll(d => d.StepId == userItem.StepId);
+            HandlerUsers.RemoveAll(d => d.GroupId == userItem.GroupId);
         }
     }
 
@@ -683,7 +683,7 @@ public partial class Workflow
 /// </summary>
 public struct HandlerGroupItem
 {
-    public string StepId { get; set; }
+    public string GroupId { get; set; }
 
     public string Id { get; set; }
 

+ 79 - 13
src/Hotline/FlowEngine/Workflows/WorkflowCountersign.cs

@@ -13,6 +13,9 @@ namespace Hotline.FlowEngine.Workflows
     {
         public string WorkflowId { get; set; }
 
+
+        #region 发起会签
+
         /// <summary>
         /// 发起会签节点id(从开始节点直接发起的会签,该id和code为start节点的id与code)
         /// </summary>
@@ -24,34 +27,84 @@ namespace Hotline.FlowEngine.Workflows
         /// <summary>
         /// 业务属性
         /// </summary>
-        [SugarColumn(DefaultValue = "0")]
         public EBusinessType StartStepBusiType { get; set; }
 
+        /// <summary>
+        /// 发起人
+        /// </summary>
+        public string StarterId { get; set; }
+
+        public string StarterName { get; set; }
+
+        /// <summary>
+        /// 发起人部门code
+        /// </summary>
+        public string StarterOrgId { get; set; }
+
+        public string? StarterOrgName { get; set; }
+
+        /// <summary>
+        /// 发起人部门行政区划代码
+        /// </summary>
+        public string StarterOrgAreaCode { get; set; }
+
+        /// <summary>
+        /// 发起人部门行政区划名称
+        /// </summary>
+        public string StarterOrgAreaName { get; set; }
+
+        #endregion
+
+        #region 结束会签
+
         /// <summary>
         /// 会签汇总节点id
         /// </summary>
-        [SugarColumn(IsNullable = true)]
         public string? EndStepId { get; set; }
 
         //冗余
-        [SugarColumn(IsNullable = true)]
         public string? EndStepCode { get; set; }
 
         /// <summary>
         /// 业务属性
         /// </summary>
-        [SugarColumn(DefaultValue = "0")]
         public EBusinessType EndStepBusiType { get; set; }
 
+        /// <summary>
+        /// 接办人
+        /// </summary>
+        public string? FinisherId { get; set; }
+
+        public string? FinisherName { get; set; }
+
+        /// <summary>
+        /// 接办人部门code
+        /// </summary>
+        public string? FinisherOrgId { get; set; }
+
+        public string? FinisherOrgName { get; set; }
+
+        /// <summary>
+        /// 接办人部门行政区划代码
+        /// </summary>
+        public string? FinisherOrgAreaCode { get; set; }
+
+        /// <summary>
+        /// 接办人部门行政区划名称
+        /// </summary>
+        public string? FinisherOrgAreaName { get; set; }
+
         /// <summary>
         /// 会签结束时间
         /// </summary>
-        public DateTime? CompleteTime { get; set; }
+        public DateTime? EndTime { get; set; }
+
+
+        #endregion
 
         /// <summary>
         /// 会签嵌套会签场景记录上级会签Id
         /// </summary>
-        [SugarColumn(IsNullable = true)]
         public string? ParentId { get; set; }
 
         /// <summary>
@@ -62,28 +115,41 @@ namespace Hotline.FlowEngine.Workflows
         /// <summary>
         /// 会签类型
         /// </summary>
-        [SugarColumn(DefaultValue = "0")]
         public ECounterSignType CounterSignType { get; set; }
 
+        /// <summary>
+        /// 发起会签节点设置的期满时间(冗余)
+        /// </summary>
+        public DateTime StartExpiredTime { get; set; }
+
+
+
         #region method
 
         /// <summary>
         /// 会签是否完成(如有嵌套会签,表示下级所有会签都完成了)
         /// </summary>
-        public bool IsCompleted() => CompleteTime.HasValue;
+        public bool IsCompleted() => EndTime.HasValue;
 
         /// <summary>
         /// 结束会签(需提前确认所有子集都已结束)
         /// </summary>
-        /// <param name="endStepId"></param>
-        /// <param name="endStepCode"></param>
-        /// <param name="businessType"></param>
-        public void Complete(string endStepId, string endStepCode, EBusinessType businessType)
+        public void End(
+            string endStepId, string endStepCode, EBusinessType businessType,
+            string userId, string userName, 
+            string orgId, string orgName,
+            string orgAreaCode, string orgAreaName)
         {
             EndStepId = endStepId;
             EndStepCode = endStepCode;
             EndStepBusiType = businessType;
-            CompleteTime = DateTime.Now;
+            FinisherId = userId;
+            FinisherName = userName;
+            FinisherOrgId = orgId;
+            FinisherOrgName = orgName;
+            FinisherOrgAreaCode = orgAreaCode;
+            FinisherOrgAreaName = orgAreaName;
+            EndTime = DateTime.Now;
         }
 
         #endregion

+ 220 - 55
src/Hotline/FlowEngine/Workflows/WorkflowDomainService.cs

@@ -4,12 +4,15 @@ using Hotline.FlowEngine.WorkflowModules;
 using Hotline.Settings;
 using Hotline.Share.Dtos;
 using Hotline.Share.Dtos.FlowEngine;
+using Hotline.Share.Dtos.FlowEngine.Definition;
 using Hotline.Share.Enums.FlowEngine;
 using MapsterMapper;
 using MediatR;
 using Microsoft.Extensions.Logging;
+using System.Threading;
 using XF.Domain.Authentications;
 using XF.Domain.Dependency;
+using XF.Domain.Entities;
 using XF.Domain.Exceptions;
 using XF.Domain.Extensions;
 using XF.Domain.Repository;
@@ -87,48 +90,69 @@ namespace Hotline.FlowEngine.Workflows
         /// <summary>
         /// 流程开始
         /// </summary>
-        /// <param name="workflow"></param>
-        /// <param name="dto"></param>
-        /// <param name="cancellationToken"></param>
-        /// <returns></returns>
-        public async Task StartAsync(Workflow workflow, BasicWorkflowDto dto, StepDefine nextStepBoxDefine,
-             FlowAssignInfo flowAssignInfo, CancellationToken cancellationToken)
+        public async Task StartAsync(Workflow workflow, BasicWorkflowDto dto, StepDefine startStepDefine,
+            StepDefine firstStepDefine, FlowAssignInfo flowAssignInfo, CancellationToken cancellationToken)
         {
-            //创建开始节点
-            var (startStepBox, startStep, firstStepBox) = await CreateStartAndFirstStepAsync(workflow, dto, cancellationToken);
+            //todo 1. 创建开始节点,first节点 (和trace)2.办理开始节点 
 
-            //1.创建开始节点trace 2.创建firstStep(开始节点的下一个节点),办理firstStep 3.创建sec节点
-            //办理firstStep
-            var firstStep = firstStepBox.Steps.First();
-            var counterSignType = GetCounterSignType(firstStep.BusinessType);
+            //startstep
+            var nextSteps = _mapper.Map<List<StepSimple>>(startStepDefine.NextSteps);
+            nextSteps.First(d => d.Code == dto.NextStepCode).Selected = true;
+            var startStep = new WorkflowStep
+            {
+                WorkflowId = workflow.Id,
+                Handlers = new List<IdName> { new(_sessionContext.RequiredUserId, _sessionContext.UserName) },
+                NextSteps = nextSteps,
+                IsMain = true,
+                Status = EWorkflowStepStatus.Handling,
+            };
+            _mapper.Map(workflow.WorkflowDefinition, startStep);
+            await _workflowStepRepository.AddAsync(startStep, cancellationToken);
+
+            //starttrace
+            var startTrace = _mapper.Map<WorkflowTrace>(startStep);
+            startTrace.StepId = startStep.Id;
+            startTrace.Status = EWorkflowTraceStatus.Normal;
+            await _workflowTraceRepository.AddAsync(startTrace, cancellationToken);
 
-            await HandleStepAsync(workflow, dto, firstStepBox, firstStep, counterSignType, cancellationToken);
+            //firststep
+            var firstStep = new WorkflowStep
+            {
+                WorkflowId = workflow.Id,
+                Handlers = flowAssignInfo.GetHandlers(),
+                NextSteps = _mapper.Map<List<StepSimple>>(firstStepDefine.NextSteps),
+                Status = EWorkflowStepStatus.WaitForAccept
+            };
+            _mapper.Map(workflow.WorkflowDefinition, firstStep);
+            await _workflowStepRepository.AddAsync(firstStep, cancellationToken);
+
+            //firsttrace
+            var firstTrace = _mapper.Map<WorkflowTrace>(firstStep);
+            firstTrace.StepId = firstStep.Id;
+            firstTrace.Status = EWorkflowTraceStatus.Normal;
+            await _workflowTraceRepository.AddAsync(firstTrace, cancellationToken);
 
-            await _workflowStepRepository.UpdateRangeAsync(new List<WorkflowStep> { firstStepBox, firstStep }, cancellationToken);
+            //办理开始节点
+            var counterSignType = GetCounterSignType(firstStep.BusinessType);
 
-            //firstStep trace
-            await NextTraceAsync(workflow, dto, firstStep, cancellationToken);
+            await HandleStepAsync(startStep, firstStepDefine, workflow, dto, counterSignType, cancellationToken);
 
-            //secondStep
-            var secondStepDefine = workflow.WorkflowDefinition.FindStepDefine(dto.NextStepCode);
-            var secondStepBox = await CreateStepAsync(workflow, secondStepDefine, dto,
-                EWorkflowStepStatus.Created, firstStepBox, firstStep, EWorkflowTraceStatus.Normal,
-                workflow.ExpiredTime, cancellationToken);
+            await _workflowStepRepository.UpdateAsync(startStep, cancellationToken);
 
             //更新实际办理节点信息
-            workflow.UpdateWorkflowActualHandleInfo(firstStepBox, firstStep,
+            workflow.UpdateWorkflowActualHandleInfo(startStep,
                 _sessionContext.RequiredUserId, _sessionContext.UserName,
-                 _sessionContext.RequiredOrgId, _sessionContext.OrgName,
+                _sessionContext.RequiredOrgId, _sessionContext.OrgName,
                 _sessionContext.OrgAreaCode, _sessionContext.OrgAreaName);
 
             //todo 计算办理工作时长
 
             //更新当前办理节点信息
-            workflow.UpdateWorkflowCurrentStepInfo(dto.IsStartCountersign, firstStep, secondStepBox.Steps.First());
+            workflow.UpdateWorkflowCurrentStepInfo(dto.IsStartCountersign, startStep, firstStep);
 
             //发起会签时记录顶层会签节点(必须在update currentStep之后)
             if (dto.IsStartCountersign && !workflow.IsInCountersign())
-                workflow.StartCountersign(firstStepBox.Code, counterSignType);
+                workflow.StartCountersign(startStep.Code, counterSignType);
 
             //更新受理人信息
             workflow.UpdateAcceptor(
@@ -139,22 +163,21 @@ namespace Hotline.FlowEngine.Workflows
                 _sessionContext.OrgName);
 
             workflow.UpdateHandlers(_sessionContext.RequiredUserId, _sessionContext.RequiredOrgId,
-            flowAssignInfo.FlowAssignType, flowAssignInfo.HandlerObjects);
+                flowAssignInfo.FlowAssignType, flowAssignInfo.HandlerObjects);
 
             //更新指派信息
-            workflow.Assign(flowAssignInfo.FlowAssignType, flowAssignInfo.GetHandlers());
+            workflow.Assign(flowAssignInfo.FlowAssignType, flowAssignInfo.GetHandlerIds());
 
             await _workflowRepository.UpdateAsync(workflow, cancellationToken);
 
             //publish
             await _mediator.Publish(new StartWorkflowNotify(workflow, dto, flowAssignInfo), cancellationToken);
-
         }
-
+        
         public async Task<Workflow> GetWorkflowAsync(string workflowId,
             bool withDefine = false, bool withSteps = false,
             bool withTraces = false, bool withSupplements = false,
-            bool withAssigns = false, bool withCountersigns = false,
+            bool withCountersigns = false,
             CancellationToken cancellationToken = default)
         {
             var query = _workflowRepository.Queryable().Where(d => d.Id == workflowId);
@@ -199,10 +222,10 @@ namespace Hotline.FlowEngine.Workflows
         /// 查询工作流包含当前用户办理权限(是否可办理)
         /// </summary>
         public async Task<(Workflow, bool)> GetWorkflowHandlePermissionAsync(string workflowId, string userId, string orgCode, bool withDefine = false,
-            bool withSteps = false, bool withTraces = false, bool withSupplements = false, bool withAssigns = false,
+            bool withSteps = false, bool withTraces = false, bool withSupplements = false,
             bool withCountersigns = false, CancellationToken cancellationToken = default)
         {
-            var workflow = await GetWorkflowAsync(workflowId, withDefine, withSteps, withTraces, withSupplements, withAssigns, withCountersigns, cancellationToken);
+            var workflow = await GetWorkflowAsync(workflowId, withDefine, withSteps, withTraces, withSupplements, withCountersigns, cancellationToken);
             var canHandle = workflow.CanHandle(userId, orgCode);
             return (workflow, canHandle);
         }
@@ -260,11 +283,67 @@ namespace Hotline.FlowEngine.Workflows
             await _mediator.Publish(new AcceptWorkflowNotify(workflow), cancellationToken);
         }
 
+        //new
+        public async Task AcceptAsync(Workflow workflow, string userId, string userName, string orgCode, string orgName,
+            CancellationToken cancellationToken)
+        {
+            if (!workflow.CanHandle(_sessionContext.RequiredUserId, _sessionContext.RequiredOrgId)) return;
+            //工单完成以后查看的场景
+            if (workflow.Status != EWorkflowStatus.Runnable) return;
+
+            var currentStep = GetUnHandleStep(workflow.Steps, _sessionContext.RequiredOrgId,
+                _sessionContext.RequiredUserId);
+            if (currentStep.Status is not EWorkflowStepStatus.WaitForAccept) return;
+
+            //var (currentStepBox, currentStep) = GetUnCompleteStepOrDefault(workflow.Steps, orgCode, userId);
+
+            //
+            if (currentStep.HandlerType is EHandlerType.AssignedUser or EHandlerType.Role)
+            {
+                //userId
+                if (currentStep.Handlers.All(d => d.Id != userId)) return;
+            }
+            else
+            {
+                //orgId
+                if (currentStep.Handlers.All(d => d.Id != orgCode)) return;
+            }
+            if (currentStep.StepType is EStepType.End)
+                throw new UserFriendlyException("当前流程已流转到最终步骤");
+
+            var changedSteps = new List<WorkflowStep> { currentStep };
+            if (currentStepBox.Status is EWorkflowStepStatus.WaitForAccept)
+            {
+                currentStepBox.Status = EWorkflowStepStatus.Handling;
+                changedSteps.Add(currentStepBox);
+            }
+            currentStep.Accept(userId, userName, _sessionContext.RequiredOrgId, _sessionContext.OrgName);
+
+            //接办时非会签并且有多个接办部门时需更新接办部门
+            if (!workflow.IsInCountersign())
+            {
+                var assigns = await _workflowAssignRepository.QueryAsync(d => d.WorkflowId == workflow.Id);
+                if (assigns.Count > 1)
+                {
+                    await _workflowAssignRepository.RemoveRangeAsync(assigns, cancellationToken);
+
+                    var assign = WorkflowAssign.Create(workflow.Id, orgCode, orgName);
+                    await _workflowAssignRepository.AddAsync(assign, cancellationToken);
+                }
+            }
+
+            await _workflowStepRepository.UpdateRangeAsync(changedSteps, cancellationToken);
+
+            await AcceptTraceAsync(workflow, currentStep, cancellationToken);
+
+            await _mediator.Publish(new AcceptWorkflowNotify(workflow), cancellationToken);
+        }
+
         /// <summary>
         /// 办理(流转至下一节点)
         /// </summary>
         public async Task NextAsync(Workflow workflow, NextWorkflowDto dto, StepDefine nextStepBoxDefine,
-            FlowAssignInfo flowAssignInfo, DateTime expiredTime, CancellationToken cancellationToken)
+        FlowAssignInfo flowAssignInfo, DateTime expiredTime, CancellationToken cancellationToken)
         {
             ValidatePermission(workflow);
             CheckWhetherRunnable(workflow.Status);
@@ -309,7 +388,7 @@ namespace Hotline.FlowEngine.Workflows
                     updateSteps.Add(countersignStartStep);
 
                     //结束会签
-                    currentCountersign.Complete(currentStep.Id, currentStep.Code, currentStep.BusinessType);
+                    currentCountersign.End(currentStep.Id, currentStep.Code, currentStep.BusinessType);
                     await _workflowCountersignRepository.UpdateAsync(currentCountersign, cancellationToken);
                 }
             }
@@ -392,7 +471,7 @@ namespace Hotline.FlowEngine.Workflows
                 workflow.StartCountersign(currentStepBox.Code, counterSignType);
 
             //更新指派信息
-            workflow.Assign(flowAssignInfo.FlowAssignType, flowAssignInfo.GetHandlers());
+            workflow.Assign(flowAssignInfo.FlowAssignType, flowAssignInfo.GetHandlerIds());
 
             await _workflowRepository.UpdateAsync(workflow, cancellationToken);
 
@@ -668,24 +747,24 @@ namespace Hotline.FlowEngine.Workflows
         /// <summary>
         /// 查询当前待办理节点
         /// </summary>
-        public WorkflowStep FindCurrentStepWaitForHandle(Workflow workflow) => 
+        public WorkflowStep FindCurrentStepWaitForHandle(Workflow workflow) =>
             GetUnHandleStep(workflow.Steps, _sessionContext.RequiredOrgId, _sessionContext.RequiredUserId);
 
         /// <summary>
         /// 查询待回访部门
         /// </summary>
         /// <returns></returns>
-        public async Task<(Kv, IReadOnlyList<Kv>)> GetUnvisitOrgsAsync(string workflowId, CancellationToken cancellationToken)
+        public async Task<(IdName, IReadOnlyList<IdName>)> GetUnvisitOrgsAsync(string workflowId, CancellationToken cancellationToken)
         {
             var workflow = await GetWorkflowAsync(workflowId, withSteps: true, cancellationToken: cancellationToken);
             if (workflow.CounterSignType is not ECounterSignType.Center)
-                return new(new Kv(workflow.ActualHandleOrgCode, workflow.ActualHandleOrgName), new List<Kv>());
+                return new(new IdName(workflow.ActualHandleOrgCode, workflow.ActualHandleOrgName), new List<IdName>());
             var steps = workflow.Steps
                 .Where(d => d.StepType is EStepType.Normal && d.BusinessType is EBusinessType.Department)
                 .SelectMany(d => d.Steps)
                 .ToList();
-            var items = steps.Select(d => new Kv(d.HandlerOrgId, d.HandlerOrgName)).ToList();
-            return (new Kv(workflow.ActualHandleOrgCode, workflow.ActualHandleOrgName), items);
+            var items = steps.Select(d => new IdName(d.HandlerOrgId, d.HandlerOrgName)).ToList();
+            return (new IdName(workflow.ActualHandleOrgCode, workflow.ActualHandleOrgName), items);
         }
 
         /// <summary>
@@ -717,7 +796,7 @@ namespace Hotline.FlowEngine.Workflows
                         var upperLevel = (--currentOrgLevel).ToString();
                         nextStepDefines = nextStepDefines
                             .Where(d => d.HandlerType is EHandlerType.OrgLevel &&
-                                        d.HandlerTypeItems.Any(x => x.Key == upperLevel))
+                                        d.HandlerTypeItems.Any(x => x.Id == upperLevel))
                             .ToList();
                     }
 
@@ -820,6 +899,41 @@ namespace Hotline.FlowEngine.Workflows
             //stepBox办理状态
             currentStepBox.CheckStepBoxStatusAndUpdate();
         }
+        //new
+        private async Task HandleStepAsync(WorkflowStep step, StepDefine nextStepDefine, Workflow workflow, BasicWorkflowDto dto,
+             ECounterSignType counterSignType, CancellationToken cancellationToken)
+        {
+            if (step.Status is EWorkflowStepStatus.Handled)
+                throw UserFriendlyException.SameMessage("当前节点状态已办理");
+
+            if (step.StepType is EStepType.End)
+                throw new UserFriendlyException("当前流程已流转到最终步骤");
+
+            //创建会签数据
+            if (dto.IsStartCountersign)
+            {
+                //if (!step.CanStartCountersign)
+                //    throw new UserFriendlyException($"当前节点不支持发起会签, workflowId:{workflow.Id}, stepId:{step.Id}", "当前节点不支持发起会签");
+                ////即使当前节点支持发起会签,但下一节点为信息汇总节点、结束节点时也不可发起会签
+                //if(nextStepDefine.StepType is EStepType.Summary or EStepType.End)
+                //    throw new UserFriendlyException($"下一节点不允许发起会签, workflowId:{workflow.Id}, stepId:{step.Id}", "下一节点不允许发起会签");
+                ////下一节点是会签汇总节点也不允许发起会签
+                //if (dto.BackToCountersignSummary)
+                //    throw new UserFriendlyException();
+
+                await StartCountersignAsync(workflow, step, dto, counterSignType, cancellationToken);
+            }
+
+            //办理参数
+            _mapper.Map(dto, step);
+
+            //step办理状态
+            step.Handled(_sessionContext.RequiredUserId, _sessionContext.UserName,
+                _sessionContext.RequiredOrgId, _sessionContext.OrgName,
+                _sessionContext.OrgAreaCode, _sessionContext.OrgAreaName,
+                dto.NextStepCode);
+        }
+
 
         /// <summary>
         /// 开始会签(创建会签数据,更新currentStep会签数据)
@@ -833,6 +947,15 @@ namespace Hotline.FlowEngine.Workflows
             currentStep.StartCountersign(countersign.Id);
         }
 
+        private async Task StartCountersignAsync(Workflow workflow, WorkflowStep startStep, BasicWorkflowDto dto, ECounterSignType counterSignType, CancellationToken cancellationToken)
+        {
+            var countersign = await CreateCountersignAsync(
+                workflow.Id, startStep, dto.NextHandlers.Count,
+                counterSignType, startStep.StepExpiredTime,
+                startStep.CountersignId, cancellationToken);
+            startStep.StartCountersign(countersign.Id);
+        }
+
         /// <summary>
         /// 检查是否从中心流转至部门
         /// </summary>
@@ -928,8 +1051,10 @@ namespace Hotline.FlowEngine.Workflows
         }
 
         private async Task<WorkflowCountersign> CreateCountersignAsync(
-            string workflowId, WorkflowStep startStep, string endStepCode, int count,
-            ECounterSignType counterSignType, string? parentId = null, CancellationToken cancellationToken = default)
+            string workflowId, WorkflowStep startStep, int memberCount,
+            ECounterSignType counterSignType, DateTime stepExpiredTime,
+            string? parentId = null,
+            CancellationToken cancellationToken = default)
         {
             var countersign = new WorkflowCountersign
             {
@@ -937,10 +1062,18 @@ namespace Hotline.FlowEngine.Workflows
                 StartStepId = startStep.Id,
                 StartStepCode = startStep.Code,
                 StartStepBusiType = startStep.BusinessType,
-                EndStepCode = endStepCode,
-                Members = count,
+
+                StarterId = _sessionContext.RequiredUserId,
+                StarterName = _sessionContext.UserName,
+                StarterOrgId = _sessionContext.RequiredOrgId,
+                StarterOrgName = _sessionContext.OrgName,
+                StarterOrgAreaCode = _sessionContext.OrgAreaCode,
+                StarterOrgAreaName = _sessionContext.OrgAreaName,
+
                 ParentId = parentId,
+                Members = memberCount,
                 CounterSignType = counterSignType,
+                StartExpiredTime = stepExpiredTime,
             };
             await _workflowCountersignRepository.AddAsync(countersign, cancellationToken);
             return countersign;
@@ -1188,7 +1321,7 @@ namespace Hotline.FlowEngine.Workflows
 
 
             //start节点的办理人分类默认为用户,即为当前发起流程的操作员
-            var handler = new Kv { Id = _sessionContext.RequiredUserId, Name = _sessionContext.UserName };
+            var handler = new IdName { Id = _sessionContext.RequiredUserId, Name = _sessionContext.UserName };
 
             //开始节点的下一个节点(工单业务:话务员节点)
             var firstStepCode = workflow.WorkflowDefinition.FindStartStepDefine().NextSteps.First().Code;
@@ -1218,7 +1351,7 @@ namespace Hotline.FlowEngine.Workflows
             var stepBox = CreateStepBox(workflow.Id, endStepDefine, prevStepBox.Id);
             await _workflowStepRepository.AddAsync(stepBox, cancellationToken);
 
-            var handler = new Kv { Id = _sessionContext.RequiredUserId, Name = _sessionContext.UserName };
+            var handler = new IdName { Id = _sessionContext.RequiredUserId, Name = _sessionContext.UserName };
             var step = await CreateEndSubStepAsync(handler, stepBox, prevStep, cancellationToken);
 
             //end trace
@@ -1272,14 +1405,14 @@ namespace Hotline.FlowEngine.Workflows
         }
 
         private async Task<WorkflowStep> CreateStartSubStepAsync(
-            Kv handler,
+            IdName handler,
             string nextStepCode,
             WorkflowStep stepBox,
             BasicWorkflowDto dto,
             CancellationToken cancellationToken)
         {
             //开始节点既不发起会签,也不处于会签中
-            var subStep = CreateSubStep(stepBox, new List<Kv> { handler }, nextStepCode, null,
+            var subStep = CreateSubStep(stepBox, new List<IdName> { handler }, nextStepCode, null,
                 null, null, EWorkflowStepStatus.Handled, EStepCountersignStatus.None, DateTime.Today,
                 _mapper.Map<StepExtension>(dto.Extension));
             subStep.Accept(_sessionContext.RequiredUserId, _sessionContext.UserName,
@@ -1298,12 +1431,12 @@ namespace Hotline.FlowEngine.Workflows
         }
 
         private async Task<WorkflowStep> CreateEndSubStepAsync(
-            Kv handler,
+            IdName handler,
             WorkflowStep currentStepBox,
             WorkflowStep prevStep,
             CancellationToken cancellationToken)
         {
-            var subStep = CreateSubStep(currentStepBox, new List<Kv> { handler }, null, null, prevStep.Id,
+            var subStep = CreateSubStep(currentStepBox, new List<IdName> { handler }, null, null, prevStep.Id,
                 null, EWorkflowStepStatus.Handled, EStepCountersignStatus.None, DateTime.Today, new());
             subStep.Accept(_sessionContext.RequiredUserId, _sessionContext.UserName, _sessionContext.RequiredOrgId,
                 _sessionContext.OrgName);
@@ -1377,6 +1510,7 @@ namespace Hotline.FlowEngine.Workflows
             return (stepBox, step);
         }
 
+        //new
         private WorkflowStep GetUnHandleStep(List<WorkflowStep> steps, string orgCode, string userId)
         {
             var step = GetStep(steps, orgCode, userId, d => d != EWorkflowStepStatus.Handled);
@@ -1405,9 +1539,10 @@ namespace Hotline.FlowEngine.Workflows
         //    return new();
         //}
 
+        //new
         private WorkflowStep? GetStep(List<WorkflowStep> steps, string orgCode, string userId, Func<EWorkflowStepStatus, bool> predicate) =>
             steps.FirstOrDefault(d =>
-                predicate(d.Status) && d.Handlers.Any(x => x.Key == orgCode || x.Key == userId));
+                predicate(d.Status) && d.Handlers.Any(x => x.Id == orgCode || x.Id == userId));
 
         private WorkflowStep CreateStepBox(string workflowId, StepDefine stepDefine, string prevStepBoxId)
         {
@@ -1424,7 +1559,7 @@ namespace Hotline.FlowEngine.Workflows
         private List<WorkflowStep> CreateSubSteps(
             bool isPrevStartCountersign,
             WorkflowStep stepBox,
-            List<Kv> handlers,
+            List<IdName> handlers,
             string nextStepCode,
             string? nextMainHandler,
             string? prevStepId,
@@ -1445,7 +1580,7 @@ namespace Hotline.FlowEngine.Workflows
             {
                 foreach (var handler in handlers)
                 {
-                    var step = CreateSubStep(stepBox, new List<Kv> { handler }, nextStepCode, nextMainHandler,
+                    var step = CreateSubStep(stepBox, new List<IdName> { handler }, nextStepCode, nextMainHandler,
                         prevStepId, countersignId, stepStatus, countersignStatus, expiredTime, extension);
 
                     steps.Add(step);
@@ -1464,7 +1599,7 @@ namespace Hotline.FlowEngine.Workflows
 
         private WorkflowStep CreateSubStep(
             WorkflowStep stepBox,
-            List<Kv> handlers,
+            List<IdName> handlers,
             string nextStepCode,
             string? nextMainHandler,
             string? prevStepId,
@@ -1520,6 +1655,36 @@ namespace Hotline.FlowEngine.Workflows
             };
         }
 
+        //////////  old new line
+
+        private WorkflowStep CreateStep(
+            string workflowId,
+            List<IdName> handlers,
+            List<StepSimple> nextSteps,
+            WorkflowStep? prevStep,
+            string mainHandler,
+            string? parentId,
+            string? countersignId,
+            EStepCountersignStatus stepCountersignStatus,
+            bool isCountersignSummaryStep,
+            string startCountersignStepId
+            )
+        {
+            var isMain = handlers.Count == 1 || handlers.Any(d => d.Id == mainHandler);
+            var step = new WorkflowStep
+            {
+                WorkflowId = workflowId,
+                Handlers = handlers,
+                NextSteps = nextSteps,
+                PreviousId = prevStep?.Id,
+                PreviousCode = prevStep?.Code,
+                IsMain = isMain,
+                Status = EWorkflowStepStatus.WaitForAccept,
+                ParentId = parentId,
+
+            };
+        }
+
         #endregion
 
     }

+ 17 - 17
src/Hotline/FlowEngine/Workflows/WorkflowDomainService1.cs

@@ -141,7 +141,7 @@ namespace Hotline.FlowEngine.Workflows
             flowAssignInfo.FlowAssignType, flowAssignInfo.HandlerObjects);
 
             //更新指派信息
-            workflow.Assign(flowAssignInfo.FlowAssignType, flowAssignInfo.GetHandlers());
+            workflow.Assign(flowAssignInfo.FlowAssignType, flowAssignInfo.GetHandlerIds());
 
             await _workflowRepository.UpdateAsync(workflow, cancellationToken);
 
@@ -306,7 +306,7 @@ namespace Hotline.FlowEngine.Workflows
                     updateSteps.Add(countersignStartStep);
 
                     //结束会签
-                    currentCountersign.Complete(currentStep.Id, currentStep.Code, currentStep.BusinessType);
+                    currentCountersign.End(currentStep.Id, currentStep.Code, currentStep.BusinessType);
                     await _workflowCountersignRepository.UpdateAsync(currentCountersign, cancellationToken);
                 }
             }
@@ -389,7 +389,7 @@ namespace Hotline.FlowEngine.Workflows
                 workflow.StartCountersign(currentStepBox.Code, counterSignType);
 
             //更新指派信息
-            workflow.Assign(flowAssignInfo.FlowAssignType, flowAssignInfo.GetHandlers());
+            workflow.Assign(flowAssignInfo.FlowAssignType, flowAssignInfo.GetHandlerIds());
 
             await _workflowRepository.UpdateAsync(workflow, cancellationToken);
 
@@ -666,17 +666,17 @@ namespace Hotline.FlowEngine.Workflows
         /// 查询待回访部门
         /// </summary>
         /// <returns></returns>
-        public async Task<(Kv, IReadOnlyList<Kv>)> GetUnvisitOrgsAsync(string workflowId, CancellationToken cancellationToken)
+        public async Task<(IdName, IReadOnlyList<IdName>)> GetUnvisitOrgsAsync(string workflowId, CancellationToken cancellationToken)
         {
             var workflow = await GetWorkflowAsync(workflowId, withSteps: true, cancellationToken: cancellationToken);
             if (workflow.CounterSignType is not ECounterSignType.Center)
-                return new(new Kv(workflow.ActualHandleOrgCode, workflow.ActualHandleOrgName), new List<Kv>());
+                return new(new IdName(workflow.ActualHandleOrgCode, workflow.ActualHandleOrgName), new List<IdName>());
             var steps = workflow.Steps
                 .Where(d => d.StepType is EStepType.Normal && d.BusinessType is EBusinessType.Department)
                 .SelectMany(d => d.Steps)
                 .ToList();
-            var items = steps.Select(d => new Kv(d.HandlerOrgId, d.HandlerOrgName)).ToList();
-            return (new Kv(workflow.ActualHandleOrgCode, workflow.ActualHandleOrgName), items);
+            var items = steps.Select(d => new IdName(d.HandlerOrgId, d.HandlerOrgName)).ToList();
+            return (new IdName(workflow.ActualHandleOrgCode, workflow.ActualHandleOrgName), items);
         }
 
         /// <summary>
@@ -708,7 +708,7 @@ namespace Hotline.FlowEngine.Workflows
                         var upperLevel = (--currentOrgLevel).ToString();
                         nextStepDefines = nextStepDefines
                             .Where(d => d.HandlerType is EHandlerType.OrgLevel &&
-                                        d.HandlerTypeItems.Any(x => x.Key == upperLevel))
+                                        d.HandlerTypeItems.Any(x => x.Id == upperLevel))
                             .ToList();
                     }
 
@@ -1178,7 +1178,7 @@ namespace Hotline.FlowEngine.Workflows
 
 
             //start节点的办理人分类默认为用户,即为当前发起流程的操作员
-            var handler = new Kv { Id = _sessionContext.RequiredUserId, Name = _sessionContext.UserName };
+            var handler = new IdName { Id = _sessionContext.RequiredUserId, Name = _sessionContext.UserName };
 
             //开始节点的下一个节点(工单业务:话务员节点)
             var firstStepCode = workflow.WorkflowDefinition.FindStartStepDefine().NextSteps.First().Code;
@@ -1208,7 +1208,7 @@ namespace Hotline.FlowEngine.Workflows
             var stepBox = CreateStepBox(workflow.Id, endStepDefine, prevStepBox.Id);
             await _workflowStepRepository.AddAsync(stepBox, cancellationToken);
 
-            var handler = new Kv { Id = _sessionContext.RequiredUserId, Name = _sessionContext.UserName };
+            var handler = new IdName { Id = _sessionContext.RequiredUserId, Name = _sessionContext.UserName };
             var step = await CreateEndSubStepAsync(handler, stepBox, prevStep, cancellationToken);
 
             //end trace
@@ -1262,14 +1262,14 @@ namespace Hotline.FlowEngine.Workflows
         }
 
         private async Task<WorkflowStep> CreateStartSubStepAsync(
-            Kv handler,
+            IdName handler,
             string nextStepCode,
             WorkflowStep stepBox,
             BasicWorkflowDto dto,
             CancellationToken cancellationToken)
         {
             //开始节点既不发起会签,也不处于会签中
-            var subStep = CreateSubStep(stepBox, new List<Kv> { handler }, nextStepCode, null,
+            var subStep = CreateSubStep(stepBox, new List<IdName> { handler }, nextStepCode, null,
                 null, null, EWorkflowStepStatus.Handled, EStepCountersignStatus.None, DateTime.Today,
                 _mapper.Map<StepExtension>(dto.Extension));
             subStep.Accept(_sessionContext.RequiredUserId, _sessionContext.UserName,
@@ -1288,12 +1288,12 @@ namespace Hotline.FlowEngine.Workflows
         }
 
         private async Task<WorkflowStep> CreateEndSubStepAsync(
-            Kv handler,
+            IdName handler,
             WorkflowStep currentStepBox,
             WorkflowStep prevStep,
             CancellationToken cancellationToken)
         {
-            var subStep = CreateSubStep(currentStepBox, new List<Kv> { handler }, null, null, prevStep.Id,
+            var subStep = CreateSubStep(currentStepBox, new List<IdName> { handler }, null, null, prevStep.Id,
                 null, EWorkflowStepStatus.Handled, EStepCountersignStatus.None, DateTime.Today, new());
             subStep.Accept(_sessionContext.RequiredUserId, _sessionContext.UserName, _sessionContext.RequiredOrgId,
                 _sessionContext.OrgName);
@@ -1400,7 +1400,7 @@ namespace Hotline.FlowEngine.Workflows
         private List<WorkflowStep> CreateSubSteps(
             bool isPrevStartCountersign,
             WorkflowStep stepBox,
-            List<Kv> handlers,
+            List<IdName> handlers,
             string nextStepCode,
             string? nextMainHandler,
             string? prevStepId,
@@ -1421,7 +1421,7 @@ namespace Hotline.FlowEngine.Workflows
             {
                 foreach (var handler in handlers)
                 {
-                    var step = CreateSubStep(stepBox, new List<Kv> { handler }, nextStepCode, nextMainHandler,
+                    var step = CreateSubStep(stepBox, new List<IdName> { handler }, nextStepCode, nextMainHandler,
                         prevStepId, countersignId, stepStatus, countersignStatus, expiredTime, extension);
 
                     steps.Add(step);
@@ -1440,7 +1440,7 @@ namespace Hotline.FlowEngine.Workflows
 
         private WorkflowStep CreateSubStep(
             WorkflowStep stepBox,
-            List<Kv> handlers,
+            List<IdName> handlers,
             string nextStepCode,
             string? nextMainHandler,
             string? prevStepId,

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

@@ -32,7 +32,7 @@ public class WorkflowStep : StepBasicEntity
     #region 会签
 
     /// <summary>
-    /// 会签流程生命周期上级节点Id
+    /// 会签流程生命周期上级节点Id(会签流程中的节点才有)
     /// </summary>
     public string? ParentId { get; set; }
 
@@ -147,9 +147,14 @@ public class WorkflowStep : StepBasicEntity
     /// </remarks>
     /// </summary>
     public string? TerminalDynamicMark { get; set; }
-    
+
     #endregion
 
+    /// <summary>
+    /// 标签
+    /// </summary>
+    public string? Tag { get; set; }
+
     #endregion
 
     #region Method
@@ -244,6 +249,10 @@ public class WorkflowStep : StepBasicEntity
         return IsInCountersign ? EStepCountersignStatus.OuterCountersign : EStepCountersignStatus.None;
     }
 
+    public bool IsCenter() => BusinessType is EBusinessType.Center or EBusinessType.Send;
+
+    public bool IsOrg() => BusinessType is EBusinessType.Department;
+
     /// <summary>
     /// 重置节点,只清除办理痕迹(退回场景,将上一级节点重置等待重新办理)
     /// </summary>
@@ -312,6 +321,10 @@ next:
   check该不该当前用户办(handlers,all不需要check)
   办理
   创建后面节点
+    是否动态实例化
+      t:检查是否到结束标识
+        t:按配置创建
+        f:按动态实例化策略创建
     是否处于会签中 
       t: 是否返回上一级汇总 (isCSEnd)
         t:  1.update上一级的会签完成情况[] 2.如果会签全完成,创建新的待办汇总节点?更新发起节点状态?thk

+ 1 - 0
src/Hotline/FlowEngine/Workflows/WorkflowTrace.cs

@@ -30,6 +30,7 @@ public class WorkflowTrace : StepBasicEntity
     /// </summary>
     [SugarColumn(IsIgnore = true)]
     public List<WorkflowTrace> Traces { get; set; }
+    
 
     #region method
 

+ 4 - 4
src/Hotline/KnowledgeBase/KnowledgeDomainService.cs

@@ -110,7 +110,7 @@ namespace Hotline.KnowledgeBase
             if (knowledgeFlow == null)
                 throw new UserFriendlyException($"无效知识编号, knowledgeFlowId: {knowledgeFlowId}", "无效知识编号");
             knowledgeFlow.WorkflowId = workFlowId;
-            knowledgeFlow.Assign(assignInfo.FlowAssignType, assignInfo.GetHandlers());
+            knowledgeFlow.Assign(assignInfo.FlowAssignType, assignInfo.GetHandlerIds());
             await _knowledgeWorkFlowRepository.UpdateAsync(knowledgeFlow, cancellationToken);
 
             if (knowledgeFlow.WorkflowModuleStatus != EKnowledgeApplyType.Delete)
@@ -120,7 +120,7 @@ namespace Hotline.KnowledgeBase
                     throw new UserFriendlyException($"无效知识编号, knowledgeId: {knowledgeFlow.KnowledgeId}", "无效知识编号");
                 knowledge.WorkflowId = workFlowId;
                 knowledge.Status = EKnowledgeStatus.Auditing;
-                knowledge.Assign(assignInfo.FlowAssignType, assignInfo.GetHandlers());
+                knowledge.Assign(assignInfo.FlowAssignType, assignInfo.GetHandlerIds());
                 await _knowledgeRepository.UpdateAsync(knowledge, cancellationToken);
             }
         }
@@ -140,14 +140,14 @@ namespace Hotline.KnowledgeBase
             if (knowledgeFlow == null)
                 throw new UserFriendlyException($"无效知识编号, knowledgeFlowId: {knowledgeFlowId}", "指派失败");
 
-            knowledgeFlow.Assign(assignInfo.FlowAssignType, assignInfo.GetHandlers());
+            knowledgeFlow.Assign(assignInfo.FlowAssignType, assignInfo.GetHandlerIds());
             await _knowledgeWorkFlowRepository.UpdateAsync(knowledgeFlow, cancellationToken);
 
             //知识主表
             var knowledge = await _knowledgeRepository.GetAsync(p => p.WorkflowId == knowledgeFlow.WorkflowId, cancellationToken);
             if (knowledge != null)
             {
-                knowledge.Assign(assignInfo.FlowAssignType, assignInfo.GetHandlers());
+                knowledge.Assign(assignInfo.FlowAssignType, assignInfo.GetHandlerIds());
                 await _knowledgeRepository.UpdateAsync(knowledge, cancellationToken);
             }
         }

+ 1 - 1
src/Hotline/Orders/OrderComplain.cs

@@ -43,7 +43,7 @@ public class OrderComplain : OrderExtensionEntity
     /// 诉求内容,多选
     /// </summary>
     [SugarColumn(ColumnDataType = "json", IsJson = true)]
-    public List<Kv> ComplainTypes { get; set; } = new();
+    public List<IdName> ComplainTypes { get; set; } = new();
 
     #endregion
 }

+ 1 - 1
src/Hotline/Orders/OrderVisit.cs

@@ -85,6 +85,6 @@ public class OrderVisit : CreationEntity
     /// 当前评价结果
     /// </summary>
     [SugarColumn(ColumnDataType = "json", IsJson = true, IsNullable = true)]
-    public Kv? NowEvaluate { get; set; }
+    public IdName? NowEvaluate { get; set; }
 
 }

+ 3 - 3
src/Hotline/Orders/OrderVisitDetail.cs

@@ -32,19 +32,19 @@ namespace Hotline.Orders
         /// 部门办件结果
         /// </summary>
         [SugarColumn(ColumnDataType = "json", IsJson = true, IsNullable = true)]
-        public Kv? OrgProcessingResults { get; set; }
+        public IdName? OrgProcessingResults { get; set; }
 
         /// <summary>
         /// 不满意原因
         /// </summary>
         [SugarColumn(ColumnDataType = "json", IsJson = true, IsNullable = true)]
-        public List<Kv>? OrgNoSatisfiedReason { get; set; }
+        public List<IdName>? OrgNoSatisfiedReason { get; set; }
 
         /// <summary>
         /// 部门办件态度
         /// </summary>
         [SugarColumn(ColumnDataType = "json", IsJson = true, IsNullable = true)]
-        public Kv? OrgHandledAttitude { get; set; }
+        public IdName? OrgHandledAttitude { get; set; }
 
         /// <summary>
         /// 回访内容

+ 6 - 6
src/Hotline/SeedData/Codes/ICode.cs

@@ -9,30 +9,30 @@ namespace Hotline.SeedData.Codes
     /// </summary>
     public interface ICode
     {
-        IReadOnlyList<Kv> GetAll<TThis>();
+        IReadOnlyList<IdName> GetAll<TThis>();
     }
 
     public interface ICode<TTarget> : ICode where TTarget : class
     {
-        IReadOnlyList<TTarget> GetAll(Func<Kv, TTarget> construct);
+        IReadOnlyList<TTarget> GetAll(Func<IdName, TTarget> construct);
     }
 
     public class ConstsHelper
     {
-        public static IEnumerable<Kv> GetAll<TSource>(TSource source)
+        public static IEnumerable<IdName> GetAll<TSource>(TSource source)
         {
             var fields = typeof(TSource).GetFields();
             foreach (var fieldInfo in fields)
             {
                 var value = fieldInfo.GetValue(source);
-                if (value is Kv idname)
+                if (value is IdName idname)
                 {
                     yield return idname;
                 }
             }
         }
 
-        public static IEnumerable<TDest> GetAll<TSource, TDest>(TSource source, Func<Kv, TDest> construct)
+        public static IEnumerable<TDest> GetAll<TSource, TDest>(TSource source, Func<IdName, TDest> construct)
         {
             var idnames = GetAll(source);
             foreach (var idname in idnames)
@@ -45,7 +45,7 @@ namespace Hotline.SeedData.Codes
     public class TestDemo : IConstTable<WorkflowModule>
     {
         public static readonly KeyValuePair<string, string> OrderManage = new("OrderManage", "工单办理");
-        public static readonly Kv OrderManage1 = new Kv("OrderManage1", "工单办理1");
+        public static readonly IdName OrderManage1 = new IdName("OrderManage1", "工单办理1");
         public IEnumerable<WorkflowModule> GetData<TSource>(TSource source) =>
             ConstsHelper.GetAll(new TestDemo(), d => new WorkflowModule(d.Id, d.Name));
     }

+ 20 - 20
src/XF.Domain.Repository/Entity.cs

@@ -143,10 +143,10 @@ public abstract class WorkflowEntity : FullStateEntity, IWorkflow
     public string? ExpiredTimeConfigId { get; set; }
 
     [SugarColumn(ColumnDataType = "json", IsJson = true)]
-    public List<string> AssignOrgCodes { get; set; } = new();
+    public List<string> FlowedOrgIds { get; set; } = new();
 
     [SugarColumn(ColumnDataType = "json", IsJson = true)]
-    public List<string> AssignUserIds { get; set; } = new();
+    public List<string> FlowedUserIds { get; set; } = new();
 
     //[SugarColumn(ColumnDataType = "varchar(600)", IsJson = true)]
     //public List<string> AssignRoles { get; set; } = new();
@@ -158,12 +158,12 @@ public abstract class WorkflowEntity : FullStateEntity, IWorkflow
         {
             case EFlowAssignType.Org:
                 var orgCodes = handler.GetHigherOrgCodes(true).ToList();
-                AssignOrgCodes.AddRange(orgCodes);
-                AssignOrgCodes = AssignOrgCodes.Distinct().ToList();
+                FlowedOrgIds.AddRange(orgCodes);
+                FlowedOrgIds = FlowedOrgIds.Distinct().ToList();
                 break;
             case EFlowAssignType.User:
-                if (!AssignUserIds.Exists(d => d == handler))
-                    AssignUserIds.Add(handler);
+                if (!FlowedUserIds.Exists(d => d == handler))
+                    FlowedUserIds.Add(handler);
                 break;
             default:
                 throw new ArgumentOutOfRangeException(nameof(type), type, null);
@@ -177,12 +177,12 @@ public abstract class WorkflowEntity : FullStateEntity, IWorkflow
         {
             case EFlowAssignType.Org:
                 var orgCodes = handlers.SelectMany(d => d.GetHigherOrgCodes(true)).ToList();
-                AssignOrgCodes.AddRange(orgCodes);
-                AssignOrgCodes = AssignOrgCodes.Distinct().ToList();
+                FlowedOrgIds.AddRange(orgCodes);
+                FlowedOrgIds = FlowedOrgIds.Distinct().ToList();
                 break;
             case EFlowAssignType.User:
-                AssignUserIds.AddRange(handlers);
-                AssignUserIds = AssignUserIds.Distinct().ToList();
+                FlowedUserIds.AddRange(handlers);
+                FlowedUserIds = FlowedUserIds.Distinct().ToList();
                 break;
             default:
                 throw new ArgumentOutOfRangeException(nameof(type), type, null);
@@ -242,10 +242,10 @@ public abstract class PositionWorkflowEntity : PositionEntity, IWorkflow
     public string? ExpiredTimeConfigId { get; set; }
 
     [SugarColumn(ColumnDataType = "json", IsJson = true)]
-    public List<string> AssignOrgCodes { get; set; } = new();
+    public List<string> FlowedOrgIds { get; set; } = new();
 
     [SugarColumn(ColumnDataType = "json", IsJson = true)]
-    public List<string> AssignUserIds { get; set; } = new();
+    public List<string> FlowedUserIds { get; set; } = new();
 
     //[SugarColumn(ColumnDataType = "varchar(600)", IsJson = true)]
     //public List<string> AssignRoles { get; set; } = new();
@@ -257,12 +257,12 @@ public abstract class PositionWorkflowEntity : PositionEntity, IWorkflow
         {
             case EFlowAssignType.Org:
                 var orgCodes = handler.GetHigherOrgCodes(true).ToList();
-                AssignOrgCodes.AddRange(orgCodes);
-                AssignOrgCodes = AssignOrgCodes.Distinct().ToList();
+                FlowedOrgIds.AddRange(orgCodes);
+                FlowedOrgIds = FlowedOrgIds.Distinct().ToList();
                 break;
             case EFlowAssignType.User:
-                if (!AssignUserIds.Exists(d => d == handler))
-                    AssignUserIds.Add(handler);
+                if (!FlowedUserIds.Exists(d => d == handler))
+                    FlowedUserIds.Add(handler);
                 break;
             default:
                 throw new ArgumentOutOfRangeException(nameof(type), type, null);
@@ -276,12 +276,12 @@ public abstract class PositionWorkflowEntity : PositionEntity, IWorkflow
         {
             case EFlowAssignType.Org:
                 var orgCodes = handlers.SelectMany(d => d.GetHigherOrgCodes(true)).ToList();
-                AssignOrgCodes.AddRange(orgCodes);
-                AssignOrgCodes = AssignOrgCodes.Distinct().ToList();
+                FlowedOrgIds.AddRange(orgCodes);
+                FlowedOrgIds = FlowedOrgIds.Distinct().ToList();
                 break;
             case EFlowAssignType.User:
-                AssignUserIds.AddRange(handlers);
-                AssignUserIds = AssignUserIds.Distinct().ToList();
+                FlowedUserIds.AddRange(handlers);
+                FlowedUserIds = FlowedUserIds.Distinct().ToList();
                 break;
             default:
                 throw new ArgumentOutOfRangeException(nameof(type), type, null);

+ 4 - 4
src/XF.Domain/Entities/IWorkflow.cs

@@ -10,14 +10,14 @@ public interface IWorkflow
     string? ExpiredTimeConfigId { get; set; }
 
     /// <summary>
-    /// 指派部门编码
+    /// 流经部门
     /// </summary>
-    List<string> AssignOrgCodes { get; set; }
+    List<string> FlowedOrgIds { get; set; }
 
     /// <summary>
-    /// 指派用户Id
+    /// 流经办理人
     /// </summary>
-    List<string> AssignUserIds { get; set; }
+    List<string> FlowedUserIds { get; set; }
 
     ///// <summary>
     ///// 指派角色名称