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 },
},
};
}
}