using Hotline.Application.Contracts.Validators.FlowEngine;
using Hotline.Caching.Interfaces;
using Hotline.FlowEngine;
using Hotline.FlowEngine.Definitions;
using Hotline.FlowEngine.WorkflowModules;
using Hotline.FlowEngine.Workflows;
using Hotline.Identity.Accounts;
using Hotline.Identity.Roles;
using Hotline.Orders;
using Hotline.Settings;
using Hotline.Settings.TimeLimits;
using Hotline.Share.Dtos;
using Hotline.Share.Dtos.FlowEngine;
using Hotline.Share.Dtos.FlowEngine.Definition;
using Hotline.Share.Dtos.Settings;
using Hotline.Share.Enums.FlowEngine;
using Hotline.Share.Enums.Identity;
using Hotline.Users;
using MapsterMapper;
using Hotline.File;
using Hotline.Share.Enums.Settings;
using XF.Domain.Authentications;
using XF.Domain.Dependency;
using XF.Domain.Entities;
using XF.Domain.Exceptions;
using XF.Domain.Extensions;
using XF.Domain.Repository;
using XF.Utility.EnumExtensions;
using Hotline.Share.Dtos.File;
using Microsoft.Extensions.Logging;
using System.Text;
using System.Diagnostics;

namespace Hotline.Application.FlowEngine;

public class WorkflowApplication : IWorkflowApplication, IScopeDependency
{
    private readonly IDefinitionDomainService _definitionDomainService;
    private readonly IWorkflowDomainService _workflowDomainService;
    private readonly IOrderDomainService _orderDomainService;
    private readonly IWorkflowRepository _workflowRepository;
    private IRepository<WorkflowDefinition> _definitionRepository;
    private IRepository<WorkflowStep> _workflowStepRepository;
    private IRepository<WorkflowTrace> _workflowTraceRepository;
    private readonly IRepository<User> _userRepository;
    private readonly IAccountRepository _accountRepository;
    private readonly ISystemOrganizeRepository _organizeRepository;
    private readonly IRepository<Role> _roleRepository;
    private readonly IWfModuleCacheManager _wfModuleCacheManager;
    private readonly ISystemDomainService _systemDomainService;
    private readonly ITimeLimitDomainService _timeLimitDomainService;
    private readonly IUserDomainService _userDomainService;
    private readonly IAccountDomainService _accountDomainService;
    private readonly ISessionContext _sessionContext;
    private readonly IMapper _mapper;
    private readonly IFileRepository _fileRepository;
    private readonly ILogger<WorkflowApplication> _logger;

    public WorkflowApplication(
        IDefinitionDomainService definitionDomainService,
        IWorkflowDomainService workflowDomainService,
        IOrderDomainService orderDomainService,
        IWorkflowRepository workflowRepository,
        IRepository<WorkflowDefinition> definitionRepository,
        IRepository<WorkflowStep> workflowStepRepository,
        IRepository<WorkflowTrace> workflowTraceRepository,
        IRepository<User> userRepository,
        IAccountRepository accountRepository,
        ISystemOrganizeRepository organizeRepository,
        IRepository<Role> roleRepository,
        IWfModuleCacheManager wfModuleCacheManager,
        ISystemDomainService systemDomainService,
        ITimeLimitDomainService timeLimitDomainService,
        ISessionContext sessionContext,
        IMapper mapper,
        IFileRepository fileRepository,
        ILogger<WorkflowApplication> logger)
    {
        _definitionDomainService = definitionDomainService;
        _workflowDomainService = workflowDomainService;
        _orderDomainService = orderDomainService;
        _workflowRepository = workflowRepository;
        _definitionRepository = definitionRepository;
        _workflowStepRepository = workflowStepRepository;
        _workflowTraceRepository = workflowTraceRepository;
        _userRepository = userRepository;
        _accountRepository = accountRepository;
        _organizeRepository = organizeRepository;
        _roleRepository = roleRepository;
        _wfModuleCacheManager = wfModuleCacheManager;
        _systemDomainService = systemDomainService;
        _timeLimitDomainService = timeLimitDomainService;
        _sessionContext = sessionContext;
        _mapper = mapper;
        _fileRepository = fileRepository;
        _logger = logger;
    }

    public async Task<string> StartWorkflowAsync(StartWorkflowDto dto, ISessionContext current, string externalId,
        DateTime? expiredTime, CancellationToken cancellationToken = default)
    {
        var validator = new StartWorkflowDtoValidator();
        var validResult = await validator.ValidateAsync(dto, cancellationToken);
        if (!validResult.IsValid)
            throw new UserFriendlyException(
                $"非法参数, {string.Join(',', validResult.Errors.Select(d => d.ErrorMessage))}");

        var wfModule = await GetWorkflowModuleAsync(dto.DefinitionModuleCode, cancellationToken);
        var definition = wfModule.Definition;
        if (definition == null)
            throw new UserFriendlyException("无效模板编码");
        if (definition.Status is not EDefinitionStatus.Enable)
            throw new UserFriendlyException("该模板不可用");

        //如果发起会签需检查是否支持发起会签
        var startStepDefine = definition.FindStartStepDefine();

        //下一节点是否为动态节点
        var isNextDynamic = startStepDefine.InstanceMode is EInstanceMode.Dynamic &&
                            !_workflowDomainService.DynamicShouldTerminal(startStepDefine, current.OrgLevel);
        var firstStepDefine = isNextDynamic
            ? startStepDefine
            : definition.FindStepDefine(dto.NextStepCode);
        if (firstStepDefine is null)
            throw new UserFriendlyException("未查询到下一步节点配置");

        //1. 如果不是按角色指派，handlers必填 2. 如果按角色指派，handlers可以不选
        if (firstStepDefine.HandlerType is not EHandlerType.Role && !dto.NextHandlers.Any())
            throw UserFriendlyException.SameMessage("未指派办理人");

        if (dto.IsStartCountersign)
        {
            if (!startStepDefine.CanStartCountersign)
                throw new UserFriendlyException("当前节点不支持发起会签");
            //if (startStepDefine.HandlerType is EHandlerType.Role)
            //    throw new UserFriendlyException("当前节点不支持发起会签");
            //即使当前节点支持发起会签，但下一节点为信息汇总节点、结束节点时也不可发起会签
            if (firstStepDefine.StepType is EStepType.Summary or EStepType.End)
                throw new UserFriendlyException("下一节点不允许发起会签");
            //下一节点是会签汇总节点也不允许发起会签
            if (dto.BackToCountersignEnd)
                throw new UserFriendlyException("下一节点不允许发起会签");
        }

        var workflow = await _workflowDomainService.CreateWorkflowAsync(wfModule, dto.Title,
            current.RequiredUserId, current.RequiredOrgId,
            externalId, cancellationToken);

        //var startStepHandles = new List<WorkflowStepHandler>{WorkflowStepHandler.Create(workflow.Id, workflow.ExternalId,
        //    EFlowAssignType.User, current.RequiredUserId, current.UserName,
        //    current.RequiredOrgId, current.OrgName)};

        var startStep = _workflowDomainService.CreateStartStep(workflow, startStepDefine, dto,
            new FlowStepHandler
            {
                Key = current.RequiredUserId,
                Value = current.UserName,
                UserId = current.UserId,
                Username = current.UserName,
                OrgId = current.RequiredOrgId,
                OrgName = current.OrgName
            }, expiredTime);

        var flowAssignInfo =
            await GetNextStepFlowAssignInfoAsync(workflow, startStep, dto, firstStepDefine, isNextDynamic, cancellationToken);

        //var firstStepHandlers = await GetNextStepHandlersAsync(workflow, firstStepDefine, dto, cancellationToken);

        var counterSignType = _workflowDomainService.GetCounterSignType(startStep.BusinessType);

        //办理开始节点
        await _workflowDomainService.HandleStepAsync(current, startStep, workflow, dto, flowAssignInfo.FlowAssignType,
            counterSignType, expiredTime, cancellationToken);
        //startStep.Handle(current.RequiredUserId, current.UserName,
        //    current.RequiredOrgId, current.OrgName,
        //    current.OrgAreaCode, current.OrgAreaName,
        //    current.OrgIsCenter, firstStepDefine.Code);

        if (dto.Files.Any())
            startStep.FileJson =
                await _fileRepository.AddFileAsync(dto.Files, workflow.ExternalId, startStep.Id, cancellationToken);

        await _workflowStepRepository.AddAsync(startStep, cancellationToken);
        //await _workflowStepRepository.AddNav(startStep)
        //    .Include(d => d.StepHandlers)
        //    .ExecuteCommandAsync();
        workflow.Steps.Add(startStep);

        //starttrace
        var startTrace = _mapper.Map<WorkflowTrace>(startStep);
        startTrace.StepId = startStep.Id;
        startTrace.TraceType = EWorkflowTraceType.Normal;
        _mapper.Map(dto, startTrace);
        await _workflowTraceRepository.AddAsync(startTrace, cancellationToken);
        workflow.Traces.Add(startTrace);

        //更新受理人信息
        workflow.UpdateAcceptor(
            current.RequiredUserId,
            current.UserName,
            current.StaffNo,
            current.RequiredOrgId,
            current.OrgName);

        await _workflowDomainService.StartAsync(workflow, startStep, dto, firstStepDefine, isNextDynamic,
            flowAssignInfo, counterSignType, expiredTime, current, cancellationToken);

        return workflow.Id;
    }

