知识库首页 知识库-世界 codex-feishu-notification-implementation.md

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.tssrc/notification/notification.ts
  • 支付逻辑入口:src/payment/provider/stripe.tssrc/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.ts
  • src/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_failed
    • charge.refunded
    • charge.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_ID
  • FEISHU_APP_SECRET
  • BITABLE_APP_TOKEN
  • BITABLE_TABLE_ID

4.3 Webhook 安全(建议)

  • RESEND_WEBHOOK_SECRET
    Resend Webhook 验签
  • UPTIME_WEBHOOK_SECRET
    Uptime(自建/机器人)Webhook 验签
  • UPTIME_ALLOWED_IPS
    允许 IP 白名单(可选)

4.4 API 余额告警(可选)

  • OPENAI_ADMIN_KEY
  • OPENAI_THRESHOLD
  • ANTHROPIC_ADMIN_KEY
  • ANTHROPIC_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 统一)

  1. request.text()原始 body(签名必须用原始字符串)
  2. 验签(如无签名,返回 401)
  3. 幂等判断: - 从 webhook_eventeventId - 有 processedAt 就直接返回
  4. 成功处理后更新 processedAt
  5. 失败记录 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

  1. 在 Stripe Dashboard 触发 checkout.session.completed
  2. 看飞书群是否出现绿卡
  3. 查多维表格是否有记录

11.2 Creem

  1. 使用 scripts/test-creem-webhook.ts
  2. 确认飞书 + 表格写入

11.3 Resend

  1. 触发一次 email.failed
  2. 确认飞书告警

11.4 Uptime

  1. 模拟宕机 webhook
  2. 确认 @所有人

11.5 API 余额

  1. 手动访问 /api/cron/api-monitor(带 CRON_SECRET
  2. 看飞书预警卡片

12. 部署步骤(按顺序做)

  1. 配置环境变量(见第 4 节)
  2. 更新 vercel.json(加 cron 配置)
  3. 部署
  4. 在 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(仅本地保留,不入库不部署)