using DS.Module.Core; using DS.Module.Core.Extensions; 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 Mapster; using Newtonsoft.Json; using SqlSugar; namespace DS.WMS.Core.Flow.Method; /// /// 工作流服务 /// public class FlowInstanceService : ServiceBase, IFlowInstanceService { static readonly ApiFox api; static FlowInstanceService() { api = new ApiFox(); } /// /// 初始化 /// /// public FlowInstanceService(IServiceProvider serviceProvider) : base(serviceProvider) { } /// /// 当工作流类型为会签时,返回首个执行人 /// /// 运行实例 /// public static string GetNextMarkers(FlowInstance instance) { string[] markers = GetMarkers(instance); if (instance.ActivityType == 0 && markers.Length > 0) return markers[0]; return string.Empty; } /// /// 获取当前工作流的所有执行人 /// /// 运行实例 /// public static string[] GetMarkers(FlowInstance instance) { if (instance.IsCompleted) return []; return instance.MakerList.Split(',', StringSplitOptions.RemoveEmptyEntries); } public DataResult> GetListByPage(PageRequest request) { //序列化查询条件 var whereList = Db.ConfigQuery.Context.Utilities.JsonToConditionalModels(request.QueryCondition); var data = Db.Queryable().Where(a => (a.MakerList == "1" || a.MakerList.Contains(User.UserId))) .LeftJoin((a, b) => a.PermissionId == b.Id) .Select() .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().Where(x => x.Id == req.Id).First(); if (!(info.FlowStatus == FlowStatusEnum.Draft || info.FlowStatus == FlowStatusEnum.Ready)) { 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().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, MarkerNotifyURL = template.MakerNotifyURL, CallbackURL = template.CallbackURL, Type = template.AuditType }; } /// /// 创建工作流实例 /// /// /// 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().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; wfruntime.FlowInstanceId = instance.Id; #endregion 根据运行实例改变当前节点状态 Db.Insertable(instance).ExecuteCommand(); //流程==4为结束,执行回调URL if (wfruntime.GetNextNodeType() == 4 && !instance.CallbackURL.IsNullOrEmpty()) { Task.Factory.StartNew(() => RunCallbackAsync(instance)); } var userInfo = Db.Queryable().First(x => x.Id == long.Parse(User.UserId)); var history = new FlowInstanceHistory { InstanceId = instance.Id, Content = "【创建】" + userInfo.UserName + "创建了一个流程进程【" + instance.CustomName + "】", UserName = userInfo.UserName }; Db.Insertable(history).ExecuteCommand(); // var info = db.Queryable().Where(x => x.Id == req.Id).First(); // // info = req.Adapt(info); // // db.Updateable(info).IgnoreColumns(ignoreAllNullColumns: true).ExecuteCommand(); return DataResult.Successed("创建工作流实例成功!", instance, MultiLanguageConst.FlowInstanceCreateSuccess); } /// /// 撤销 /// /// /// public DataResult CancelFlowInstance(CancelFlowInstanceReq req) { var instance = Db.Queryable().First(x => x.Id == req.Id); if (instance.IsNull()) { return DataResult.Failed("该工作流不存在!", MultiLanguageConst.FlowInstanceNotExist); } if (instance.FlowStatus == FlowStatusEnum.Approve) { 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; 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().First(x => x.Id == long.Parse(User.UserId)); var history = new FlowInstanceHistory { InstanceId = instance.Id, Content = $"【撤销】由{userInfo.UserName}撤销,备注:{req.Note}", Result = -1, UserName = userInfo.UserName }; Db.Insertable(history).ExecuteCommand(); return DataResult.Successed("撤销成功!", MultiLanguageConst.FlowInstanceCancelSuccess); } /// /// 获取流程操作历史 /// /// /// public DataResult> GetFlowInstanceHistoryList(string id) { var data = Db.Queryable() .Where(a => a.InstanceId == long.Parse(id)) .Select() .ToList(); return DataResult>.Success(data); } public DataResult StartFlowInstance(string id) { var instance = Db.Queryable().First(x => x.Id == long.Parse(id)); if (instance.IsNull()) { 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) : "1"); instance.FlowStatus = (wfruntime.GetNextNodeType() == 4 ? FlowStatusEnum.Approve : FlowStatusEnum.Running); 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().First(x => x.Id == long.Parse(User.UserId)); var history = new FlowInstanceHistory { InstanceId = instance.Id, Content = $"【启动】由{userInfo.UserName}启动该流程。", UserName = userInfo.UserName }; Db.Insertable(history).ExecuteCommand(); return DataResult.Successed("更新成功!", instance, MultiLanguageConst.FlowInstanceUpdateSuccess); } /// /// 审核工作流实例 /// /// /// public DataResult AuditFlowInstance(FlowInstanceAuditReq req) { var instance = GetFlowInstance(req.Id); if (instance.IsNull()) { return DataResult.Failed("该工作流不存在!", MultiLanguageConst.FlowInstanceNotExist); } else if (instance.FlowStatus == FlowStatusEnum.Approve) { return DataResult.Failed("该工作流已完成!", MultiLanguageConst.FlowInstanceFinished); } return AuditFlowCore(req.Status, req.AuditNote, instance); } /// /// 工作流实例审核实现 /// /// /// /// /// protected virtual DataResult AuditFlowCore(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 == TagState.No.ToString("D")) { instance.FlowStatus = FlowStatusEnum.Reject; } 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 ? "1" : GetNextMakers(runtime); } } else { //会签过程中,需要更新用户 instance.MakerList = GetForkNodeMakers(runtime, runtime.CurrentNodeId); } var marker = GetNextMarkers(instance); //获取会签下一执行人,进行通知 if (!string.IsNullOrEmpty(marker) && !instance.MarkerNotifyURL.IsNullOrEmpty()) { Task.Factory.StartNew(() => NotifyMakerChangedAsync(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 = "-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 userId = long.Parse(User.UserId); var userInfo = Db.Queryable().Where(x => x.Id == userId).Select(x => new { x.Id, x.UserName }).First(); 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 = userInfo?.UserName, Result = status, Note = auditNote }; Db.Insertable(history).ExecuteCommand(); return DataResult.Successed("审批成功!", MultiLanguageConst.FlowInstanceAuditSuccess); } /// /// 运行执行人变更回调 /// /// 审批结果 /// 运行实例 /// 下一执行人ID /// protected virtual async Task NotifyMakerChangedAsync(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, Type = instance.Type, 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(instance.MarkerNotifyURL, callback); } /// /// 对指定的回调URL发起异步请求 /// /// 运行实例 protected virtual async Task RunCallbackAsync(FlowInstance instance) { ArgumentNullException.ThrowIfNull(instance, nameof(instance)); if (instance.CallbackURL.IsNullOrEmpty()) return; //请求参数设置 var callback = new FlowCallback { InstanceId = instance.Id, BusinessId = instance.BusinessId, BusinessType = instance.BusinessType, Type = instance.Type, FlowStatus = instance.FlowStatus, RejectReason = instance.Note }; if (api.DefaultHeaders.Contains("Authorization")) api.DefaultHeaders.Remove("Authorization"); api.DefaultHeaders.Add("Authorization", "Bearer " + User.GetToken()); var result = await api.PostAsync(instance.CallbackURL, callback); if (result.Succeeded) { //更新回调执行标识 await Db.Updateable().SetColumns(it => new FlowInstance { IsCallbackExecuted = true }) .Where(it => it.Id == instance.Id).ExecuteCommandAsync(); } } /// /// 获取运行实例信息 /// /// /// public FlowInstance GetFlowInstance(long id) { return Db.Queryable().Where(x => x.Id == id) .InnerJoin((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, Type = f.Type }).First(); } protected virtual FlowRuntime CreateRuntimeService(FlowInstance instance) { return new FlowRuntime(instance, Db, null, User); } public DataResult GetFlowInstanceInfo(string id) { var data = Db.Queryable() // .LeftJoin((a, b) => a.PermissionId == b.Id) .Where(a => a.Id == long.Parse(id)) .Select() .First(); return DataResult.Success(data); } /// /// 寻找下一步的执行人 /// 一般用于本节点审核完成后,修改流程实例的当前执行人,可以做到通知等功能 /// /// 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().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 { makerList = GetNodeMarkers(wfruntime.CurrentNode); if (string.IsNullOrEmpty(makerList)) { throw (new Exception("无法寻找到节点的审核者,请查看流程设计是否有问题!")); } } return makerList; } /// /// 寻找下一步的执行人 /// 一般用于本节点审核完成后,修改流程实例的当前执行人,可以做到通知等功能 /// /// 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().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; } /// /// 获取会签开始节点的所有可执行者 /// /// 会签开始节点 /// 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().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; } /// /// 寻找该节点执行人 /// /// /// 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().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; } }