    /// <summary>
    /// 流转至下一节点（节点办理）
    /// </summary>
    public async Task<Workflow> NextAsync(NextWorkflowDto dto, ISessionContext current, DateTime? expiredTime = null,
        CancellationToken cancellationToken = default)
    {
        var workflow = await _workflowDomainService.GetWorkflowAsync(dto.WorkflowId, withDefine: true, withSteps: true,
            withTraces: true, withCountersigns: true, cancellationToken: cancellationToken);

        var currentStep = _workflowDomainService.FindCurrentStepWaitForHandle(workflow,
            current.RequiredUserId, current.RequiredOrgId, current.Roles);
        if (currentStep.Status is EWorkflowStepStatus.Handled)
            throw new UserFriendlyException("该状态不支持继续办理");

        var currentStepDefine = _workflowDomainService.GetStepDefine(workflow.WorkflowDefinition, currentStep.Code);

        //下一节点是否为动态节点
        var isNextDynamic = currentStepDefine.InstanceMode is EInstanceMode.Dynamic &&
                            !_workflowDomainService.DynamicShouldTerminal(currentStepDefine, current.OrgLevel);

        StepDefine nextStepDefine;
        if (isNextDynamic
            || (currentStep.IsInCountersign() && !currentStep.IsTopCountersignEndStep(workflow.TopCountersignStepId))
            || dto.IsStartCountersign)
        {
            //下一步配置为当前节点配置
            nextStepDefine = currentStepDefine;
        }
        else
        {
            //下一步配置为下一步节点配置
            nextStepDefine = _workflowDomainService.GetStepDefine(workflow.WorkflowDefinition, dto.NextStepCode);
        }

        //需求：按角色选择办理人可以不选，表示该角色下所有人都可以办理，同时依据配置：是否本部门人办理显示待选办理人。角色下只要一人办理即可（即:角色下不发起会签）
        if (nextStepDefine.HandlerType != EHandlerType.Role && !dto.NextHandlers.Any())
            throw new UserFriendlyException("未指定节点处理者");

        if (dto.IsStartCountersign)
        {
            if (!currentStepDefine.CanStartCountersign)
                throw new UserFriendlyException("当前节点不支持发起会签");
            //if (currentStepDefine.HandlerType is EHandlerType.Role)
            //    throw new UserFriendlyException("当前节点不支持发起会签");
            //即使当前节点支持发起会签，但下一节点为信息汇总节点、结束节点时也不可发起会签
            if (nextStepDefine.StepType is EStepType.Summary or EStepType.End)
                throw new UserFriendlyException("下一节点不允许发起会签");
            //下一节点是会签汇总节点也不允许发起会签
            if (dto.BackToCountersignEnd)
                throw new UserFriendlyException("下一节点不允许发起会签");
        }

        var flowAssignInfo =
            await GetNextStepFlowAssignInfoAsync(workflow, currentStep, dto, nextStepDefine, isNextDynamic, cancellationToken);

        //var nextStepHandlers = await GetNextStepHandlersAsync(workflow, nextStepDefine, dto, cancellationToken);

        await _workflowDomainService.NextAsync(workflow, currentStep, dto, nextStepDefine, isNextDynamic,
            flowAssignInfo, expiredTime, current, cancellationToken);

        return workflow;
    }

    /// <summary>
    /// 退回（返回前一节点）
    /// </summary>
    public async Task<EFlowDirection> PreviousAsync(PreviousWorkflowDto dto, CancellationToken cancellationToken)
    {
        var workflow = await _workflowDomainService.GetWorkflowAsync(dto.WorkflowId, withSteps: true,
            withTraces: true, withCountersigns: true, cancellationToken: cancellationToken);
        //var user = await _userRepository.Queryable()
        //    .Includes(x => x.Organization)
        //    .FirstAsync(x => x.Id == _sessionContext.RequiredUserId, cancellationToken);
        return await _workflowDomainService.PreviousAsync(workflow, dto,
            _sessionContext.RequiredUserId, _sessionContext.RequiredOrgId, _sessionContext.Roles,
            _sessionContext, cancellationToken);
    }

    /// <summary>
    /// 工单退回（返回前一节点）
    /// </summary>
    public async Task<EFlowDirection> PreviousAsync(PreviousWorkflowDto dto, string applicantId, string applicantOrgId, string[] applicantRoleIds,
        ISessionContext current, CancellationToken cancellationToken)
    {
        var workflow = await _workflowDomainService.GetWorkflowAsync(dto.WorkflowId, withSteps: true,
            withTraces: true, withCountersigns: true, cancellationToken: cancellationToken);
        return await _workflowDomainService.PreviousAsync(workflow, dto,
            applicantId, applicantOrgId, applicantRoleIds, _sessionContext, cancellationToken);
    }

