# -*- coding: utf-8 -*- """环境盘点:远程库 8.129.84.155 表结构 + 存量数据清查(票 02 → env-inventory.md) 坑㉕:每步新连接 autocommit=True;坑㉑:py -X utf8 执行。 """ import sys, io, json sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8') import pymysql CONF = dict(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) def q(sql, args=None): with pymysql.connect(**CONF) as conn, conn.cursor() as cur: cur.execute(sql, args) return cur.fetchall() # 0) 全表清单 tables = [r[list(r)[0]] for r in q('SHOW TABLES')] print(f'== 全表 {len(tables)} 张 ==') groups = {} for t in sorted(tables): pfx = t.split('_')[0] groups.setdefault(pfx, []).append(t) for pfx in sorted(groups): print(f' [{pfx}] {" ".join(groups[pfx])}') def cnt(t, where='1=1'): try: return q(f'SELECT COUNT(*) c FROM {t} WHERE {where}')[0]['c'] except Exception as e: return f'ERR:{str(e)[:60]}' # 1) 客户域存量 print('\n== 客户域计数(total / deleted=0) ==') for t in ['customer', 'customer_contact', 'customer_oplog', 'customer_team_member', 'customer_view_log', 'customer_focus', 'customer_follow', 'customer_transfer_record', 'customer_import_batch', 'customer_pending_notice']: if t in tables: print(f' {t}: {cnt(t)} / {cnt(t, "deleted=0")}') else: print(f' {t}: (表不存在)') # 2) 公海/阶段/归档分布 print('\n== customer 分布 ==') try: print(' owner为空(公海):', q("SELECT COUNT(*) c FROM customer WHERE deleted=0 AND owner_user_id IS NULL")[0]['c']) print(' 归档分布:', q("SELECT archive_status, COUNT(*) c FROM customer WHERE deleted=0 GROUP BY archive_status")) print(' 阶段分布:', q("SELECT customer_stage, COUNT(*) c FROM customer WHERE deleted=0 GROUP BY customer_stage ORDER BY c DESC LIMIT 10")) except Exception as e: print(' ERR:', str(e)[:120]) # 3) e2e 残留 print('\n== e2e 残留 ==') print(' 客户 KH202609%:', cnt("customer", "customer_no LIKE 'KH202609%'")) print(' 客户名 e2e%:', cnt("customer", "customer_name LIKE 'e2e%'")) if 'opportunity' in tables: namecol = 'name' try: q(f'SELECT {namecol} FROM opportunity LIMIT 1') except Exception: namecol = 'opportunity_name' try: print(f' 商机({namecol}) e2e-%:', q(f"SELECT COUNT(*) c FROM opportunity WHERE {namecol} LIKE 'e2e-%'")[0]['c']) except Exception as e: print(' 商机 ERR:', str(e)[:80]) print(' opportunity_customer:', cnt('opportunity_customer')) print(' opportunity_customer delete_key=0:', cnt('opportunity_customer', 'delete_key=0')) # 4) 用户/部门/角色 print('\n== 用户/部门/角色 ==') for t in tables: if t.endswith('_user') or t in ('sys_user', 'crm_user'): cols = [r['Field'] for r in q(f'SHOW COLUMNS FROM {t}')] print(f' {t} 列: {cols}') break try: rows = q("SELECT id, name, dept_id FROM sys_user WHERE deleted=0 LIMIT 20") if 'sys_user' in tables else [] for r in rows: print(f" user {r['id']} {r['name']} dept={r['dept_id']}") except Exception as e: print(' user ERR:', str(e)[:120]) try: rows = q("SELECT id, name, parent_id FROM sys_dept WHERE deleted=0 ORDER BY id LIMIT 30") if 'sys_dept' in tables else [] for r in rows: print(f" dept {r['id']} {r['name']} parent={r['parent_id']}") except Exception as e: print(' dept ERR:', str(e)[:120]) # debug 用户角色 try: rows = q("SELECT ur.role_id, r.code, r.name FROM sys_user_role ur JOIN sys_role r ON r.id=ur.role_id WHERE ur.user_id='739564171091247104'") print(' debug用户角色:', rows) except Exception as e: print(' role ERR:', str(e)[:120]) # 5) 字典 print('\n== 字典 ==') dict_tables = [t for t in tables if 'dict' in t] print(' 字典表:', dict_tables) for t in dict_tables: print(f' {t}: {cnt(t)}') try: rows = q("SELECT group_code, COUNT(*) c FROM sys_dict_item WHERE deleted=0 GROUP BY group_code ORDER BY group_code") if 'sys_dict_item' in tables else [] print(f' 字典组 {len(rows)} 组:', [(r['group_code'], r['c']) for r in rows]) except Exception as e: print(' dict ERR:', str(e)[:120]) # 6) 客户域外键口径(opportunity_customer 结构) print('\n== opportunity_customer 结构 ==') try: for r in q('SHOW COLUMNS FROM opportunity_customer'): print(f" {r['Field']} {r['Type']}") except Exception as e: print(' ERR:', str(e)[:120]) print('\nDONE')