123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520 |
- 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.WfModules;
- using Microsoft.AspNetCore.Authorization;
- using XF.Domain.Authentications;
- using XF.Domain.Exceptions;
- using XF.Utility.EnumExtensions;
- using System.Reflection;
- using XF.Domain.Repository;
- namespace Hotline.Api.Controllers;
- /// <summary>
- /// 工作流管理
- /// </summary>
- public class WorkflowController : BaseController
- {
- private readonly IDefinitionDomainService _definitionDomainService;
- private readonly IRepository<Definition> _definitionRepository;
- private readonly IWorkflowApplication _workflowApplication;
- private readonly IWorkflowDomainService _workflowDomainService;
- private readonly IWorkflowRepository _workflowRepository;
- private readonly IRepository<User> _userRepository;
- private readonly ISystemOrganizeRepository _organizeRepository;
- private readonly IRepository<Role> _roleRepository;
- private readonly ISystemDomainService _systemDomainService;
- private readonly IWfModuleDomainService _wfModuleDomainService;
- private readonly IRepository<WorkflowModule> _wfModuleRepository;
- private readonly ISessionContext _sessionContext;
- private readonly IMapper _mapper;
- public WorkflowController(
- IDefinitionDomainService definitionDomainService,
- IRepository<Definition> definitionRepository,
- IWorkflowApplication workflowApplication,
- IWorkflowDomainService workflowDomainService,
- IWorkflowRepository workflowRepository,
- IRepository<User> userRepository,
- ISystemOrganizeRepository organizeRepository,
- IRepository<Role> roleRepository,
- ISystemDomainService systemDomainService,
- IWfModuleDomainService wfModuleDomainService,
- IRepository<WorkflowModule> wfModuleRepository,
- ISessionContext sessionContext,
- IMapper mapper)
- {
- _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;
- }
- /// <summary>
- /// 分页查询最新版本号的模板
- /// </summary>
- /// <param name="dto"></param>
- /// <returns></returns>
- [HttpGet("definition/latest")]
- public async Task<PagedDto<DefinitionDto>> 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<DefinitionDto>(query2.Count, _mapper.Map<IReadOnlyList<DefinitionDto>>(items));
- }
- /// <summary>
- /// 分页查询流程模板
- /// </summary>
- /// <param name="dto"></param>
- /// <returns></returns>
- [Permission(EPermission.FlowDefinitionQuery)]
- [HttpGet("definition")]
- public async Task<PagedDto<DefinitionDto>> 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<DefinitionDto>(total, _mapper.Map<IReadOnlyList<DefinitionDto>>(items));
- }
- /// <summary>
- /// 查询流程模板
- /// </summary>
- /// <param name="id"></param>
- /// <returns></returns>
- [Permission(EPermission.GetFlow)]
- [HttpGet("definition/{id}")]
- public async Task<DefinitionDto> GetDefinition(string id)
- {
- var definition = await _definitionRepository.GetAsync(id, HttpContext.RequestAborted);
- if (definition == null) return new();
- return _mapper.Map<DefinitionDto>(definition);
- }
- /// <summary>
- /// 新增流程模板草稿
- /// </summary>
- /// <param name="dto"></param>
- /// <returns></returns>
- [Permission(EPermission.FlowDefinitionAdd)]
- [HttpPost("definition")]
- public async Task<string> AddDefinition([FromBody] AddDefinitionDto dto)
- {
- return await _definitionDomainService.AddAsync(dto, HttpContext.RequestAborted);
- }
- /// <summary>
- /// 更新流程模板草稿
- /// </summary>
- /// <param name="dto"></param>
- /// <returns></returns>
- [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<Definition>(dto);
- await _definitionRepository.AddAsync(newDefinition, HttpContext.RequestAborted);
- }
- }
- /// <summary>
- /// 删除草稿
- /// </summary>
- /// <param name="id"></param>
- /// <returns></returns>
- /// <exception cref="UserFriendlyException"></exception>
- [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);
- }
- /// <summary>
- /// 发布(列表操作)
- /// </summary>
- /// <returns></returns>
- [Permission(EPermission.FlowDefinitionPublish)]
- [HttpPost("definition/{id}/publish")]
- public async Task Publish(string id)
- {
- await _definitionDomainService.PublishAsync(id, HttpContext.RequestAborted);
- }
- /// <summary>
- /// 发布(保存并发布)
- /// </summary>
- /// <returns></returns>
- [Obsolete]
- [Permission(EPermission.FlowDefinitionPublish)]
- [HttpPost("definition/publish")]
- public async Task Publish([FromBody] AddDefinitionDto dto)
- {
- await _definitionDomainService.PublishAsync(dto, HttpContext.RequestAborted);
- }
- /// <summary>
- /// 分页查询流程
- /// </summary>
- /// <param name="dto"></param>
- /// <returns></returns>
- [Permission(EPermission.FlowQuery)]
- [HttpGet]
- public async Task<PagedDto<WorkflowDto>> 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<WorkflowDto>(total, _mapper.Map<IReadOnlyList<WorkflowDto>>(items));
- }
- /// <summary>
- /// 查询流程办理下一步可选节点
- /// </summary>
- [HttpGet("{workflowId}/nextsteps")]
- public async Task<DefinedStepDto> GetNextStepDefine(string workflowId)
- {
- var workflow = await _workflowDomainService.GetWorkflowAsync(workflowId, true, true,
- cancellationToken: HttpContext.RequestAborted);
- var current = _workflowDomainService.FindCurrentStep(workflow);
- var nextStepDefines = workflow.Definition.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
- {
- Id = workflow.DefinitionId,
- Steps = _mapper.Map<IReadOnlyList<StepBasicDto>>(nextStepDefines),//nextStepDefines.Select(d => new KeyValuePair<string, string>(d.Code, d.Name)).ToList(),
- ExpiredTime = workflow.ExpiredTime,
- Components = current.StepBox.Components,
- };
- }
- /// <summary>
- /// 查询流程下一节点待选配置
- /// </summary>
- [HttpGet("step-options")]
- public async Task<NextStepOptionDto> 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);
- }
- /// <summary>
- /// 办理节点
- /// </summary>
- [Permission(EPermission.FlowNext)]
- [HttpPost("next")]
- public async Task Next([FromBody] NextWorkflowDto dto)
- {
- await _workflowApplication.NextAsync(dto, HttpContext.RequestAborted);
- }
- /// <summary>
- /// 退回(返回前一节点)
- /// </summary>
- [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);
- }
- /// <summary>
- /// 获取撤回可选节点
- /// </summary>
- /// <param name="workflowId"></param>
- /// <returns></returns>
- [Permission(EPermission.FlowRecall)]
- [HttpGet("{workflowId}/recall")]
- public async Task<DefinedStepDto> GetRecallSteps(string workflowId)
- {
- var workflow = await _workflowDomainService.GetWorkflowAsync(workflowId, true, true, cancellationToken: HttpContext.RequestAborted);
- var stepCodes = workflow.StepBoxes.Where(d => d.StepType != EStepType.Start && d.StepType != EStepType.End)
- .Select(d => d.Code).ToList();
- var nextStepDefines = workflow.Definition.FindStepDefines(stepCodes);
- return new DefinedStepDto
- {
- Id = workflow.DefinitionId,
- Steps = _mapper.Map<IReadOnlyList<StepBasicDto>>(nextStepDefines)//nextStepDefines.Select(d => new KeyValuePair<string, string>(d.Code, d.Name)).ToList()
- };
- }
- /// <summary>
- /// 撤回至任意节点
- /// </summary>
- /// <param name="dto"></param>
- /// <returns></returns>
- [Permission(EPermission.FlowRecall)]
- [HttpPost("recall")]
- public async Task Recall([FromBody] RecallDto dto)
- {
- await _workflowApplication.RecallAsync(dto, HttpContext.RequestAborted);
- }
- /// <summary>
- /// 获取跳转可选节点
- /// </summary>
- /// <param name="workflowId"></param>
- /// <returns></returns>
- [HttpGet("{workflowId}/jump")]
- public async Task<DefinedStepDto> GetJumpSteps(string workflowId)
- {
- var workflow = await _workflowDomainService.GetWorkflowAsync(workflowId, true, true, cancellationToken: HttpContext.RequestAborted);
- var steps = workflow.Definition.Steps.Where(d => d.StepType != EStepType.Start && d.StepType != EStepType.End).ToList();
- var nextStepDefines = workflow.Definition.FindStepDefines(steps.Select(d => d.Code));
- return new DefinedStepDto
- {
- Id = workflow.DefinitionId,
- Steps = _mapper.Map<IReadOnlyList<StepBasicDto>>(nextStepDefines)//nextStepDefines.Select(d => new KeyValuePair<string, string>(d.Code, d.Name)).ToList()
- };
- }
- /// <summary>
- /// 跳转至任意节点
- /// </summary>
- /// <param name="dto"></param>
- /// <returns></returns>
- [Permission(EPermission.FlowJump)]
- [HttpPost("jump")]
- public async Task Jump([FromBody] RecallDto dto)
- {
- await _workflowApplication.JumpAsync(dto, HttpContext.RequestAborted);
- }
- /// <summary>
- /// 获取重办可选节点
- /// </summary>
- /// <param name="workflowId"></param>
- /// <returns></returns>
- [HttpGet("{workflowId}/redo")]
- public async Task<DefinedStepDto> GetRedoSteps(string workflowId)
- {
- var workflow = await _workflowDomainService.GetWorkflowAsync(workflowId, true, true, cancellationToken: HttpContext.RequestAborted);
- var steps = workflow.Definition.Steps.Where(d => d.StepType is EStepType.Normal && d.IsOrg()).ToList();
- var nextStepDefines = workflow.Definition.FindStepDefines(steps.Select(d => d.Code));
- return new DefinedStepDto
- {
- Id = workflow.DefinitionId,
- Steps = _mapper.Map<IReadOnlyList<StepBasicDto>>(nextStepDefines)//nextStepDefines.Select(d => new KeyValuePair<string, string>(d.Code, d.Name)).ToList()
- };
- }
- /// <summary>
- /// 终止流程
- /// </summary>
- [Permission(EPermission.FlowTerminate)]
- [HttpPost("terminate")]
- public async Task Terminate([FromBody] TerminateDto dto)
- {
- await _workflowDomainService.TerminateAsync(dto, HttpContext.RequestAborted);
- }
- /// <summary>
- /// 撤销流程
- /// </summary>
- [HttpPost("cancel")]
- public async Task Cancel([FromBody] CancelDto dto)
- {
- //todo
- await _workflowDomainService.CancelAsync(dto, HttpContext.RequestAborted);
- }
- /// <summary>
- /// 补充
- /// </summary>
- /// <param name="dto"></param>
- /// <returns></returns>
- [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);
- }
- /// <summary>
- /// 查询办理类型参数
- /// </summary>
- [HttpGet("handlerclassify/{handlerType}")]
- public async Task<List<KeyValuePair<string, string>>> GetHandlerClassifies(EHandlerType handlerType)
- {
- switch (handlerType)
- {
- case EHandlerType.Role:
- var roles = await _roleRepository.QueryAsync();
- return roles.Select(d => new KeyValuePair<string, string>(d.Name, d.DisplayName)).ToList();
- case EHandlerType.OrgLevel:
- var orgs1 = await _systemDomainService.QueryOrgLevelStringOptionsAsync(HttpContext.RequestAborted);
- return orgs1.ToList();
- case EHandlerType.OrgType:
- return EnumExts.GetDescriptions<EOrgType>().Select(d => new KeyValuePair<string, string>(d.Key.ToString(), d.Value)).ToList();
- case EHandlerType.AssignOrg:
- var orgs = await _organizeRepository.GetOrgJson();
- return orgs.Select(d => new KeyValuePair<string, string>(d.OrgCode, d.OrgName)).ToList();
- case EHandlerType.AssignUser:
- default:
- throw new ArgumentOutOfRangeException(nameof(handlerType), handlerType, null);
- }
- }
- /// <summary>
- /// 查询流程流转记录
- /// </summary>
- /// <param name="workflowId"></param>
- /// <returns></returns>
- [HttpGet("{workflowId}/traces")]
- public async Task<WorkflowDto> GetWorkflowTraces(string workflowId)
- {
- var workflow = await _workflowDomainService.GetWorkflowAsync(workflowId, withTraces: true,
- cancellationToken: HttpContext.RequestAborted);
- workflow.Traces = workflow.Traces.Where(d => !string.IsNullOrEmpty(d.AcceptUserId)).ToList();
- return _mapper.Map<WorkflowDto>(workflow);
- }
- [HttpGet("base-data")]
- public async Task<dynamic> BaseData()
- {
- return new
- {
- ModuleOptions = WorkflowModuleConsts.AllModules.Select(d => new KeyValuePair<string, string>(d.Code, d.Name)),
- HandlerTypeOptions = EnumExts.GetDescriptions<EHandlerType>(),
- CountersignMode = EnumExts.GetDescriptions<ECountersignMode>().Where(d => d.Key != 1),
- BusinessPropertyOptions = EnumExts.GetDescriptions<EBusinessProperty>(),
- StepPropertiesOptions = await _systemDomainService.GetSysDicDataByCodeAsync(SysDicTypeConsts.WorkflowStepComponent, HttpContext.RequestAborted),
- PathPolicyOptions = EnumExts.GetDescriptions<EPathPolicy>(),
- };
- }
- /// <summary>
- /// 持久化新增工作流业务
- /// </summary>
- /// <returns></returns>
- [AllowAnonymous]
- [HttpGet("wfmodule/persistence")]
- public async Task PersistenceWfModule()
- {
- await _wfModuleDomainService.PersistenceModulesAsync(HttpContext.RequestAborted);
- }
- /// <summary>
- /// 查询所有工作流模块
- /// </summary>
- /// <returns></returns>
- [HttpGet("wfmodules")]
- public async Task<IReadOnlyList<WorkflowModule>> QueryWfModules()
- {
- return await _wfModuleRepository.Queryable()
- .Includes(d => d.Definition)
- .ToListAsync();
- }
- /// <summary>
- /// 为工作流业务匹配或取消流程模板
- /// </summary>
- /// <param name="dto"></param>
- /// <returns></returns>
- [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);
- }
- }
- }
|