    /// <summary>
    /// 撤回至任意节点
    /// </summary>
    public async Task RecallAsync(RecallDto dto, DateTime? expiredTime, CancellationToken cancellationToken)
    {
        var validator = new RecallDtoValidator();
        var validationResult = await validator.ValidateAsync(dto, cancellationToken);
        if (!validationResult.IsValid)
            throw new UserFriendlyException(string.Join(',', validationResult.Errors));

        var workflow = await _workflowDomainService.GetWorkflowAsync(dto.WorkflowId, withDefine: true, withSteps: true,
           withTraces: true, cancellationToken: cancellationToken);

        //await _orderDomainService.ReadyToRecallAsync(workflow.ExternalId, cancellationToken);

        var targetStepDefine = _workflowDomainService.GetStepDefine(workflow.WorkflowDefinition, dto.NextStepCode);
        if (targetStepDefine.StepType is EStepType.End)
            throw UserFriendlyException.SameMessage("结束节点不支持撤回");
        //var isStartCountersign = targetStepDefine.CouldPrevStartCountersign(dto.NextHandlers.Count);
        var flowAssignInfo = await GetNextStepFlowAssignInfoByDefineAsync(targetStepDefine, dto.HandlerType, dto.IsStartCountersign,
            dto.NextHandlers.Select(d => new Kv(d.Key, d.Value)).ToList(), cancellationToken);

        //var stepHandlers = await GetNextStepHandlersAsync(workflow, targetStepDefine, dto, cancellationToken);

        await _workflowDomainService.RecallAsync(workflow, dto, targetStepDefine, flowAssignInfo, expiredTime, _sessionContext, cancellationToken);
    }

    /// <summary>
    /// 无视流程模板配置直接将当前节点办理至结束节点
    /// </summary>
    public async Task HandleToEndAsync(ISessionContext current, string workflowId, string opinion, List<FileDto> files,
        EReviewResult reviewResult = EReviewResult.Unknown, CancellationToken cancellationToken = default)
    {
        var workflow = await _workflowDomainService.GetWorkflowAsync(workflowId, withDefine: true, withSteps: true,
            cancellationToken: cancellationToken);
        var endStepDefine = workflow.WorkflowDefinition.FindEndStepDefine();
        if (endStepDefine is null)
            throw new UserFriendlyException("未正确配置结束节点");
        var dto = new NextWorkflowDto
        {
            WorkflowId = workflowId,
            NextStepCode = endStepDefine.Code,
            NextStepName = endStepDefine.Name,
            FlowDirection = EFlowDirection.OrgToFile,
            BusinessType = endStepDefine.BusinessType,
            ReviewResult = reviewResult,
            Opinion = opinion,
            Files = files
        };
        await NextAsync(dto, current, cancellationToken: cancellationToken);
    }

    /// <summary>
    /// 跳转至结束节点(无视流程模板配置以及当前办理对象，直接跳至结束节点)
    /// </summary>
    public async Task JumpToEndAsync(ISessionContext current, string workflowId, string opinion, List<FileDto> files, DateTime? expiredTime,
        EReviewResult reviewResult = EReviewResult.Unknown, CancellationToken cancellationToken = default)
    {
        var workflow = await _workflowDomainService.GetWorkflowAsync(workflowId, withDefine: true, withSteps: true,
            withTraces: true, cancellationToken: cancellationToken);
        var endStepDefine = workflow.WorkflowDefinition.FindEndStepDefine();
        if (endStepDefine is null)
            throw new UserFriendlyException("未正确配置结束节点");

        //var currentStep = workflow.GetActualStep();
        //if (currentStep is null)
        //    throw new UserFriendlyException("未找到实际办理节点");

        var dto = new BasicWorkflowDto
        {
            NextStepCode = endStepDefine.Code,
            NextStepName = endStepDefine.Name,
            FlowDirection = EFlowDirection.OrgToFile,
            BusinessType = endStepDefine.BusinessType,
            ReviewResult = reviewResult,
            Opinion = opinion,
            Files = files
        };

        var unhandleSteps = workflow.Steps
            .Where(d => d.Status != EWorkflowStepStatus.Handled).ToList();

        var unhandleTraces = workflow.Traces
            .Where(d => d.Status != EWorkflowStepStatus.Handled).ToList();

        //todo 结束会签

        foreach (var step in unhandleSteps)
        {
            await _workflowDomainService.HandleStepAsync(current, step, workflow, dto, null,
                null, null, cancellationToken);

            var trace = unhandleTraces.First(d => d.StepId == step.Id);
            _mapper.Map(dto, trace);
            _mapper.Map(step, trace);
        }
        await _workflowStepRepository.UpdateRangeAsync(unhandleSteps, cancellationToken);
        await _workflowTraceRepository.UpdateRangeAsync(unhandleTraces, cancellationToken);

        await _workflowDomainService.EndAsync(workflow, dto,
            endStepDefine, unhandleSteps.First(), current, expiredTime, cancellationToken);
    }

    /// <summary>
    /// 查询开始流程的下一步待选节点
    /// </summary>
    public async Task<NextStepsDto<NextStepOption>> GetStartStepsAsync(string moduleCode, CancellationToken cancellationToken)
    {
        var wfModule = await GetWorkflowModuleAsync(moduleCode, cancellationToken);
        var definition = wfModule.Definition;
        if (definition == null)
            throw new UserFriendlyException("无效模板编码");
        if (definition.Status is not EDefinitionStatus.Enable)
            throw new UserFriendlyException("该模板不可用");

        var startStepDefine = definition.FindStartStepDefine();
        if (startStepDefine.InstanceMode is EInstanceMode.Dynamic &&
            !_workflowDomainService.DynamicShouldTerminal(startStepDefine, _sessionContext.OrgLevel))
        {
            var nextStepOption = await GetDynamicStepAsync(startStepDefine.InstancePolicy.Value,
                   startStepDefine.StepType, startStepDefine.BusinessType, cancellationToken);
            return new NextStepsDto<NextStepOption>
            {
                Steps = new List<NextStepOption> { nextStepOption }
            };
        }

        var firstStepDefines = definition.FindStepDefines(startStepDefine.NextSteps.Select(d => d.Code));
        if (!firstStepDefines.Any())
            throw new UserFriendlyException("未正确配置首个办理节点");

        return new NextStepsDto<NextStepOption>
        {
            Steps = await GetConfigStepsAsync(definition.FlowType, startStepDefine.StepType, startStepDefine.BusinessType,
                firstStepDefines, cancellationToken)
        };
    }

