IpqcInspectionFlowSeed.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. using System.Diagnostics;
  2. using System.Text.Json;
  3. using Admin.NET.Plugin.AiDOP.Entity;
  4. using Admin.NET.Plugin.AiDOP.Manufacturing;
  5. using Admin.NET.Plugin.ApprovalFlow;
  6. using Admin.NET.Plugin.ApprovalFlow.Service;
  7. using Yitter.IdGenerator;
  8. using ApprovalFlowEntity = Admin.NET.Plugin.ApprovalFlow.ApprovalFlow;
  9. namespace Admin.NET.Plugin.AiDOP.Infrastructure;
  10. /// <summary>
  11. /// S6 过程检验单(IPQC)检验员+主管流程种子(幂等)。
  12. ///
  13. /// 顺序:① 扩展表 ado_s6_ipqc_inspection_flow_state + 处置表 ado_s6_ipqc_quality_disposition(CodeFirst 建表,无 SQL)
  14. /// ② 角色 ROLE_S6_IPQC_INSPECTOR / SUPERVISOR / QUALITY_ENGINEER ③ UAT 成员绑定(superAdmin.NET 同绑三角色,仅 UAT 自审)
  15. /// ④ ApprovalBizType(IPQC_INSPECTION) ⑤ ApprovalFlow v2 已发布定义
  16. /// (start→N1_INSPECT→N2_SUP_REVIEW→GW_RESULT(disposition_required==1→N3 / 否则→end)→N3_QUALITY_DISPOSITION→end)。
  17. /// v1→v2 幂等升级:已发布定义含 N3 则跳过;仍为 v1 则下架旧版发布 v2;历史 Running 实例用各自 FlowJsonSnapshot 冻结不受影响。
  18. /// superAdmin.NET 不存在时仅告警不崩溃。
  19. /// </summary>
  20. public static class IpqcInspectionFlowSeed
  21. {
  22. public static void EnsureSeed(ISqlSugarClient db)
  23. {
  24. try
  25. {
  26. db.CodeFirst.InitTables(
  27. typeof(ApprovalBizType),
  28. typeof(ApprovalFlowEntity),
  29. typeof(ApprovalFlowVersion));
  30. // 流程状态轻量扩展表 + 质量处置表(CodeFirst 建表,无 SQL)
  31. db.CodeFirst.InitTables(typeof(AdoS6IpqcInspectionFlowState));
  32. db.CodeFirst.InitTables(typeof(AdoS6IpqcQualityDisposition));
  33. EnsureRoles(db);
  34. EnsureUatMembers(db);
  35. EnsureBizType(db);
  36. EnsurePublishedFlow(db);
  37. }
  38. catch (Exception ex)
  39. {
  40. Trace.TraceWarning("S6 IPQC inspection flow seed: " + ex);
  41. }
  42. }
  43. private static void EnsureRoles(ISqlSugarClient db)
  44. {
  45. EnsureRole(db, IpqcInspectionFlowConst.RoleInspectorId, IpqcInspectionFlowConst.RoleInspectorCode, "过程检验员", 820);
  46. EnsureRole(db, IpqcInspectionFlowConst.RoleSupervisorId, IpqcInspectionFlowConst.RoleSupervisorCode, "过程检验主管", 821);
  47. EnsureRole(db, IpqcInspectionFlowConst.RoleQualityEngineerId, IpqcInspectionFlowConst.RoleQualityEngineerCode, "过程检验质量工程师", 822);
  48. }
  49. private static void EnsureRole(ISqlSugarClient db, long id, string code, string name, int orderNo)
  50. {
  51. var exists = db.Queryable<SysRole>().ClearFilter().Any(x => x.Code == code);
  52. if (exists) return;
  53. db.Insertable(new SysRole
  54. {
  55. Id = id,
  56. TenantId = SqlSugarConst.DefaultTenantId,
  57. Name = name,
  58. Code = code,
  59. OrderNo = orderNo,
  60. DataScope = DataScopeEnum.Self,
  61. Status = StatusEnum.Enable,
  62. Remark = "S6 过程检验流程角色",
  63. CreateTime = DateTime.Now,
  64. }).ExecuteCommand();
  65. }
  66. private static void EnsureUatMembers(ISqlSugarClient db)
  67. {
  68. var user = db.Queryable<SysUser>().ClearFilter()
  69. .Where(x => x.Account == IpqcInspectionFlowConst.UatAccount)
  70. .First();
  71. if (user == null)
  72. {
  73. Trace.TraceWarning($"S6 IPQC flow seed: UAT 账号 {IpqcInspectionFlowConst.UatAccount} 不存在,跳过成员绑定(阶段2 冒烟前须指定成员)");
  74. return;
  75. }
  76. EnsureUserRole(db, user.Id, IpqcInspectionFlowConst.RoleInspectorId);
  77. EnsureUserRole(db, user.Id, IpqcInspectionFlowConst.RoleSupervisorId);
  78. EnsureUserRole(db, user.Id, IpqcInspectionFlowConst.RoleQualityEngineerId);
  79. }
  80. private static void EnsureUserRole(ISqlSugarClient db, long userId, long roleId)
  81. {
  82. var exists = db.Queryable<SysUserRole>().ClearFilter().Any(x => x.UserId == userId && x.RoleId == roleId);
  83. if (exists) return;
  84. db.Insertable(new SysUserRole
  85. {
  86. Id = YitIdHelper.NextId(),
  87. UserId = userId,
  88. RoleId = roleId,
  89. }).ExecuteCommand();
  90. }
  91. private static void EnsureBizType(ISqlSugarClient db)
  92. {
  93. var exists = db.Queryable<ApprovalBizType>().Any(x => x.Code == IpqcInspectionFlowConst.BizType);
  94. if (exists) return;
  95. db.Insertable(new ApprovalBizType
  96. {
  97. Id = YitIdHelper.NextId(),
  98. Code = IpqcInspectionFlowConst.BizType,
  99. Name = "过程检验单审批",
  100. Remark = "S6 过程检验单 检验员→主管 审批流",
  101. IsEnabled = true,
  102. CreateTime = DateTime.Now,
  103. UpdateTime = DateTime.Now,
  104. }).ExecuteCommand();
  105. }
  106. private static void EnsurePublishedFlow(ISqlSugarClient db)
  107. {
  108. var orgIds = db.Queryable<SysOrg>()
  109. .Select(x => x.Id)
  110. .ToList()
  111. .Where(x => x > 0)
  112. .Distinct()
  113. .ToList();
  114. if (orgIds.Count == 0) orgIds.Add(0);
  115. var now = DateTime.Now;
  116. var flowJson = BuildFlowJson();
  117. foreach (var orgId in orgIds)
  118. {
  119. var existing = db.Queryable<ApprovalFlowEntity>()
  120. .Where(x => x.BizType == IpqcInspectionFlowConst.BizType && x.OrgId == orgId && x.IsPublished && !x.IsDelete)
  121. .OrderByDescending(x => x.Version)
  122. .First();
  123. // v1→v2 幂等升级:已发布定义若已含 N3/GW_RESULT(v2)则跳过;若仍为 v1(无 N3)则下架旧版、发布 v2。
  124. // 历史 Running/Approved 实例使用各自 FlowJsonSnapshot 冻结,不受本次 republish 影响。
  125. if (existing != null)
  126. {
  127. var isV2 = (existing.FlowJson ?? "").Contains(IpqcInspectionFlowConst.NodeDisposition)
  128. && (existing.FlowJson ?? "").Contains(IpqcInspectionFlowConst.GatewayResult);
  129. if (isV2) continue;
  130. db.Updateable<ApprovalFlowEntity>()
  131. .SetColumns(x => x.IsPublished == false)
  132. .SetColumns(x => x.UpdateTime == now)
  133. .Where(x => x.Id == existing.Id)
  134. .ExecuteCommand();
  135. }
  136. var nextVersion = (existing?.Version ?? 0) + 1;
  137. var flow = new ApprovalFlowEntity
  138. {
  139. Id = YitIdHelper.NextId(),
  140. Code = $"S6IPQC_{orgId}",
  141. Name = "过程检验单审批",
  142. BizType = IpqcInspectionFlowConst.BizType,
  143. FormJson = "{}",
  144. FlowJson = flowJson,
  145. Status = 1,
  146. Remark = "系统默认流程 v2:开始-检验员检验-检验主管审核-网关(整批判定)-质量处置-结束",
  147. Version = nextVersion,
  148. IsPublished = true,
  149. OrgId = orgId,
  150. CreateTime = now,
  151. UpdateTime = now,
  152. };
  153. db.Insertable(flow).ExecuteCommand();
  154. db.Insertable(new ApprovalFlowVersion
  155. {
  156. Id = YitIdHelper.NextId(),
  157. FlowId = flow.Id,
  158. Version = flow.Version,
  159. FlowJson = flow.FlowJson,
  160. FormJson = flow.FormJson,
  161. PublishTime = now,
  162. PublisherName = "系统初始化",
  163. CreateTime = now,
  164. UpdateTime = now,
  165. }).ExecuteCommand();
  166. }
  167. }
  168. private static string BuildFlowJson()
  169. {
  170. var item = new ApprovalFlowItem
  171. {
  172. Nodes = new List<ApprovalFlowNodeItem>
  173. {
  174. new()
  175. {
  176. Id = "start",
  177. Type = "bpmn:startEvent",
  178. X = 100,
  179. Y = 120,
  180. Properties = new FlowProperties { NodeName = "开始" },
  181. Text = new FlowTextItem { X = 100, Y = 154, Value = "开始" },
  182. },
  183. new()
  184. {
  185. Id = IpqcInspectionFlowConst.NodeInspect,
  186. Type = "bpmn:userTask",
  187. X = 300,
  188. Y = 120,
  189. Properties = new FlowProperties
  190. {
  191. NodeName = "检验员检验",
  192. ApproverType = nameof(ApproverTypeEnum.Role),
  193. ApproverIds = IpqcInspectionFlowConst.RoleInspectorId.ToString(),
  194. ApproverNames = "过程检验员",
  195. MultiApproveMode = "Any",
  196. },
  197. Text = new FlowTextItem { X = 300, Y = 154, Value = "检验员检验" },
  198. },
  199. new()
  200. {
  201. Id = IpqcInspectionFlowConst.NodeSupervisor,
  202. Type = "bpmn:userTask",
  203. X = 500,
  204. Y = 120,
  205. Properties = new FlowProperties
  206. {
  207. NodeName = "检验主管审核",
  208. ApproverType = nameof(ApproverTypeEnum.Role),
  209. ApproverIds = IpqcInspectionFlowConst.RoleSupervisorId.ToString(),
  210. ApproverNames = "过程检验主管",
  211. MultiApproveMode = "Any",
  212. },
  213. Text = new FlowTextItem { X = 500, Y = 154, Value = "检验主管审核" },
  214. },
  215. new()
  216. {
  217. Id = IpqcInspectionFlowConst.GatewayResult,
  218. Type = "bpmn:exclusiveGateway",
  219. X = 660,
  220. Y = 120,
  221. Properties = new FlowProperties
  222. {
  223. NodeName = "整批判定分流",
  224. Conditions = new List<GatewayCondition>
  225. {
  226. new()
  227. {
  228. Expression = "disposition_required == 1",
  229. TargetNodeId = IpqcInspectionFlowConst.NodeDisposition,
  230. IsDefault = false,
  231. Label = "整批不合格转处置",
  232. },
  233. new()
  234. {
  235. TargetNodeId = "end",
  236. IsDefault = true,
  237. Label = "整批合格完成",
  238. },
  239. },
  240. },
  241. Text = new FlowTextItem { X = 660, Y = 154, Value = "整批判定分流" },
  242. },
  243. new()
  244. {
  245. Id = IpqcInspectionFlowConst.NodeDisposition,
  246. Type = "bpmn:userTask",
  247. X = 820,
  248. Y = 120,
  249. Properties = new FlowProperties
  250. {
  251. NodeName = "质量处置",
  252. ApproverType = nameof(ApproverTypeEnum.Role),
  253. ApproverIds = IpqcInspectionFlowConst.RoleQualityEngineerId.ToString(),
  254. ApproverNames = "过程检验质量工程师",
  255. MultiApproveMode = "Any",
  256. },
  257. Text = new FlowTextItem { X = 820, Y = 154, Value = "质量处置" },
  258. },
  259. new()
  260. {
  261. Id = "end",
  262. Type = "bpmn:endEvent",
  263. X = 980,
  264. Y = 120,
  265. Properties = new FlowProperties { NodeName = "结束" },
  266. Text = new FlowTextItem { X = 980, Y = 154, Value = "结束" },
  267. },
  268. },
  269. Edges = new List<ApprovalFlowEdgeItem>
  270. {
  271. BuildEdge("edge-start-n1", "start", IpqcInspectionFlowConst.NodeInspect, 140, 120, 260, 120),
  272. BuildEdge("edge-n1-n2", IpqcInspectionFlowConst.NodeInspect, IpqcInspectionFlowConst.NodeSupervisor, 340, 120, 460, 120),
  273. BuildEdge("edge-n2-gw", IpqcInspectionFlowConst.NodeSupervisor, IpqcInspectionFlowConst.GatewayResult, 540, 120, 640, 120),
  274. BuildEdge("edge-gw-n3", IpqcInspectionFlowConst.GatewayResult, IpqcInspectionFlowConst.NodeDisposition, 680, 120, 780, 120),
  275. BuildEdge("edge-gw-end", IpqcInspectionFlowConst.GatewayResult, "end", 680, 120, 960, 120),
  276. BuildEdge("edge-n3-end", IpqcInspectionFlowConst.NodeDisposition, "end", 860, 120, 960, 120),
  277. }
  278. };
  279. return JsonSerializer.Serialize(item);
  280. }
  281. private static ApprovalFlowEdgeItem BuildEdge(string id, string source, string target, float sx, float sy, float ex, float ey)
  282. {
  283. return new ApprovalFlowEdgeItem
  284. {
  285. Id = id,
  286. Type = "bpmn:sequenceFlow",
  287. SourceNodeId = source,
  288. TargetNodeId = target,
  289. StartPoint = new FlowEdgePointItem { X = sx, Y = sy },
  290. EndPoint = new FlowEdgePointItem { X = ex, Y = ey },
  291. Properties = new FlowProperties(),
  292. PointsList = new List<FlowEdgePointItem>
  293. {
  294. new() { X = sx, Y = sy },
  295. new() { X = ex, Y = ey },
  296. },
  297. };
  298. }
  299. }