Files
shaguabijia-app-server/docs/api/notifications.md
T
左辰勇 a8ac5dc0c7 消息通知中心 3 接口(mock 阶段) + 五厂商推送(补华为/通用发送/测试三件套)
- 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>
2026-07-14 18:57:04 +08:00

7.5 KiB

消息通知中心(notifications 族)

所属:notifications 组(前缀 /api/v1/notifications,源 app/api/v1/notifications.py) | 鉴权:全部 Bearer(消息按用户隔离) | ← 返回 API 索引

对应 PRD《消息通知中心》(通知类型清单 / 点击跳转 / 字段元素 / 未读红点 / Push 文案)。 Push 侧(厂商直推 + 测试)见 push-vendor-test.md

⚠️ 虚拟数据阶段:数据来自内存 mock(app/repositories/notification_mock.py),每个用户首次访问生成一套固定样例(13 种类型全覆盖 + 今天/昨天/当年/跨年四种时间 + 已读未读混合,共 16 条,id 从 90001 起);已读状态在服务端内存维护,重启服务复位。后续接真实数据只换 repository 实现,接口契约不变。

⚠️ 字段命名:本组接口(含 push 测试组)对外为 camelCase(sentAt / isRead / pageSize…),与库内其他 snake_case 接口不同——按 PRD 前端契约对接,勿混用。

通知类型速查(13 种)

列表服务端已排好序:全列表按时间倒序(最新在前,不做分类分组——PRD §1 的"按分类分组"为笔误,2026-07-14 需求方确认取消),前端按返回顺序渲染即可。category 仅用于卡片头部的分类标签展示。

category 分类标签 type 类型 cardStyle 版式 actionText 操作行 extra 里带什么
withdraw_assistant 提现助手 reward_expiring 金币现金奖励即将失效 dual_amount 双金额卡 立即激活您的收益 batchId
withdraw_assistant 提现助手 reward_expired 金币现金奖励已失效 dual_amount 双金额卡 立即赚取新收益
withdraw_assistant 提现助手 withdraw_success 提现成功 withdraw 提现卡 null(无操作行,点击仅消红点)
withdraw_assistant 提现助手 withdraw_failed 提现失败,款项已退回 withdraw 提现卡 重新提现 withdrawId
system 系统通知 perm_accessibility 比价功能异常(无障碍) plain_text 纯文本卡 去开启 permission:"accessibility"
system 系统通知 perm_battery 比价续航保护异常(省电策略) plain_text 纯文本卡 去开启 permission:"battery"
system 系统通知 perm_autostart 比价启动保护异常(自启动) plain_text 纯文本卡 去开启 permission:"autostart"
system 系统通知 perm_overlay 比价按钮异常(悬浮窗) plain_text 纯文本卡 去开启 permission:"overlay"
feedback 我的反馈 feedback_reply 官方回复 plain_text 纯文本卡 查看详情 feedbackId
feedback 我的反馈 feedback_reward 反馈奖励(必带官方留言行) coin_reward 金币奖励卡 查看反馈详情 feedbackId
report 我的爆料 report_approved 爆料审核通过 coin_reward 金币奖励卡 查看爆料详情 reportId
invite 好友邀请 invite_order_reward 好友下单奖励到账 friend_cash 好友现金卡 邀请更多好友赚现金 inviteeNickname
invite 好友邀请 invite_remind 好友催单提醒 plain_text 纯文本卡 去提醒 TA inviteeNickname, scrollTo:"remind"

点击跳转逻辑按 PRD §2 由客户端按 type 分发;点击目标 = 整张卡片(不区分主体和操作行),任何点击都先调 POST /read 消该条红点。

GET /api/v1/notifications — 消息列表(分页)

入参(query)

参数 类型 必填 说明
page int 页码,1 起,默认 1
pageSize int 每页条数,默认 20,最大 100

出参

