# -*- coding: utf-8 -*- """D-07 修复前置:sys_dept.ancestors 数据完整性校验(前缀查询口径 vs 递归口径一致才可上)。 py -X utf8 .scratch/customer-defectfix/check-ancestors.py """ import pymysql def dbq(sql, args=None): with pymysql.connect(host='8.129.84.155', port=3306, user='root', password='Itc@123456', database='crm', charset='utf8mb4', autocommit=True, connect_timeout=10, cursorclass=pymysql.cursors.DictCursor) as conn, \ conn.cursor() as cur: cur.execute(sql, args) return cur.fetchall() rows = dbq("SELECT COUNT(*) total, " "SUM(CASE WHEN ancestors IS NULL OR ancestors='' THEN 1 ELSE 0 END) blank " "FROM sys_dept") print(f"sys_dept 总数={rows[0]['total']} ancestors空={rows[0]['blank']}") # 以「广东保伦」根部门(罗伟健所在)为样本:递归子树 vs 前缀查询 必须同数 root = dbq("SELECT dept_id FROM crm_auth_user WHERE id=739564171091247104")[0]['dept_id'] drow = dbq("SELECT id, dept_name, ancestors, parent_id FROM sys_dept WHERE id=%s", (root,))[0] print(f"样本根部门 id={drow['id']} name={drow['dept_name']} ancestors={drow['ancestors']} " f"parent_id={drow['parent_id']}") chain = f"{drow['ancestors']},{drow['id']}" rec = dbq("WITH RECURSIVE t AS (SELECT id FROM sys_dept WHERE id=%s " "UNION ALL SELECT d.id FROM sys_dept d JOIN t ON d.parent_id=t.id) " "SELECT COUNT(*) c FROM t", (root,))[0]['c'] pfx = dbq("SELECT COUNT(*) c FROM sys_dept WHERE ancestors=%s OR ancestors LIKE %s", (chain, chain + ',%'))[0]['c'] + 1 # +1 = 自身 print(f"递归口径子树={rec} 前缀口径(含自身)={pfx} {'✅ 一致' if rec == pfx else '❌ 不一致——禁止用前缀查询'}") # 逐部门抽查:每个部门的 ancestors 末段必须等于其 parent_id(链未断) bad = dbq("SELECT COUNT(*) c FROM sys_dept WHERE parent_id<>0 AND " "ancestors NOT LIKE CONCAT('%', parent_id)") print(f"ancestors 末段≠parent_id 的断链行={bad[0]['c']}") # 顶层行(parent_id=0)ancestors 必须为 '0' badroot = dbq("SELECT COUNT(*) c FROM sys_dept WHERE parent_id=0 AND ancestors<>'0'") print(f"顶层 ancestors≠'0' 的行={badroot[0]['c']}")