短信网关 — 系统需求
⚠ MVP 不实现独立短信网关。本文保留为系统需求与后续建设参考;MVP 阶段不建设统一短信网关,不承诺通用业务短信、营销短信闭环。
三方接入域总览见 00-总览;还款提醒见 01-还款提醒,MVP 阶段由 FCS 直接对接 crs,不依赖本文短信网关。
纯 API 系统,模板管理可在运营系统。负责消息模板管理和统一发送。
业务目的
各系统需要发消息时(验证码/催收/还款通知),统一走消息网关。模板一处管理,供应商切换对上游透明。
能力模型
上游系统 消息网关 消息供应商
┌─────────┐ ┌──────────────┐ ┌──────────────┐
│ 客户系统 │ ──发验证码─▶ │ 短信发送 │ ──选择──▶ │ Termii │
│ │ │ WhatsApp发送 │ │ Twilio │
│ 催收系统 │ ──逾期提醒─▶ │ App Push │ │ Infobip │
│ │ │ 模板管理 │ │ WhatsApp API │
│ 还款中心 │ ──失败通知─▶ │ 供应商路由 │ └──────────────┘
│ │ │ 限流控制 │
│ 运营系统 │ ──营销推送─▶ │ 发送记录 │
└─────────┘ └──────────────┘
发送规则
| 规则 | 说明 |
|---|
| 场景编码 | 调用方传入场景编码(如 verification_code / overdue_reminder),网关自动匹配模板和供应商 |
| 模板选择 | 场景编码 → 语言(默认英语,按客户偏好)→ 匹配模板 |
| 变量替换 | 调用方传参 {code: "123456", name: "John"},网关替换模板中的占位符 |
| 供应商选择 | 按场景配置首选供应商,失败时自动切换备选 |
| 发送优先级 | 验证码 > 催收 > 还款通知 > 营销(营销触发受频控约束) |
| 频控约束 | 同一客户 5 分钟内最多 3 条,同一场景 24 小时内最多 5 条 |
接口清单
上游调用
| 接口 | 方向 | 说明 |
|---|
| 发送消息 | ← 各系统 | 传入场景编码 + 接收方 + 模板变量 |
| 批量发送 | ← 运营系统 | 传入客户列表 + 场景编码 + 变量列表 |
| 查询发送状态 | ← 各系统 | 按消息 ID 查询投递状态 |
回调
| 接口 | 方向 | 说明 |
|---|
| 投递报告 | → 上游系统 | 供应商异步回执后,通知调用方终态 |
模板管理
| 接口 | 方向 | 说明 |
|---|
| 创建模板 | ← 运营系统 | 定义场景编码 + 语言 + 模板内容 + 参数列表 |
| 更新模板 | ← 运营系统 | 版本管理,旧版本保留 |
| 查询模板 | ← 各系统 | 按场景编码查询模板列表 |
状态机
message_log
| 状态 | 说明 |
|---|
| pending | 收到请求,待发送 |
| sent | 已发送至供应商,等待回执 |
| delivered | 供应商确认投递成功 |
| failed | 投递失败(供应商返回明确失败) |
| undelivered | 投递失败(供应商无法送达但未明确原因) |
| expired | 发送超时未获回执 |
| blocked | 被频控拦截,未实际发送 |
核心字段
message_template
| 字段 | 类型 | 说明 |
|---|
| template_id | string | 模板 ID |
| scene_code | string | 场景编码(verification_code / overdue_reminder / repayment_failure / marketing) |
| channel | enum | sms / whatsapp / push |
| language | string | en / ha / yo / ig(英语 / 豪萨语 / 约鲁巴语 / 伊博语) |
| content | string | 模板正文,变量用 {var_name} 占位 |
| params | json | 参数列表定义(参数名 + 类型 + 说明) |
| version | int | 版本号 |
| status | enum | active / archived |
| created_at | timestamp | |
message_log
| 字段 | 类型 | 说明 |
|---|
| message_id | string | 消息 ID(全局唯一) |
| scene_code | string | 场景编码 |
| source_system | string | 来源系统 |
| source_reference | string | 来源系统的关联 ID(如申请 ID、借据号) |
| recipient | string | 接收方(手机号 / WhatsApp ID / 设备 token) |
| channel | enum | sms / whatsapp / push |
| provider | string | 实际使用的供应商 |
| template_id | string | 使用的模板 ID |
| rendered_content | text | 已替换变量的完整文本 |
| status | enum | pending / sent / delivered / failed / undelivered / expired / blocked |
| provider_message_id | string | 供应商侧消息 ID |
| failure_reason | string | 失败原因 |
| block_reason | string | 频控拦截原因 |
| sent_at | timestamp | |
| delivered_at | timestamp | |
| provider_callback_raw | text | 供应商回执原始报文 |
| retry_count | int | 重试次数 |
异常路径
| 场景 | 处理 |
|---|
| 供应商返回失败 | 自动切换备用供应商(若配置),最多重试 2 次 |
| 供应商超时无回执 | TTL(默认 30s)后标记 expired,不重试(验证码除外) |
| 模板变量不匹配 | 模版定义了 {name} 但调用方未传,保留未替换文本发送,记录告警 |
| 接收方格式无效 | 返回校验错误,不发送 |
| 频控拦截 | 不发送,记录 status=blocked + 拦截原因,返回成功但标记频控 |
| 同场景同接收方重复请求 | 验证码场景 1 分钟内去重(防刷接口) |
MVP 边界
MVP 阶段短信网关整体不实现。下表仅作为后续版本拆分参考。
| 能力 | MVP | 二期 |
|---|
| 短信发送 | ❌ | 后续版本建设 |
| WhatsApp 发送 | ❌ | 催收核心场景 |
| App Push | ❌ | |
| 模板管理 | ❌ | 至少验证码/催收/通知三类模板 |
| 发送记录 | ❌ | |
| 频控 | ❌ | 基本防刷(一分钟内同号码去重) |
| 供应商自动切换 | ❌ | MVP 只接 1 家 |
| 批量发送 | ❌ | 运营系统营销场景 |
| 投递回调通知 | ❌ | 向上游异步通知投递状态 |
| 多语言支持 | ❌ | 模板预留 lang 字段,二期启用 |