|
|
|
@ -1,5 +1,6 @@
|
|
|
|
|
using System.Linq.Expressions;
|
|
|
|
|
using DS.Module.Core;
|
|
|
|
|
using DS.Module.Core.Data;
|
|
|
|
|
using DS.Module.Core.Extensions;
|
|
|
|
|
using DS.WMS.Core.Flow.Dtos;
|
|
|
|
|
using DS.WMS.Core.Flow.Entity;
|
|
|
|
@ -8,20 +9,32 @@ using DS.WMS.Core.Op.Entity;
|
|
|
|
|
using DS.WMS.Core.Sys.Entity;
|
|
|
|
|
using Mapster;
|
|
|
|
|
using Masuit.Tools.Systems;
|
|
|
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
|
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
|
using SqlSugar;
|
|
|
|
|
|
|
|
|
|
namespace DS.WMS.Core.Flow.Method;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 租户端工作流实例管理
|
|
|
|
|
/// </summary>
|
|
|
|
|
public class ClientFlowInstanceService : FlowInstanceService, IClientFlowInstanceService
|
|
|
|
|
public class ClientFlowInstanceService : ServiceBase, IClientFlowInstanceService
|
|
|
|
|
{
|
|
|
|
|
static readonly ApiFox api;
|
|
|
|
|
|
|
|
|
|
static ClientFlowInstanceService()
|
|
|
|
|
{
|
|
|
|
|
api = new ApiFox();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ILogger<ClientFlowInstanceService> logger;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 初始化
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="serviceProvider"></param>
|
|
|
|
|
public ClientFlowInstanceService(IServiceProvider serviceProvider) : base(serviceProvider)
|
|
|
|
|
{
|
|
|
|
|
logger = serviceProvider.GetRequiredService<ILogger<ClientFlowInstanceService>>();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
@ -31,9 +44,9 @@ public class ClientFlowInstanceService : FlowInstanceService, IClientFlowInstanc
|
|
|
|
|
/// <param name="businessType">业务类型</param>
|
|
|
|
|
/// <param name="ids">业务ID</param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public async Task<bool> IsRunning(TaskBaseTypeEnum type, BusinessType? businessType = null, params long[] ids)
|
|
|
|
|
public async Task<bool> IsRunningAsync(TaskBaseTypeEnum type, BusinessType? businessType = null, params long[] ids)
|
|
|
|
|
{
|
|
|
|
|
return await Exists(type, businessType,
|
|
|
|
|
return await ExistsAsync(type, businessType,
|
|
|
|
|
x => x.FlowStatus == FlowStatusEnum.Ready || x.FlowStatus == FlowStatusEnum.Running, ids);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -45,15 +58,13 @@ public class ClientFlowInstanceService : FlowInstanceService, IClientFlowInstanc
|
|
|
|
|
/// <param name="expression">自定义查询条件</param>
|
|
|
|
|
/// <param name="ids">业务ID</param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public async Task<bool> Exists(TaskBaseTypeEnum? type = null,
|
|
|
|
|
BusinessType? businessType = null,
|
|
|
|
|
Expression<Func<FlowInstance, bool>>? expression = null,
|
|
|
|
|
params long[] ids)
|
|
|
|
|
public async Task<bool> ExistsAsync(TaskBaseTypeEnum? type = null, BusinessType? businessType = null,
|
|
|
|
|
Expression<Func<FlowInstance, bool>>? expression = null, params long[] ids)
|
|
|
|
|
{
|
|
|
|
|
if (ids == null || ids.Length == 0)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
return await Db.Queryable<FlowInstance>().Where(x => ids.Contains(x.BusinessId))
|
|
|
|
|
return await TenantDb.Queryable<FlowInstance>().Where(x => ids.Contains(x.BusinessId))
|
|
|
|
|
.WhereIF(type.HasValue, x => x.AuditType == type)
|
|
|
|
|
.WhereIF(businessType.HasValue, x => x.BusinessType == businessType)
|
|
|
|
|
.WhereIF(expression != null, expression)
|
|
|
|
@ -67,24 +78,111 @@ public class ClientFlowInstanceService : FlowInstanceService, IClientFlowInstanc
|
|
|
|
|
/// /// <param name="businessType">业务类型</param>
|
|
|
|
|
/// <param name="ids">业务ID</param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public async Task<List<FlowInstance>> GetInstanceByBSIdAsync(TaskBaseTypeEnum type, BusinessType? businessType = null, params long[] ids)
|
|
|
|
|
public async Task<List<FlowInstance>> GetInstancesAsync(TaskBaseTypeEnum type, BusinessType? businessType = null, params long[] ids)
|
|
|
|
|
{
|
|
|
|
|
if (ids == null || ids.Length == 0)
|
|
|
|
|
return [];
|
|
|
|
|
|
|
|
|
|
return await Db.Queryable<FlowInstance>().Where(x => x.AuditType == type && ids.Contains(x.BusinessId) &&
|
|
|
|
|
return await TenantDb.Queryable<FlowInstance>().Where(x => x.AuditType == type && ids.Contains(x.BusinessId) &&
|
|
|
|
|
(x.FlowStatus == FlowStatusEnum.Ready || x.FlowStatus == FlowStatusEnum.Running))
|
|
|
|
|
.WhereIF(businessType.HasValue, x => x.BusinessType == businessType)
|
|
|
|
|
.OrderByDescending(x => x.CreateTime).Take(1).ToListAsync();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected override FlowInstance? BuildInstance(CreateFlowInstanceReq req)
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 获取工作流实例信息
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="businessId">业务ID</param>
|
|
|
|
|
/// <param name="businessType">业务类型</param>
|
|
|
|
|
/// <param name="types">审批类型</param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public async Task<DataResult<List<FlowInstanceRes>>> GetInstancesByBizAsync(long businessId, BusinessType? businessType, params TaskBaseTypeEnum[]? types)
|
|
|
|
|
{
|
|
|
|
|
var query = TenantDb.Queryable<FlowInstance>().Where(x => x.BusinessId == businessId && x.FlowStatus != FlowStatusEnum.Ready)
|
|
|
|
|
.WhereIF(businessType.HasValue, x => x.BusinessType == businessType.Value)
|
|
|
|
|
.WhereIF(types != null && types.Length > 0, x => types.Contains(x.AuditType.Value));
|
|
|
|
|
|
|
|
|
|
var list = await query.Select(x => new FlowInstance
|
|
|
|
|
{
|
|
|
|
|
Id = x.Id,
|
|
|
|
|
Content = x.Content,
|
|
|
|
|
AuditType = x.AuditType,
|
|
|
|
|
FlowStatus = x.FlowStatus
|
|
|
|
|
}).ToListAsync();
|
|
|
|
|
|
|
|
|
|
var list2 = list.Select(x => x.Adapt<FlowInstanceRes>()).ToList();
|
|
|
|
|
foreach (var item in list2)
|
|
|
|
|
{
|
|
|
|
|
item.AuditTypeName = item.AuditType?.GetDescription();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return DataResult<List<FlowInstanceRes>>.Success(list2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 列表
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="request"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public async Task<DataResult<List<FlowInstanceRes>>> GetListAsync(PageRequest request)
|
|
|
|
|
{
|
|
|
|
|
//序列化查询条件
|
|
|
|
|
var whereList = request.GetConditionalModels(Db);
|
|
|
|
|
var data = await TenantDb.Queryable<FlowInstance>().Where(a => a.MakerList == "" || a.MakerList.Contains(User.UserId))
|
|
|
|
|
.LeftJoin<SysPermission>((a, b) => a.PermissionId == b.Id)
|
|
|
|
|
.Select((a, b) => new FlowInstanceRes
|
|
|
|
|
{
|
|
|
|
|
CreateTime = a.CreateTime,
|
|
|
|
|
}, true)
|
|
|
|
|
.Where(whereList).ToQueryPageAsync(request.PageCondition);
|
|
|
|
|
return data;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 确定是否为最后审批人
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="taskType">任务类型</param>
|
|
|
|
|
/// <param name="bsId">业务ID</param>
|
|
|
|
|
/// <param name="bsType">业务类型</param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public DataResult<bool> IsLastMarker(TaskBaseTypeEnum taskType, long bsId, BusinessType? bsType)
|
|
|
|
|
{
|
|
|
|
|
var flow = TenantDb.Queryable<FlowInstance>().Where(x => x.AuditType == taskType && x.BusinessId == bsId && x.BusinessType == bsType && x.FlowStatus == FlowStatusEnum.Running)
|
|
|
|
|
.Select(x => new { x.Id, x.MakerList }).First();
|
|
|
|
|
|
|
|
|
|
bool isLastMarker = default;
|
|
|
|
|
if (flow != null && flow.MakerList == User.UserId)
|
|
|
|
|
isLastMarker = true;
|
|
|
|
|
|
|
|
|
|
return DataResult<bool>.Success(isLastMarker);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task<DataResult> EditAsync(FlowInstanceReq req)
|
|
|
|
|
{
|
|
|
|
|
if (req.Id == 0)
|
|
|
|
|
{
|
|
|
|
|
return DataResult.Failed("非法请求!", MultiLanguageConst.IllegalRequest);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
var info = await TenantDb.Queryable<FlowInstance>().Where(x => x.Id == req.Id).FirstAsync();
|
|
|
|
|
if (!(info.FlowStatus == FlowStatusEnum.Draft || info.FlowStatus == FlowStatusEnum.Ready))
|
|
|
|
|
{
|
|
|
|
|
return DataResult.Failed("只能修改【就绪】和【撤销】状态的流程", MultiLanguageConst.FlowEditOnlyReadyAndCancel);
|
|
|
|
|
}
|
|
|
|
|
info = req.Adapt(info);
|
|
|
|
|
|
|
|
|
|
await TenantDb.Updateable(info).IgnoreColumns(ignoreAllNullColumns: true).ExecuteCommandAsync();
|
|
|
|
|
return DataResult.Successed("更新成功!", MultiLanguageConst.DataUpdateSuccess);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected virtual async Task<FlowInstance?> BuildInstanceAsync(CreateFlowInstanceReq req)
|
|
|
|
|
{
|
|
|
|
|
if (string.IsNullOrEmpty(req.TemplateId.ToString()))
|
|
|
|
|
return null;
|
|
|
|
|
|
|
|
|
|
FlowTemplateTenant template = Db.Queryable<FlowTemplateTenant>().First(x => x.Id == req.TemplateId);
|
|
|
|
|
|
|
|
|
|
FlowTemplate template = await TenantDb.Queryable<FlowTemplate>().FirstAsync(x => x.Id == req.TemplateId);
|
|
|
|
|
return template == null ? null : new FlowInstance
|
|
|
|
|
{
|
|
|
|
|
CustomName = template.Name,
|
|
|
|
@ -100,16 +198,581 @@ public class ClientFlowInstanceService : FlowInstanceService, IClientFlowInstanc
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected override string GetForkNodeMakers(FlowRuntime wfruntime, string forkNodeId)
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 创建工作流实例
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="req"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public async Task<DataResult> CreateAsync(CreateFlowInstanceReq req)
|
|
|
|
|
{
|
|
|
|
|
var instance = await BuildInstanceAsync(req);
|
|
|
|
|
if (instance == null)
|
|
|
|
|
return DataResult.Failed("该流程模板已不存在,请重新设计流程!", MultiLanguageConst.FlowTemplateNotExist);
|
|
|
|
|
|
|
|
|
|
string userName = User.UserName;
|
|
|
|
|
//创建运行实例
|
|
|
|
|
var runtime = CreateRuntimeService(instance);
|
|
|
|
|
if (runtime.ShouldSkip)
|
|
|
|
|
{
|
|
|
|
|
instance.FlowStatus = FlowStatusEnum.Approve;
|
|
|
|
|
instance.Note = "已设置为自动跳过工作流执行";
|
|
|
|
|
await TenantDb.Insertable(instance).ExecuteCommandAsync();
|
|
|
|
|
|
|
|
|
|
var history1 = new FlowInstanceHistory
|
|
|
|
|
{
|
|
|
|
|
InstanceId = instance.Id,
|
|
|
|
|
Content = "【创建】"
|
|
|
|
|
+ userName
|
|
|
|
|
+ "创建了一个流程进程【"
|
|
|
|
|
+ instance.CustomName + "】",
|
|
|
|
|
UserName = userName
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var history2 = new FlowInstanceHistory
|
|
|
|
|
{
|
|
|
|
|
InstanceId = instance.Id,
|
|
|
|
|
Content = "【审核】"
|
|
|
|
|
+ userName
|
|
|
|
|
+ "流程条件触发自动审核【"
|
|
|
|
|
+ instance.CustomName + "】",
|
|
|
|
|
UserName = userName
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
FlowInstanceHistory[] histories = [history1, history2];
|
|
|
|
|
await TenantDb.Insertable(histories).ExecuteCommandAsync();
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrEmpty(instance.CallbackURL))
|
|
|
|
|
await RunCallbackAsync(instance);
|
|
|
|
|
|
|
|
|
|
var result = DataResult.Successed("创建工作流实例成功!", instance, MultiLanguageConst.FlowInstanceCreateSuccess);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (runtime.CurrentNode is { AssigneeType: "user", Users.Count: > 0 })
|
|
|
|
|
{
|
|
|
|
|
if (!runtime.CurrentNode.Users.Contains(User.UserId))
|
|
|
|
|
{
|
|
|
|
|
return DataResult.Failed("该工作流指定用户非本人,没有发起审批的权限", MultiLanguageConst.FlowInstanceAssignUser);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (runtime.CurrentNode is { AssigneeType: "role", Roles.Count: > 0 })
|
|
|
|
|
{
|
|
|
|
|
var userRoles = Db.Queryable<SysRoleUser>().Where(x => x.UserId == long.Parse(User.UserId))
|
|
|
|
|
.Select(n => n.RoleId.ToString()).ToList();
|
|
|
|
|
var intersectRoles = runtime.CurrentNode.Roles.Intersect(userRoles).ToList();
|
|
|
|
|
if (intersectRoles.Count == 0)
|
|
|
|
|
{
|
|
|
|
|
return DataResult.Failed("该工作流指定角色非本人,没有发起审批的权限", MultiLanguageConst.FlowInstanceAssignRole);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#region 根据运行实例改变当前节点状态
|
|
|
|
|
|
|
|
|
|
instance.ActivityId = runtime.CurrentNodeId;
|
|
|
|
|
instance.ActivityType = runtime.GetNodeType(runtime.StartNodeId);
|
|
|
|
|
instance.ActivityName = runtime.CurrentNode.Name;
|
|
|
|
|
instance.PreviousId = "";
|
|
|
|
|
instance.MakerList = GetCurrentMakers(runtime);
|
|
|
|
|
instance.FlowStatus = FlowStatusEnum.Ready;
|
|
|
|
|
|
|
|
|
|
#endregion 根据运行实例改变当前节点状态
|
|
|
|
|
|
|
|
|
|
await TenantDb.Insertable(instance).ExecuteCommandAsync();
|
|
|
|
|
|
|
|
|
|
//流程==4为结束,执行回调URL
|
|
|
|
|
if (runtime.GetNextNodeType() == 4 && !instance.CallbackURL.IsNullOrEmpty())
|
|
|
|
|
await RunCallbackAsync(instance);
|
|
|
|
|
|
|
|
|
|
var history = new FlowInstanceHistory
|
|
|
|
|
{
|
|
|
|
|
InstanceId = instance.Id,
|
|
|
|
|
Content = "【创建】"
|
|
|
|
|
+ userName
|
|
|
|
|
+ "创建了一个流程进程【"
|
|
|
|
|
+ instance.CustomName + "】",
|
|
|
|
|
UserName = userName
|
|
|
|
|
};
|
|
|
|
|
await TenantDb.Insertable(history).ExecuteCommandAsync();
|
|
|
|
|
|
|
|
|
|
return DataResult.Successed("创建工作流实例成功!", instance, MultiLanguageConst.FlowInstanceCreateSuccess);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 获取流程操作历史
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="bsId">业务ID</param>
|
|
|
|
|
/// <param name="bsType">bsType</param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public async Task<DataResult<List<FlowInstanceHistoryRes>>> GetHistoriesAsync(long bsId, BusinessType? bsType)
|
|
|
|
|
{
|
|
|
|
|
var data = await TenantDb.Queryable<FlowInstanceHistory>()
|
|
|
|
|
.Where(x => SqlFunc.Subqueryable<FlowInstance>().Where(y => x.InstanceId == y.Id && y.BusinessId == bsId)
|
|
|
|
|
.WhereIF(bsType.HasValue, y => y.BusinessType == bsType.Value)
|
|
|
|
|
.Any())
|
|
|
|
|
.Select<FlowInstanceHistoryRes>()
|
|
|
|
|
.ToListAsync();
|
|
|
|
|
return DataResult<List<FlowInstanceHistoryRes>>.Success(data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 启动
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="id"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public async Task<DataResult> StartAsync(long id)
|
|
|
|
|
{
|
|
|
|
|
var instance = await TenantDb.Queryable<FlowInstance>().FirstAsync(x => x.Id == id);
|
|
|
|
|
if (instance == null)
|
|
|
|
|
return DataResult.Failed("该工作流不存在!", MultiLanguageConst.FlowInstanceNotExist);
|
|
|
|
|
|
|
|
|
|
if (instance.FlowStatus == FlowStatusEnum.Approve)
|
|
|
|
|
return DataResult.Failed("该工作流已完成!", MultiLanguageConst.FlowInstanceFinished);
|
|
|
|
|
|
|
|
|
|
//创建运行实例
|
|
|
|
|
var wfruntime = CreateRuntimeService(instance);
|
|
|
|
|
|
|
|
|
|
#region 根据运行实例改变当前节点状态
|
|
|
|
|
|
|
|
|
|
instance.ActivityId = wfruntime.NextNodeId;
|
|
|
|
|
instance.ActivityType = wfruntime.GetNextNodeType();
|
|
|
|
|
instance.ActivityName = wfruntime.NextNode.Name;
|
|
|
|
|
instance.PreviousId = wfruntime.CurrentNodeId;
|
|
|
|
|
instance.MakerList = wfruntime.GetNextNodeType() != 4 ? GetNextMakers(wfruntime) : "";
|
|
|
|
|
instance.FlowStatus = wfruntime.GetNextNodeType() == 4 ? FlowStatusEnum.Approve : FlowStatusEnum.Running;
|
|
|
|
|
|
|
|
|
|
#endregion 根据运行实例改变当前节点状态
|
|
|
|
|
|
|
|
|
|
if (string.IsNullOrEmpty(instance.MakerList))
|
|
|
|
|
{
|
|
|
|
|
instance.FlowStatus = FlowStatusEnum.Approve;
|
|
|
|
|
var template = await TenantDb.Queryable<FlowTemplate>().Where(x => x.Id == instance.TemplateId)
|
|
|
|
|
.Select(x => new { x.MarkerNotifyURL, x.CallbackURL }).FirstAsync();
|
|
|
|
|
instance.MarkerNotifyURL = template.MarkerNotifyURL;
|
|
|
|
|
instance.CallbackURL = template.CallbackURL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await TenantDb.Updateable(instance).ExecuteCommandAsync();
|
|
|
|
|
|
|
|
|
|
//流程==4为结束,执行回调URL
|
|
|
|
|
if (wfruntime.GetNextNodeType() == 4 || instance.FlowStatus == FlowStatusEnum.Approve || instance.FlowStatus == FlowStatusEnum.Reject)
|
|
|
|
|
await RunCallbackAsync(instance);
|
|
|
|
|
|
|
|
|
|
var history = new FlowInstanceHistory
|
|
|
|
|
{
|
|
|
|
|
InstanceId = instance.Id,
|
|
|
|
|
Content = $"【启动】由{User.UserName}启动该流程。",
|
|
|
|
|
UserName = User.UserName
|
|
|
|
|
};
|
|
|
|
|
Db.Insertable(history).ExecuteCommand();
|
|
|
|
|
return DataResult.Successed("更新成功!", instance, MultiLanguageConst.FlowInstanceUpdateSuccess);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 审核工作流实例
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="req"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public async Task<DataResult> AuditAsync(FlowInstanceAuditReq req)
|
|
|
|
|
{
|
|
|
|
|
var instance = await TenantDb.Queryable<FlowInstance>().Where(x => x.Id == req.Id)
|
|
|
|
|
.InnerJoin<FlowTemplate>((f, ft) => f.TemplateId == ft.Id).Select((f, ft) => new FlowInstance
|
|
|
|
|
{
|
|
|
|
|
ActivityId = f.ActivityId,
|
|
|
|
|
ActivityName = f.ActivityName,
|
|
|
|
|
ActivityType = f.ActivityType,
|
|
|
|
|
BusinessId = f.BusinessId,
|
|
|
|
|
BusinessType = f.BusinessType,
|
|
|
|
|
CallbackURL = ft.CallbackURL,
|
|
|
|
|
MarkerNotifyURL = ft.MarkerNotifyURL,
|
|
|
|
|
ColumnView = f.ColumnView,
|
|
|
|
|
Content = f.Content,
|
|
|
|
|
FlowStatus = f.FlowStatus,
|
|
|
|
|
Id = f.Id,
|
|
|
|
|
MakerList = f.MakerList,
|
|
|
|
|
PreviousId = f.PreviousId,
|
|
|
|
|
PermissionId = f.PermissionId,
|
|
|
|
|
AuditType = ft.AuditType
|
|
|
|
|
}).FirstAsync();
|
|
|
|
|
|
|
|
|
|
if (instance == null)
|
|
|
|
|
{
|
|
|
|
|
return DataResult.Failed("该工作流不存在!", MultiLanguageConst.FlowInstanceNotExist);
|
|
|
|
|
}
|
|
|
|
|
else if (instance.FlowStatus == FlowStatusEnum.Approve)
|
|
|
|
|
{
|
|
|
|
|
return DataResult.Failed("该工作流已完成!", MultiLanguageConst.FlowInstanceFinished);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return await AuditCoreAsync(req.Status, req.AuditNote, instance);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 工作流实例审核实现
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="status">1=通过,2=驳回</param>
|
|
|
|
|
/// <param name="auditNote">审核备注</param>
|
|
|
|
|
/// <param name="instance"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
protected virtual async Task<DataResult> AuditCoreAsync(int status, string auditNote, FlowInstance instance)
|
|
|
|
|
{
|
|
|
|
|
ArgumentNullException.ThrowIfNull(instance, nameof(instance));
|
|
|
|
|
|
|
|
|
|
if (!instance.MakerList.Contains(User.UserId))
|
|
|
|
|
return DataResult.FailedWithDesc(nameof(MultiLanguageConst.AuditUnauthorization));
|
|
|
|
|
|
|
|
|
|
var tag = new FlowTag
|
|
|
|
|
{
|
|
|
|
|
UserName = User.UserName,
|
|
|
|
|
UserId = User.UserId,
|
|
|
|
|
Description = auditNote,
|
|
|
|
|
Taged = status
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var runtime = CreateRuntimeService(instance);
|
|
|
|
|
|
|
|
|
|
if (runtime.CurrentNodeId != instance.ActivityId)
|
|
|
|
|
return DataResult.Failed("该工作流审批节点与当前节点不一致!", MultiLanguageConst.FlowInstanceNodeIdConflict);
|
|
|
|
|
|
|
|
|
|
#region 会签
|
|
|
|
|
|
|
|
|
|
if (instance.ActivityType == 0) //当前节点是会签节点
|
|
|
|
|
{
|
|
|
|
|
//会签时的【当前节点】一直是会签开始节点
|
|
|
|
|
runtime.MakeTagNode(runtime.CurrentNodeId, tag); //标记审核节点状态
|
|
|
|
|
|
|
|
|
|
var res = runtime.NodeConfluence(runtime.CurrentNodeId, tag);
|
|
|
|
|
if (res == ((int)TagState.No).ToString()) //驳回
|
|
|
|
|
{
|
|
|
|
|
instance.FlowStatus = FlowStatusEnum.Reject;
|
|
|
|
|
if (runtime.NextNodeType == 4)
|
|
|
|
|
{
|
|
|
|
|
instance.PreviousId = instance.ActivityId;
|
|
|
|
|
instance.ActivityId = runtime.NextNodeId;
|
|
|
|
|
instance.ActivityType = runtime.NextNodeType;
|
|
|
|
|
instance.ActivityName = runtime.NextNode.Name;
|
|
|
|
|
instance.MakerList = runtime.NextNodeType == 4 ? string.Empty : GetNextMakers(runtime);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (!string.IsNullOrEmpty(res)) //通过
|
|
|
|
|
{
|
|
|
|
|
if (runtime.NextNodeType == 5)
|
|
|
|
|
{
|
|
|
|
|
instance.MakerList = runtime.GetOtherUsers(tag);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
instance.PreviousId = instance.ActivityId;
|
|
|
|
|
instance.ActivityId = runtime.NextNodeId;
|
|
|
|
|
instance.ActivityType = runtime.NextNodeType;
|
|
|
|
|
instance.ActivityName = runtime.NextNode.Name;
|
|
|
|
|
instance.FlowStatus = runtime.NextNodeType == 4 ? FlowStatusEnum.Approve : FlowStatusEnum.Running;
|
|
|
|
|
instance.MakerList = runtime.NextNodeType == 4 ? string.Empty : GetNextMakers(runtime);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else //继续审批
|
|
|
|
|
{
|
|
|
|
|
//会签过程中,需要更新用户
|
|
|
|
|
instance.MakerList = GetForkNodeMakers(runtime, runtime.CurrentNodeId);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var marker = FlowInstanceService.GetNextMarkers(instance);
|
|
|
|
|
//获取会签下一执行人,进行通知
|
|
|
|
|
if (!string.IsNullOrEmpty(marker) && !instance.MarkerNotifyURL.IsNullOrEmpty())
|
|
|
|
|
{
|
|
|
|
|
Task.Factory.StartNew(() => NotifyMarkerChangedAsync(status, instance, long.Parse(marker)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion 会签
|
|
|
|
|
|
|
|
|
|
#region 或签
|
|
|
|
|
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
runtime.MakeTagNode(runtime.CurrentNodeId, tag);
|
|
|
|
|
if (tag.Taged == (int)TagState.Ok)
|
|
|
|
|
{
|
|
|
|
|
instance.PreviousId = instance.ActivityId;
|
|
|
|
|
instance.ActivityId = runtime.NextNodeId;
|
|
|
|
|
instance.ActivityType = runtime.NextNodeType;
|
|
|
|
|
instance.ActivityName = runtime.NextNode.Name;
|
|
|
|
|
instance.MakerList = runtime.NextNodeType == 4 ? "1" : GetNextMakers(runtime);
|
|
|
|
|
instance.FlowStatus = runtime.NextNodeType == 4 ? FlowStatusEnum.Approve : FlowStatusEnum.Running;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
instance.FlowStatus = FlowStatusEnum.Reject; //表示该节点不同意
|
|
|
|
|
runtime.NextNodeId = string.Empty;
|
|
|
|
|
runtime.NextNodeType = 4;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion 或签
|
|
|
|
|
|
|
|
|
|
int nextNodeType = runtime.GetNextNodeType();
|
|
|
|
|
await TenantDb.Ado.BeginTranAsync();
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
//流程=4为结束,执行回调URL
|
|
|
|
|
if (runtime.NextNodeType != 5 && (nextNodeType == 4 || nextNodeType == -1))
|
|
|
|
|
{
|
|
|
|
|
instance.FlowStatus = status == 1 ? FlowStatusEnum.Approve : FlowStatusEnum.Reject;
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrEmpty(instance.CallbackURL))
|
|
|
|
|
await RunCallbackAsync(instance);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//var serializerSettings = new JsonSerializerSettings
|
|
|
|
|
//{
|
|
|
|
|
// // 设置为驼峰命名
|
|
|
|
|
// ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver()
|
|
|
|
|
//};
|
|
|
|
|
//instance.Content = JsonConvert.SerializeObject(runtime.ToFlowRoot(), Formatting.None, serializerSettings);
|
|
|
|
|
instance.Note = auditNote;
|
|
|
|
|
await TenantDb.Updateable(instance).ExecuteCommandAsync();
|
|
|
|
|
|
|
|
|
|
var history = new FlowInstanceHistory
|
|
|
|
|
{
|
|
|
|
|
InstanceId = instance.Id,
|
|
|
|
|
Content = "【" + runtime.CurrentNode.Name
|
|
|
|
|
+ "】【" + DateTime.Now.ToString("yyyy-MM-dd HH:mm")
|
|
|
|
|
+ "】" + (tag.Taged == 1 ? "同意" : "不同意") + ",备注:"
|
|
|
|
|
+ tag.Description,
|
|
|
|
|
UserName = User.UserName,
|
|
|
|
|
Result = status,
|
|
|
|
|
Note = auditNote
|
|
|
|
|
};
|
|
|
|
|
await TenantDb.Insertable(history).ExecuteCommandAsync();
|
|
|
|
|
await TenantDb.Ado.CommitTranAsync();
|
|
|
|
|
return DataResult.Successed("审核成功!", MultiLanguageConst.FlowInstanceAuditSuccess);
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
await TenantDb.Ado.RollbackTranAsync();
|
|
|
|
|
await ex.LogAsync(Db);
|
|
|
|
|
return DataResult.Failed(ex.Message, nameof(MultiLanguageConst.Operation_Failed));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 运行执行人变更回调
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="status">审批结果</param>
|
|
|
|
|
/// <param name="instance">运行实例</param>
|
|
|
|
|
/// <param name="nextUserId">下一执行人ID</param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
protected virtual async Task NotifyMarkerChangedAsync(int status, FlowInstance instance, params long[] nextUserId)
|
|
|
|
|
{
|
|
|
|
|
ArgumentNullException.ThrowIfNull(instance, nameof(instance));
|
|
|
|
|
|
|
|
|
|
//请求参数设置
|
|
|
|
|
var callback = new MarkerChangedCallback
|
|
|
|
|
{
|
|
|
|
|
InstanceId = instance.Id,
|
|
|
|
|
BusinessId = instance.BusinessId,
|
|
|
|
|
BusinessType = instance.BusinessType,
|
|
|
|
|
AuditType = instance.AuditType,
|
|
|
|
|
Status = status == 1 ? FlowStatusEnum.Approve : FlowStatusEnum.Reject,
|
|
|
|
|
NextUserId = nextUserId
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (api.DefaultHeaders.Contains("Authorization"))
|
|
|
|
|
api.DefaultHeaders.Remove("Authorization");
|
|
|
|
|
|
|
|
|
|
api.DefaultHeaders.Add("Authorization", "Bearer " + User.GetToken());
|
|
|
|
|
await api.PostAsync<DataResult>(instance.MarkerNotifyURL, callback);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 对指定的回调URL发起异步请求
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="instance">运行实例</param>
|
|
|
|
|
protected virtual async Task RunCallbackAsync(FlowInstance instance)
|
|
|
|
|
{
|
|
|
|
|
ArgumentNullException.ThrowIfNull(instance, nameof(instance));
|
|
|
|
|
|
|
|
|
|
if (string.IsNullOrEmpty(instance.CallbackURL))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
//请求参数设置
|
|
|
|
|
var callback = new FlowCallback
|
|
|
|
|
{
|
|
|
|
|
InstanceId = instance.Id,
|
|
|
|
|
BusinessId = instance.BusinessId,
|
|
|
|
|
BusinessType = instance.BusinessType,
|
|
|
|
|
AuditType = instance.AuditType,
|
|
|
|
|
FlowStatus = instance.FlowStatus,
|
|
|
|
|
RejectReason = instance.Note
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (api.DefaultHeaders.Contains("Authorization"))
|
|
|
|
|
api.DefaultHeaders.Remove("Authorization");
|
|
|
|
|
|
|
|
|
|
api.DefaultHeaders.Add("Authorization", "Bearer " + User.GetToken());
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
var result = await api.PostAsync<DataResult>(instance.CallbackURL, callback);
|
|
|
|
|
if (result.Succeeded)
|
|
|
|
|
{
|
|
|
|
|
//更新回调执行标识
|
|
|
|
|
instance.IsCallbackExecuted = true;
|
|
|
|
|
await TenantDb.Updateable(instance).UpdateColumns(x => x.IsCallbackExecuted).ExecuteCommandAsync();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
logger.LogError($"访问回调URL:{instance?.CallbackURL} 时返回了错误:" + result?.Message);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
await ex.LogAsync(Db);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 运行回调更新
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="taskType">审核类型</param>
|
|
|
|
|
/// <param name="bsId">业务ID</param>
|
|
|
|
|
/// <param name="businessType">业务类型</param>
|
|
|
|
|
/// <param name="callbackURL">指定的回调URL</param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public async Task<DataResult> RunCallbackAsync(TaskBaseTypeEnum taskType,
|
|
|
|
|
long bsId, BusinessType? businessType = null,
|
|
|
|
|
string? callbackURL = null)
|
|
|
|
|
{
|
|
|
|
|
var instance = await TenantDb.Queryable<FlowInstance>().Where(x => x.AuditType == taskType && x.BusinessId == bsId)
|
|
|
|
|
.WhereIF(businessType.HasValue, x => x.BusinessType == businessType)
|
|
|
|
|
.InnerJoin<FlowTemplate>((f, ft) => f.TemplateId == ft.Id).Select((f, ft) => new FlowInstance
|
|
|
|
|
{
|
|
|
|
|
BusinessId = f.BusinessId,
|
|
|
|
|
BusinessType = f.BusinessType,
|
|
|
|
|
CallbackURL = ft.CallbackURL,
|
|
|
|
|
MarkerNotifyURL = ft.MarkerNotifyURL,
|
|
|
|
|
FlowStatus = f.FlowStatus,
|
|
|
|
|
Id = f.Id,
|
|
|
|
|
AuditType = ft.AuditType
|
|
|
|
|
}).OrderByDescending(f => f.CreateTime).FirstAsync();
|
|
|
|
|
|
|
|
|
|
if (instance == null)
|
|
|
|
|
return DataResult.FailedWithDesc(MultiLanguageConst.EmptyData);
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrEmpty(callbackURL))
|
|
|
|
|
instance.CallbackURL = callbackURL;
|
|
|
|
|
|
|
|
|
|
await RunCallbackAsync(instance);
|
|
|
|
|
return DataResult.Success;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 创建工作流运行时
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="instance"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
protected virtual FlowRuntime CreateRuntimeService(FlowInstance instance)
|
|
|
|
|
{
|
|
|
|
|
return new FlowRuntime(instance, Db, TenantDb);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 获取工作流
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="id"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public async Task<DataResult<FlowInstanceRes>> GetAsync(long id)
|
|
|
|
|
{
|
|
|
|
|
var data = await TenantDb.Queryable<FlowInstance>()
|
|
|
|
|
.Where(a => a.Id == id)
|
|
|
|
|
.Select<FlowInstanceRes>()
|
|
|
|
|
.FirstAsync();
|
|
|
|
|
return DataResult<FlowInstanceRes>.Success(data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 寻找下一步的执行人
|
|
|
|
|
// 一般用于本节点审核完成后,修改流程实例的当前执行人,可以做到通知等功能
|
|
|
|
|
internal string GetCurrentMakers(FlowRuntime wfruntime, CreateFlowInstanceReq? request = null)
|
|
|
|
|
{
|
|
|
|
|
string makerList = "";
|
|
|
|
|
if (wfruntime.NextNodeId == "-1")
|
|
|
|
|
throw new ApplicationException("无法寻找到下一个节点");
|
|
|
|
|
|
|
|
|
|
var nextNode = wfruntime.NextNode;
|
|
|
|
|
if (wfruntime.CurrentNodeType == 0) //如果是会签节点
|
|
|
|
|
{
|
|
|
|
|
makerList = GetForkNodeMakers(wfruntime, wfruntime.NextNodeId);
|
|
|
|
|
}
|
|
|
|
|
else if (wfruntime.CurrentNode.AssigneeType == "role")
|
|
|
|
|
{
|
|
|
|
|
var users = Db.Queryable<SysRoleUser>().Where(x =>
|
|
|
|
|
wfruntime.CurrentNode.Roles.Contains(x.RoleId.ToString()))
|
|
|
|
|
.Select(x => x.UserId).Distinct().ToList();
|
|
|
|
|
|
|
|
|
|
makerList = string.Join(',', users);
|
|
|
|
|
}
|
|
|
|
|
else if (wfruntime.CurrentNode.AssigneeType == "user")
|
|
|
|
|
{
|
|
|
|
|
makerList = string.Join(",", wfruntime.CurrentNode.Users);
|
|
|
|
|
}
|
|
|
|
|
else if (wfruntime.CurrentNode.AssigneeType == FlowChild.Dynamic)
|
|
|
|
|
{
|
|
|
|
|
makerList = GetDynamicMarkers(wfruntime.CurrentNode, wfruntime);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
makerList = GetNodeMarkers(wfruntime.CurrentNode);
|
|
|
|
|
if (string.IsNullOrEmpty(makerList))
|
|
|
|
|
{
|
|
|
|
|
throw (new Exception("无法寻找到节点的审核者,请查看流程设计是否有问题!"));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return makerList;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 寻找下一步的执行人
|
|
|
|
|
// 一般用于本节点审核完成后,修改流程实例的当前执行人,可以做到通知等功能
|
|
|
|
|
internal string GetNextMakers(FlowRuntime wfruntime, CreateFlowInstanceReq? request = null)
|
|
|
|
|
{
|
|
|
|
|
string makerList = "";
|
|
|
|
|
if (wfruntime.NextNodeId == "-1")
|
|
|
|
|
throw new ApplicationException("无法寻找到下一个节点");
|
|
|
|
|
|
|
|
|
|
if (wfruntime.NextNode.AssigneeType == "role")
|
|
|
|
|
{
|
|
|
|
|
var users = Db.Queryable<SysRoleUser>().Where(x =>
|
|
|
|
|
wfruntime.NextNode.Roles.Contains(x.RoleId.ToString()))
|
|
|
|
|
.Select(x => x.UserId).Distinct().ToList();
|
|
|
|
|
makerList = string.Join(",", users);
|
|
|
|
|
}
|
|
|
|
|
else if (wfruntime.NextNode.AssigneeType == "user")
|
|
|
|
|
{
|
|
|
|
|
makerList = string.Join(",", wfruntime.NextNode.Users);
|
|
|
|
|
}
|
|
|
|
|
else if (wfruntime.NextNode.AssigneeType == FlowChild.Dynamic)
|
|
|
|
|
{
|
|
|
|
|
makerList = GetDynamicMarkers(wfruntime.NextNode, wfruntime);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
makerList = GetNodeMarkers(wfruntime.NextNode);
|
|
|
|
|
if (string.IsNullOrEmpty(makerList))
|
|
|
|
|
{
|
|
|
|
|
throw new Exception("无法寻找到节点的审核者,请查看流程设计是否有问题!");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return makerList;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 获取会签开始节点的所有可执行者
|
|
|
|
|
internal string GetForkNodeMakers(FlowRuntime wfruntime, string forkNodeId)
|
|
|
|
|
{
|
|
|
|
|
string makerList = "";
|
|
|
|
|
var nextNode = wfruntime.NextNode;
|
|
|
|
|
var nextConditionNodeId = wfruntime.GetNextConditionNodeId(nextNode);
|
|
|
|
|
var nextConditionNode = wfruntime.ChildNodes.Find(x => x.Id == nextConditionNodeId);
|
|
|
|
|
if (nextConditionNode == null)
|
|
|
|
|
return makerList;
|
|
|
|
|
var nextConditionNode = wfruntime.ChildNodes.First(x => x.Id == nextConditionNodeId);
|
|
|
|
|
|
|
|
|
|
if (nextConditionNode.AssigneeType == "role")
|
|
|
|
|
{
|
|
|
|
@ -122,43 +785,64 @@ public class ClientFlowInstanceService : FlowInstanceService, IClientFlowInstanc
|
|
|
|
|
{
|
|
|
|
|
makerList = string.Join(",", nextConditionNode.Users);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else if (nextConditionNode.AssigneeType == FlowChild.Dynamic)
|
|
|
|
|
{
|
|
|
|
|
makerList = GetDynamicMarkers(nextConditionNode, wfruntime);
|
|
|
|
|
}
|
|
|
|
|
return makerList;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected override FlowRuntime CreateRuntimeService(FlowInstance instance)
|
|
|
|
|
// 寻找该节点执行人
|
|
|
|
|
internal string GetNodeMarkers(FlowChild node, string flowinstanceCreateUserId = "")
|
|
|
|
|
{
|
|
|
|
|
return new FlowRuntime(instance, Db, TenantDb);
|
|
|
|
|
string makerList = "";
|
|
|
|
|
if (node.Type == FlowChild.START && (!string.IsNullOrEmpty(flowinstanceCreateUserId))) //如果是开始节点,通常情况下是驳回到开始了
|
|
|
|
|
{
|
|
|
|
|
makerList = flowinstanceCreateUserId;
|
|
|
|
|
}
|
|
|
|
|
else if (node.AssigneeType != null)
|
|
|
|
|
{
|
|
|
|
|
if (node.AssigneeType == "role")
|
|
|
|
|
{
|
|
|
|
|
var users = Db.Queryable<SysRoleUser>().Where(x =>
|
|
|
|
|
node.Roles.Contains(x.RoleId.ToString()))
|
|
|
|
|
.Select(x => x.UserId).Distinct().ToList();
|
|
|
|
|
makerList = string.Join(",", users);
|
|
|
|
|
}
|
|
|
|
|
else if (node.AssigneeType == "user")
|
|
|
|
|
{
|
|
|
|
|
makerList = node.Users.ToString();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else //如果没有设置节点信息,默认所有人都可以审核
|
|
|
|
|
{
|
|
|
|
|
makerList = "";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return makerList;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 获取工作流实例信息
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="businessId">业务ID</param>
|
|
|
|
|
/// <param name="businessType">业务类型</param>
|
|
|
|
|
/// <param name="types">审批类型</param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public DataResult<List<FlowInstanceRes>> GetFlowInstances(long businessId, BusinessType? businessType, params TaskBaseTypeEnum[]? types)
|
|
|
|
|
//获取动态节点的执行人
|
|
|
|
|
internal string GetDynamicMarkers(FlowChild flowNode, FlowRuntime runtime)
|
|
|
|
|
{
|
|
|
|
|
var query = Db.Queryable<FlowInstance>().Where(x => x.BusinessId == businessId && x.FlowStatus != FlowStatusEnum.Ready)
|
|
|
|
|
.WhereIF(businessType.HasValue, x => x.BusinessType == businessType.Value)
|
|
|
|
|
.WhereIF(types != null && types.Length > 0, x => types.Contains(x.AuditType.Value));
|
|
|
|
|
|
|
|
|
|
var list = query.Select(x => new FlowInstance
|
|
|
|
|
{
|
|
|
|
|
Id = x.Id,
|
|
|
|
|
Content = x.Content,
|
|
|
|
|
AuditType = x.AuditType,
|
|
|
|
|
FlowStatus = x.FlowStatus
|
|
|
|
|
}).ToList();
|
|
|
|
|
if (flowNode == null || string.IsNullOrEmpty(flowNode.MarkerSQLText) || runtime == null)
|
|
|
|
|
return string.Empty;
|
|
|
|
|
|
|
|
|
|
var list2 = list.Select(x => x.Adapt<FlowInstanceRes>()).ToList();
|
|
|
|
|
foreach (var item in list2)
|
|
|
|
|
List<string> markers = [];
|
|
|
|
|
using (var reader = TenantDb.Ado.GetDataReader(flowNode.MarkerSQLText, new SugarParameter($"@{nameof(BaseModel<long>.Id)}", runtime.BusinessId)))
|
|
|
|
|
{
|
|
|
|
|
item.AuditTypeName = item.AuditType?.GetDescription();
|
|
|
|
|
while (reader.Read())
|
|
|
|
|
markers.Add(reader.GetString(0));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return DataResult<List<FlowInstanceRes>>.Success(list2);
|
|
|
|
|
string markerList = string.Join(",", markers);
|
|
|
|
|
if (string.IsNullOrEmpty(markerList))
|
|
|
|
|
throw new ApplicationException("未能根据动态节点设置的查询语句获取工作流执行人,请检查模板配置");
|
|
|
|
|
|
|
|
|
|
TenantDb.Updateable<FlowInstance>()
|
|
|
|
|
.SetColumns(x => x.MakerList == markerList).Where(x => x.Id == runtime.InstanceId)
|
|
|
|
|
.ExecuteCommand();
|
|
|
|
|
return markerList;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
@ -176,10 +860,10 @@ public class ClientFlowInstanceService : FlowInstanceService, IClientFlowInstanc
|
|
|
|
|
if (info.Instance.FlowStatus == FlowStatusEnum.Approve)
|
|
|
|
|
return DataResult.Failed("该工作流已完成!", MultiLanguageConst.FlowInstanceFinished);
|
|
|
|
|
|
|
|
|
|
if (info.Instance.CallbackURL.IsNullOrEmpty())
|
|
|
|
|
if (string.IsNullOrEmpty(info.Instance.CallbackURL))
|
|
|
|
|
{
|
|
|
|
|
var template = Db.Queryable<FlowTemplateTenant>().Where(x => x.Id == info.Instance.TemplateId)
|
|
|
|
|
.Select(x => new { x.MarkerNotifyURL, x.CallbackURL }).First();
|
|
|
|
|
var template = await TenantDb.Queryable<FlowTemplate>().Where(x => x.Id == info.Instance.TemplateId)
|
|
|
|
|
.Select(x => new { x.MarkerNotifyURL, x.CallbackURL }).FirstAsync();
|
|
|
|
|
|
|
|
|
|
info.Instance.CallbackURL = template.CallbackURL;
|
|
|
|
|
info.Instance.MarkerNotifyURL = template.MarkerNotifyURL;
|
|
|
|
@ -199,7 +883,7 @@ public class ClientFlowInstanceService : FlowInstanceService, IClientFlowInstanc
|
|
|
|
|
public async Task<DataResult> WithdrawAsync(TaskBaseTypeEnum taskType, long[] bsIds,
|
|
|
|
|
BusinessType? businessType = null, string? note = null)
|
|
|
|
|
{
|
|
|
|
|
var instances = await GetInstanceByBSIdAsync(taskType, businessType, bsIds);
|
|
|
|
|
var instances = await GetInstancesAsync(taskType, businessType, bsIds);
|
|
|
|
|
return await WithdrawCoreAsync(instances, note);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -217,11 +901,7 @@ public class ClientFlowInstanceService : FlowInstanceService, IClientFlowInstanc
|
|
|
|
|
ActivityId = FlowChild.END,
|
|
|
|
|
MakerList = string.Empty,
|
|
|
|
|
FlowStatus = FlowStatusEnum.Draft,
|
|
|
|
|
Note = note,
|
|
|
|
|
//Deleted = true,
|
|
|
|
|
//DeleteBy = userId,
|
|
|
|
|
//DeleteTime = dt,
|
|
|
|
|
//DeleteUserName = userInfo.UserName
|
|
|
|
|
Note = note
|
|
|
|
|
}).ToList();
|
|
|
|
|
return await WithdrawCoreAsync(instances, note);
|
|
|
|
|
}
|
|
|
|
@ -229,42 +909,33 @@ public class ClientFlowInstanceService : FlowInstanceService, IClientFlowInstanc
|
|
|
|
|
internal async Task<DataResult> WithdrawCoreAsync(List<FlowInstance> instances, string? note = null)
|
|
|
|
|
{
|
|
|
|
|
ArgumentNullException.ThrowIfNull(instances, nameof(instances));
|
|
|
|
|
|
|
|
|
|
long userId = long.Parse(User.UserId);
|
|
|
|
|
var userInfo = await Db.Queryable<SysUser>().Where(x => x.Id == userId).Select(x => new { x.Id, x.UserName }).FirstAsync();
|
|
|
|
|
//DateTime dt = DateTime.Now;
|
|
|
|
|
|
|
|
|
|
await Db.Ado.BeginTranAsync();
|
|
|
|
|
await TenantDb.Ado.BeginTranAsync();
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
await Db.Updateable(instances).UpdateColumns(x => new
|
|
|
|
|
await TenantDb.Updateable(instances).UpdateColumns(x => new
|
|
|
|
|
{
|
|
|
|
|
x.ActivityId,
|
|
|
|
|
x.MakerList,
|
|
|
|
|
x.FlowStatus,
|
|
|
|
|
x.Note,
|
|
|
|
|
//x.Deleted,
|
|
|
|
|
//x.DeleteBy,
|
|
|
|
|
//x.DeleteTime,
|
|
|
|
|
//x.DeleteUserName
|
|
|
|
|
x.Note
|
|
|
|
|
}).ExecuteCommandAsync();
|
|
|
|
|
|
|
|
|
|
var historys = instances.Select(x => new FlowInstanceHistory
|
|
|
|
|
{
|
|
|
|
|
InstanceId = x.Id,
|
|
|
|
|
Content = $"【撤销】由{userInfo.UserName}撤销,备注:{note}",
|
|
|
|
|
Content = $"【撤销】由{User.UserName}撤销,备注:{note}",
|
|
|
|
|
Result = -1,
|
|
|
|
|
UserName = userInfo.UserName
|
|
|
|
|
UserName = User.UserName
|
|
|
|
|
}).ToList();
|
|
|
|
|
await Db.Insertable(historys).ExecuteCommandAsync();
|
|
|
|
|
|
|
|
|
|
await Db.Ado.CommitTranAsync();
|
|
|
|
|
await TenantDb.Insertable(historys).ExecuteCommandAsync();
|
|
|
|
|
await TenantDb.Ado.CommitTranAsync();
|
|
|
|
|
return DataResult.Successed("撤销成功!", MultiLanguageConst.FlowInstanceCancelSuccess);
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
await TenantDb.Ado.RollbackTranAsync();
|
|
|
|
|
await ex.LogAsync(Db);
|
|
|
|
|
await Db.Ado.RollbackTranAsync();
|
|
|
|
|
return DataResult.FailedWithDesc(MultiLanguageConst.Operation_Failed);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|