FqcInspBillFlowSeed.cs 13 KB

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