| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178 |
- # -*- coding: utf-8 -*-
- """
- 批量初始化 AiDOP LayoutItem + HomeModule 种子(仅对"当前无布局"的模块写入)。
- 规则(Q2-C):
- - L1:统一为 home-grid 行,panel_zone=NULL
- - L2/L3:按 MetricName 命名模式分配 panel_zone
- * 含"周期" 或 "人效" → left
- * 含"满足率" 或 "周转" → right
- * 其他 → 奇数 sortNo=left,偶数 sortNo=right
- - is_enabled = 1 全部启用(Q2-C 不截断)
- - HomeModule:若该模块无记录,插入 layout_pattern='card_grid'
- 幂等:模块已有 LayoutItem 时整模块跳过(不覆盖用户手动配置)。
- 两个租户都会跑。
- """
- from __future__ import annotations
- import sys
- from datetime import datetime
- import pymysql
- sys.stdout.reconfigure(encoding='utf-8')
- CONN = dict(
- host='123.60.180.165', port=3306,
- user='aidopremote', password='1234567890aiDOP#',
- database='aidopdev', charset='utf8mb4', autocommit=False,
- )
- MODULES = ["S1", "S2", "S3", "S5", "S6", "S7", "S9"] # 跳过 S4(已配)、S8(独立)
- TENANTS = [1300000000001, 1300000000888]
- FACTORY_ID = 1
- def assign_zone(metric_name: str, sort_no: int) -> str:
- """按命名模式分配 panel_zone。"""
- nm = metric_name or ""
- if ("周期" in nm) or ("人效" in nm):
- return "left"
- if ("满足率" in nm) or ("周转" in nm):
- return "right"
- return "left" if (sort_no % 2 == 1) else "right"
- def next_id(cur) -> int:
- cur.execute("SELECT MAX(Id) AS mx FROM ado_smart_ops_layout_item")
- row = cur.fetchone()
- return (row['mx'] or 0) + 1
- def next_hm_id(cur) -> int:
- cur.execute("SELECT MAX(Id) AS mx FROM ado_smart_ops_home_module")
- row = cur.fetchone()
- return (row['mx'] or 0) + 1
- def process_tenant_module(cur, tenant_id: int, module_code: str) -> dict:
- """处理单个 (tenant, module)。返回统计。"""
- # 1. 检查是否已有布局
- cur.execute(
- "SELECT COUNT(*) AS c FROM ado_smart_ops_layout_item "
- "WHERE TenantId=%s AND ModuleCode=%s",
- (tenant_id, module_code),
- )
- existing = cur.fetchone()['c']
- if existing > 0:
- return {"skip": True, "existing": existing, "inserted": 0, "hm": False}
- # 2. 查出该模块所有启用的 KpiMaster
- cur.execute(
- "SELECT MetricCode, MetricName, MetricLevel, SortNo, Formula "
- "FROM ado_smart_ops_kpi_master "
- "WHERE TenantId=%s AND ModuleCode=%s AND IsEnabled=1 "
- "ORDER BY MetricLevel, SortNo",
- (tenant_id, module_code),
- )
- kpis = cur.fetchall()
- if not kpis:
- return {"skip": True, "existing": 0, "inserted": 0, "hm": False, "reason": "no kpi"}
- # 3. 生成 LayoutItem 行
- now = datetime.now()
- next_layout_id = next_id(cur)
- inserted = 0
- for k in kpis:
- lv = k['MetricLevel']
- code = k['MetricCode']
- name = k['MetricName']
- sort_no = k['SortNo'] or 0
- row_id = f"{module_code}-L{lv}-{code}"
- zone = None
- if lv >= 2:
- zone = assign_zone(name, sort_no)
- formula_text = (k['Formula'] or None)
- # 简化 formula:若超长,截断到 400 字符
- if formula_text and len(formula_text) > 400:
- formula_text = formula_text[:400]
- cur.execute(
- """
- INSERT INTO ado_smart_ops_layout_item
- (Id, TenantId, FactoryId, ModuleCode, RowId, MetricLevel, MetricCode,
- DisplayName, SortNo, ParentRowId, FormulaText, PanelZone, IsEnabled, UpdateTime)
- VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, 1, %s)
- """,
- (
- next_layout_id, tenant_id, FACTORY_ID, module_code, row_id, lv, code,
- None, sort_no, None, formula_text, zone, now,
- ),
- )
- next_layout_id += 1
- inserted += 1
- # 4. 检查 HomeModule
- cur.execute(
- "SELECT COUNT(*) AS c FROM ado_smart_ops_home_module "
- "WHERE TenantId=%s AND FactoryId=%s AND ModuleCode=%s",
- (tenant_id, FACTORY_ID, module_code),
- )
- hm_exists = cur.fetchone()['c']
- hm_inserted = False
- if hm_exists == 0:
- hm_id = next_hm_id(cur)
- cur.execute(
- """
- INSERT INTO ado_smart_ops_home_module
- (Id, TenantId, FactoryId, ModuleCode, LayoutPattern, UpdateTime)
- VALUES (%s, %s, %s, %s, 'card_grid', %s)
- """,
- (hm_id, tenant_id, FACTORY_ID, module_code, now),
- )
- hm_inserted = True
- return {"skip": False, "existing": 0, "inserted": inserted, "hm": hm_inserted}
- def main(dry_run: bool = False) -> None:
- conn = pymysql.connect(**CONN)
- total_inserted = 0
- total_hm_inserted = 0
- try:
- with conn.cursor(pymysql.cursors.DictCursor) as cur:
- print("=" * 96)
- print(f"{'模式':<8}{'DRY_RUN' if dry_run else '实写入'} ({'不会提交' if dry_run else '会提交'})")
- print("=" * 96)
- for tid in TENANTS:
- print(f"\n>>> 租户 {tid}")
- for m in MODULES:
- r = process_tenant_module(cur, tid, m)
- if r['skip']:
- reason = r.get('reason', f"已有 {r['existing']} 条布局")
- print(f" {m:<4} 跳过({reason})")
- else:
- tag = "(+HomeModule)" if r['hm'] else ""
- print(f" {m:<4} 插入 {r['inserted']} 条 LayoutItem {tag}")
- total_inserted += r['inserted']
- if r['hm']:
- total_hm_inserted += 1
- if dry_run:
- print("\nDRY_RUN 模式:回滚事务")
- conn.rollback()
- else:
- print("\n提交事务…")
- conn.commit()
- print(f"\n合计:LayoutItem 新增 {total_inserted} 条,HomeModule 新增 {total_hm_inserted} 条。")
- except Exception as ex:
- conn.rollback()
- print(f"\n出错回滚:{ex}")
- raise
- finally:
- conn.close()
- if __name__ == "__main__":
- dry = "--dry-run" in sys.argv or "-n" in sys.argv
- main(dry_run=dry)
|