# -*- 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)