{
  "items": [
    {
      "id": 90001,
      "category": "withdraw_assistant",   // 分类 key(5 种,见上表)
      "categoryLabel": "提现助手",         // 卡片头部左上角分类标签
      "type": "reward_expiring",          // 类型 key(13 种,决定点击行为)
      "cardStyle": "dual_amount",         // 版式:dual_amount/withdraw/plain_text/coin_reward/friend_cash
      "title": "金币现金奖励即将失效",      // 卡片标题
      "coins": 86,                        // 金币数,整数;无金币的版式为 null
      "cashCents": 1280,                  // 现金金额(分);无现金的版式为 null
      "cashYuan": "12.80",                // 现金展示串(元,两位小数),与 cashCents 同源
      "infoRows": [                       // 信息行,已按 PRD 拼好文案,逐行 label: value 渲染
        { "label": "过期说明", "value": "您有86金币和12.80元现金即将失效,完成一次一键领券或一键比价即可激活收益" },
        { "label": "过期时间", "value": "3天后失效" }
      ],
      "actionText": "立即激活您的收益",    // 操作行;null = 无操作行(提现成功卡)
      "extra": { "batchId": "batch_20260714" },  // 跳转/联动参数,按 type 取用(见上表)
      "sentAt": "2026-07-14T14:59:58+08:00",     // ISO8601 带时区
      "isRead": false                     // false → 分类标签右侧显示 6px 红点(#E53935)
    }
  ],
  "page": 1,
  "pageSize": 20,
  "total": 16,
  "hasMore": false,
  "unreadCount": 12    // 与 /unread-count 同口径,进页面可顺手刷角标
}

时间显示规则(前端处理 sentAt):今天 →「今天」;昨天 →「昨天」;当年 →「M月D日」(不补零);跨年 →「YYYY年M月D日」。mock 样例特意覆盖了这四种情况。

数值约束(PRD §3):金币整数不带小数;现金/提现金额两位小数(直接用 cashYuan)。

GET /api/v1/notifications/unread-count — 未读总数(首页铃铛角标)

无入参。出参:

{ "count": 12, "badgeText": "12" }   // count>99 时 badgeText="99+";count=0 时 badgeText=null → 整个角标隐藏

刷新时机(PRD §4):进入首页时、从通知中心/其他页面返回首页时(原型监听 pageshow)。

POST /api/v1/notifications/read — 标记已读

入参(JSON),两种模式二选一(同时传时 all 优先)

模式 body 使用场景
单条/多条 { "ids": [90001, 90003] } ① 点击某张消息卡片(点击后无论跳转/弹窗/无动作都算已读);② 用户点击 push 直达落地页后,客户端拿 push extras 里的 notificationId 同步置读
全量清零 { "all": true } 进入通知中心自动清零(只浏览列表就消红点,无需逐条点击;退出通知中心时也可再调一次兜底)

出参

{ "ok": true, "markedCount": 2, "unreadCount": 10 }   // unreadCount = 处理后剩余未读,可直接刷新角标

幂等:不存在/已读的 id 忽略,重复调用 markedCount=0 不报错。

错误:400 ids 与 all 都没传(或 ids 为空数组);401 未鉴权。

联调小抄

# 1. 登录拿 token(SMS mock:任意手机号 + 任意 6 位验证码)
curl -X POST :8770/api/v1/auth/sms/send  -d '{"phone":"13800001234"}'
curl -X POST :8770/api/v1/auth/sms/login -d '{"phone":"13800001234","code":"123456"}'
# 2. 列表 / 角标 / 置读
curl ":8770/api/v1/notifications?page=1&pageSize=20" -H "Authorization: Bearer $TOKEN"
curl ":8770/api/v1/notifications/unread-count"        -H "Authorization: Bearer $TOKEN"
curl -X POST ":8770/api/v1/notifications/read" -d '{"all":true}' -H "Authorization: Bearer $TOKEN"

要重置 mock 数据(重新生成整套样例):重启服务即可。想造「push → 站内联动」数据,用 POST /api/v1/push/testcreateNotification:true