insert_business_fact_menu.py 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. # -*- coding: utf-8 -*-
  2. """幂等插入「业务事实字典」菜单到 dev 库。
  3. 首次启动时 SysMenuSeedData 不会自动补新菜单(IgnoreUpdateSeed),
  4. AidopMenuLinkSync 仅补租户/角色链接而不会建菜单本身。
  5. 故 dev 环境需一次性手工插。重跑无副作用。
  6. """
  7. from __future__ import annotations
  8. import sys
  9. from datetime import datetime
  10. import pymysql
  11. sys.stdout.reconfigure(encoding='utf-8')
  12. CONN = dict(
  13. host='123.60.180.165', port=3306,
  14. user='aidopremote', password='1234567890aiDOP#',
  15. database='aidopdev', charset='utf8mb4', autocommit=False,
  16. )
  17. MENU_ID = 1320990000313
  18. PARENT_ID = 1320990000300 # Ai-DOP 智慧运营目录(从 kpi-master 菜单推得,同父级)
  19. ROUTE_PATH = '/aidop/smart-ops/business-fact'
  20. ROUTE_NAME = 'aidopSmartOpsBusinessFact'
  21. COMPONENT = '/aidop/kanban/businessFact'
  22. TITLE = '业务事实字典'
  23. def main() -> None:
  24. conn = pymysql.connect(**CONN)
  25. cur = conn.cursor(pymysql.cursors.DictCursor)
  26. try:
  27. # 先找出 kpi-master 菜单的 Pid 作为父级
  28. cur.execute("SELECT Pid, OrderNo FROM SysMenu WHERE Id=%s", (1320990000312,))
  29. parent_row = cur.fetchone()
  30. if not parent_row:
  31. raise RuntimeError("未找到 kpi-master 菜单(1320990000312),无法推断父级目录")
  32. pid = parent_row['Pid']
  33. order_no = int(parent_row['OrderNo']) + 1
  34. print(f"父级目录 Pid={pid}, 排序={order_no}")
  35. # 探测 SysMenu 列集
  36. cur.execute("SHOW COLUMNS FROM SysMenu")
  37. cols = {r['Field'] for r in cur.fetchall()}
  38. print(f"SysMenu 列: {sorted(cols)[:12]}... (共 {len(cols)})")
  39. # 1) 菜单:从 kpi-master 克隆后覆盖关键字段
  40. cur.execute("SELECT COUNT(*) AS c FROM SysMenu WHERE Id=%s", (MENU_ID,))
  41. if cur.fetchone()['c'] > 0:
  42. print(f"[SKIP] SysMenu.Id={MENU_ID} 已存在")
  43. else:
  44. cur.execute("SELECT * FROM SysMenu WHERE Id=%s", (1320990000312,))
  45. tmpl = cur.fetchone()
  46. if not tmpl:
  47. raise RuntimeError("模板菜单 1320990000312 不存在")
  48. tmpl['Id'] = MENU_ID
  49. tmpl['Title'] = TITLE
  50. tmpl['Path'] = ROUTE_PATH
  51. tmpl['Name'] = ROUTE_NAME
  52. tmpl['Component'] = COMPONENT
  53. tmpl['Icon'] = 'ele-Collection'
  54. tmpl['OrderNo'] = order_no
  55. tmpl['Remark'] = TITLE
  56. tmpl['CreateTime'] = datetime.now()
  57. if 'UpdateTime' in tmpl:
  58. tmpl['UpdateTime'] = None
  59. ks = ','.join(f'`{k}`' for k in tmpl)
  60. ph = ','.join(['%s'] * len(tmpl))
  61. cur.execute(f"INSERT INTO SysMenu ({ks}) VALUES ({ph})", tuple(tmpl.values()))
  62. print(f"[OK] 新增 SysMenu.Id={MENU_ID}(按 kpi-master 菜单克隆)")
  63. # 2) 租户菜单 - 给所有租户都挂上
  64. cur.execute("SELECT Id FROM SysTenant")
  65. tenants = [r['Id'] for r in cur.fetchall()]
  66. cur.execute("SHOW COLUMNS FROM SysTenantMenu")
  67. tm_cols = {r['Field'] for r in cur.fetchall()}
  68. tm_added = 0
  69. for tid in tenants:
  70. cur.execute(
  71. "SELECT COUNT(*) AS c FROM SysTenantMenu WHERE TenantId=%s AND MenuId=%s",
  72. (tid, MENU_ID),
  73. )
  74. if cur.fetchone()['c'] > 0:
  75. continue
  76. cur.execute("SELECT IFNULL(MAX(Id),0)+1 AS nx FROM SysTenantMenu")
  77. link_id = cur.fetchone()['nx']
  78. tm_row = {'Id': link_id, 'TenantId': tid, 'MenuId': MENU_ID}
  79. tm_row = {k: v for k, v in tm_row.items() if k in tm_cols}
  80. ks = ','.join(f'`{k}`' for k in tm_row)
  81. ph = ','.join(['%s'] * len(tm_row))
  82. cur.execute(f"INSERT INTO SysTenantMenu ({ks}) VALUES ({ph})", tuple(tm_row.values()))
  83. tm_added += 1
  84. print(f"[OK] 补 SysTenantMenu x {tm_added} 条(共 {len(tenants)} 租户)")
  85. # 3) 角色菜单 - 给系统管理员 + 已授权 kpi-master 的角色
  86. cur.execute("SELECT DISTINCT RoleId FROM SysRoleMenu WHERE MenuId=%s", (1320990000312,))
  87. roles = {r['RoleId'] for r in cur.fetchall()}
  88. roles.add(1300000000101) # SysAdmin
  89. cur.execute("SHOW COLUMNS FROM SysRoleMenu")
  90. rm_cols = {r['Field'] for r in cur.fetchall()}
  91. inserted = 0
  92. for rid in roles:
  93. cur.execute(
  94. "SELECT COUNT(*) AS c FROM SysRoleMenu WHERE RoleId=%s AND MenuId=%s",
  95. (rid, MENU_ID),
  96. )
  97. if cur.fetchone()['c'] > 0:
  98. continue
  99. # 避免 Id 撞号:用 MAX+1 策略
  100. cur.execute("SELECT IFNULL(MAX(Id),0)+1 AS nx FROM SysRoleMenu")
  101. nx = cur.fetchone()['nx']
  102. rm_row = {'Id': nx, 'RoleId': rid, 'MenuId': MENU_ID}
  103. rm_row = {k: v for k, v in rm_row.items() if k in rm_cols}
  104. ks = ','.join(f'`{k}`' for k in rm_row)
  105. ph = ','.join(['%s'] * len(rm_row))
  106. cur.execute(f"INSERT INTO SysRoleMenu ({ks}) VALUES ({ph})", tuple(rm_row.values()))
  107. inserted += 1
  108. print(f"[OK] 补 SysRoleMenu x {inserted} 角色(共候选 {len(roles)})")
  109. conn.commit()
  110. print("\n[DONE] 菜单入库完成。请在前端 ctrl-shift-R 刷新或重新登录以加载。")
  111. except Exception as ex:
  112. conn.rollback()
  113. print(f"回滚:{ex}")
  114. raise
  115. finally:
  116. cur.close()
  117. conn.close()
  118. if __name__ == "__main__":
  119. main()