    /// <summary>
    /// 查询办理流程的下一步待选节点
    /// </summary>
    public async Task<NextStepsWithOpinionDto<NextStepOption>> GetNextStepsAsync(string workflowId, CancellationToken cancellationToken)
    {
        var workflow = await _workflowDomainService.GetWorkflowAsync(workflowId, withDefine: true, withSteps: true,
            cancellationToken: cancellationToken);
        var currentStep = _workflowDomainService.FindCurrentStepWaitForHandle(workflow,
            _sessionContext.RequiredUserId, _sessionContext.RequiredOrgId, _sessionContext.Roles);
        if (currentStep.StepType is EStepType.End)
            throw new UserFriendlyException("结束节点无需办理");

        var dto = new NextStepsWithOpinionDto<NextStepOption>
        {
            CanReject = workflow.IsReviewType() && currentStep.CanReject,
            //ExpiredTime = workflow.ExpiredTime,
            CanStartCountersign = currentStep.CanStartCountersign,
            CurrentStepBusinessType = currentStep.BusinessType,
            TimeTypeOptions = EnumExts.GetDescriptions<ETimeType>().ToList(),
            IsMainHandlerShow = workflow.WorkflowDefinition.IsMainHandlerShow,
        };

        var currentStepDefine = _workflowDomainService.GetStepDefine(workflow.WorkflowDefinition, currentStep.Code);

        if (currentStep.InstanceMode is EInstanceMode.Dynamic &&
            !_workflowDomainService.DynamicShouldTerminal(currentStepDefine, _sessionContext.OrgLevel))
        {
            //动态生成下一步
            var nextStepOption = await GetDynamicStepAsync(currentStep.InstancePolicy.Value, currentStep.StepType,
                currentStep.BusinessType, cancellationToken);
            dto.Steps = new List<NextStepOption> { nextStepOption };
            return dto;
        }

        if (currentStep.IsInCountersign())
        {
            if (currentStep.IsCountersignEndStep)
            {
                // 宜宾需求：会签汇总节点展示会签办理节点办理意见
                var countersignHandleSteps = workflow.Steps.Where(d =>
                        d.CountersignId == currentStep.CountersignId &&
                        d.CountersignPosition is ECountersignPosition.Multi or ECountersignPosition.Single).ToList();
                var sb = new StringBuilder();
                foreach (var countersignHandleStep in countersignHandleSteps)
                {
                    //sb.AppendLine($"{countersignHandleStep.GetActualHandler()?.GetHandler().Value} : {countersignHandleStep.Opinion}");
                    sb.AppendLine($"{countersignHandleStep.GetHandler().Value} : {countersignHandleStep.Opinion}");
                }
                dto.Opinion = sb.ToString();

                //当前待办节点为会签汇总节点时：检查是否为顶级会签汇总节点，t:按配置往下走，f:继续往上汇总，不需要重复往下指派
                if (!currentStep.IsTopCountersignEndStep(workflow.TopCountersignStepId))
                {
                    var startCountersignStep = GetCsLoopStartStep(workflow, currentStep);

                    ////查找当前节点对应会签开始节点的上级作为下一个cs汇总节点的汇总对象
                    //var startCountersignStep =
                    //    workflow.Steps.FirstOrDefault(d => d.Id == currentStep.CountersignStartStepId);
                    //if (startCountersignStep is null)
                    //    throw new UserFriendlyException(
                    //        $"未查询到会签开始节点，workflowId: {workflow.Id}, startStepId: {currentStep.CountersignStartStepId}",
                    //        "未查询到会签开始节点，数据异常");
                    var countersignEndOption = GetCsEndStepByTargetPrev(workflow.Steps, startCountersignStep);

                    //按会签策略
                    var nextStepOption = await GetDynamicStepAsync(currentStep.CountersignPolicy.Value,
                        EStepType.Normal, currentStep.BusinessType, cancellationToken);

                    dto.Steps = new List<NextStepOption> { nextStepOption, countersignEndOption };
                    return dto;
                }
            }
            else
            {
                //汇总节点
                var countersignEndOption = GetCsEndStepByTargetPrev(workflow.Steps, currentStep);
                //按会签策略
                var nextStepOption =
                    await GetDynamicStepAsync(currentStep.CountersignPolicy.Value,
                        EStepType.Normal, currentStep.BusinessType, cancellationToken);
                dto.Steps = new List<NextStepOption> { nextStepOption, countersignEndOption };
                return dto;
            }
        }

        var nextDefines = workflow.WorkflowDefinition.FindStepDefines(currentStep.NextSteps.Select(d => d.Code));
        if (!nextDefines.Any())
            throw new UserFriendlyException("未正确配置下一节点");

        dto.Steps = await GetConfigStepsAsync(workflow, currentStep, nextDefines, cancellationToken);

        // 宜宾需求：汇总节点展示前一级节点办理意见
        if (currentStep.StepType is EStepType.Summary &&
            !currentStep.IsCountersignEndStep &&
            string.IsNullOrEmpty(dto.Opinion))
        {
            var prevStep = workflow.Steps.FirstOrDefault(d => d.Id == currentStep.PrevStepId);
            dto.Opinion = $"{prevStep?.GetHandler().Value} : {prevStep?.Opinion}";
        }

        return dto;
    }

    private WorkflowStep GetCsLoopStartStep(Workflow workflow, WorkflowStep currentStep)
    {
        var startCountersignStep =
            workflow.Steps.FirstOrDefault(d => d.Id == currentStep.CountersignStartStepId);
        if (startCountersignStep is null)
            throw new UserFriendlyException(
                $"未查询到会签开始节点，workflowId: {workflow.Id}, startStepId: {currentStep.CountersignStartStepId}",
                "未查询到会签开始节点，数据异常");
        if (!startCountersignStep.IsCountersignEndStep)
            return startCountersignStep;
        return GetCsLoopStartStep(workflow, startCountersignStep);
    }

    /// <summary>
    /// 查询撤回可选节点及办理对象
    /// </summary>
    public async Task<NextStepsDto<RecallStepOption>> GetRecallStepsAsync(string workflowId, CancellationToken cancellationToken)
    {
        var workflow = await _workflowDomainService.GetWorkflowAsync(workflowId, withDefine: true, withSteps: true,
            cancellationToken: cancellationToken);
        var isEnd = workflow.Steps.Any(x => x.StepType == EStepType.End);
        var currentStep = workflow.Steps.FirstOrDefault(d => d.Id == workflow.ActualHandleStepId);
        if (currentStep is null)
            throw new UserFriendlyException("无效当前节点编号");
        var quer = workflow.Steps.Where(d =>
            d.StepType != EStepType.End &&
            d.IsOrigin);
        if (!isEnd)
        {
            quer = quer.Where(d => d.Id != currentStep.Id);
        }
        var originSteps = quer.ToList();
        var stepCodes = originSteps.Select(d => d.Code).ToList();
        var stepDefines = workflow.WorkflowDefinition.FindStepDefines(stepCodes);

        var dto = new NextStepsDto<RecallStepOption>
        {
            TimeTypeOptions = EnumExts.GetDescriptions<ETimeType>().ToList()
        };

        var steps = await GetRecallConfigStepsAsync(originSteps, stepDefines, currentStep,
            workflow.FlowType,
            workflow.Status is EWorkflowStatus.Completed,
            cancellationToken);
        dto.Steps = steps;

        return dto;
    }

    private async Task<List<RecallStepOption>> GetRecallConfigStepsAsync(
        List<WorkflowStep> originSteps, List<StepDefine> stepDefines,
        WorkflowStep currentStep, EFlowType flowType, bool isWorkflowFiled, CancellationToken cancellationToken)
    {
        var steps = new List<RecallStepOption>();
        foreach (var originStep in originSteps)
        {
            var stepDefine = stepDefines.First(d => d.Code == originStep.Code);
            var nextStepOption = await GetConfigStepAsync(flowType, stepDefine, cancellationToken);
            var stepOption = _mapper.Map<RecallStepOption>(nextStepOption);
            if (stepDefine.StepType is EStepType.End)
            {
                steps.Add(stepOption);
                continue;
            }

            stepOption.InputRealHandler = false;

            ////已归档工单，撤回至中心看作otc,撤回至部门看作cto
            //stepOption.FlowDirection = isWorkflowFiled
            //    ? stepDefine.BusinessType is EBusinessType.Center or EBusinessType.Send
            //        ? EFlowDirection.OrgToCenter
            //        : stepDefine.BusinessType is EBusinessType.Department
            //            ? EFlowDirection.CenterToOrg
            //            : null
            //    : CheckFlowDirection(currentStep.BusinessType, stepDefine.BusinessType);

            //需求已调整为特提必重算期满时间

            //需求：撤回选择办理对象时，默认选中该节点原办理对象
            //if (originStep.Handlers.Any())
            //    stepOption.Handler = originStep.Handlers.FirstOrDefault(d => d.Key == originStep.HandlerId || d.Key == originStep.HandlerOrgId);
            stepOption.Handler = originStep.GetWorkflowStepHandler();

            //if (originStep.StepHandlers.Any())
            //    stepOption.Handler = originStep.GetActualHandler()?.GetHandler() ?? new();

            steps.Add(stepOption);
        }

        return steps;
    }

