feat: add crm webhook worker

This commit is contained in:
Codex
2026-06-29 04:35:19 +08:00
parent 0cff0d102d
commit ead1296f80
23 changed files with 1098 additions and 14 deletions

107
docs/refactor/crm-worker.md Normal file
View File

@@ -0,0 +1,107 @@
# CRM Webhook Worker
更新时间2026-06-29
`apps/worker` 是后端异步任务进程,当前首个落地任务是 CRM 客资 webhook 推送。API 只负责在客资首绑时写入 `crm_webhook_queue`worker 负责消费到期任务、签名、发送、失败重试和写入 `crm_webhook_log`
## 运行命令
本地单次消费一批任务:
```bash
npm --workspace @tiku-saas/worker run crm:once
```
或使用根脚本测试:
```bash
npm run test:worker:crm
```
生产常驻:
```bash
npm --workspace @tiku-saas/worker run start
```
建议 API 和 worker 作为两个独立进程部署,共用同一个 `DATABASE_URL`
## 配置
环境变量:
```text
WORKER_CRM_BATCH_SIZE=20
WORKER_CRM_POLL_INTERVAL_MS=10000
WORKER_CRM_MAX_ATTEMPTS=5
WORKER_CRM_BACKOFF_SECONDS=5,30,120,600,1800
WORKER_CRM_REQUEST_TIMEOUT_MS=10000
WORKER_CRM_ALLOW_INSECURE_LOCALHOST=false
```
租户后台配置仍走:
```text
PUT /api/crm/config
GET /api/crm/config
GET /api/crm/queue
```
密钥必须写入 `app_private.tenant_secrets`,公共表 `crm_config.secret_ref` 只保留引用,例如:
```text
app_private.tenant_secrets:crm:webhook
```
## Provider
worker 根据队列 payload 的 `provider` 或 webhook URL 自动判断:
| provider | URL 特征 | 行为 |
| --- | --- | --- |
| `generic` | 默认 | POST `{ event: "lead.created", data: ... }` |
| `dingtalk` | `dingtalk.com` | markdown 消息;按钉钉自定义机器人安全设置做 timestamp/sign |
| `feishu` | `feishu.cn` / `larksuite.com` | interactive card按飞书自定义机器人签名做 timestamp/sign |
| `wecom` | `qyapi.weixin.qq.com` / `work.weixin.qq.com` | 企业微信群机器人 markdown 消息 |
官方参考:
- 钉钉开放平台:[自定义机器人安全设置](https://open.dingtalk.com/document/robots/customize-robot-security-settings)`timestamp + "\n" + secret` 做 HMAC-SHA256 后 Base64。
- 飞书开放平台:[自定义机器人签名校验](https://open.feishu.cn/document/client-docs/bot-v3/add-custom-bot),使用 timestamp 和 secret 生成签名。
- 企业微信开发者文档:[群机器人配置说明](https://developer.work.weixin.qq.com/document/path/91770),群机器人 webhook 使用 `msgtype` 消息体。
## 队列状态
`crm_webhook_queue.status`
```text
pending -> processing -> sent
pending -> processing -> retrying -> processing -> sent
pending -> processing -> retrying -> failed
pending -> discarded
```
说明:
- `pending` / `retrying`:等待 worker 消费。
- `processing`worker 已抢占任务。
- `sent`:目标 webhook 返回 2xx。
- `failed`:超过最大重试次数。
- `discarded`:租户 CRM 未启用或 URL 缺失,任务不会继续重试。
每次尝试都会写入 `crm_webhook_log`,日志只记录 provider、目标 host、请求体和响应摘要不写入 webhook secret。
## 安全边界
- 前端不能直接写 `crm_webhook_queue`
- 前端不能持有 CRM webhook secret。
- 生产环境 webhook 必须使用 HTTPS。
- `WORKER_CRM_ALLOW_INSECURE_LOCALHOST=true` 只用于本地 fake webhook 测试。
- worker 使用数据库服务端连接读取 `app_private.tenant_secrets`,不要把 secret 复制到公共表。
## 后续增强
- 轮询/定向分配销售。
- 钉钉/飞书/企微富卡片模板。
- 失败告警和死信运营后台。
- 批量 CRM 推送、跟进效果统计。