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;
using Hotline.File;
using Hotline.Orders;
using Hotline.Share.Dtos.Order;
using Hotline.Share.Dtos.FlowEngine.Definition;
using Microsoft.AspNetCore.Authorization;
using Hotline.Caching.Services;
using Hotline.Settings.TimeLimits;
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 _workflowStepRepository;
private readonly IRepository _wfModuleRepository;
private readonly IRepository _workflowTraceRepository;
private readonly IRepository _workflowCountersignRepository;
private readonly IRepository _workflowCountersignMemberRepository;
private readonly ISessionContext _sessionContext;
private readonly IMapper _mapper;
private readonly ISystemDicDataCacheManager _systemDicDataCacheManager;
private readonly IFileRepository _fileRepository;
private readonly ISystemDicDataCacheManager _sysDicDataCacheManager;
public WorkflowController(
IDefinitionDomainService definitionDomainService,
IRepository definitionRepository,
IWorkflowApplication workflowApplication,
IWorkflowDomainService workflowDomainService,
IWorkflowRepository workflowRepository,
IRepository userRepository,
ISystemOrganizeRepository organizeRepository,
IRepository roleRepository,
ISystemDomainService systemDomainService,
IWfModuleDomainService wfModuleDomainService,
IRepository workflowStepRepository,
IRepository wfModuleRepository,
IRepository workflowTraceRepository,
IRepository workflowCountersignRepository,
ISessionContext sessionContext,
IMapper mapper,
ISystemDicDataCacheManager systemDicDataCacheManager,
IFileRepository fileRepository,
IRepository workflowCountersignMemberRepository,
ISystemDicDataCacheManager sysDicDataCacheManager
)
{
_definitionDomainService = definitionDomainService;
_definitionRepository = definitionRepository;
_workflowApplication = workflowApplication;
_workflowDomainService = workflowDomainService;
_workflowRepository = workflowRepository;
_userRepository = userRepository;
_organizeRepository = organizeRepository;
_roleRepository = roleRepository;
_systemDomainService = systemDomainService;
_wfModuleDomainService = wfModuleDomainService;
_workflowStepRepository = workflowStepRepository;
_wfModuleRepository = wfModuleRepository;
_sessionContext = sessionContext;
_mapper = mapper;
_workflowTraceRepository = workflowTraceRepository;
_workflowCountersignRepository = workflowCountersignRepository;
_systemDicDataCacheManager = systemDicDataCacheManager;
_fileRepository = fileRepository;
_workflowCountersignMemberRepository = workflowCountersignMemberRepository;
_sysDicDataCacheManager = sysDicDataCacheManager;
}
#region definition
///
/// 分页查询最新版本号的模板
///
///
///
[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);
}
#endregion
#region WorkflowModule
///
/// 持久化新增工作流业务
///
///
[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);
}
}
#endregion
#region workflow
///
/// 分页查询流程
///
///
///
[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)
{
return await _workflowApplication.GetNextStepsAsync(workflowId, HttpContext.RequestAborted);
}
///
/// 办理节点
///
[HttpPost("next")]
public async Task Next([FromBody] NextWorkflowDto dto)
{
await _workflowApplication.NextAsync(dto, _sessionContext, cancellationToken: HttpContext.RequestAborted);
}
///
/// 退回(返回前一节点)
///
[HttpPost("previous")]
public async Task Previous([FromBody] PreviousWorkflowDto dto)
{
await _workflowApplication.PreviousAsync(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, null, 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, DateTime.Now, _sessionContext, 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, withTracesTree: true,
cancellationToken: HttpContext.RequestAborted);
var workflowDto = _mapper.Map(workflow);
if (workflowDto.Traces.Any())
{
workflowDto.Traces = await _fileRepository.WorkflowTraceRecursion(workflowDto.Traces, HttpContext.RequestAborted);
}
return workflowDto;
}
///
/// 查询被督办/催办部门
///
///
///
[HttpGet("{workflowId}/urge")]
public async Task> GetUrgeOrgs(string workflowId)
{
/*
* 非会签:当前部门上至一级部门(过期)
* 中心会签:一级部门下至所有当前办理部门(遍历所有会签分支)(过期)
*/
var workflow = await _workflowDomainService.GetWorkflowAsync(workflowId, withSteps: true,
cancellationToken: HttpContext.RequestAborted);
//所有节点的待办对象
return workflow.Steps
.Where(d => d.StepType != EStepType.Start && d.StepType != EStepType.End)
.SelectMany(d => d.Handlers)
.DistinctBy(d => d.Key)
.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 is 0 or 2 ? centerIsTop : orgs
}),
FlowTypeOptions = EnumExts.GetDescriptions()
};
}
///
/// 查询会签信息
///
///
///
[HttpGet("countersign")]
public async Task> QueryWorkflowCountersigns([FromQuery] QueryWorkflowCountersignDto dto)
{
RefAsync total = 0;
var query = _workflowCountersignRepository.Queryable()
.Includes(x => x.Members)
.LeftJoin((c, w) => c.WorkflowId == w.Id)
.InnerJoin((c, w, o) => w.ExternalId == o.Id)
.WhereIF(!_sessionContext.OrgIsCenter, (c, w, o) => c.Members.Any(m => m.Key == _sessionContext.OrgId))
.WhereIF(dto.IsProvince.HasValue, (c, w, o) => o.IsProvince == dto.IsProvince.Value)
.WhereIF(!string.IsNullOrEmpty(dto.Keyword),
(c, w, o) => o.No.Contains(dto.Keyword) || o.Title.Contains(dto.Keyword));
//if (dto.IsOnlyStarter)
// query = query.Where((c, w, o) => c.StarterId == _sessionContext.RequiredUserId);
var items = await query
.OrderByDescending((c, w, o) => o.ExpiredTime)
.Select((c, w, o) => new { c, o })
.ToPageListAsync(dto.PageIndex, dto.PageSize, total, HttpContext.RequestAborted);
var dtos = items.Select(d =>
{
var dto = _mapper.Map(d.c);
dto.Order = _mapper.Map(d.o);
return dto;
}).ToList();
return new PagedDto(total, dtos);
}
///
/// 查询会签信息
///
///
///
[HttpGet("order-countersign")]
public async Task> QueryOrderCountersigns([FromQuery] QueryOrderCountersignDto dto)
{
RefAsync total = 0;
var Role = _sessionContext.Roles;
var query = _workflowCountersignRepository.Queryable()
.Includes(x => x.Members)
.LeftJoin((c, w) => c.WorkflowId == w.Id)
.InnerJoin((c, w, o) => w.ExternalId == o.Id)
.WhereIF(!string.IsNullOrEmpty(dto.Title), (c, w, o) => o.Title.Contains(dto.Title))
.WhereIF(!string.IsNullOrEmpty(dto.OrderNo), (c, w, o) => o.No.Contains(dto.OrderNo))
.WhereIF(dto.AcceptTypes.Any(), (c, w, o) => dto.AcceptTypes.Contains(o.AcceptTypeCode)) //受理类型
.WhereIF(dto.Channels.Any(), (c, w, o) => dto.Channels.Contains(o.SourceChannelCode)) //来源渠道
.WhereIF(dto.HotspotIds.Any(), (c, w, o) => dto.HotspotIds.Contains(o.HotspotId)) //热点类型
.WhereIF(!string.IsNullOrEmpty(dto.OrgName), (c, w, o) => c.FinisherOrgName.Contains(dto.OrgName)) //接办部门
.WhereIF(dto.CounterSignType != null, (c, w, o) => c.CounterSignType == dto.CounterSignType) //会签类型
;
//发起会签:班长角色能看所有会签信件;
//派单员角色只能看到自己发起的会签信件;
//承办部门用户能看到自己发起的和同级部门用户发起的会签件
if (dto.InitiatedCountersignature.HasValue && dto.InitiatedCountersignature == true)
{
if (_sessionContext.Roles.Any(p => p == "banzhang"))
{
}
else
if (_sessionContext.Roles.Any(p => p == "paidanyuan"))
{
query = query.Where((c, w, o) => c.StarterId == _sessionContext.UserId);
}
else
{
query = query.Where((c, w, o) => c.StarterOrgId == _sessionContext.RequiredOrgId);
}
}
//会签已办(会签状态为已结束):
//班长角色能看所有已结束的会签信件;
//派单员不会办理会签件只会发起会签件所以这一点派单员角色可以忽略;
//承办部门用户能看到和同级部门用户已办理过的会签件
if (dto.HandleCountersignature.HasValue && dto.HandleCountersignature == true)
{
if (_sessionContext.Roles.Any(p => p == "banzhang"))
{
query = query.Where((c, w, o) => c.EndTime.HasValue);
}
else
if (_sessionContext.Roles.Any(p => p == "paidanyuan"))
{
query = query.Where((c, w, o) => c.Members.Any(m => m.Key == _sessionContext.RequiredOrgId) && c.EndTime.HasValue);
}
else
{
query = query.Where((c, w, o) => c.Members.Any(m => m.Key == _sessionContext.OrgId || m.Key == _sessionContext.RequiredOrgId) && c.EndTime.HasValue);
}
}
var items = await query
.OrderByDescending((c, w, o) => o.ExpiredTime)
.Select((c, w, o) => new { c, o })
.ToPageListAsync(dto.PageIndex, dto.PageSize, total, HttpContext.RequestAborted);
var dtos = items.Select(d =>
{
var dto = _mapper.Map(d.c);
dto.Order = _mapper.Map(d.o);
dto.CounterSignCount = _workflowCountersignRepository.Queryable().Where(p => p.WorkflowId == d.c.WorkflowId).CountAsync().GetAwaiter().GetResult();
return dto;
}).ToList();
return new PagedDto(total, dtos);
}
///
///
///
///
[HttpGet("order-countersign-base-data")]
public async Task