You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
147 lines
6.6 KiB
147 lines
6.6 KiB
|
17 hours ago
|
# -*- coding: utf-8 -*-
|
||
|
|
"""D-07 assignable 前置探测 + D-05/D-04 DB 列证据(customer-defectfix effort)。
|
||
|
|
|
||
|
|
用法:py -X utf8 .scratch/customer-defectfix/probe-assignable.py
|
||
|
|
产物:.scratch/customer-defectfix/probe-assignable-result.json
|
||
|
|
- assignable?id= 计时(timeout=180s,双次),修复前后各跑一轮对比
|
||
|
|
- SHOW COLUMNS 留证:customer.is_biz_negotiated / customer_import_fail.fail_reason /
|
||
|
|
customer_import_task.fail_reason
|
||
|
|
- collab 样例(e2c-协同-样例)owner 现状(heavy 复跑副作用盲区核对)
|
||
|
|
"""
|
||
|
|
import json
|
||
|
|
import os
|
||
|
|
import sys
|
||
|
|
import time
|
||
|
|
|
||
|
|
import pymysql
|
||
|
|
import requests
|
||
|
|
|
||
|
|
BASE = 'http://localhost:8080'
|
||
|
|
ADMIN = '739564171091247104'
|
||
|
|
OUT = os.path.dirname(os.path.abspath(__file__))
|
||
|
|
RESULT = os.path.join(OUT, 'probe-assignable-result.json')
|
||
|
|
TIMEOUT = 180
|
||
|
|
|
||
|
|
COLLAB_OWNER_EXPECT = ('744842318024015872', '曾偲青', '744841292483133440', '职员部')
|
||
|
|
|
||
|
|
|
||
|
|
def db():
|
||
|
|
return 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)
|
||
|
|
|
||
|
|
|
||
|
|
def dbq(sql, args=None):
|
||
|
|
with db() as conn, conn.cursor() as cur:
|
||
|
|
cur.execute(sql, args)
|
||
|
|
return cur.fetchall()
|
||
|
|
|
||
|
|
|
||
|
|
def main():
|
||
|
|
ev = {'phase': sys.argv[1] if len(sys.argv) > 1 else 'unknown',
|
||
|
|
'ts': time.strftime('%Y-%m-%d %H:%M:%S')}
|
||
|
|
|
||
|
|
# ---- 1. DB 列证据(D-05 / D-04)----
|
||
|
|
cols = {}
|
||
|
|
for table, like in (('customer', 'is_biz_negotiated'), ('customer', 'is_child'),
|
||
|
|
('customer_import_fail', 'fail_reason'),
|
||
|
|
('customer_import_task', 'fail_reason')):
|
||
|
|
rows = dbq(f"SHOW COLUMNS FROM {table} LIKE '{like}'")
|
||
|
|
c = rows[0] if rows else None
|
||
|
|
cols[f'{table}.{like}'] = ({k: str(v) for k, v in c.items()} if c else None)
|
||
|
|
ev['columns'] = cols
|
||
|
|
print('== SHOW COLUMNS 证据 ==')
|
||
|
|
for k, v in cols.items():
|
||
|
|
print(f" {k}: type={v and v.get('Type')} null={v and v.get('Null')} default={v and v.get('Default')}")
|
||
|
|
|
||
|
|
# ---- 2. collab 样例 owner 现状(heavy 副作用盲区)----
|
||
|
|
collab = dbq("SELECT id, customer_name, owner_user_id, owner_user_name_snapshot, "
|
||
|
|
"owner_dept_id, owner_dept_name_snapshot FROM customer "
|
||
|
|
"WHERE customer_name='e2c-协同-样例' AND deleted=0")
|
||
|
|
ev['collab'] = [{k: str(v) for k, v in r.items()} for r in collab]
|
||
|
|
print('== collab 样例 owner 现状 ==')
|
||
|
|
for r in collab:
|
||
|
|
drift = str(r['owner_user_id']) != COLLAB_OWNER_EXPECT[0]
|
||
|
|
print(f" id={r['id']} owner={r['owner_user_id']}({r['owner_user_name_snapshot']}) "
|
||
|
|
f"dept={r['owner_dept_id']}({r['owner_dept_name_snapshot']}) "
|
||
|
|
f"{'⚠ 漂移' if drift else '正常(曾偲青)'}")
|
||
|
|
|
||
|
|
# ---- 3. 最新交接单(assignable 探测目标)----
|
||
|
|
transfers = dbq("SELECT id, transfer_no, status, from_user_id, to_director_id, total_count, "
|
||
|
|
"assigned_count FROM customer_transfer ORDER BY id DESC LIMIT 5")
|
||
|
|
ev['transfers'] = [{k: str(v) for k, v in r.items()} for r in transfers]
|
||
|
|
print('== 最近交接单 ==')
|
||
|
|
for r in transfers:
|
||
|
|
print(f" id={r['id']} no={r['transfer_no']} status={r['status']} "
|
||
|
|
f"director={r['to_director_id']} total={r['total_count']}")
|
||
|
|
if not transfers:
|
||
|
|
print('!! 无交接单可用,无法探测 assignable')
|
||
|
|
json.dump(ev, open(RESULT, 'w', encoding='utf-8'), ensure_ascii=False, indent=1)
|
||
|
|
return
|
||
|
|
|
||
|
|
tid = transfers[0]['id']
|
||
|
|
|
||
|
|
# ---- 4. admin token + assignable 计时(双次)----
|
||
|
|
tok = None
|
||
|
|
for _ in range(3):
|
||
|
|
try:
|
||
|
|
r = requests.get(f'{BASE}/api/auth/debug/token', params={'userId': ADMIN}, timeout=30)
|
||
|
|
if r.status_code == 200:
|
||
|
|
d = r.json().get('data')
|
||
|
|
tok = d if isinstance(d, str) else (d or {}).get('token')
|
||
|
|
if tok:
|
||
|
|
break
|
||
|
|
except Exception as e:
|
||
|
|
print(f' token 重试: {e}')
|
||
|
|
time.sleep(2)
|
||
|
|
if not tok:
|
||
|
|
print('!! 服务未就绪(debug/token 失败)——请先起服')
|
||
|
|
json.dump(ev, open(RESULT, 'w', encoding='utf-8'), ensure_ascii=False, indent=1)
|
||
|
|
return
|
||
|
|
|
||
|
|
probes = []
|
||
|
|
for i in range(2):
|
||
|
|
t0 = time.time()
|
||
|
|
try:
|
||
|
|
r = requests.get(f'{BASE}/api/customer/transfer/assignable', params={'id': tid},
|
||
|
|
headers={'Authorization': f'Bearer {tok}'}, timeout=TIMEOUT)
|
||
|
|
elapsed = time.time() - t0
|
||
|
|
body = r.json() if r.status_code == 200 else {}
|
||
|
|
n = len(body.get('data') or [])
|
||
|
|
probes.append({'attempt': i + 1, 'elapsed_sec': round(elapsed, 2),
|
||
|
|
'http': r.status_code, 'code': body.get('code'), 'users': n})
|
||
|
|
print(f" assignable #{i+1}: {elapsed:.2f}s http={r.status_code} users={n}")
|
||
|
|
except Exception as e:
|
||
|
|
elapsed = time.time() - t0
|
||
|
|
probes.append({'attempt': i + 1, 'elapsed_sec': round(elapsed, 2), 'error': str(e)[:120]})
|
||
|
|
print(f" assignable #{i+1}: 异常 @{elapsed:.2f}s {str(e)[:100]}")
|
||
|
|
time.sleep(2)
|
||
|
|
ev['assignable_probes'] = probes
|
||
|
|
ev['assignable_target_transfer'] = str(tid)
|
||
|
|
|
||
|
|
# ---- 5. N+1 规模参考:总监子树用户/部门数(WITH RECURSIVE)----
|
||
|
|
try:
|
||
|
|
drow = dbq("SELECT dept_id FROM crm_auth_user WHERE id=%s", (transfers[0]['to_director_id'],))
|
||
|
|
if drow and drow[0]['dept_id']:
|
||
|
|
root = drow[0]['dept_id']
|
||
|
|
sub = 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(*) dept_cnt FROM t", (root,))
|
||
|
|
ucnt = 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(*) user_cnt FROM crm_auth_user u "
|
||
|
|
"WHERE u.deleted=0 AND u.dept_id IN (SELECT id FROM t)", (root,))
|
||
|
|
ev['subtree'] = {'root_dept': str(root), 'dept_cnt': sub[0]['dept_cnt'],
|
||
|
|
'user_cnt': ucnt[0]['user_cnt']}
|
||
|
|
print(f" 总监子树规模:部门 {sub[0]['dept_cnt']} / 用户 {ucnt[0]['user_cnt']}"
|
||
|
|
f"(N+1 往返基数)")
|
||
|
|
except Exception as e:
|
||
|
|
print(f' 子树规模统计跳过:{e}')
|
||
|
|
|
||
|
|
json.dump(ev, open(RESULT, 'w', encoding='utf-8'), ensure_ascii=False, indent=1)
|
||
|
|
print(f"证据已落盘 {RESULT}")
|
||
|
|
|
||
|
|
|
||
|
|
if __name__ == '__main__':
|
||
|
|
main()
|