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.
142 lines
5.4 KiB
142 lines
5.4 KiB
|
2 days ago
|
# -*- coding: utf-8 -*-
|
||
|
|
"""票 10-N3g 扩展:①重采文档现行示例客户 detail-head(projectCount 真值口径)
|
||
|
|
②图谱轻夹具(create+quickAdd+DB 改值)采 graph/detail isInternal/giftRemark 真值。
|
||
|
|
产物:并回 specimens-proj-truth.json。"""
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
import io
|
||
|
|
import json
|
||
|
|
import sys
|
||
|
|
import time
|
||
|
|
from pathlib import Path
|
||
|
|
|
||
|
|
import pymysql
|
||
|
|
import requests
|
||
|
|
|
||
|
|
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
|
||
|
|
|
||
|
|
BASE = 'http://localhost:8080'
|
||
|
|
ADMIN = '739564171091247104'
|
||
|
|
OUT = Path('.scratch/customer-integration-ready')
|
||
|
|
DOC_CUST = '750844477165273088' # 详情公共头部.bru 现行示例客户(e2c-全字段-科技)
|
||
|
|
PFX = 'e2c-r3d-n3'
|
||
|
|
TS = str(int(time.time()))[-6:]
|
||
|
|
|
||
|
|
specimens = json.loads((OUT / 'specimens-proj-truth.json').read_text(encoding='utf-8'))
|
||
|
|
|
||
|
|
|
||
|
|
def get_token(uid):
|
||
|
|
r = requests.get(f'{BASE}/api/auth/debug/token', params={'userId': uid}, timeout=10)
|
||
|
|
r.raise_for_status()
|
||
|
|
return r.json()['data']
|
||
|
|
|
||
|
|
|
||
|
|
def api(method, path, params=None, form=None, step=''):
|
||
|
|
headers = {'Authorization': f'Bearer {get_token(ADMIN)}'}
|
||
|
|
kw = {'headers': headers, 'timeout': 60}
|
||
|
|
if method == 'POST':
|
||
|
|
kw['data'] = form or {}
|
||
|
|
else:
|
||
|
|
kw['params'] = params or {}
|
||
|
|
r = requests.request(method, BASE + path, **kw)
|
||
|
|
try:
|
||
|
|
body = r.json()
|
||
|
|
except Exception:
|
||
|
|
body = {'_raw': r.text[:300], '_status': r.status_code}
|
||
|
|
print(f" [{step or path}] http={r.status_code} code={body.get('code') if isinstance(body, dict) else '?'}")
|
||
|
|
return r, body
|
||
|
|
|
||
|
|
|
||
|
|
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 dbx(sql, args=None):
|
||
|
|
with db() as conn, conn.cursor() as cur:
|
||
|
|
cur.execute(sql, args)
|
||
|
|
return cur.rowcount
|
||
|
|
|
||
|
|
|
||
|
|
def sweep():
|
||
|
|
rows = dbq("SELECT id FROM customer WHERE customer_name LIKE %s", (PFX + '%',))
|
||
|
|
for row in rows:
|
||
|
|
cid = row['id']
|
||
|
|
for t in ['customer_contact_edge', 'customer_contact_graph', 'customer_contact_reveal_log',
|
||
|
|
'customer_contact', 'customer_oplog', 'team_member', 'customer_focus']:
|
||
|
|
try:
|
||
|
|
dbx(f'DELETE FROM {t} WHERE customer_id=%s', (cid,))
|
||
|
|
except Exception:
|
||
|
|
pass
|
||
|
|
dbx('DELETE FROM customer WHERE id=%s', (cid,))
|
||
|
|
print(f' sweep: 清扫 {len(rows)} 组')
|
||
|
|
|
||
|
|
|
||
|
|
def main():
|
||
|
|
# 1) 重采文档示例客户 detail-head(对比旧快照只回填漂移字段)
|
||
|
|
_, b = api('GET', '/api/customer/detail-head', params={'id': DOC_CUST}, step='detail-head doc 客户')
|
||
|
|
specimens['GET /api/customer/detail-head(doc-cust)'] = {
|
||
|
|
'request': {'id': DOC_CUST}, 'response': b}
|
||
|
|
|
||
|
|
# 2) 图谱轻夹具
|
||
|
|
sweep()
|
||
|
|
name = f'{PFX}-图客-{TS}'
|
||
|
|
r, cust = api('POST', '/api/customer/create', form={
|
||
|
|
'customerName': name, 'customerType': 'customer_type_01',
|
||
|
|
'provinceCode': '440000', 'cityCode': '440100', 'districtCode': '440103',
|
||
|
|
'industryCode': 'other', 'customerStarLevel': 3, 'relationStarLevel': 3,
|
||
|
|
'isBizNegotiated': 0, 'isChild': 0, 'ownerUserId': ADMIN,
|
||
|
|
}, step='create 图客')
|
||
|
|
cid = (cust.get('data') or {}).get('id')
|
||
|
|
assert cid, f'图客创建失败: {str(cust)[:200]}'
|
||
|
|
print(f' 图客 id={cid}')
|
||
|
|
|
||
|
|
try:
|
||
|
|
r, qa = api('POST', '/api/customer/contact/quickAdd', form={
|
||
|
|
'customerId': str(cid),
|
||
|
|
'rows[0].name': f'n3{TS}张内线', 'rows[0].jobTitleCode': 'chairman',
|
||
|
|
'rows[0].phone': '13800099901', 'rows[0].source': 'group_meeting',
|
||
|
|
'rows[0].isKeyContact': '0',
|
||
|
|
}, step='quickAdd x1')
|
||
|
|
d = qa.get('data') or {}
|
||
|
|
assert d.get('successCount') == 1 and not d.get('failedRows'), f'quickAdd 失败: {str(qa)[:300]}'
|
||
|
|
|
||
|
|
rows = dbq('SELECT id, is_internal, gift_remark FROM customer_contact '
|
||
|
|
'WHERE customer_id=%s ORDER BY id LIMIT 1', (cid,))
|
||
|
|
assert rows, '联系人未落库'
|
||
|
|
c0 = rows[0]
|
||
|
|
orig = (c0['is_internal'], c0['gift_remark'])
|
||
|
|
print(f" 联系人 {c0['id']} 原值 is_internal={orig[0]} gift={orig[1]!r}")
|
||
|
|
|
||
|
|
dbx('UPDATE customer_contact SET is_internal=1, gift_remark=%s WHERE id=%s',
|
||
|
|
('e2c-r3d-n3-礼品-龙井茶两盒', c0['id']))
|
||
|
|
try:
|
||
|
|
_, b3 = api('GET', '/api/customer/contact/graph/detail',
|
||
|
|
params={'customerId': cid}, step='graph/detail')
|
||
|
|
specimens['GET /api/customer/contact/graph/detail'] = {
|
||
|
|
'request': {'customerId': str(cid)}, 'response': b3}
|
||
|
|
node = ((b3.get('data') or {}).get('nodes') or [{}])[0]
|
||
|
|
print(' nodes[0].isInternal =', node.get('isInternal'),
|
||
|
|
' giftRemark =', node.get('giftRemark'))
|
||
|
|
finally:
|
||
|
|
dbx('UPDATE customer_contact SET is_internal=%s, gift_remark=%s WHERE id=%s',
|
||
|
|
(orig[0], orig[1], c0['id']))
|
||
|
|
print(' 联系人已还原')
|
||
|
|
finally:
|
||
|
|
sweep()
|
||
|
|
|
||
|
|
(OUT / 'specimens-proj-truth.json').write_text(
|
||
|
|
json.dumps(specimens, ensure_ascii=False, indent=2), encoding='utf-8')
|
||
|
|
print('== specimens-proj-truth.json 更新,', len(specimens), '条')
|
||
|
|
|
||
|
|
|
||
|
|
if __name__ == '__main__':
|
||
|
|
main()
|