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; /// /// 工作流服务 /// public class FlowInstanceService : IFlowInstanceService { private readonly IServiceProvider _serviceProvider; protected readonly ISqlSugarClient db; protected readonly IUser user; private readonly ICommonService _commonService; /// /// /// /// public FlowInstanceService(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; db = _serviceProvider.GetRequiredService(); user = _serviceProvider.GetRequiredService(); _commonService = _serviceProvider.GetRequiredService(); } 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.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().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 }; } /// /// 创建工作流实例 /// /// /// 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.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().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.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().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.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().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("更新成功!", MultiLanguageConst.FlowInstanceUpdateSuccess); } public DataResult AuditFlowInstance(FlowInstanceAuditReq req) { var instance = db.Queryable().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 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); } /// /// 对指定的回调URL发起异步请求 /// /// 运行实例 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; } //更新回调执行标识 var id = instance.Id; db.Updateable().SetColumns(it => new FlowInstance { IsCallbackExecuted = true }) .Where(it => it.Id == id).ExecuteCommand(); } 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 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; } }