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.

137 lines
6.5 KiB

9 hours ago
# -*- coding: utf-8 -*-
"""acceptance-probe-testenv.py — 测试环境(https://ai.itc.vip/crm-api)验收前探针。
目的验收开始前一次性回答四件事
1) 服务可达ping 401 = 鉴权门活着属正常
2) verify profile / debug token 可用demo 免登前提
3) 数据状态mine/pool 存量 + 四个关键字典组 W-01 contact_source 观察点
4) 构建新鲜度判别D-03 无写库副作用POST /api/customer/edit 不带 version
67005 = customer-defectfix 修复的新构建67001+乐观锁= 旧构建验收会全部误报未修
只读探测edit 探针在 CAS 校验前置失败不产生任何写库
用法py -X utf8 .scratch/customer-defectfix/acceptance-probe-testenv.py
产物acceptance-probe-testenv-result.json与本脚本同目录
"""
import io
import json
import os
import sys
import time
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
import requests
BASE = 'https://ai.itc.vip/crm-api'
ADMIN = '739564171091247104' # 罗伟健(admin)
OUT = os.path.dirname(os.path.abspath(__file__))
RESULT = os.path.join(OUT, 'acceptance-probe-testenv-result.json')
ev = {'base': BASE, 'ts': time.strftime('%Y-%m-%d %H:%M:%S')}
VERDICT = []
def note(msg):
print(msg)
VERDICT.append(msg)
# ---- 1. ping(鉴权门)----
try:
r = requests.get(f'{BASE}/api/customer/ping', timeout=15)
ev['ping_http'] = r.status_code
note(f"1. ping → HTTP {r.status_code}" + ("(401 = 鉴权门活着,服务在)" if r.status_code == 401 else ""))
except Exception as e:
ev['ping_error'] = str(e)[:200]
note(f"1. ping 连接失败: {str(e)[:160]} —— 测试环境不通(nginx/FRP/服务链路),验收无法开始")
json.dump(ev, open(RESULT, 'w', encoding='utf-8'), ensure_ascii=False, indent=1)
sys.exit(1)
# ---- 2. debug token ----
tok = None
try:
r = requests.get(f'{BASE}/api/auth/debug/token', params={'userId': ADMIN}, timeout=15)
body = r.json() if r.status_code == 200 else {}
d = body.get('data')
tok = d if isinstance(d, str) else (d or {}).get('token')
ev['debug_token_http'] = r.status_code
except Exception as e:
ev['debug_token_error'] = str(e)[:200]
if not tok:
note("2. debug token 不可用 —— verify profile 未激活或端点被拦;demo 免登会失败,需检查测试环境启动配置")
json.dump(ev, open(RESULT, 'w', encoding='utf-8'), ensure_ascii=False, indent=1)
sys.exit(1)
note("2. debug token OK(verify profile 激活,demo 可免登)")
H = {'Authorization': f'Bearer {tok}'}
S = requests.Session()
S.headers.update(H)
def code_of(r):
try:
return (r.json() or {}).get('code')
except Exception:
return None
# ---- 3. 数据状态 ----
try:
m = S.post(f'{BASE}/api/customer/workspace/mine/page', data={'current': 1, 'size': 3}, timeout=20).json()
p = S.post(f'{BASE}/api/customer/workspace/pool/page', data={'current': 1, 'size': 3}, timeout=20).json()
md, pd = m.get('data') or {}, p.get('data') or {}
ev['mine_total'], ev['pool_total'] = md.get('total'), pd.get('total')
note(f"3. 数据态:mine total={md.get('total')} pool total={pd.get('total')}")
for row in (md.get('content') or [])[:3]:
print(f" - {row.get('customerNo')} {row.get('customerName')}")
if not md.get('total') and not pd.get('total'):
note(" ⚠ 测试环境无客户数据 —— 需先跑 demo_showcase.py --base https://ai.itc.vip/crm-api --skip-db 造验收数据")
for g in ('customer_type', 'follow_way', 'industry', 'contact_source'):
try:
d = S.get(f'{BASE}/api/dict/item/enabled-list', params={'groupCode': g}, timeout=15).json()
items = d.get('data') or []
ev[f'dict_{g}'] = len(items)
print(f" dict {g}: {len(items)}" + (" ⚠ 空(W-01 同款观察点)" if not items else ""))
except Exception as e:
print(f" dict {g}: 探测失败 {str(e)[:80]}")
except Exception as e:
note(f"3. 数据态探测失败: {str(e)[:160]}")
# ---- 4. 构建新鲜度判别(D-03,无写库)----
try:
rows = ((S.post(f'{BASE}/api/customer/workspace/mine/page', data={'current': 1, 'size': 1}, timeout=20).json()).get('data') or {}).get('content') or []
if not rows:
note("4. 构建判别跳过:mine 无客户可取样(先造数再判构建)")
else:
fid = rows[0]['id']
det = S.get(f'{BASE}/api/customer/detail', params={'id': fid}, timeout=20).json()
d = det.get('data') or {}
form = {
'customerName': d.get('customerName') or rows[0].get('customerName'),
'customerType': d.get('customerType'), 'provinceCode': d.get('provinceCode'),
'cityCode': d.get('cityCode'), 'districtCode': d.get('districtCode'),
'industryCode': d.get('industryCode'), 'customerStarLevel': d.get('customerStarLevel') or 3,
'relationStarLevel': d.get('relationStarLevel') or 3,
'isBizNegotiated': d.get('isBizNegotiated') if d.get('isBizNegotiated') is not None else 0,
'isChild': d.get('isChild') if d.get('isChild') is not None else 0,
}
# 刻意不带 version:CAS 前置失败,无写库
t0 = time.time()
r = S.post(f'{BASE}/api/customer/edit', params={'id': fid}, data=form, timeout=60)
got = code_of(r)
ev['d03_probe'] = {'customer_id': str(fid), 'http': r.status_code, 'code': got,
'message': str((r.json() or {}).get('message'))[:80] if r.headers.get('content-type', '').startswith('application/json') else '',
'elapsed_sec': round(time.time() - t0, 2)}
msg = ev['d03_probe']['message']
if str(got) == '67005':
note("4. 构建判别:缺 version → 67005 —— **含 D-02/03/04/05/07 修复的新构建**,可以开始验收")
elif str(got) == '67001' and '乐观锁' in msg:
note("4. 构建判别:缺 version → 67001(乐观锁)—— **旧构建(缺陷修复未部署)**,验收前须先发新 jar(mvn clean package → 上传 → deploy/restart.sh)")
else:
note(f"4. 构建判别结果异常:code={got} msg={msg} —— 需人工看一眼(请求形态可能被必填校验先拦)")
except Exception as e:
note(f"4. 构建判别失败: {str(e)[:160]}")
ev['verdict'] = VERDICT
json.dump(ev, open(RESULT, 'w', encoding='utf-8'), ensure_ascii=False, indent=1)
print(f"\n证据已落盘 {RESULT}")