    /// <summary>
    /// 否决
    /// </summary>
    public async Task RejectAsync(RejectDto dto, CancellationToken cancellationToken)
    {
        var workflow = await _workflowDomainService.GetWorkflowAsync(dto.WorkflowId, withDefine: true,
            cancellationToken: cancellationToken);
        //var basicDto = _mapper.Map<BasicWorkflowDto>(dto);
        //basicDto.NextStepCode = string.Empty;
        //basicDto.IsStartCountersign = false;
        //await _workflowDomainService.RejectAsync(workflow, basicDto, cancellationToken);

        var endStepDefine = workflow.WorkflowDefinition.FindEndStepDefine();
        var nextDto = _mapper.Map<NextWorkflowDto>(dto);
        nextDto.ReviewResult = EReviewResult.Failed;
        nextDto.NextStepCode = endStepDefine.Code;
        nextDto.NextStepName = endStepDefine.Name;
        nextDto.FlowDirection = _sessionContext.OrgIsCenter
            ? EFlowDirection.CenterToFile
            : EFlowDirection.OrgToFile;

        await NextAsync(nextDto, _sessionContext, cancellationToken: cancellationToken);
    }

    //供开启流程调用
    private async Task<List<NextStepOption>> GetConfigStepsAsync(
        EFlowType flowType,
        EStepType currentStepType,
        EBusinessType currentBusinessType,
        List<StepDefine> stepDefines,
        CancellationToken cancellationToken)
    {
        var stepOptions = new List<NextStepOption>();
        foreach (var stepDefine in stepDefines)
        {
            var nextStepOption = await GetConfigStepAsync(flowType, stepDefine, cancellationToken);
            nextStepOption.InputRealHandler = currentStepType == EStepType.Normal &&
                                              stepDefine.StepType is EStepType.Summary or EStepType.End;
            stepOptions.Add(nextStepOption);

            if (stepDefine.StepType is EStepType.End) continue;

            nextStepOption.FlowDirection = _workflowDomainService.GetFlowDirection(currentBusinessType, stepDefine.BusinessType);
            //stepOptions.Add(nextStepOption);
        }

        return stepOptions;
    }

    /// <summary>
    /// 办理流程调用（根据办理过程过滤汇总节点待选办理对象）
    /// </summary>
    private async Task<List<NextStepOption>> GetConfigStepsAsync(
        Workflow workflow,
        WorkflowStep currentStep,
        //EStepType currentStepType,
        //EBusinessType currentBusinessType,
        List<StepDefine> stepDefines,
        CancellationToken cancellationToken)
    {
        var stepOptions = new List<NextStepOption>();
        foreach (var stepDefine in stepDefines)
        {
            NextStepOption nextStepOption;

            //汇总节点只能选择对应节点办理对象
            if (workflow.FlowType is EFlowType.Handle && stepDefine.StepType is EStepType.Summary)
            {
                //根据汇总对象id找到被汇总节点
                var summaryTargetStep = workflow.Steps.FirstOrDefault(d =>
                    d.StepType == EStepType.Normal &&
                    d.Code == stepDefine.SummaryTargetCode &&
                    d.IsOrigin);
                if (summaryTargetStep is null)
                    throw UserFriendlyException.SameMessage("未查询到汇总对象节点");

                //var handlers = summaryTargetStep.Handlers
                //    .Where(d => d.Key == summaryTargetStep.HandlerId || d.Key == summaryTargetStep.HandlerOrgId).ToList();

                var handler = new FlowStepHandler
                {
                    Key = summaryTargetStep.HandlerId,
                    Value = summaryTargetStep.HandlerName,
                    UserId = summaryTargetStep.HandlerId,
                    Username = summaryTargetStep.HandlerName
                };

                nextStepOption = new NextStepOption
                {
                    Key = stepDefine.Code,
                    Value = stepDefine.Name,
                    StepType = stepDefine.StepType,
                    BusinessType = stepDefine.BusinessType,
                    //HandlerType = stepDefine.HandlerType,
                    HandlerType = EHandlerType.AssignedUser,//指定办理人(业务需求)
                    Items = new List<FlowStepHandler> { handler }//handlers
                };
            }
            else
            {
                nextStepOption = await GetConfigStepAsync(workflow.FlowType, stepDefine, cancellationToken);
            }

            nextStepOption.InputRealHandler = currentStep.StepType == EStepType.Normal &&
                                              stepDefine.StepType is EStepType.Summary or EStepType.End;
            stepOptions.Add(nextStepOption);

            if (stepDefine.StepType is EStepType.End) continue;

            nextStepOption.FlowDirection = _workflowDomainService.GetFlowDirection(currentStep.BusinessType, stepDefine.BusinessType);
        }

        return stepOptions;
    }

