codex feishu notification implementation
本地来源:Knowledge/World/项目/文档/feishu/codex-feishu-notification-implementation.md
飞书业务通知聚合系统(Next.js 集成版执行方案)
文件目标:任何人照着做,像搭积木一样一步一步完成,不需要再确认。
方案选择:接到现有 Next.js App Router(推荐),不新起 Cloudflare Worker。
重点:飞书群机器人 + 多维表格 双写;支付/邮件/监控/API 余额全覆盖。
0. 一句话目标
把“支付、邮件异常、网站宕机、API 余额告警”都统一汇总到飞书群 + 飞书多维表格,即时提醒 + 历史留痕,不改动核心业务逻辑,避免新 bug。
1. 项目现状(先摸清底子)
这些文件已经存在,后续要“接上新通知系统”:
- Stripe Webhook 路由:
src/app/api/webhooks/stripe/route.ts - Creem Webhook 路由:
src/app/api/webhooks/creem/route.ts - 付款成功通知(目前只发文本):
src/notification/feishu.ts、src/notification/notification.ts - 支付逻辑入口:
src/payment/provider/stripe.ts、src/payment/provider/creem.ts - Webhook 幂等记录:
src/db/schema.ts里的webhook_event表 - 通用 HMAC 校验工具:
src/lib/webhook-verification.ts - 限流工具:
src/lib/rate-limit.ts - Cron 路由示例:
src/app/api/cron/*/route.ts
结论:支付回调已经有了,但“飞书多维表格、邮件异常、监控、API 余额告警”还没有。
2. 总体方案(用大白话说)
我们不搞新服务,直接在 Next.js 里加几个 API 路由,把通知做成“统一中转站”。
数据流就是:
第三方 Webhook / 定时任务
↓
Next.js API 路由(验签 + 幂等 + 解析)
↓
飞书群机器人(即时通知)
+
飞书多维表格(历史记录)
3. 必改/必新增文件清单(手把手)
3.1 新建目录(按要求放到 extensions)
src/extensions/notification/
新增文件建议结构(明确职责,避免混乱):
src/extensions/notification/feishu-client.ts- 负责:发送机器人消息、处理签名
src/extensions/notification/feishu-bitable.ts- 负责:获取
tenant_access_token+ 写入多维表格 src/extensions/notification/formatters.ts- 负责:把事件变成“飞书卡片 + 表格字段”
src/extensions/notification/index.ts- 负责:统一出口(比如
notifyPayment()/notifyAlert())
3.2 修改已有通知入口(兼容旧逻辑)
src/notification/feishu.ts- 改为调用
src/extensions/notification/的新函数 - 这样原来支付逻辑不用改太多,旧调用还能跑
3.3 新增 Webhook 路由
src/app/api/webhooks/resend/route.tssrc/app/api/webhooks/uptime/route.ts
3.4 新增定时任务路由(Vercel Cron)
新增:
- src/app/api/cron/api-monitor/route.ts
并在 vercel.json 新增 cron 配置(或 Vercel 控制台配置):
/api/cron/api-monitor 每小时运行一次
3.5 扩展支付 Webhook 的通知
- Stripe:
src/payment/provider/stripe.ts - 在
handleWebhookEvent增加事件处理:invoice.payment_failedcharge.refundedcharge.dispute.created
- Creem:
src/payment/provider/creem.ts - 增加
dispute.created(如果 Creem 支持) - 退款/取消继续发送通知
4. 环境变量清单(不加会报错)
注意:
.env.example里写了“不要修改”,所以这里只说明 新增变量,实际填到.env.local或 Vercel 环境变量里。
4.1 飞书机器人(必需)
-
FEISHU_BOT_WEBHOOK
飞书群机器人 Webhook URL
兼容旧的FEISHU_WEBHOOK_URL(旧变量可保留) -
FEISHU_BOT_SECRET
飞书机器人签名密钥(如果开启签名校验)
4.2 飞书多维表格(必需)
FEISHU_APP_IDFEISHU_APP_SECRETBITABLE_APP_TOKENBITABLE_TABLE_ID
4.3 Webhook 安全(建议)
RESEND_WEBHOOK_SECRET
Resend Webhook 验签UPTIME_WEBHOOK_SECRET
Uptime(自建/机器人)Webhook 验签UPTIME_ALLOWED_IPS
允许 IP 白名单(可选)
4.4 API 余额告警(可选)
OPENAI_ADMIN_KEYOPENAI_THRESHOLDANTHROPIC_ADMIN_KEYANTHROPIC_THRESHOLD
5. 通知模块设计(核心逻辑)
5.1 feishu-client.ts
职责: - 生成飞书签名(如果启用) - 发送机器人消息 - 统一返回成功/失败
关键点:
- 失败不抛出(不要影响 webhook 主逻辑)
- Promise.allSettled 批量执行,单个失败不影响整体
5.2 feishu-bitable.ts
职责:
- 获取 tenant_access_token(有效期 2 小时)
- 写入多维表格记录
关键点: - 模块级缓存 token(减少请求次数) - token 过期自动刷新
5.3 formatters.ts
职责:把不同事件统一成“飞书卡片 + 表格字段”
必须包含的字段(和多维表格一致):
| 字段 | 说明 |
|---|---|
| 时间 | Date.now() |
| 来源 | Stripe / Creem / Resend / Uptime / API |
| 事件类型 | checkout.completed / refund.created / ... |
| 级别 | 🟢 / 🟡 / 🔴 |
| 客户邮箱 | 可空 |
| 产品 | 可空 |
| 金额 | 数字 |
| 货币 | USD / CNY |
| 订单ID | 可空 |
| 状态 | 默认“待处理” |
| 备注 | 可空 |
| 原始数据 | JSON 字符串 |
5.4 index.ts
职责:提供统一入口方法,例如:
notifyPaymentSuccess()notifyPaymentAlert()(退款/争议/失败)notifyEmailAlert()(邮件 bounce/complaint)notifyUptimeAlert()(宕机/恢复)notifyApiBalanceAlert()
6. Webhook 路由规范(不踩坑版本)
6.1 共通流程(所有 webhook 统一)
request.text()拿 原始 body(签名必须用原始字符串)- 验签(如无签名,返回 401)
- 幂等判断:
- 从
webhook_event查eventId- 有processedAt就直接返回 - 成功处理后更新
processedAt - 失败记录
processedAt = null,方便重试
6.2 Stripe Webhook
文件:src/app/api/webhooks/stripe/route.ts
已有逻辑保留,只补“通知触发点”。
建议:在 handleWebhookEvent 里针对新事件触发通知。
6.3 Creem Webhook
文件:src/app/api/webhooks/creem/route.ts
已有逻辑保留,只补“通知触发点”。
参考事故文档:docs/creem-webhook-incident-2026-01-07.md
6.4 Resend Webhook(新建)
文件:src/app/api/webhooks/resend/route.ts
只通知重要事件(避免刷屏):
- email.bounced
- email.complained
- email.failed
事件 ID:
- 使用 event.data.id(如果有)
- 没有就用 sha256(rawBody) 作为 eventId
6.5 Uptime Webhook(新建)
文件:src/app/api/webhooks/uptime/route.ts
规则:
- down → 红色卡片 + @所有人
- up → 绿色卡片
事件 ID 生成(保证幂等):
uptime_${monitorId}_${status}_${timestamp}
7. API 余额告警(Cron)
文件:src/app/api/cron/api-monitor/route.ts
流程:
1. 验证 CRON_SECRET
2. 拉取 OpenAI/Anthropic 消费
3. 超过阈值就发飞书卡片
4. 可写入多维表格(来源=API)
8. 飞书卡片规范(统一视觉)
建议模板颜色:
| 场景 | 颜色 | 备注 |
|---|---|---|
| 支付成功 | green | 普通好消息 |
| 退款/争议 | red/orange | 紧急 |
| 邮件异常 | red | 需要处理 |
| 宕机 | red | @所有人 |
| 恢复 | green | 无需@ |
| API 余额 | orange | 预警 |
9. 幂等与失败策略(防止重复/漏发)
幂等
所有通知入口都写入 webhook_event 表:
- eventId 唯一
- processedAt 成功后写时间
- 失败保持 null,方便重试
失败不影响主逻辑
飞书/多维表格失败不会阻断支付逻辑
支付逻辑本身必须照常完成
10. 安全点(防伪造)
| 来源 | 验签方式 |
|---|---|
| Stripe | stripe.webhooks.constructEvent |
| Creem | HMAC-SHA256(现有) |
| Resend | HMAC(根据官方头部) |
| Uptime | Secret 或 IP 白名单 |
| Cron | CRON_SECRET |
11. 测试清单(无脑执行)
11.1 Stripe
- 在 Stripe Dashboard 触发
checkout.session.completed - 看飞书群是否出现绿卡
- 查多维表格是否有记录
11.2 Creem
- 使用
scripts/test-creem-webhook.ts - 确认飞书 + 表格写入
11.3 Resend
- 触发一次
email.failed - 确认飞书告警
11.4 Uptime
- 模拟宕机 webhook
- 确认 @所有人
11.5 API 余额
- 手动访问
/api/cron/api-monitor(带CRON_SECRET) - 看飞书预警卡片
12. 部署步骤(按顺序做)
- 配置环境变量(见第 4 节)
- 更新
vercel.json(加 cron 配置) - 部署
- 在 Vercel 控制台确认 cron 生效
13. 可能的坑(提前写好)
- Stripe 验签必须用 raw body
- 飞书签名有时间戳校验,时钟必须准
- 多维表格必须把应用加为协作者
- Webhook 超时:飞书/表格写入用
Promise.allSettled - Resend/监控事件缺 ID:用
sha256(rawBody)保证幂等
14. 最终检查清单(勾完就上线)
- [ ] 新建
src/extensions/notification/完成 - [ ] 支付成功/失败/退款/争议都能通知
- [ ] Resend 邮件异常能通知
- [ ] Uptime 宕机/恢复能通知
- [ ] API 余额告警能通知
- [ ] 多维表格有完整记录
- [ ] 环境变量全部配置完
到这里就完工了。
这份执行方案已经把所有方向敲定,照着做不会跑偏。
接下来只剩“按文件改代码 + 测试”两步。
本文档为站内渲染。原始文件本地路径:saas/source/knowledge-world/Knowledge-World-项目-文档-feishu-codex-feishu-notification-imple-37b39f.md(仅本地保留,不入库不部署)