You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

629 lines
23 KiB
C#

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

using System.Text;
using DS.Module.Core;
using DS.Module.Core.Extensions;
using DS.Module.UserModule;
using DS.WMS.Core.Flow.Dtos;
using DS.WMS.Core.Flow.Entity;
using DS.WMS.Core.Flow.Interface;
using DS.WMS.Core.Sys.Entity;
using DS.WMS.Core.Sys.Interface;
using Mapster;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;
using SqlSugar;
namespace DS.WMS.Core.Flow.Method;
/// <summary>
/// 工作流服务
/// </summary>
public class FlowInstanceService : IFlowInstanceService
{
private readonly IServiceProvider _serviceProvider;
protected readonly ISqlSugarClient db;
protected readonly IUser user;
private readonly ICommonService _commonService;
/// <summary>
///
/// </summary>
/// <param name="serviceProvider"></param>
public FlowInstanceService(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
db = _serviceProvider.GetRequiredService<ISqlSugarClient>();
user = _serviceProvider.GetRequiredService<IUser>();
_commonService = _serviceProvider.GetRequiredService<ICommonService>();
}
public DataResult<List<FlowInstanceRes>> GetListByPage(PageRequest request)
{
//序列化查询条件
var whereList = db.ConfigQuery.Context.Utilities.JsonToConditionalModels(request.QueryCondition);
var data = db.Queryable<FlowInstance>().Where(a => (a.MakerList == "1" || a.MakerList.Contains(user.UserId)))
.LeftJoin<SysPermission>((a, b) => a.PermissionId == b.Id)
.Select<FlowInstanceRes>()
.Where(whereList).ToQueryPage(request.PageCondition);
return data;
}
public DataResult EditFlowInstance(FlowInstanceReq req)
{
if (req.Id == 0)
{
return DataResult.Failed("非法请求!", MultiLanguageConst.IllegalRequest);
}
else
{
var info = db.Queryable<FlowInstance>().Where(x => x.Id == req.Id).First();
if (!(info.FlowStatus == FlowStatusEnum.Draft.ToEnumInt() || info.FlowStatus == FlowStatusEnum.Ready.ToEnumInt()))
{
return DataResult.Failed("只能修改【就绪】和【撤销】状态的流程", MultiLanguageConst.FlowEditOnlyReadyAndCancel);
}
info = req.Adapt(info);
db.Updateable(info).IgnoreColumns(ignoreAllNullColumns: true).ExecuteCommand();
return DataResult.Successed("更新成功!", MultiLanguageConst.DataUpdateSuccess);
}
}
protected virtual FlowInstance BuildInstance(CreateFlowInstanceReq req)
{
if (string.IsNullOrEmpty(req.TemplateId.ToString()))
return null;
FlowTemplate template = db.Queryable<FlowTemplate>().First(x => x.Id == req.TemplateId);
return template == null ? null : new FlowInstance
{
CustomName = template.Name,
TemplateId = template.Id,
BusinessId = req.BusinessId,
BusinessType = req.BusinessType,
PermissionId = template.PermissionId,
ColumnView = template.ColumnView,
Content = template.Content,
CallbackURL = template.CallbackURL,
AuditType = template.AuditType
};
}
/// <summary>
/// 创建工作流实例
/// </summary>
/// <param name="req"></param>
/// <returns></returns>
public DataResult CreateFlowInstance(CreateFlowInstanceReq req)
{
var instance = BuildInstance(req);
if (instance == null)
return DataResult.Failed("该流程模板已不存在,请重新设计流程!", MultiLanguageConst.FlowTemplateNotExist);
//创建运行实例
var wfruntime = CreateRuntimeService(instance);
if (wfruntime.CurrentNode is { AssigneeType: "user", Users.Count: > 0 })
{
if (!wfruntime.CurrentNode.Users.Contains(user.UserId))
{
return DataResult.Failed("该工作流指定用户非本人!", MultiLanguageConst.FlowInstanceAssignUser);
}
}
if (wfruntime.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 = wfruntime.CurrentNode.Roles.Intersect(userRoles).ToList();
if (intersectRoles.Count == 0)
{
return DataResult.Failed("该工作流指定角色非本人!", MultiLanguageConst.FlowInstanceAssignRole);
}
}
#region 根据运行实例改变当前节点状态
instance.ActivityId = wfruntime.CurrentNodeId;
instance.ActivityType = wfruntime.GetNodeType(wfruntime.StartNodeId);
instance.ActivityName = wfruntime.CurrentNode.Name;
instance.PreviousId = "";
instance.MakerList = GetCurrentMakers(wfruntime);
instance.FlowStatus = FlowStatusEnum.Ready.ToEnumInt();
wfruntime.FlowInstanceId = instance.Id;
#endregion 根据运行实例改变当前节点状态
db.Insertable(instance).ExecuteCommand();
//流程==4为结束执行回调URL
if (wfruntime.GetNextNodeType() == 4 && !instance.CallbackURL.IsNullOrEmpty())
{
Task.Factory.StartNew(() => RunCallbackAsync(instance));
//RunCallbackAsync(instance);
}
var userInfo = db.Queryable<SysUser>().First(x => x.Id == long.Parse(user.UserId));
var history = new FlowInstanceHistory
{
InstanceId = instance.Id,
Content = "【创建】"
+ userInfo.UserName
+ "创建了一个流程进程【"
+ instance.CustomName + "】"
};
db.Insertable(history).ExecuteCommand();
// var info = db.Queryable<FlowInstance>().Where(x => x.Id == req.Id).First();
//
// info = req.Adapt(info);
//
// db.Updateable(info).IgnoreColumns(ignoreAllNullColumns: true).ExecuteCommand();
return DataResult.Successed("创建工作流实例成功!", instance, MultiLanguageConst.FlowInstanceCreateSuccess);
}
/// <summary>
/// 撤销
/// </summary>
/// <param name="req"></param>
/// <returns></returns>
public DataResult CancelFlowInstance(CancelFlowInstanceReq req)
{
var instance = db.Queryable<FlowInstance>().First(x => x.Id == req.Id);
if (instance.IsNull())
{
return DataResult.Failed("该工作流不存在!", MultiLanguageConst.FlowInstanceNotExist);
}
if (instance.FlowStatus == FlowStatusEnum.Approve.ToEnumInt())
{
return DataResult.Failed("该工作流已完成!", MultiLanguageConst.FlowInstanceFinished);
}
instance.ActivityId = "";
//创建运行实例
var wfruntime = CreateRuntimeService(instance);
wfruntime.Cancel();
#region 根据运行实例改变当前节点状态
string startNodeId = wfruntime.StartNodeId; //起始节点
instance.PreviousId = instance.ActivityId;
instance.ActivityId = startNodeId;
instance.ActivityType = wfruntime.GetNodeType(startNodeId);
instance.ActivityName = wfruntime.ChildNodes.First(x => x.Id == startNodeId).Name;
instance.MakerList =
(wfruntime.GetNextNodeType() != 4 ? GetCurrentMakers(wfruntime) : "1");
instance.FlowStatus = FlowStatusEnum.Draft.ToEnumInt();
wfruntime.FlowInstanceId = instance.Id;
#endregion 根据运行实例改变当前节点状态
var serializerSettings = new JsonSerializerSettings
{
// 设置为驼峰命名
ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver()
};
instance.Content = JsonConvert.SerializeObject(wfruntime.ToFlowRoot(), Formatting.None, serializerSettings);
db.Updateable(instance).ExecuteCommand();
var userInfo = db.Queryable<SysUser>().First(x => x.Id == long.Parse(user.UserId));
var history = new FlowInstanceHistory
{
InstanceId = instance.Id,
Content = $"【撤销】由{userInfo.UserName}撤销,备注:{req.Note}"
};
db.Insertable(history).ExecuteCommand();
return DataResult.Successed("撤销成功!", MultiLanguageConst.FlowInstanceCancelSuccess);
}
/// <summary>
/// 获取流程操作历史
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public DataResult<List<FlowInstanceHistoryRes>> GetFlowInstanceHistoryList(string id)
{
var data = db.Queryable<FlowInstanceHistory>()
.Where(a => a.InstanceId == long.Parse(id))
.Select<FlowInstanceHistoryRes>()
.ToList();
return DataResult<List<FlowInstanceHistoryRes>>.Success(data);
}
public DataResult StartFlowInstance(string id)
{
var instance = db.Queryable<FlowInstance>().First(x => x.Id == long.Parse(id));
if (instance.IsNull())
{
return DataResult.Failed("该工作流不存在!", MultiLanguageConst.FlowInstanceNotExist);
}
if (instance.FlowStatus == FlowStatusEnum.Approve.ToEnumInt())
{
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) : "1");
instance.FlowStatus = (wfruntime.GetNextNodeType() == 4
? FlowStatusEnum.Approve.ToEnumInt()
: FlowStatusEnum.Running.ToEnumInt());
wfruntime.FlowInstanceId = instance.Id;
#endregion 根据运行实例改变当前节点状态
db.Updateable(instance).ExecuteCommand();
//流程==4为结束执行回调URL
if (wfruntime.GetNextNodeType() == 4 && !instance.CallbackURL.IsNullOrEmpty())
{
Task.Factory.StartNew(() => RunCallbackAsync(instance));
}
var userInfo = db.Queryable<SysUser>().First(x => x.Id == long.Parse(user.UserId));
var history = new FlowInstanceHistory
{
InstanceId = instance.Id,
Content = $"【启动】由{userInfo.UserName}启动该流程。"
};
db.Insertable(history).ExecuteCommand();
return DataResult.Successed("更新成功!", MultiLanguageConst.FlowInstanceUpdateSuccess);
}
public DataResult AuditFlowInstance(FlowInstanceAuditReq req)
{
var instance = db.Queryable<FlowInstance>().First(x => x.Id == req.Id);
if (instance.IsNull())
{
return DataResult.Failed("该工作流不存在!", MultiLanguageConst.FlowInstanceNotExist);
}
if (instance.FlowStatus == FlowStatusEnum.Approve.ToEnumInt())
{
return DataResult.Failed("该工作流已完成!", MultiLanguageConst.FlowInstanceFinished);
}
return AuditFlowCore(req.Status, req.AuditNote, instance);
}
protected virtual DataResult AuditFlowCore(int status, string auditNote, FlowInstance instance)
{
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 == TagState.No.ToString("D"))
{
instance.FlowStatus = FlowStatusEnum.Reject.ToEnumInt();
}
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.ToEnumInt()
: FlowStatusEnum.Running.ToEnumInt();
instance.MakerList = runtime.NextNodeType == 4 ? "1" : GetNextMakers(runtime);
}
// AddTransHistory(wfruntime);
}
else
{
//会签过程中,需要更新用户
instance.MakerList = GetForkNodeMakers(runtime, runtime.CurrentNodeId);
// AddTransHistory(wfruntime);
}
}
#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.ToEnumInt()
: FlowStatusEnum.Running.ToEnumInt());
}
else
{
instance.FlowStatus = FlowStatusEnum.Reject.ToEnumInt(); //表示该节点不同意
runtime.NextNodeId = "-1";
runtime.NextNodeType = 4;
}
}
#endregion 一般审核
var serializerSettings = new JsonSerializerSettings
{
// 设置为驼峰命名
ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver()
};
instance.Content = JsonConvert.SerializeObject(runtime.ToFlowRoot(), Formatting.None, serializerSettings);
db.Updateable(instance).ExecuteCommand();
//流程==4为结束执行回调URL
int nextNodeType = runtime.GetNextNodeType();
if (runtime.NextNodeType != 5 && (nextNodeType == 4 || nextNodeType == -1) && !instance.CallbackURL.IsNullOrEmpty())
{
instance.Note = auditNote;
Task.Factory.StartNew(() => RunCallbackAsync(instance));
}
var history = new FlowInstanceHistory
{
InstanceId = instance.Id,
Content = "【" + runtime.CurrentNode.Name
+ "】【" + DateTime.Now.ToString("yyyy-MM-dd HH:mm")
+ "】" + (tag.Taged == 1 ? "同意" : "不同意") + ",备注:"
+ tag.Description
};
db.Insertable(history).ExecuteCommand();
return DataResult.Successed("审批成功!", MultiLanguageConst.FlowInstanceAuditSuccess);
}
/// <summary>
/// 对指定的回调URL发起异步请求
/// </summary>
/// <param name="instance">运行实例</param>
protected virtual async Task RunCallbackAsync(FlowInstance instance)
{
if (!Uri.TryCreate(instance.CallbackURL, UriKind.RelativeOrAbsolute, out Uri? uri))
return;
HttpClient http = new HttpClient();
http.DefaultRequestHeaders.Add("User-Agent", "X-HttpClient");
http.DefaultRequestHeaders.Add("Authorization", "Bearer " + user.GetToken());
//请求参数设置
var callback = new FlowCallback
{
InstanceId = instance.Id,
BusinessId = instance.BusinessId,
BusinessType = instance.BusinessType,
AuditType = instance.AuditType,
FlowStatus = (FlowStatusEnum)instance.FlowStatus,
RejectReason = instance.Note
};
var jsonRequest = new StringContent(JsonConvert.SerializeObject(callback), Encoding.UTF8, "application/json");
try
{
var response = await http.PostAsync(uri, jsonRequest);
if (!response.IsSuccessStatusCode)
{
await new HttpRequestException("回调请求失败", null, response.StatusCode).LogAsync(db);
return;
}
//更新回调执行标识
instance.IsCallbackExecuted = true;
await db.Updateable(instance).UpdateColumns(x => new { x.IsCallbackExecuted }).ExecuteCommandAsync();
}
catch (Exception ex)
{
await ex.LogAsync(db);
}
finally
{
http?.Dispose();
}
}
protected virtual FlowRuntime CreateRuntimeService(FlowInstance instance)
{
return new FlowRuntime(instance, db, null, user);
}
public DataResult<FlowInstanceRes> GetFlowInstanceInfo(string id)
{
var data = db.Queryable<FlowInstance>()
// .LeftJoin<SysPermission>((a, b) => a.PermissionId == b.Id)
.Where(a => a.Id == long.Parse(id))
.Select<FlowInstanceRes>()
.First();
return DataResult<FlowInstanceRes>.Success(data);
}
/// <summary>
/// 寻找下一步的执行人
/// 一般用于本节点审核完成后,修改流程实例的当前执行人,可以做到通知等功能
/// </summary>
/// <returns></returns>
protected string GetCurrentMakers(FlowRuntime wfruntime, CreateFlowInstanceReq request = null)
{
string makerList = "";
if (wfruntime.NextNodeId == "-1")
{
throw (new Exception("无法寻找到下一个节点"));
}
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 = GenericHelper.ArrayToString(users, makerList);
makerList = string.Join(',', users);
}
else if (wfruntime.CurrentNode.AssigneeType == "user")
{
makerList = string.Join(",", wfruntime.CurrentNode.Users);
}
else
{
makerList = GetNodeMarkers(wfruntime.CurrentNode);
if (string.IsNullOrEmpty(makerList))
{
throw (new Exception("无法寻找到节点的审核者,请查看流程设计是否有问题!"));
}
}
return makerList;
}
/// <summary>
/// 寻找下一步的执行人
/// 一般用于本节点审核完成后,修改流程实例的当前执行人,可以做到通知等功能
/// </summary>
/// <returns></returns>
protected string GetNextMakers(FlowRuntime wfruntime, CreateFlowInstanceReq request = null)
{
string makerList = "";
if (wfruntime.NextNodeId == "-1")
{
throw (new Exception("无法寻找到下一个节点"));
}
// if (wfruntime.NextNodeType == 0) //如果是会签节点
// {
// makerList = GetForkNodeMakers(wfruntime, wfruntime.NextNodeId);
// } else
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 = GenericHelper.ArrayToString(users, makerList);
}
else if (wfruntime.NextNode.AssigneeType == "user")
{
makerList = string.Join(",", wfruntime.NextNode.Users);
}
else
{
makerList = GetNodeMarkers(wfruntime.NextNode);
if (string.IsNullOrEmpty(makerList))
{
throw (new Exception("无法寻找到节点的审核者,请查看流程设计是否有问题!"));
}
}
return makerList;
}
/// <summary>
/// 获取会签开始节点的所有可执行者
/// </summary>
/// <param name="forkNodeId">会签开始节点</param>
/// <returns></returns>
protected virtual string GetForkNodeMakers(FlowRuntime wfruntime, string forkNodeId)
{
string makerList = "";
var nextNode = wfruntime.NextNode;
var nextConditionNodeId = wfruntime.GetNextConditionNodeId(nextNode);
var nextConditionNode = wfruntime.ChildNodes.First(x => x.Id == nextConditionNodeId);
if (nextConditionNode.AssigneeType == "role")
{
var users = db.Queryable<SysRoleUser>().Where(x =>
nextConditionNode.Roles.Contains(x.RoleId.ToString()))
.Select(x => x.UserId).Distinct().ToList();
makerList = GenericHelper.ArrayToString(users, makerList);
}
else if (nextConditionNode.AssigneeType == "user")
{
makerList = string.Join(",", nextConditionNode.Users);
}
// foreach (string fromForkStartNodeId in wfruntime.FromNodeLines[forkNodeId].Select(u => u.to))
// {
// var fromForkStartNode = wfruntime.Nodes[fromForkStartNodeId]; //与会前开始节点直接连接的节点
// if (makerList != "")
// {
// makerList += ",";
// }
//
// makerList += GetOneForkLineMakers(fromForkStartNode, wfruntime);
// }
return makerList;
}
/// <summary>
/// 寻找该节点执行人
/// </summary>
/// <param name="node"></param>
/// <returns></returns>
protected string GetNodeMarkers(FlowChild node, string flowinstanceCreateUserId = "")
{
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 = GenericHelper.ArrayToString(users, makerList);
}
else if (node.AssigneeType == "user")
{
makerList = node.Users.ToString();
}
}
else //如果没有设置节点信息,默认所有人都可以审核
{
makerList = "1";
}
return makerList;
}
}