    public async Task<NextStepOption> GetConfigStepAsync(EFlowType flowType, StepDefine stepDefine, CancellationToken cancellationToken)
    {
        var handlers = new List<FlowStepHandler>();
        if (stepDefine.StepType is EStepType.End)
        {
            return new NextStepOption
            {
                Key = stepDefine.Code,
                Value = stepDefine.Name,
                StepType = stepDefine.StepType,
                BusinessType = stepDefine.BusinessType,
                HandlerType = stepDefine.HandlerType,
                Items = handlers
            };
        }

        var orgId = _sessionContext.RequiredOrgId;
        var levelOneOrgId = orgId.GetHigherOrgId();
        var isCenter = levelOneOrgId.IsCenter();
        switch (stepDefine.HandlerType)
        {
            case EHandlerType.AssignedUser:
                handlers = await _userRepository.Queryable()
                    .Includes(d => d.Organization)
                    .Where(d => stepDefine.HandlerTypeItems.Select(x => x.Key).Contains(d.Id))
                    .Select(d => new FlowStepHandler
                    {
                        Key = d.Id,
                        Value = d.Name,
                        UserId = d.Id,
                        Username = d.Name,
                        OrgId = d.OrgId,
                        OrgName = d.Organization.Name,
                    })
                    .ToListAsync(cancellationToken);
                break;
            case EHandlerType.AssignedOrg:
                handlers = await _organizeRepository.Queryable() //stepDefine.HandlerTypeItems;
                    .Where(d => d.IsEnable && stepDefine.HandlerTypeItems.Select(x => x.Key).Contains(d.Id))
                    .Select(d => new FlowStepHandler
                    {
                        Key = d.Id,
                        Value = d.Name,
                        OrgId = d.Id,
                        OrgName = d.Name
                    })
                    .ToListAsync(cancellationToken);
                break;
            case EHandlerType.Role:
                //当前操作人所属部门的下级部门并且属于配置包含角色
                var roles = await _roleRepository.Queryable()
                    .Includes(
                        d => d.Accounts.Where(x =>
                            !x.IsDeleted && x.Status == EAccountStatus.Normal && x.AccountType == EAccountType.Personal).ToList(),
                        x => x.User, s => s.Organization)
                    .Where(d => stepDefine.HandlerTypeItems.Select(x => x.Key).Contains(d.Name))
                    .ToListAsync(cancellationToken);
                _logger.LogInformation($"角色: {string.Join(",", roles.Select(d => d.Name))}");
                var users1 = roles.SelectMany(d => d.Accounts).Select(d => d.User);

                //工单办理：除一级部门选择中心汇总(中心会签流程，返回topCountersignStep场景)，其余只能选下级部门
                if (flowType is EFlowType.Handle
                    && (stepDefine.StepType != EStepType.Summary && stepDefine.BusinessType != EBusinessType.Center))
                    users1 = users1.Where(d => d.OrgId.StartsWith(levelOneOrgId));

                handlers = users1.Where(d => d != null && !string.IsNullOrEmpty(d.Id))
                    .Select(d => new FlowStepHandler
                    {
                        Key = d.Id,
                        Value = d.Name,
                        UserId = d.Id,
                        Username = d.Name,
                        OrgId = d.OrgId,
                        OrgName = d.Organization.Name
                    })
                    .ToList();
                break;
            case EHandlerType.OrgLevel:
                //当前操作人所属部门的垂直部门并且属于配置orgLevel的部门
                var levels = stepDefine.HandlerTypeItems.Select(d => int.Parse(d.Key)).ToList();
                // var orgs1 = await _organizeRepository.Queryable()
                //     .Where(d => d.IsEnable && levels.Contains(d.Level))
                //     .WhereIF(!isCenter, d => d.Id.StartsWith(levelOneOrgId))
                //     .ToListAsync(cancellationToken);
                _logger.LogInformation($"部门等级: {string.Join(",", levels)}");

                var query = _organizeRepository.Queryable()
                    .Where(d => d.IsEnable);
                List<SystemOrganize> orgs1;
                if (isCenter)
                {
                    orgs1 = await query
                        .Where(d => levels.Contains(d.Level) && !d.IsCenter)
                        .ToListAsync(cancellationToken);
                }
                else
                {
                    var upLevels = levels.Where(d => d <= _sessionContext.OrgLevel).ToList();
                    var lowLevels = levels.Where(d => d > _sessionContext.OrgLevel).ToList();
                    orgs1 = await query
                        .Where(d => (upLevels.Contains(d.Level) && d.Id.StartsWith(levelOneOrgId)) ||
                                    (lowLevels.Contains(d.Level) && d.Id.Contains(orgId)))
                        .ToListAsync(cancellationToken);
                }

                handlers = orgs1.Select(d => new FlowStepHandler
                {
                    Key = d.Id,
                    Value = d.Name,
                    OrgId = d.Id,
                    OrgName = d.Name
                })
                    .ToList();
                break;
            case EHandlerType.OrgType:
                var types = stepDefine.HandlerTypeItems.Select(d => d.Key)
                    .Select(d => Enum.Parse<EOrgType>(d));
                var orgs2 = await _organizeRepository.Queryable()
                    .Where(d => d.IsEnable && types.Contains(d.OrgType))
                    .WhereIF(!isCenter, d => d.Id.StartsWith(levelOneOrgId))
                    .ToListAsync(cancellationToken);

                handlers = orgs2.Select(d => new FlowStepHandler
                {
                    Key = d.Id,
                    Value = d.Name,
                    OrgId = d.Id,
                    OrgName = d.Name
                })
                    .ToList();
                break;
            default:
                throw new ArgumentOutOfRangeException();
        }

        return new NextStepOption
        {
            Key = stepDefine.Code,
            Value = stepDefine.Name,
            StepType = stepDefine.StepType,
            BusinessType = stepDefine.BusinessType,
            HandlerType = stepDefine.HandlerType,
            Items = handlers
        };
    }

    private NextStepOption GetCsEndStepByTargetPrev(List<WorkflowStep> steps, WorkflowStep step)
    {
        var prevStep = steps.FirstOrDefault(d => d.Id == step.PrevStepId);
        if (prevStep is null)
            throw new UserFriendlyException("未查找到会签上级节点");
        var text = prevStep.HandlerOrgIsCenter.Value
            ? "热线中心会签汇总"
            : $"{prevStep.HandlerOrgId.CalcOrgLevel().ToChinese()}级部门会签汇总";
        var handlers = prevStep.Handlers
            .Where(d => d.Key == prevStep.HandlerId || d.Key == prevStep.HandlerOrgId).ToList();
        //var handler = prevStep.GetActualHandler()?.GetHandler();
        var handler = new FlowStepHandler
        {
            Key = prevStep.HandlerId,
            Value = prevStep.HandlerName,
            UserId = prevStep.HandlerId,
            Username = prevStep.HandlerName,
            OrgId = prevStep.HandlerOrgId,
            OrgName = prevStep.HandlerOrgName
        };

        return new NextStepOption
        {
            Key = prevStep.Code,
            Value = text,
            BackToCountersignEnd = true,
            StepType = EStepType.Summary,
            BusinessType = prevStep.BusinessType,
            //HandlerType = prevStep.HandlerType,
            HandlerType = EHandlerType.AssignedUser,//指定办理人(业务需求)
            Items = new List<FlowStepHandler> { handler }//handlers //new List<Kv> { new(prevStep.HandlerId, prevStep.HandlerName) },
        };
    }

    /// <summary>
    /// 依据当前操作人及动态策略生成下一步节点
    /// </summary>
    private NextStepOption CreateDynamicStep(EDynamicPolicy policy)
    {
        int orgLevel;
        switch (policy)
        {
            case EDynamicPolicy.OrgUpCenterTop:
                orgLevel = _sessionContext.OrgLevel - 1;
                if (orgLevel < 0) orgLevel = 0;
                break;
            case EDynamicPolicy.OrgUp:
                orgLevel = _sessionContext.OrgLevel - 1;
                if (orgLevel <= 0) orgLevel = 1;
                break;
            case EDynamicPolicy.OrgDownCenterTop:
                orgLevel = _sessionContext.OrgIsCenter
                    ? 1
                    : _sessionContext.OrgLevel + 1;
                break;
            case EDynamicPolicy.OrgDown:
                orgLevel = _sessionContext.OrgLevel + 1;
                break;
            default:
                throw new ArgumentOutOfRangeException(nameof(policy), policy, null);
        }

        return new NextStepOption
        {
            Key = orgLevel.ToString(),
            Value = orgLevel == 0 ? "热线中心" : $"{orgLevel.ToChinese()}级部门",
        };
    }

