Files
soul-yongping/scripts/test/process/test_backfill_persons_ckb_api_key.py
Alex-larget 9210b931c4 Enhance profile editing and sharing functionality
- Added a new feature for sharing profile cards, including special handling for forwarding to friends and displaying a canvas cover with user information.
- Updated the mini program's profile-edit page to generate a shareable card with a structured layout, including user avatar, nickname, and additional information.
- Improved the documentation to reflect the new sharing capabilities and updated the last modified date for relevant entries.
2026-03-16 17:18:49 +08:00

167 lines
5.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# -*- coding: utf-8 -*-
"""
流程测试:从存客宝获取所有计划的 apiKey补齐本地 persons.ckb_api_key
场景persons 表有 ckb_plan_id 但 ckb_api_key 为空时,调用存客宝 plan/detail 获取 apiKey 并更新本地
前置条件:
- 测试环境SOUL_TEST_ENV=souldev 或 local
- soul-api 可连通CKB_OPEN_API_KEY、CKB_OPEN_ACCOUNT 已配置soul-api/.env
"""
import hashlib
import time
import pytest
import requests
from config import SOUL_API_ENV
from util import admin_headers
CKB_OPEN_BASE = "https://ckbapi.quwanzhi.com"
def _ckb_open_sign(account: str, ts: int, api_key: str) -> str:
"""存客宝开放 API 签名sign = MD5(MD5(account+timestamp) + apiKey)"""
plain = account + str(ts)
first = hashlib.md5(plain.encode()).hexdigest()
return hashlib.md5((first + api_key).encode()).hexdigest()
def _ckb_get_token(api_key: str, account: str) -> str:
"""获取存客宝开放 API JWT"""
ts = int(time.time())
sign = _ckb_open_sign(account, ts, api_key)
r = requests.post(
f"{CKB_OPEN_BASE}/v1/open/auth/token",
json={"apiKey": api_key, "account": account, "timestamp": ts, "sign": sign},
timeout=15,
)
data = r.json()
if data.get("code") != 200:
raise RuntimeError(f"存客宝鉴权失败: {data.get('message', r.text)}")
token = (data.get("data") or {}).get("token")
if not token:
raise RuntimeError("存客宝返回无 token")
return token
def _ckb_get_plan_api_key(token: str, plan_id: int) -> str:
"""调用 plan/detail 获取计划级 apiKey"""
r = requests.get(
f"{CKB_OPEN_BASE}/v1/plan/detail",
params={"planId": plan_id},
headers={"Authorization": f"Bearer {token}", "Content-Type": "application/json"},
timeout=15,
)
data = r.json()
if data.get("code") != 200:
raise RuntimeError(f"获取计划详情失败 planId={plan_id}: {data.get('message', r.text)}")
api_key = (data.get("data") or {}).get("apiKey")
if not api_key:
raise RuntimeError(f"计划 {plan_id} 详情中无 apiKey")
return api_key
def _load_ckb_config() -> tuple[str, str]:
"""从 soul-api/.env 加载 CKB 配置"""
def _parse_env(path):
out = {}
if not path.exists():
return out
for line in path.read_text(encoding="utf-8", errors="ignore").splitlines():
line = line.strip()
if not line or line.startswith("#") or "=" not in line:
continue
k, v = line.split("=", 1)
out[k.strip()] = v.strip().strip('"').strip("'")
return out
for name in [".env", ".env.development", ".env.production"]:
env_path = SOUL_API_ENV / name
loaded = _parse_env(env_path)
api_key = (loaded.get("CKB_OPEN_API_KEY") or "").strip()
account = (loaded.get("CKB_OPEN_ACCOUNT") or "").strip()
if api_key and account:
return api_key, account
return "", ""
def test_backfill_persons_ckb_api_key(admin_token, base_url):
"""
从存客宝获取所有计划的 apiKey补齐本地 persons.ckb_api_key 为空的记录
"""
if not admin_token:
pytest.skip("admin 登录失败,跳过")
ckb_api_key, ckb_account = _load_ckb_config()
if not ckb_api_key or not ckb_account:
pytest.skip("CKB_OPEN_API_KEY 或 CKB_OPEN_ACCOUNT 未配置,跳过")
# 1. 拉取 persons 列表
r = requests.get(
f"{base_url}/api/db/persons",
headers=admin_headers(admin_token),
timeout=10,
)
assert r.status_code == 200, f"拉取 persons 失败: {r.text}"
data = r.json()
assert data.get("success") is True, f"拉取 persons 失败: {data}"
persons = data.get("persons") or []
# 2. 筛选需要补全的ckb_plan_id > 0 且 ckb_api_key 为空
need_backfill = [
p
for p in persons
if (p.get("ckbPlanId") or 0) > 0
and not (p.get("ckbApiKey") or "").strip()
]
if not need_backfill:
pytest.skip("无需要补全 ckb_api_key 的 Person跳过")
# 3. 获取存客宝 JWT
ckb_token = _ckb_get_token(ckb_api_key, ckb_account)
# 4. 逐个补全
updated = 0
failed = []
for p in need_backfill:
plan_id = p.get("ckbPlanId") or 0
person_id = p.get("personId") or ""
name = p.get("name") or ""
try:
api_key = _ckb_get_plan_api_key(ckb_token, plan_id)
except Exception as e:
failed.append((name, str(e)))
continue
# 5. 调用 soul-api 更新 PersonPOST 带 personId 为更新,传完整字段避免覆盖)
payload = {
"personId": person_id,
"name": name,
"label": (p.get("label") or ""),
"ckbApiKey": api_key,
"greeting": (p.get("greeting") or ""),
"tips": (p.get("tips") or ""),
"remarkType": (p.get("remarkType") or ""),
"remarkFormat": (p.get("remarkFormat") or ""),
"startTime": (p.get("startTime") or "09:00"),
"endTime": (p.get("endTime") or "18:00"),
}
if p.get("addFriendInterval"):
payload["addFriendInterval"] = p["addFriendInterval"]
r_update = requests.post(
f"{base_url}/api/db/persons",
headers=admin_headers(admin_token),
json=payload,
timeout=15,
)
if r_update.status_code == 200 and r_update.json().get("success"):
updated += 1
else:
failed.append((name, r_update.text or "更新失败"))
assert not failed, f"补全失败: {failed}"
assert updated > 0, f"应至少补全 1 条,实际补全 {updated}"
print(f"\n[backfill] 成功补全 {updated} 条 persons.ckb_api_key")