| 规格项 | 文档要求 | 本次实现 | 状态 |
|---|---|---|---|
| 数据表 | 16 张(14 业务 + 2 辅助) | 16 张业务表 + 6 张运行时辅助表,真建表、真索引 | 已实现 |
| REST 接口 | 16 条 /api/v1 | 16 条 + 8 条配套接口,逐条 method 显式判断 | 已实现 |
| 状态机 | 8 状态,非法 409 / LEAD_002 | 服务端硬校验,禁止跳到 signed,迁移写 lead_events | 已实现 |
| GEO | PostGIS ST_DWithin + 地图解析 | Haversine 真算 + 本地地址库;未配地图 Key 时 GEO_101 | 可替换 |
| 线索分级 | 可配置规则 JSON,A≥80/B≥55/C≥25 | 规则存 config 表,可热改且写审计,两条 −100 硬拦截 | 已实现 |
| 合规风控 | 阻断 / 替换 / 提示三级 | 发送前校验,RISK_201 整体阻断 + 固定应答 + 待办 + 哈希 | 已实现 |
| 模型编排 | OpenAI 兼容三配置项 | 协议层已用本地 mock upstream 真网络实测 14 项(llm_probe.js:参数名、Bearer、返回结构、重试、超时、降级);真供应商 Key 仍未实测,默认关闭走降级模板 | 协议已验 / 真 Key 待验 |
| PostGIS / pgvector / Redis | PostgreSQL 16 + 扩展 + Redis 7 | 本机未安装,用 node:sqlite + 内存 token 替代,DAO 可换 | 待建 |
| 渠道开放平台 | 私信 / 评论 / 企微 / 外呼 | 仅本地接入骨架 + 验签;无平台审批与真 Key,不真发 | 待建 |
| 前端管理后台 | Vue 3 + Ant Design Vue | 本控制台为单文件零依赖版,功能等价、可换 Vue | 简化 |
| 层 | 做法 | 抓什么 |
|---|---|---|
| L1 真实 HTTP 接口回归 | node api_test.js 另起服务 + 独立测试库,逐条打 16 接口 | 业务规则、鉴权越权、错误码、幂等、签名重放、敏感路径 |
| L2 前端静态契约 | 扫描内联事件函数定义、id 引用、API 路径、页面不含任何密钥 | 点击才炸的未定义函数、路径写错 |
| L3 真实浏览器端到端 | python tools/e2e_test.py Playwright 缓存 Chromium + 零依赖 CDP,真点击 / 真接口 / 390px 真视口 | 白屏、整段 script 中断、运行时异常、布局塌陷、页签与视图切换 |
| L4 模型协议联调 | node llm_probe.js 本机 OpenAI 兼容 mock upstream 走真实 TCP | 请求参数名、Authorization、返回结构、5xx 重试、超时、降级、密钥不泄露 |
LEAD_001 必填字段缺失 LEAD_002 非法状态迁移(409) GEO_101 地址无法解析 GEO_102 地址粒度过粗 RISK_201 命中阻断级词(451) RISK_202 命中替换级词 CHN_301 渠道签名校验失败 LLM_401 模型调用超时/降级 补充:AUTH_501 未登录 / AUTH_502 越权 / IDEM_601 缺幂等键 / IDEM_602 幂等键重放冲突 / REQ_701 非法 JSON
TS=$(date +%s); EVENT="evt-001"; NONCE=$(date +%s%N)
BODY='{"channel":"douyin","conv_key":"c1","sender_id":"u1","msg_type":"text","timestamp":"'$TS'","text":"想开面馆","phone":"13800000009","city":"苏州"}'
SIG=$(printf '%s' "$EVENT|$TS|$BODY" | openssl dgst -sha256 -hmac "$CHANNEL_SECRET" -hex | awk '{print $NF}')
curl -X POST http://127.0.0.1:8788/api/v1/messages/ingress \
-H "X-Event-Id: $EVENT" -H "X-Timestamp: $TS" -H "X-Nonce: $NONCE" \
-H "X-Signature: $SIG" -H "Content-Type: application/json" -d "$BODY"