    private async Task<NextStepOption> GetDynamicStepAsync(
        EDynamicPolicy policy, EStepType stepType,
        EBusinessType currentBusinessType, CancellationToken cancellationToken)
    {
        int orgLevel;
        List<FlowStepHandler> items;
        string upperOrgId;
        EBusinessType businessType;
        EFlowDirection? flowDirection = null;
        switch (policy)
        {
            case EDynamicPolicy.OrgUpCenterTop:
                orgLevel = _sessionContext.OrgLevel - 1;
                if (orgLevel < 0) orgLevel = 0;

                if (orgLevel == 0)
                {
                    businessType = EBusinessType.Send;
                    if (currentBusinessType == EBusinessType.Department)
                        flowDirection = EFlowDirection.OrgToCenter;

                    items = await _organizeRepository.Queryable()
                        .Where(d => d.IsCenter)
                        .Select(d => new FlowStepHandler
                        {
                            Key = d.Id,
                            Value = d.Name,
                            OrgId = d.Id,
                            OrgName = d.Name
                        })
                        .ToListAsync(cancellationToken);
                }
                else
                {
                    businessType = EBusinessType.Department;

                    //上级部门Id
                    upperOrgId = _sessionContext.RequiredOrgId.GetHigherOrgId(orgLevel);
                    items = await _organizeRepository.Queryable()
                        .Where(d => d.Id == upperOrgId)
                        .Select(d => new FlowStepHandler
                        {
                            Key = d.Id,
                            Value = d.Name,
                            OrgId = d.Id,
                            OrgName = d.Name
                        })
                        .ToListAsync(cancellationToken);
                }

                break;
            case EDynamicPolicy.OrgUp:
                businessType = _sessionContext.OrgIsCenter
                    ? EBusinessType.Send
                    : _sessionContext.RequiredOrgId.CalcOrgLevel() == 1
                        ? EBusinessType.Send
                        : EBusinessType.Department;
                orgLevel = _sessionContext.OrgLevel - 1;
                if (orgLevel <= 0) orgLevel = 1;
                //上级部门Id
                upperOrgId = _sessionContext.RequiredOrgId.GetHigherOrgId(orgLevel);
                items = await _organizeRepository.Queryable()
                    .Where(d => d.Id == upperOrgId)
                    .Select(d => new FlowStepHandler
                    {
                        Key = d.Id,
                        Value = d.Name,
                        OrgId = d.Id,
                        OrgName = d.Name
                    })
                    .ToListAsync(cancellationToken);
                break;
            case EDynamicPolicy.OrgDownCenterTop:
                businessType = EBusinessType.Department;
                if (_sessionContext.OrgIsCenter)
                {
                    orgLevel = 1;
                    items = await _organizeRepository.Queryable()
                        .Where(d => !d.IsCenter && d.Level == orgLevel)
                        .Select(d => new FlowStepHandler
                        {
                            Key = d.Id,
                            Value = d.Name,
                            OrgId = d.Id,
                            OrgName = d.Name
                        })
                        .ToListAsync(cancellationToken);
                }
                else
                {
                    orgLevel = _sessionContext.OrgLevel + 1;
                    items = await _organizeRepository.Queryable()
                        .Where(d => !d.IsCenter && d.Level == orgLevel &&
                                    d.Id.StartsWith(_sessionContext.RequiredOrgId))
                        .Select(d => new FlowStepHandler
                        {
                            Key = d.Id,
                            Value = d.Name,
                            OrgId = d.Id,
                            OrgName = d.Name
                        })
                        .ToListAsync(cancellationToken);
                }

                break;
            case EDynamicPolicy.OrgDown:
                businessType = EBusinessType.Department;
                orgLevel = _sessionContext.OrgLevel + 1;
                items = await _organizeRepository.Queryable()
                    .Where(d => d.Level == orgLevel && d.Id.StartsWith(_sessionContext.RequiredOrgId))
                    .Select(d => new FlowStepHandler
                    {
                        Key = d.Id,
                        Value = d.Name,
                        OrgId = d.Id,
                        OrgName = d.Name
                    })
                    .ToListAsync(cancellationToken);
                break;
            default:
                throw new ArgumentOutOfRangeException(nameof(policy), policy, null);
        }

        return new NextStepOption
        {
            Key = orgLevel.ToString(),
            Value = orgLevel == 0 ? "热线中心" : $"{orgLevel.ToChinese()}级部门",
            FlowDirection = flowDirection,
            StepType = stepType,
            BusinessType = businessType,
            HandlerType = EHandlerType.OrgLevel, //目前所有动态策略均属于部门等级
            Items = items
        };
    }

    /// <summary>
    /// 查询下一节点办理对象类型（user or org）及实际办理对象
    /// </summary>
    public async Task<FlowAssignInfo> GetNextStepFlowAssignInfoAsync(Workflow workflow, WorkflowStep currentStep,
        BasicWorkflowDto dto, StepDefine nextStepDefine, bool isNextDynamic, CancellationToken cancellationToken)
    {
        if (nextStepDefine.StepType is EStepType.End) return new();

        var isStartCountersign = dto.IsStartCountersign;
        var handlers = dto.NextHandlers.Select(d => new Kv(d.Key, d.Value)).ToList();

        if (isStartCountersign)
        {
            var assignType = FlowAssignInfo.GetAssignType(dto.HandlerType, dto.NextHandlers.Any());
            //按会签策略判断,目前所有策略为org
            return FlowAssignInfo.Create(assignType, handlers, isStartCountersign);
        }

        //if (currentStep.IsInCountersign() && !currentStep.IsTopCountersignEndStep(workflow.TopCountersignStepId))
        //    return FlowAssignInfo.Create(EFlowAssignType.Org, handlers, isStartCountersign);

        if (currentStep.IsInCountersign())
        {
            if (currentStep.IsCountersignEndStep)
            {
                if (!currentStep.IsTopCountersignEndStep(workflow.TopCountersignStepId))
                {
                    //汇总节点（非顶级）
                    //var csStartStep = workflow.Steps.FirstOrDefault(d => d.Id == currentStep.CountersignStartStepId);
                    //if (csStartStep is null)
                    //    throw new UserFriendlyException("未查询到会签开始节点");
                    var csStartStep = GetCsLoopStartStep(workflow, currentStep);

                    var prevStep = workflow.Steps.FirstOrDefault(d => d.Id == csStartStep.PrevStepId);
                    if (prevStep is null)
                        throw new UserFriendlyException("未查询到目标节点的前一节点");

                    return FlowAssignInfo.Create(prevStep.FlowAssignType.Value, prevStep.Handlers, isStartCountersign);
                }
            }
            else
            {
                if (dto.BackToCountersignEnd)
                {
                    var prevStep = workflow.Steps.FirstOrDefault(d => d.Id == currentStep.PrevStepId);
                    if (prevStep is null)
                        throw new UserFriendlyException($"未查询到当前节点的上级节点");
                    return FlowAssignInfo.Create(prevStep.FlowAssignType.Value, prevStep.Handlers, isStartCountersign);
                }
                else
                {
                    var assignType = FlowAssignInfo.GetAssignType(dto.HandlerType, dto.NextHandlers.Any());
                    //按会签策略判断,目前所有策略为org
                    return FlowAssignInfo.Create(assignType, handlers, isStartCountersign);
                }
            }
        }

        if (isNextDynamic)
            return FlowAssignInfo.Create(EFlowAssignType.Org, handlers, isStartCountersign);

        return await GetNextStepFlowAssignInfoByDefineAsync(nextStepDefine, dto.HandlerType, isStartCountersign, handlers,
            cancellationToken);
    }

