run_wp_sd4_approval_baseline.py 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. #!/usr/bin/env python3
  2. """WP-SD4: publish formal SMART_OPS_IMPROVEMENT flow and verify UAT role members."""
  3. from __future__ import annotations
  4. import json
  5. import re
  6. from pathlib import Path
  7. import pymysql
  8. from pymysql.constants import CLIENT
  9. ROOT = Path(__file__).resolve().parents[4]
  10. CONFIG = ROOT / "server" / "Admin.NET.Application" / "Configuration" / "Database.json"
  11. FLOW_JSON = Path(__file__).with_name("smart_ops_improvement_flow.json")
  12. EVIDENCE = ROOT / "doc" / "plan" / "UAT留证" / "2026-08-17-智慧诊断正式落地"
  13. TENANTS = {
  14. "A": 838257186181189,
  15. "B": 838257212780613,
  16. "DEMO": 838257237606469,
  17. }
  18. ROLES = (
  19. ("SMART_OPS_DEPT_HEAD", "责任部门负责人"),
  20. ("SMART_OPS_MODULE_OWNER", "模块负责人"),
  21. ("SMART_OPS_OPS_OWNER", "运营负责人"),
  22. )
  23. def connect() -> pymysql.Connection:
  24. raw = CONFIG.read_text(encoding="utf-8-sig")
  25. value = next(
  26. item
  27. for item in re.findall(r'(?m)^\s*"ConnectionString"\s*:\s*"([^"]+)"', raw)
  28. if "Database=aidopdev" in item
  29. )
  30. parts = {
  31. item.split("=", 1)[0].strip().lower(): item.split("=", 1)[1].strip()
  32. for item in value.split(";")
  33. if "=" in item
  34. }
  35. return pymysql.connect(
  36. host=parts["server"],
  37. port=int(parts["port"]),
  38. user=parts["uid"],
  39. password=parts["pwd"],
  40. database=parts["database"],
  41. charset="utf8mb4",
  42. autocommit=True,
  43. client_flag=CLIENT.MULTI_STATEMENTS,
  44. cursorclass=pymysql.cursors.DictCursor,
  45. )
  46. def next_id(cur, table: str) -> int:
  47. cur.execute(f"SELECT IFNULL(MAX(Id), 0) + 1 AS n FROM {table}")
  48. return int(cur.fetchone()["n"])
  49. def ensure_fallback_members(cur) -> None:
  50. for tenant_id in TENANTS.values():
  51. for code, _name in ROLES:
  52. cur.execute(
  53. """
  54. SELECT r.Id AS role_id
  55. FROM SysRole r
  56. WHERE r.TenantId=%s AND r.Code=%s AND r.Status=1
  57. LIMIT 1
  58. """,
  59. (tenant_id, code),
  60. )
  61. role = cur.fetchone()
  62. if not role:
  63. continue
  64. cur.execute(
  65. """
  66. SELECT COUNT(*) AS n
  67. FROM SysUserRole ur
  68. JOIN SysUser u ON u.Id=ur.UserId
  69. WHERE ur.RoleId=%s AND u.TenantId=%s AND u.Status=1
  70. """,
  71. (role["role_id"], tenant_id),
  72. )
  73. if cur.fetchone()["n"] > 0:
  74. continue
  75. cur.execute(
  76. """
  77. SELECT Id FROM SysUser
  78. WHERE TenantId=%s AND Status=1
  79. ORDER BY AccountType DESC, Id
  80. LIMIT 1
  81. """,
  82. (tenant_id,),
  83. )
  84. user = cur.fetchone()
  85. if not user:
  86. continue
  87. cur.execute(
  88. "INSERT INTO SysUserRole (Id, UserId, RoleId) VALUES (%s, %s, %s)",
  89. (next_id(cur, "SysUserRole"), user["Id"], role["role_id"]),
  90. )
  91. def publish_formal_flow(cur) -> dict:
  92. flow_json = FLOW_JSON.read_text(encoding="utf-8")
  93. cur.execute(
  94. """
  95. SELECT Id, OrgId, Version, IsPublished, FlowJson, Remark
  96. FROM ApprovalFlow
  97. WHERE BizType='SMART_OPS_IMPROVEMENT' AND IFNULL(IsDelete,0)=0
  98. """
  99. )
  100. rows = cur.fetchall()
  101. updated = 0
  102. for row in rows:
  103. raw = row.get("FlowJson") or ""
  104. if "SMART_OPS_DEPT_HEAD" in raw and "exclusiveGateway" in raw:
  105. if not row.get("IsPublished"):
  106. cur.execute(
  107. "UPDATE ApprovalFlow SET IsPublished=1, UpdateTime=NOW() WHERE Id=%s",
  108. (row["Id"],),
  109. )
  110. updated += 1
  111. continue
  112. new_version = max(int(row.get("Version") or 1), 1) + 1
  113. cur.execute(
  114. """
  115. UPDATE ApprovalFlow
  116. SET FlowJson=%s, Version=%s, IsPublished=1, Status=1,
  117. Remark='正式流程:黄/红/跨部门网关,按角色解析审批人',
  118. UpdateTime=NOW()
  119. WHERE Id=%s
  120. """,
  121. (flow_json, new_version, row["Id"]),
  122. )
  123. cur.execute(
  124. """
  125. INSERT INTO ApprovalFlowVersion
  126. (Id, FlowId, Version, FlowJson, FormJson, PublishTime, PublisherName, CreateTime, UpdateTime)
  127. VALUES (%s, %s, %s, %s, '{}', NOW(), 'WP-SD4', NOW(), NOW())
  128. """,
  129. (next_id(cur, "ApprovalFlowVersion"), row["Id"], new_version, flow_json),
  130. )
  131. updated += 1
  132. return {"existing": len(rows), "updated": updated}
  133. def inventory(cur) -> dict:
  134. result = {"tenants": {}, "legacyJsonPlans": 0, "actionRows": 0}
  135. for code, tenant_id in TENANTS.items():
  136. roles = {}
  137. for role_code, role_name in ROLES:
  138. cur.execute(
  139. """
  140. SELECT r.Id AS role_id,
  141. (SELECT COUNT(*) FROM SysUserRole ur
  142. JOIN SysUser u ON u.Id=ur.UserId
  143. WHERE ur.RoleId=r.Id AND u.TenantId=%s AND u.Status=1) AS members
  144. FROM SysRole r
  145. WHERE r.TenantId=%s AND r.Code=%s
  146. """,
  147. (tenant_id, tenant_id, role_code),
  148. )
  149. row = cur.fetchone()
  150. roles[role_code] = {
  151. "name": role_name,
  152. "roleId": str(row["role_id"]) if row else None,
  153. "members": int(row["members"]) if row else 0,
  154. }
  155. result["tenants"][code] = {"tenantId": str(tenant_id), "roles": roles}
  156. cur.execute(
  157. """
  158. SELECT COUNT(*) AS n
  159. FROM ado_smart_ops_improvement_plan
  160. WHERE ActionItemsJson IS NOT NULL
  161. AND ActionItemsJson NOT IN ('', '[]', 'null')
  162. """
  163. )
  164. result["legacyJsonPlans"] = int(cur.fetchone()["n"])
  165. cur.execute("SELECT COUNT(*) AS n FROM ado_smart_ops_improvement_action")
  166. result["actionRows"] = int(cur.fetchone()["n"])
  167. cur.execute(
  168. """
  169. SELECT Id, OrgId, Version, IsPublished
  170. FROM ApprovalFlow
  171. WHERE BizType='SMART_OPS_IMPROVEMENT' AND IFNULL(IsDelete,0)=0
  172. ORDER BY Version DESC
  173. """
  174. )
  175. result["flows"] = [
  176. {
  177. "id": str(x["Id"]),
  178. "orgId": str(x["OrgId"]),
  179. "version": x["Version"],
  180. "published": bool(x["IsPublished"]),
  181. }
  182. for x in cur.fetchall()
  183. ]
  184. return result
  185. def main() -> None:
  186. conn = connect()
  187. try:
  188. with conn.cursor() as cur:
  189. ensure_fallback_members(cur)
  190. flow = publish_formal_flow(cur)
  191. data = inventory(cur)
  192. data["flowUpdate"] = flow
  193. finally:
  194. conn.close()
  195. EVIDENCE.mkdir(parents=True, exist_ok=True)
  196. out = EVIDENCE / "04-approval-baseline.json"
  197. out.write_text(json.dumps(data, ensure_ascii=False, indent=2), encoding="utf-8")
  198. print(json.dumps(data, ensure_ascii=False, indent=2))
  199. if __name__ == "__main__":
  200. main()