- GET /api/v1/notifications(分页,时间倒序不分组)/unread-count /read(ids|all),
对外 camelCase(PRD 前端契约);数据为内存 mock(13 类型全覆盖,重启复位),
接真实数据只换 repositories/notification_mock.py
- vendor_push 补华为 Push Kit(OAuth+messages:send),抽通用 send_notification
(任意文案+extras+mock 模式),send_accessibility_disabled 改薄封装行为不变
- 新增 /api/v1/push/{vendors,templates,test}:凭据状态/13 类 PRD 文案模板/测试发送
(默认 mock,mock=false 真发,可联动插站内 mock 消息闭环验证已读)
- .env.example 补 HUAWEI_* 键;docs/api 新增 notifications.md + push-vendor-test.md
- alembic merge 1a924c274fce 收拢 direct_vendor_push_fields/feedback_type_reply
双 head,恢复 upgrade head 可用
- tests: test_notifications + test_push_center 共 35 例
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
6.0 KiB
厂商推送测试三件套(push 族)
所属:push 组(前缀
/api/v1/push,源app/api/v1/push.py) | 鉴权:全部 Bearer | ← 返回 API 索引发送实现:
app/integrations/vendor_push.py(荣耀 / 华为 / 小米 / OPPO / vivo 五通道,send_notification()通用入口)。站内消息中心见 notifications.md。 与POST /api/v1/device/push-test(无障碍召回通道延迟自测)互补:本组面向消息中心 13 类 push 的文案/参数/通道联调。字段命名同 notifications 族:camelCase。
链路总览
真实业务事件(奖励过期/提现回执/审核通过/…)
└→ vendor_push.send_notification(vendor, token, title, body, extras)
extras = { type, notificationId, ...业务参数 } ← 客户端深链 + 已读联动的钥匙
客户端点击 push → 按 extras.type 直达落地页(与站内点击一致)
→ 调 POST /notifications/read {ids:[extras.notificationId]} 同步消红点(PRD §4)
推送目标来源:客户端集成各厂商 push SDK 拿到 regId/token 后,通过 POST /api/v1/device/register 上报 push_vendor + push_token,服务端存 device_liveness 表。
GET /api/v1/push/vendors — 厂商配置状态
检查 5 家厂商服务端凭据是否配齐(只读 .env,不打厂商接口)。missingKeys 即还要补的配置键;mock 测试不依赖任何凭据。
{ "vendors": [
{ "vendor": "honor", "label": "荣耀", "configured": false, "missingKeys": ["HONOR_PUSH_APP_ID", "HONOR_PUSH_CLIENT_ID", "HONOR_PUSH_CLIENT_SECRET"] },
{ "vendor": "huawei", "label": "华为", "configured": false, "missingKeys": ["HUAWEI_PUSH_APP_ID", "HUAWEI_PUSH_APP_SECRET"] },
{ "vendor": "xiaomi", "label": "小米", "configured": true, "missingKeys": [] },
{ "vendor": "oppo", "label": "OPPO", "configured": false, "missingKeys": ["OPPO_PUSH_APP_KEY", "OPPO_PUSH_MASTER_SECRET"] },
{ "vendor": "vivo", "label": "vivo", "configured": false, "missingKeys": ["VIVO_PUSH_APP_ID", "VIVO_PUSH_APP_KEY", "VIVO_PUSH_APP_SECRET"] }
] }
GET /api/v1/push/templates — 13 类通知的 push 模板预览
PRD §5 的 13 条 push 文案(标题固定 ≤11 字不带变量;正文 {var} 为变量,示例值即 PRD 示例)。对文案、看变量名用。
{ "templates": [
{
"type": "withdraw_success",
"category": "withdraw_assistant", "categoryLabel": "提现助手", "cardStyle": "withdraw",
"pushTitle": "提现到账提醒",
"pushBodySample": "¥0.50已存入您的微信钱包,点击查看到账详情", // 用示例值渲染后的效果
"pushBodyTemplate": "¥{amount}已存入您的微信钱包,点击查看到账详情",
"variables": ["amount"],
"sampleVars": { "amount": "0.50" }
}
// ... 共 13 条,顺序即 PRD 编号
] }
POST /api/v1/push/test — 测试发送(默认 mock)
入参(JSON)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
vendor |
string | ❌* | honor/huawei/xiaomi/oppo/vivo,中文「华为」「小米」等别名也识别;留空时用 deviceId 设备上报的 vendor |
pushToken |
string | ❌* | 厂商 push token/regId;留空则按 deviceId 反查已注册设备(mock 模式两者都缺时用占位 token,只看渲染结果) |
deviceId |
string | ❌ | 客户端 DeviceId.get() 的设备 id,用于反查 vendor+token |
type |
string | ❌ | 13 种类型 key 之一 → 按 PRD 模板渲染;不传且没直给文案 → 发通用测试文案 |
vars |
object | ❌ | 覆盖模板变量,如 {"coins":"520","cash":"6.66"};缺省用 PRD 示例值 |
title / content |
string | ❌ | 直接指定标题/正文(优先于 type 模板) |
createNotification |
bool | ❌ | true = 同时往该用户消息中心插一条同类型未读 mock 通知,extras 带其 notificationId → 可闭环验证「点 push → 调 /notifications/read 消红点」(仅 type 合法时生效) |
mock |
bool | ❌ | 默认 true = 不真调厂商 API,回显渲染结果;false = 真发到手机(要求该厂商凭据已配) |
出参
{
"ok": true, "mock": true, "vendor": "huawei",
"title": "反馈奖励已到账",
"body": "谢谢您帮傻瓜比价变得更好,300金币已到账,还有一条给您的留言~",
"extras": { "type": "feedback_reward", "feedbackId": "3002", "notificationId": "90017" },
"notificationId": 90017, // createNotification=true 时的站内 mock 通知 id
"missingKeys": ["HUAWEI_PUSH_APP_ID", "HUAWEI_PUSH_APP_SECRET"], // 真发前还缺的配置(真发成功时必为空)
"vendorResponse": null // 真发时为厂商 API 原始响应
}
错误:400 vendor/type 非法、真发但凭据未配(detail 列缺失键);409 真发但拿不到 pushToken;502 厂商 API 返回失败(detail 带厂商原始错误)。
真发注意:
- 目标手机必须先装 App 且客户端已集成对应厂商 SDK、
/device/register上报过 token; - vivo 未上架前走测试推送(
VIVO_PUSH_MODE=1),目标手机需在 vivo 开放平台加入测试设备; - 小米新设备需在开放平台把签名/包名配好,token 才有效。
厂商凭据怎么拿(.env 键名)
| 厂商 | 后台 | 需要的键 |
|---|---|---|
| 华为 | AGC 控制台 → 项目设置 → 常规 → 应用 | HUAWEI_PUSH_APP_ID、HUAWEI_PUSH_APP_SECRET(OAuth client_id 即 AppId) |
| 荣耀 | 荣耀开发者服务平台 → 推送服务 | HONOR_PUSH_APP_ID、HONOR_PUSH_CLIENT_ID、HONOR_PUSH_CLIENT_SECRET |
| 小米 | 开放平台 → 消息推送 → 应用秘钥 | XIAOMI_PUSH_APP_SECRET(服务端只要这个;AppID/AppKey 是客户端 SDK 用) |
| OPPO | 开放平台 → 推送服务 | OPPO_PUSH_APP_KEY、OPPO_PUSH_MASTER_SECRET(注意是服务端 MasterSecret) |
| vivo | 开放平台 → 推送 | VIVO_PUSH_APP_ID、VIVO_PUSH_APP_KEY、VIVO_PUSH_APP_SECRET |
各家发送协议差异(鉴权方式/成功码/payload 结构)封装在 integrations/vendor_push.py,业务侧只面对 send_notification()。