    //private async ValueTask<List<WorkflowStepHandler>> GetNextStepHandlersAsync(Workflow workflow,
    //    StepDefine nextStepDefine, BasicWorkflowDto dto, CancellationToken cancellationToken)
    //{
    //    var assignType = FlowAssignInfo.GetAssignType(dto.HandlerType);
    //    //var assignType = AssignInfo.GetAssignType(nextStepDefine.HandlerType, dto.NextHandlers.Any());
    //    switch (assignType)
    //    {
    //        case EFlowAssignType.Org:
    //            return dto.NextHandlers.Select(d => WorkflowStepHandler.Create(workflow.Id, workflow.ExternalId,
    //                assignType, orgId: d.Key, orgName: d.Value)).ToList();
    //        case EFlowAssignType.User:
    //            if (!dto.NextHandlers.Any() && dto.HandlerType is EHandlerType.Role)
    //            {
    //                var stepOption = await GetConfigStepAsync(EFlowType.Handle, nextStepDefine, cancellationToken);
    //                var uIds = stepOption.Items.Select(d => d.Key).ToList();
    //                var users1 = await _userRepository.Queryable()
    //                    .Includes(d => d.Organization)
    //                    .Where(d => uIds.Contains(d.Id))
    //                    .ToListAsync(cancellationToken);
    //                return users1.Select(d => WorkflowStepHandler.Create(workflow.Id, workflow.ExternalId,
    //                        assignType, d.Id, d.Name, d.OrgId, d.Organization.Name))
    //                    .ToList();
    //            }
    //            var userIds = dto.NextHandlers.Select(d => d.Key).ToList();
    //            var users = await _userRepository.Queryable()
    //                .Includes(d => d.Organization)
    //                .Where(d => userIds.Contains(d.Id))
    //                .ToListAsync(cancellationToken);
    //            return users.Select(d => WorkflowStepHandler.Create(workflow.Id, workflow.ExternalId,
    //                    assignType, d.Id, d.Name, d.OrgId, d.Organization.Name))
    //                .ToList();
    //        //case EFlowAssignType.Role:
    //        //    handlers = dto.NextHandlers.Select(d => WorkflowStepHandler.Create(workflow.Id, workflow.ExternalId,
    //        //        assignType, roleId: d.Key, roleName: d.Value)).ToList();
    //        //    break;
    //        default:
    //            throw new ArgumentOutOfRangeException();
    //    }
    //}

    /// <summary>
    /// 按流程模板配置创建下一步办理对象
    /// </summary>
    private async Task<FlowAssignInfo> GetNextStepFlowAssignInfoByDefineAsync(StepDefine nextStepDefine,
        EHandlerType handlerType, bool isStartCountersign, List<Kv> handlers, CancellationToken cancellationToken)
    {
        switch (handlerType)
        {
            case EHandlerType.Role:
                if (!handlers.Any())
                {
                    //var roles = await _roleRepository.Queryable()
                    //    .Includes(d => d.Accounts, x => x.User)
                    //    .Where(d => nextStepDefine.HandlerTypeItems.Select(x => x.Key).Contains(d.Name))
                    //    .ToListAsync(cancellationToken);
                    //handlers = roles.SelectMany(d => d.Accounts).Distinct()
                    //    .Select(d => new Kv(d.Id, d.User.Name))
                    //    .ToList();
                    handlers = nextStepDefine.HandlerTypeItems;
                    return FlowAssignInfo.Create(EFlowAssignType.Role, handlers, isStartCountersign);
                }

                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();
        }
    }


    #region private

    /// <summary>
    /// 查询流程业务模块
    /// </summary>
    /// <param name="code"></param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    /// <exception cref="UserFriendlyException"></exception>
    public async Task<WorkflowModule> GetWorkflowModuleAsync(string code, CancellationToken cancellationToken)
    {
        var wfModule = await _wfModuleCacheManager.GetWorkflowModuleAsync(code, cancellationToken);
        if (wfModule == null)
            throw UserFriendlyException.SameMessage("无效流程模块编码");
        if (wfModule.Definition is null)
            throw new UserFriendlyException($"{code} 未配置流程模板", "未配置流程模板");
        return wfModule;
    }

    /// <summary>
    /// 检查退回节点信息
    /// </summary>
    public async Task<(WorkflowStep currentStep, WorkflowStep prevStep, bool isOrgToCenter, bool isSecondToFirstOrgLevel)>
        GetPreviousInformationAsync(string workflowId, string operatorId, string operatorOrgId, string[] roles,
            CancellationToken cancellationToken)
    {
        var workflow = await _workflowDomainService.GetWorkflowAsync(workflowId, withSteps: true,
            withCountersigns: true, cancellationToken: cancellationToken);
        var (currentStep, prevStep, _) = _workflowDomainService.GetPreviousStep(workflow, operatorId, operatorOrgId, roles);
        var isOrgToCenter = currentStep.BusinessType is EBusinessType.Department &&
                            prevStep.BusinessType is EBusinessType.Center or EBusinessType.Send;
        var isSecondToFirstOrgLevel = currentStep.HandlerType is EHandlerType.OrgLevel &&
                                      currentStep.Handlers.First().Key.CheckIfOrgLevelIs(2) &&
                                      prevStep.HandlerType is EHandlerType.OrgLevel &&
                                      prevStep.Handlers.First().Key.CheckIfOrgLevelIs(1) &&
                                      !prevStep.Handlers.First().Key.IsCenter();
        return (currentStep, prevStep, isOrgToCenter, isSecondToFirstOrgLevel);
    }

    /// <summary>
    /// 开启流程直接归档
    /// </summary>
    public async Task StartToEndAsync(StartWorkflowDto dto, ISessionContext current, string externalId, DateTime? expiredTime = null,
        CancellationToken cancellationToken = default)
    {
        var wfModule = await GetWorkflowModuleAsync(dto.DefinitionModuleCode, cancellationToken);
        var definition = wfModule.Definition;
        if (definition == null)
            throw new UserFriendlyException("无效模板编码");
        if (definition.Status is not EDefinitionStatus.Enable)
            throw new UserFriendlyException("该模板不可用");

        var endStepDefine = definition.FindEndStepDefine();
        dto.NextStepCode = endStepDefine.Code;
        dto.NextStepName = endStepDefine.Name;
        dto.FlowDirection = EFlowDirection.CenterToFile;

        await StartWorkflowAsync(dto, current, externalId, expiredTime, cancellationToken);
    }

    /// <summary>
    /// 更新省平台办理结果节点附件
    /// </summary>
    public async Task UpdateProvinceHandleResultFilesAsync(string workflowId, List<FileDto> files, CancellationToken cancellationToken)
    {
        var workflow = await _workflowDomainService.GetWorkflowAsync(workflowId, withSteps: true, withTraces: true,
            cancellationToken: cancellationToken);
        var step = workflow.Steps.FirstOrDefault(d =>
            //d.StepHandlers.Any(d => d.OrgId == "001171" || d.OrgId == "001178"));
            d.HandlerOrgId == "001171" || d.HandlerOrgId == "001178");
        if (step is not null)
        {
            step.FileJson = await _fileRepository.AddFileAsync(files, workflow.ExternalId, step.Id, cancellationToken);
            var trace = workflow.Traces.First(d => d.StepId == step.Id);
            trace.FileJson = step.FileJson;
            await _workflowStepRepository.UpdateAsync(step, cancellationToken);
            await _workflowTraceRepository.UpdateAsync(trace, cancellationToken);
        }
    }

    #endregion
}