using System.Diagnostics; using System.Text.Json; using Admin.NET.Plugin.AiDOP.Entity; using Admin.NET.Plugin.AiDOP.MaterialWarehouse; using Admin.NET.Plugin.ApprovalFlow; using Admin.NET.Plugin.ApprovalFlow.Service; using Yitter.IdGenerator; using ApprovalFlowEntity = Admin.NET.Plugin.ApprovalFlow.ApprovalFlow; namespace Admin.NET.Plugin.AiDOP.Infrastructure; /// /// S5 来料检验单 检验员+主管流程种子(幂等)。 /// /// 顺序:① 角色 ROLE_S5_IQC_INSPECTOR / ROLE_S5_IQC_SUPERVISOR ② UAT 成员绑定(superAdmin.NET 同绑两角色) /// ③ ApprovalBizType(S5_IQC_INSPBILL) ④ ApprovalFlow 已发布定义(start→N1_INSPECT→N2_SUP_REVIEW→end,节点绑角色)。 /// 全部幂等:存在则跳过,不覆盖、不删除既有角色/流程。superAdmin.NET 不存在时仅告警不崩溃。 /// public static class IqcInspBillFlowSeed { public static void EnsureSeed(ISqlSugarClient db) { try { db.CodeFirst.InitTables( typeof(ApprovalBizType), typeof(ApprovalFlowEntity), typeof(ApprovalFlowVersion)); // D' SQE 处置表(CodeFirst 建表,无 SQL) db.CodeFirst.InitTables(typeof(AdoS5IqcSqeDisposition)); EnsureRoles(db); EnsureUatMembers(db); EnsureBizType(db); EnsurePublishedFlow(db); } catch (Exception ex) { Trace.TraceWarning("S5 IQC inspbill flow seed: " + ex); } } private static void EnsureRoles(ISqlSugarClient db) { EnsureRole(db, IqcInspBillFlowConst.RoleInspectorId, IqcInspBillFlowConst.RoleInspectorCode, "来料检验员", 810); EnsureRole(db, IqcInspBillFlowConst.RoleSupervisorId, IqcInspBillFlowConst.RoleSupervisorCode, "来料检验主管", 811); EnsureRole(db, IqcInspBillFlowConst.RoleSqeId, IqcInspBillFlowConst.RoleSqeCode, "来料检验SQE", 812); } private static void EnsureRole(ISqlSugarClient db, long id, string code, string name, int orderNo) { var exists = db.Queryable().ClearFilter().Any(x => x.Code == code); if (exists) return; db.Insertable(new SysRole { Id = id, TenantId = SqlSugarConst.DefaultTenantId, Name = name, Code = code, OrderNo = orderNo, DataScope = DataScopeEnum.Self, Status = StatusEnum.Enable, Remark = "S5 来料检验流程角色", CreateTime = DateTime.Now, }).ExecuteCommand(); } private static void EnsureUatMembers(ISqlSugarClient db) { var user = db.Queryable().ClearFilter() .Where(x => x.Account == IqcInspBillFlowConst.UatAccount) .First(); if (user == null) { Trace.TraceWarning($"S5 IQC flow seed: UAT 账号 {IqcInspBillFlowConst.UatAccount} 不存在,跳过成员绑定(阶段2 冒烟前须指定成员)"); return; } EnsureUserRole(db, user.Id, IqcInspBillFlowConst.RoleInspectorId); EnsureUserRole(db, user.Id, IqcInspBillFlowConst.RoleSupervisorId); EnsureUserRole(db, user.Id, IqcInspBillFlowConst.RoleSqeId); } private static void EnsureUserRole(ISqlSugarClient db, long userId, long roleId) { var exists = db.Queryable().ClearFilter().Any(x => x.UserId == userId && x.RoleId == roleId); if (exists) return; db.Insertable(new SysUserRole { Id = YitIdHelper.NextId(), UserId = userId, RoleId = roleId, }).ExecuteCommand(); } private static void EnsureBizType(ISqlSugarClient db) { var exists = db.Queryable().Any(x => x.Code == IqcInspBillFlowConst.BizType); if (exists) return; db.Insertable(new ApprovalBizType { Id = YitIdHelper.NextId(), Code = IqcInspBillFlowConst.BizType, Name = "来料检验单审批", Remark = "S5 来料检验单 检验员→主管 审批流", IsEnabled = true, CreateTime = DateTime.Now, UpdateTime = DateTime.Now, }).ExecuteCommand(); } private static void EnsurePublishedFlow(ISqlSugarClient db) { var orgIds = db.Queryable() .Select(x => x.Id) .ToList() .Where(x => x > 0) .Distinct() .ToList(); if (orgIds.Count == 0) orgIds.Add(0); var now = DateTime.Now; var flowJson = BuildFlowJson(); foreach (var orgId in orgIds) { var existing = db.Queryable() .Where(x => x.BizType == IqcInspBillFlowConst.BizType && x.OrgId == orgId && x.IsPublished && !x.IsDelete) .OrderByDescending(x => x.Version) .First(); // v1→v2 幂等升级:已发布定义若已含 N3_SQE(v2)则跳过;若仍为 v1(无 N3)则下架旧版、发布 v2。 // 历史 Running/Approved 实例使用各自 FlowJsonSnapshot,不受本次 republish 影响。 if (existing != null) { var isV2 = (existing.FlowJson ?? "").Contains(IqcInspBillFlowConst.NodeSqe) && (existing.FlowJson ?? "").Contains(IqcInspBillFlowConst.GatewayPd); if (isV2) continue; db.Updateable() .SetColumns(x => x.IsPublished == false) .SetColumns(x => x.UpdateTime == now) .Where(x => x.Id == existing.Id) .ExecuteCommand(); } var nextVersion = (existing?.Version ?? 0) + 1; var flow = new ApprovalFlowEntity { Id = YitIdHelper.NextId(), Code = $"S5IQC_{orgId}", Name = "来料检验单审批", BizType = IqcInspBillFlowConst.BizType, FormJson = "{}", FlowJson = flowJson, Status = 1, Remark = "系统默认流程 v2:开始-检验员检验-检验主管审核-网关(pd)-SQE处置-结束", Version = nextVersion, IsPublished = true, OrgId = orgId, CreateTime = now, UpdateTime = now, }; db.Insertable(flow).ExecuteCommand(); db.Insertable(new ApprovalFlowVersion { Id = YitIdHelper.NextId(), FlowId = flow.Id, Version = flow.Version, FlowJson = flow.FlowJson, FormJson = flow.FormJson, PublishTime = now, PublisherName = "系统初始化", CreateTime = now, UpdateTime = now, }).ExecuteCommand(); } } private static string BuildFlowJson() { var item = new ApprovalFlowItem { Nodes = new List { new() { Id = "start", Type = "bpmn:startEvent", X = 100, Y = 120, Properties = new FlowProperties { NodeName = "开始" }, Text = new FlowTextItem { X = 100, Y = 154, Value = "开始" }, }, new() { Id = IqcInspBillFlowConst.NodeInspect, Type = "bpmn:userTask", X = 300, Y = 120, Properties = new FlowProperties { NodeName = "检验员检验", ApproverType = nameof(ApproverTypeEnum.Role), ApproverIds = IqcInspBillFlowConst.RoleInspectorId.ToString(), ApproverNames = "来料检验员", MultiApproveMode = "Any", }, Text = new FlowTextItem { X = 300, Y = 154, Value = "检验员检验" }, }, new() { Id = IqcInspBillFlowConst.NodeSupervisor, Type = "bpmn:userTask", X = 500, Y = 120, Properties = new FlowProperties { NodeName = "检验主管审核", ApproverType = nameof(ApproverTypeEnum.Role), ApproverIds = IqcInspBillFlowConst.RoleSupervisorId.ToString(), ApproverNames = "来料检验主管", MultiApproveMode = "Any", }, Text = new FlowTextItem { X = 500, Y = 154, Value = "检验主管审核" }, }, new() { Id = IqcInspBillFlowConst.GatewayPd, Type = "bpmn:exclusiveGateway", X = 660, Y = 120, Properties = new FlowProperties { NodeName = "判定分流", Conditions = new List { new() { Expression = "pd == 1", TargetNodeId = IqcInspBillFlowConst.NodeSqe, IsDefault = false, Label = "不合格转SQE", }, new() { TargetNodeId = "end", IsDefault = true, Label = "合格完成", }, }, }, Text = new FlowTextItem { X = 660, Y = 154, Value = "判定分流" }, }, new() { Id = IqcInspBillFlowConst.NodeSqe, Type = "bpmn:userTask", X = 820, Y = 120, Properties = new FlowProperties { NodeName = "SQE处置", ApproverType = nameof(ApproverTypeEnum.Role), ApproverIds = IqcInspBillFlowConst.RoleSqeId.ToString(), ApproverNames = "来料检验SQE", MultiApproveMode = "Any", }, Text = new FlowTextItem { X = 820, Y = 154, Value = "SQE处置" }, }, new() { Id = "end", Type = "bpmn:endEvent", X = 980, Y = 120, Properties = new FlowProperties { NodeName = "结束" }, Text = new FlowTextItem { X = 980, Y = 154, Value = "结束" }, }, }, Edges = new List { BuildEdge("edge-start-n1", "start", IqcInspBillFlowConst.NodeInspect, 140, 120, 260, 120), BuildEdge("edge-n1-n2", IqcInspBillFlowConst.NodeInspect, IqcInspBillFlowConst.NodeSupervisor, 340, 120, 460, 120), BuildEdge("edge-n2-gw", IqcInspBillFlowConst.NodeSupervisor, IqcInspBillFlowConst.GatewayPd, 540, 120, 640, 120), BuildEdge("edge-gw-n3", IqcInspBillFlowConst.GatewayPd, IqcInspBillFlowConst.NodeSqe, 680, 120, 780, 120), BuildEdge("edge-gw-end", IqcInspBillFlowConst.GatewayPd, "end", 680, 120, 960, 120), BuildEdge("edge-n3-end", IqcInspBillFlowConst.NodeSqe, "end", 860, 120, 960, 120), } }; return JsonSerializer.Serialize(item); } private static ApprovalFlowEdgeItem BuildEdge(string id, string source, string target, float sx, float sy, float ex, float ey) { return new ApprovalFlowEdgeItem { Id = id, Type = "bpmn:sequenceFlow", SourceNodeId = source, TargetNodeId = target, StartPoint = new FlowEdgePointItem { X = sx, Y = sy }, EndPoint = new FlowEdgePointItem { X = ex, Y = ey }, Properties = new FlowProperties(), PointsList = new List { new() { X = sx, Y = sy }, new() { X = ex, Y = ey }, }, }; } }