Files
xiaoqu_mcp_service/xiaoqu-agent-service/doc/crm-integration-guide.md
T

144 lines
7.3 KiB
Markdown
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.
# CRM 接入小趣 MCP 手册
> 适用版本:REQ-20260911-0013(增量同步框架)交付后
> 端点:`https://agent.ququclean.com/mcp`(生产)/ `http://192.168.4.236:8088/mcp`(内网)
> 协议:MCP Streamable HTTP(JSON-RPC),所有响应为 SSE,取 `data:` 行
>
> 当前生产可用性、阻塞项和下一步以[对接进度](crm-sync-integration-progress.md)为准;本手册描述目标调用方式,不代表发布门禁已经通过。
## 0. 边界(先读这段)
小趣 MCP 只做三件事:**供数据、供身份、守权限**。
到期规则计算、AI 决策、推送、通知负责人、经营事项流转——全部是 CRM / 小趣 AI 系统的职责,MCP 不提供也不预留任何相关字段。
## 1. 一次性开通
| 步骤 | 操作方 | 说明 |
|---|---|---|
| 1 | 小趣管理员 | 登录 `https://agent.ququclean.com/`,进入「服务密钥」→「创建服务密钥」 |
| 2 | 小趣管理员 | 调用方名称填 `crm`,勾选 `sync` 作用域,保存并立即复制只展示一次的 `X-Open-Key` |
| 3 | CRM 后端 | 用下面的请求换取**长期服务 Token**(`xqak-...`),安全保管 |
```bash
curl -X POST 'https://agent.ququclean.com/api/open/token/ensure' \
-H 'X-Open-Key: <管理后台创建后显示的服务密钥>' \
-H 'Content-Type: application/json' \
-d '{"scopeCode":"sync","principalType":"service","serviceName":"crm"}'
```
响应的 `token` 就是后续放进 MCP `Authorization: Bearer <token>` 的服务 Token。`X-Open-Key` 不是 MCP Token,不能直接放进 Authorization 头。
完成后 CRM 持有两样东西:**服务 Token**(后台扫描用)、**服务密钥**(重新 ensure 获取 Token 或给销售换个人 Token 用)。若还需要给销售换 QUERY 个人 Token,创建/编辑服务密钥时还要勾选对应 QUERY 作用域;只绑定 `sync` 的密钥不能跨域签发个人 Token。
密钥泄露时,管理员在同一页面点「轮换」(旧密钥立即失效)或「吊销」。数据库只保存密钥哈希,关闭创建/轮换结果弹窗后无法找回明文。
## 2. 每次调用前的身份自检
```
tools/call integration.context.get
```
返回 `principal_type` / `scopes` / `capability_levels` / `data_scope_version`。
- 后台扫描任务启动时必须确认 `principal_type=tenant_service` 且 capability 含 SYNC;拿错 Token(user_delegated)时**拒绝执行扫描**,防止串权。
- `data_scope_version` 变化说明管理员改了授权范围,建议触发一次全量重建。
## 3. 后台通道:每日定时扫描(增量同步)
三个变化流工具:`users.changes.list` / `properties.changes.list` / `contracts.changes.list`。
入参只有三个(**禁止传 tenant_id/scope**,传了直接 PERMISSION_DENIED):
```json
{ "syncToken": null, "cursor": null, "limit": 200 }
```
### 协议语义
- **快照**:每轮同步签发 `snapshot_id` + `data_cutoff_at`,同一快照内翻页结果稳定,页间不会有数据漂移。
- **游标**:页内按 `(modify_time, id)` 复合游标推进;`has_more=true` 时用 `next_cursor` 翻下一页。
- **续传**:只有最后一页(`has_more=false`)才签发 `next_sync_token`。中断/宕机后从本地保存的 `sync_token + cursor` 继续,**不重不漏**。
- **tombstone**:`deleted_at` 非空的条目是被删除的数据,CRM 本地镜像应标记删除,不进 AI。
- **limit**:上限 200,传更大会被截断到 200 并带 notice。
### CRM 侧定时任务伪代码
```
token, cursor = load_local() # 首次均为 null → 全量
loop:
resp = call("contracts.changes.list", {syncToken: token, cursor: cursor, limit: 200})
for item in resp.items:
if item.deleted_at: 本地镜像标记删除(item.contract_id)
else: 本地镜像 upsert(item)
save_local(token, resp.next_cursor) # 每页都存,断点恢复
if resp.has_more: cursor = resp.next_cursor; continue
save_local(resp.next_sync_token, null) # 本轮结束,钉死新游标
break
```
### 错误分支(必须实现)
| 错误码 | 含义 | CRM 动作 |
|---|---|---|
| `SYNC_CURSOR_EXPIRED` | 快照/游标过期或被篡改 | 清空本地镜像,syncToken=null 全量重建 |
| `RATE_LIMITED` | 触发 SYNC 限流档 | 指数退避重试(建议 1s/2s/4s…上限 60s) |
| `UNAUTHENTICATED` | Token 被吊销/人员停用 | 重新 ensure 取 Token,仍失败则告警人工介入 |
### 两个错误用法
- 不要拿 `version` 字段做并发控制——它由 `modify_time` 派生,只用于调试核对。
- 不要把扫描任务和销售在线查询混用同一个 Token——服务 Token 调 QUERY 工具会被拒(scope 白名单),这是设计如此。
### 发送前复核(不能省略)
日同步只负责发现候选。CRM 在晨报、午报或日结真正入推送队列前,必须使用同一个服务 Token 批量调用:
```text
tools/call contracts.current.batch_get
arguments: {"contractIds":["1001","1002"]}
```
- 每批 1—200 个稳定合同 ID,返回 `checked_at/count/items/not_found_ids`。
- `items` 使用与合同变化流相同的公开字段;软删合同返回删除标记,查无记录进入 `not_found_ids`。
- CRM 必须逐项复核状态、到期日、负责人、客户归属、提醒配置和续约结果。任一变化、缺失、删除、响应不完整、证据时间异常或服务不可用,对应合同本次都不得发送。
- 该工具属于 SYNC 能力和限流档,调用方传 `tenant_id/tenantId/scope` 会被拒绝;数据域只能来自服务 Token。
## 4. 前台通道:销售在线查询(实时 ACL)
```
1. 销售登录 CRM → CRM 后端调 /api/open/token/ensure {mobile: 销售手机号}
幂等,同一人永远拿到同一个个人 Token;缓存 "CRM用户 ↔ xqak ↔ userId" 映射。
2. 销售查合同时,带他的个人 Token 调:
- contract.list.query (列表,12 字段,自动按实时 ACL 行过滤)
- contract.detail.query (详情,含 version、files、processes)
3. 个人 Token 调 SYNC 工具直接被 scope 白名单拦截——销售永远扫不到全量数据。
```
个人 Token 缓存在 CRM 侧即可;收到 `UNAUTHENTICATED`(如人员被停用,60 秒内生效)时清缓存重取。
## 5. 两条通道速查
| | 前台(销售点查) | 后台(定时扫描) |
|---|---|---|
| Token 类型 | user_delegated | tenant_service |
| 可用工具 | contract.list/detail.query 等 QUERY | *.changes.list + contracts.current.batch_get + context.get |
| 数据范围 | 实时 ACL 行过滤 | 管理员授权的 sync scope |
| 限流 | 常规档 | SYNC 独立档,互不占配额 |
| tenant_id 入参 | 不允许 | 不允许,从 Token 推导 |
## 6. 通用输出规范
所有工具返回统一信封 `structuredContent`:
```json
{ "schema_version": 2, "request_id": "...", "data": { ... } }
```
- 请求头 `X-Mcp-Schema-Version: 2` 启用新信封;不带则返回老结构(v1,兼容期内有效)。
- 所有请求把 `request_id` 记入 CRM 日志,排查时两边对得上。
- **Token 和手机号明文不要落日志**(MCP 侧已对手机号打码,CRM 侧请同样处理)。
## 7. 后续扩展
同步协议是通用框架:以后新增设备/订单/工单同步,CRM 侧代码不变,只是多调一个 `xxx.changes.list`、多存一对 `sync_token/cursor`。输出信封与错误码也不变。