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; using DS.WMS.Core.Flow.Interface; using DS.WMS.Core.Op.Entity; 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 = request.GetConditionalModels(Db); var data = Db.Queryable().Where(a => a.MakerList == "1" || a.MakerList.Contains(User.UserId)) .LeftJoin((a, b) => a.PermissionId == b.Id) .Select((a, b) => new FlowInstanceRes { CreateTime = a.CreateTime, }, true) .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, AuditType = template.AuditType }; } /// /// 创建工作流实例 /// /// /// public DataResult CreateFlowInstance(CreateFlowInstanceReq req) { var instance = BuildInstance(req); if (instance == null) return DataResult.Failed("该流程模板已不存在,请重新设计流程!", MultiLanguageConst.FlowTemplateNotExist); var userName = Db.Queryable().Where(x => x.Id == long.Parse(User.UserId)).Select(x => x.UserName).First(); //创建运行实例 var runtime = CreateRuntimeService(instance); if (runtime.ShouldSkip) { instance.FlowStatus = FlowStatusEnum.Approve; instance.Note = "已设置为自动跳过工作流执行"; Db.Insertable(instance).ExecuteCommand(); 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]; Db.Insertable(histories).ExecuteCommand(); if (!string.IsNullOrEmpty(instance.CallbackURL)) Task.Factory.StartNew(() => 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().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 根据运行实例改变当前节点状态 Db.Insertable(instance).ExecuteCommand(); //流程==4为结束,执行回调URL if (runtime.GetNextNodeType() == 4 && !instance.CallbackURL.IsNullOrEmpty()) { Task.Factory.StartNew(() => RunCallbackAsync(instance)); } var history = new FlowInstanceHistory { InstanceId = instance.Id, Content = "【创建】" + userName + "创建了一个流程进程【" + instance.CustomName + "】", UserName = userName }; Db.Insertable(history).ExecuteCommand(); return DataResult.Successed("创建工作流实例成功!", instance, MultiLanguageConst.FlowInstanceCreateSuccess); } /// /// 撤销 /// /// /// public DataResult WithdrawFlowInstance(CancelFlowInstanceReq req) { var instance = Db.Queryable().First(x => x.Id == req.Id); if (instance == null) { 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; #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 == 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) : "1"; instance.FlowStatus = (wfruntime.GetNextNodeType() == 4 ? FlowStatusEnum.Approve : FlowStatusEnum.Running); #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 == null) { 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 == ((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 = 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(); //var serializerSettings = new JsonSerializerSettings //{ // // 设置为驼峰命名 // ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver() //}; //instance.Content = JsonConvert.SerializeObject(runtime.ToFlowRoot(), Formatting.None, serializerSettings); instance.Note = auditNote; Db.Updateable(instance).ExecuteCommand(); //流程=4为结束,执行回调URL if (runtime.NextNodeType != 5 && (nextNodeType == 4 || nextNodeType == -1) && !instance.CallbackURL.IsNullOrEmpty()) 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 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(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, 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()); var result = await api.PostAsync(instance.CallbackURL, callback); if (result.Succeeded) { //更新回调执行标识 await Db.Updateable().SetColumns(it => it.IsCallbackExecuted == true) .Where(it => it.Id == instance.Id).ExecuteCommandAsync(); } } /// /// 运行回调更新 /// /// 业务ID /// 业务类型 /// 回调URL /// public async Task RunCallbackAsync(long bsId, BusinessType? businessType = null, string? callbackURL = null) { var instance = await Db.Queryable().Where(x => x.BusinessId == bsId && x.BusinessType == businessType) .InnerJoin((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 }).FirstAsync(); if (instance == null) return DataResult.FailedWithDesc(MultiLanguageConst.EmptyData); if (!string.IsNullOrEmpty(callbackURL)) instance.CallbackURL = callbackURL; await RunCallbackAsync(instance); return DataResult.Success; } /// /// 获取运行实例信息 /// /// /// 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, AuditType = ft.AuditType }).First(); } protected virtual FlowRuntime CreateRuntimeService(FlowInstance instance) { return new FlowRuntime(instance, Db, null); } 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 ApplicationException("无法寻找到下一个节点"); 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 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; } /// /// 寻找下一步的执行人 /// 一般用于本节点审核完成后,修改流程实例的当前执行人,可以做到通知等功能 /// /// protected 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().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; } /// /// 获取会签开始节点的所有可执行者 /// /// 会签开始节点 /// 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 = string.Join(",", users); } else if (nextConditionNode.AssigneeType == "user") { makerList = string.Join(",", nextConditionNode.Users); } else if (nextConditionNode.AssigneeType == FlowChild.Dynamic) { makerList = GetDynamicMarkers(nextConditionNode, 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 = string.Join(",", users); } else if (node.AssigneeType == "user") { makerList = node.Users.ToString(); } } else //如果没有设置节点信息,默认所有人都可以审核 { makerList = "1"; } return makerList; } //获取动态节点的执行人 internal string GetDynamicMarkers(FlowChild flowNode, FlowRuntime runtime) { if (flowNode == null || string.IsNullOrEmpty(flowNode.MarkerSQLText) || runtime == null) return string.Empty; List markers = []; using (var reader = TenantDb.Ado.GetDataReader(flowNode.MarkerSQLText, new SugarParameter($"@{nameof(BaseModel.Id)}", runtime.BusinessId))) { while (reader.Read()) markers.Add(reader.GetString(0)); } string markerList = string.Join(",", markers); if (string.IsNullOrEmpty(markerList)) throw new ApplicationException("未能根据动态节点设置的查询语句获取工作流执行人,请检查模板配置"); Db.Updateable() .SetColumns(x => x.MakerList == markerList).Where(x => x.Id == runtime.InstanceId) .ExecuteCommand(); return markerList; } }