using Hotline.Application.FlowEngine; using Hotline.FlowEngine.Definitions; using Hotline.FlowEngine.Workflows; using Hotline.Identity.Roles; using Hotline.Permissions; using Hotline.Repository.SqlSugar.Extensions; using Hotline.Settings; using Hotline.Share.Dtos; using Hotline.Share.Dtos.FlowEngine; using Hotline.Share.Enums.FlowEngine; using Hotline.Users; using MapsterMapper; using Microsoft.AspNetCore.Mvc; using SqlSugar; using Hotline.FlowEngine.WorkflowModules; using XF.Domain.Authentications; using XF.Domain.Exceptions; using XF.Domain.Extensions; using XF.Utility.EnumExtensions; using XF.Domain.Repository; using Hotline.Share.Dtos.FlowEngine.Workflow; using Hotline.Caching.Interfaces; namespace Hotline.Api.Controllers; /// /// 工作流管理 /// public class WorkflowController : BaseController { private readonly IDefinitionDomainService _definitionDomainService; private readonly IRepository _definitionRepository; private readonly IWorkflowApplication _workflowApplication; private readonly IWorkflowDomainService _workflowDomainService; private readonly IWorkflowRepository _workflowRepository; private readonly IRepository _userRepository; private readonly ISystemOrganizeRepository _organizeRepository; private readonly IRepository _roleRepository; private readonly ISystemDomainService _systemDomainService; private readonly IWfModuleDomainService _wfModuleDomainService; private readonly IRepository _wfModuleRepository; private readonly ISessionContext _sessionContext; private IRepository _workflowTraceRepository; private readonly IMapper _mapper; private readonly ISystemDicDataCacheManager _systemDicDataCacheManager; public WorkflowController( IDefinitionDomainService definitionDomainService, IRepository definitionRepository, IWorkflowApplication workflowApplication, IWorkflowDomainService workflowDomainService, IWorkflowRepository workflowRepository, IRepository userRepository, ISystemOrganizeRepository organizeRepository, IRepository roleRepository, ISystemDomainService systemDomainService, IWfModuleDomainService wfModuleDomainService, IRepository wfModuleRepository, ISessionContext sessionContext, IRepository workflowTraceRepository, IMapper mapper, ISystemDicDataCacheManager systemDicDataCacheManager ) { _definitionDomainService = definitionDomainService; _definitionRepository = definitionRepository; _workflowApplication = workflowApplication; _workflowDomainService = workflowDomainService; _workflowRepository = workflowRepository; _userRepository = userRepository; _organizeRepository = organizeRepository; _roleRepository = roleRepository; _systemDomainService = systemDomainService; _wfModuleDomainService = wfModuleDomainService; _wfModuleRepository = wfModuleRepository; _sessionContext = sessionContext; _mapper = mapper; _workflowTraceRepository = workflowTraceRepository; _systemDicDataCacheManager = systemDicDataCacheManager; } /// /// 分页查询最新版本号的模板 /// /// /// [HttpGet("definition/latest")] public async Task> QueryDefinitionLatest([FromQuery] QueryDefinitionDto dto) { var query2 = await _definitionRepository.Queryable() .WhereIF(dto.Status.HasValue, d => d.Status == dto.Status) .WhereIF(!string.IsNullOrEmpty(dto.Keyword), d => d.Code.Contains(dto.Keyword) || d.Name.Contains(dto.Keyword)) .Select(d => new { i = SqlFunc.RowNumber($"{d.Version} desc", d.Code), d }) .MergeTable() .Where(d => d.i == 1) .ToListAsync(); var items = query2.Select(d => d.d).ToList(); return new PagedDto(query2.Count, _mapper.Map>(items)); } /// /// 分页查询流程模板 /// /// /// [Permission(EPermission.FlowDefinitionQuery)] [HttpGet("definition")] public async Task> QueryDefinitions([FromQuery] QueryDefinitionDto dto) { #region old version:只查询草稿、禁用以及已启用模板的最新版本 ////todo 数据量大需重构 //var query1 = await _definitionRepository.Queryable() // .Where(d => d.Status == EDefinitionStatus.Temporary) // .ToListAsync(); //var query2 = await _definitionRepository.Queryable() // .Where(d => d.Status != EDefinitionStatus.Temporary) // .Select(d => new { i = SqlFunc.RowNumber($"{d.Version} desc", d.Code), d }) // .MergeTable() // .Where(d => d.i == 1) // .ToListAsync(); //var query = query1.Union(query2.Select(d => d.d)); //var total = query.Count(); //var items = query // .OrderBy(d => d.Status) // .ThenByDescending(d => d.CreationTime) // .Skip(dto.Skip()) // .Take(dto.PageSize) // .ToList(); #endregion var (total, items) = await _definitionRepository.Queryable() .WhereIF(dto.Status.HasValue, d => d.Status == dto.Status) .WhereIF(!string.IsNullOrEmpty(dto.Keyword), d => d.Code.Contains(dto.Keyword!) || d.Name.Contains(dto.Keyword!)) .OrderBy(d => d.Status) .OrderBy(d => d.Code) .OrderByDescending(d => d.Version) .ToPagedListAsync(dto, HttpContext.RequestAborted); return new PagedDto(total, _mapper.Map>(items)); } /// /// 查询流程模板 /// /// /// [HttpGet("definition/{id}")] public async Task GetDefinition(string id) { var definition = await _definitionRepository.GetAsync(id, HttpContext.RequestAborted); if (definition == null) return new(); return _mapper.Map(definition); } /// /// 新增流程模板草稿 /// /// /// [Permission(EPermission.FlowDefinitionAdd)] [HttpPost("definition")] public async Task AddDefinition([FromBody] AddDefinitionDto dto) { return await _definitionDomainService.AddAsync(dto, HttpContext.RequestAborted); } /// /// 更新流程模板草稿 /// /// /// [Permission(EPermission.FlowDefinitionUpdate)] [HttpPut("definition")] public async Task UpdateDefinition([FromBody] UpdateDefinitionDto dto) { var definition = await _definitionRepository.GetAsync(dto.Id, HttpContext.RequestAborted); if (definition == null) throw UserFriendlyException.SameMessage("无效模板编号"); if (definition.Status == EDefinitionStatus.Temporary) { _mapper.Map(dto, definition); await _definitionRepository.UpdateAsync(definition, HttpContext.RequestAborted); } else { var newDefinition = _mapper.Map(dto); await _definitionRepository.AddAsync(newDefinition, HttpContext.RequestAborted); } } /// /// 删除草稿 /// /// /// /// [Permission(EPermission.FlowDefinitionRemove)] [HttpDelete("definition/{id}")] public async Task RemoveDefinition(string id) { var definition = await _definitionRepository.GetAsync(id, HttpContext.RequestAborted); if (definition == null) return; if (definition.Status != EDefinitionStatus.Temporary) throw new UserFriendlyException("已发布模板不能删除"); await _definitionRepository.RemoveAsync(id, false, HttpContext.RequestAborted); } /// /// 发布(列表操作) /// /// [Permission(EPermission.FlowDefinitionPublish)] [HttpPost("definition/{id}/publish")] public async Task Publish(string id) { await _definitionDomainService.PublishAsync(id, HttpContext.RequestAborted); } /// /// 发布(保存并发布) /// /// [Obsolete] [Permission(EPermission.FlowDefinitionPublish)] [HttpPost("definition/publish")] public async Task Publish([FromBody] AddDefinitionDto dto) { await _definitionDomainService.PublishAsync(dto, HttpContext.RequestAborted); } /// /// 分页查询流程 /// /// /// [HttpGet] public async Task> QueryPaged([FromQuery] QueryWorkflowPagedDto dto) { var (total, items) = await _workflowRepository.Queryable() .WhereIF(!string.IsNullOrEmpty(dto.ModuleCode), d => d.ModuleCode == dto.ModuleCode) .WhereIF(!string.IsNullOrEmpty(dto.Keyword), d => d.Id.Contains(dto.Keyword!) || d.Title.Contains(dto.Keyword!)) .OrderByDescending(d => d.CreationTime) .ToPagedListAsync(dto, HttpContext.RequestAborted); return new PagedDto(total, _mapper.Map>(items)); } /// /// 查询流程办理下一步可选节点 /// [HttpGet("{workflowId}/nextsteps")] public async Task GetNextStepDefine(string workflowId) { //var workflow = await _workflowDomainService.GetWorkflowAsync(workflowId, true, true, // cancellationToken: HttpContext.RequestAborted); //var current = _workflowDomainService.FindCurrentStep(workflow); //var nextStepDefines = workflow.WorkflowDefinition.FindStepDefines(current.StepBox.NextSteps.Select(d => d.Code)); //if (current.StepBox.PathPolicy is not EPathPolicy.None && current.StepBox.NextSteps.Count > 1) // _workflowDomainService.NextStepDefineFilter(current.StepBox.PathPolicy, nextStepDefines); //return new DefinedStepDto //{ // DefinitionId = workflow.DefinitionId, // Steps = _mapper.Map>(nextStepDefines),//nextStepDefines.Select(d => new KeyValuePair(d.Code, d.Name)).ToList(), // ExpiredTime = workflow.ExpiredTime, // Components = current.StepBox.Components, //}; return await _workflowApplication.GetNextStepsAsync(workflowId, HttpContext.RequestAborted); } ///// ///// 查询流程下一节点待选配置 ///// //[HttpGet("step-options")] //public async Task GetNextStepOptions([FromQuery] QueryNextStepOptionDto dto) //{ // var definition = await _definitionRepository.GetAsync(dto.DefineId, HttpContext.RequestAborted); // if (definition == null) // throw new UserFriendlyException("无效DefineId"); // var defineStep = definition.FindStepDefine(dto.Code); // if (defineStep is null) // throw UserFriendlyException.SameMessage("未查询到对应节点配置"); // return await _workflowApplication.GetNextStepOptionsAsync(defineStep, HttpContext.RequestAborted); //} /// /// 办理节点 /// //[Permission(EPermission.FlowNext)] [HttpPost("next")] public async Task Next([FromBody] NextWorkflowDto dto) { await _workflowApplication.NextAsync(dto, HttpContext.RequestAborted); } /// /// 退回(返回前一节点) /// //[Permission(EPermission.FlowPrevious)] [HttpPost("previous")] public async Task Previous([FromBody] PreviousWorkflowDto dto) { var workflow = await _workflowDomainService.GetWorkflowAsync(dto.WorkflowId, withSteps: true, cancellationToken: HttpContext.RequestAborted); await _workflowDomainService.PreviousAsync(workflow, dto, HttpContext.RequestAborted); } /// /// 获取撤回可选节点 /// /// /// [HttpGet("{workflowId}/recall")] public async Task GetRecallSteps(string workflowId) { return await _workflowApplication.GetRecallStepsAsync(workflowId, HttpContext.RequestAborted); } /// /// 撤回至任意节点 /// /// /// [HttpPost("recall")] public async Task Recall([FromBody] RecallDto dto) { await _workflowApplication.RecallAsync(dto, HttpContext.RequestAborted); } /// /// 获取跳转可选节点 /// /// /// [HttpGet("{workflowId}/jump")] public async Task GetJumpSteps(string workflowId) { return await _workflowApplication.GetJumpStepsAsync(workflowId, HttpContext.RequestAborted); } /// /// 跳转至任意节点(暂无需求设计) /// /// /// [HttpPost("jump")] [Obsolete] public async Task Jump([FromBody] RecallDto dto) { await _workflowApplication.JumpAsync(dto, HttpContext.RequestAborted); } /// /// 获取重办可选节点 /// /// /// [HttpGet("{workflowId}/redo")] public async Task GetRedoSteps(string workflowId) { return await _workflowApplication.GetRedoStepsAsync(workflowId, HttpContext.RequestAborted); } /// /// 终止流程 /// [HttpPost("terminate")] public async Task Terminate([FromBody] TerminateDto dto) { await _workflowDomainService.TerminateAsync(dto, HttpContext.RequestAborted); } /// /// 撤销流程 /// [HttpPost("cancel")] public async Task Cancel([FromBody] CancelDto dto) { await _workflowDomainService.CancelAsync(dto, HttpContext.RequestAborted); } /// /// 否决 /// [HttpPost("reject")] public async Task Reject([FromBody] RejectDto dto) { await _workflowApplication.RejectAsync(dto, HttpContext.RequestAborted); } /// /// 补充 /// /// /// //[Permission(EPermission.FlowSupplement)] [HttpPost("supplement")] public async Task Supplement([FromBody] SupplementDto dto) { var workflow = await _workflowDomainService.GetWorkflowAsync(dto.WorkflowId); await _workflowDomainService.SupplementAsync(workflow, dto, HttpContext.RequestAborted); } /// /// 查询办理类型参数 /// [HttpGet("handlerclassify/{handlerType}")] public async Task>> GetHandlerClassifies(EHandlerType handlerType) { switch (handlerType) { case EHandlerType.Role: var roles = await _roleRepository.QueryAsync(); return roles.Select(d => new KeyValuePair(d.Name, d.DisplayName)).ToList(); case EHandlerType.OrgLevel: var orgs1 = await _systemDomainService.QueryOrgLevelStringOptionsAsync(HttpContext.RequestAborted); return orgs1.ToList(); case EHandlerType.OrgType: return EnumExts.GetDescriptions() .Select(d => new KeyValuePair(d.Key.ToString(), d.Value)).ToList(); case EHandlerType.AssignedOrg: var orgs = await _organizeRepository.GetOrgJson(); return orgs.Select(d => new KeyValuePair(d.Id, d.Name)).ToList(); case EHandlerType.AssignedUser: default: throw new ArgumentOutOfRangeException(nameof(handlerType), handlerType, null); } } /// /// 查询流程流转记录 /// /// /// [HttpGet("{workflowId}/traces")] public async Task GetWorkflowTraces(string workflowId) { var workflow = await _workflowDomainService.GetWorkflowAsync(workflowId, cancellationToken: HttpContext.RequestAborted); workflow.Traces = await _workflowTraceRepository.Queryable() .Where(d => d.WorkflowId == workflow.Id) .OrderBy(d => d.CreationTime) .ToTreeAsync(d => d.Traces, d => d.ParentId, null); return _mapper.Map(workflow); } /// /// 查询被督办/催办部门 /// /// /// [HttpGet("{workflowId}/urge")] public async Task> GetUrgeOrgs(string workflowId) { /* * 非会签:当前部门上至一级部门 * 中心会签:一级部门下至所有当前办理部门(遍历所有会签分支) */ var workflow = await _workflowDomainService.GetWorkflowAsync(workflowId, withSteps: true, cancellationToken: HttpContext.RequestAborted); if (workflow.IsInCountersign && workflow.CounterSignType is ECounterSignType.Center) { var handlers = workflow.Steps .Where(d => d.BusinessType == EBusinessType.Send && !d.IsOrigin) .SelectMany(d => d.Handlers) .Distinct() .ToList(); return handlers.Where(d => !d.Key.IsCenter()).ToList(); } return workflow.Steps .Where(d => d.StepType != EStepType.Start && d.StepType != EStepType.End && d.IsOrigin) .SelectMany(d => d.Handlers) .Distinct() .ToList(); } [HttpGet("base-data")] public async Task BaseData() { var levels = new List { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; var orgs = levels.Select(d => new KeyValuePair(d.ToString(), $"{d.ToChinese()}级部门办理")).ToList(); var center = new KeyValuePair("0", "中心办理"); var centerIsTop = new List> { center }; centerIsTop.AddRange(orgs); return new { ModuleOptions = WorkflowModuleConsts.AllModules.Select(d => new KeyValuePair(d.Code, d.Name)), HandlerTypeOptions = EnumExts.GetDescriptions(), BusinessTypeOptions = EnumExts.GetDescriptions(), StepPropertiesOptions = _systemDicDataCacheManager.GetSysDicDataCache(SysDicTypeConsts.WorkflowStepComponent), //PathPolicyOptions = EnumExts.GetDescriptions(), ExecuteModeOptions = EnumExts.GetDescriptions(), InstanceModeOptions = EnumExts.GetDescriptions(), StepTypeOptions = EnumExts.GetDescriptions().Where(d => d.Key != 1 && d.Key != 2), DynamicPolicyOptions = EnumExts.GetDescriptions() .Select(d => new { Key = d.Key, Value = d.Value, Items = d.Key == 0 || d.Key == 2 ? centerIsTop : orgs }), FlowTypeOptions = EnumExts.GetDescriptions() }; } /// /// 持久化新增工作流业务 /// /// [HttpGet("wfmodule/persistence")] public async Task PersistenceWfModule() { await _wfModuleDomainService.PersistenceModulesAsync(HttpContext.RequestAborted); } /// /// 查询所有工作流模块 /// /// [HttpGet("wfmodules")] public async Task> QueryWfModules() { return await _wfModuleRepository.Queryable() .Includes(d => d.Definition) .ToListAsync(); } /// /// 为工作流业务匹配或取消流程模板 /// /// /// [HttpPut("wfmodule/match")] public async Task MatchDefinition([FromBody] MatchDefinitionDto dto) { if (string.IsNullOrEmpty(dto.DefinitionId)) { //取消当前已配置模板 await _wfModuleDomainService.MatchDefinitionAsync(dto, HttpContext.RequestAborted); } else { var definition = await _definitionRepository.GetAsync(dto.DefinitionId); if (definition == null) throw UserFriendlyException.SameMessage("无效模板编号"); if (definition.Status != EDefinitionStatus.Enable) throw UserFriendlyException.SameMessage("该模板未发布"); await _wfModuleDomainService.MatchDefinitionAsync(dto, HttpContext.RequestAborted); } } }