Bladeren bron

docs(spec-kit): define native push routing requirements

qmj 2 weken geleden
bovenliggende
commit
a229d9d930
1 gewijzigde bestanden met toevoegingen van 130 en 0 verwijderingen
  1. 130 0
      specs/033-native-push-routing/spec.md

+ 130 - 0
specs/033-native-push-routing/spec.md

@@ -0,0 +1,130 @@
+# Feature Specification: 可切换的原生业务推送通道
+
+**Feature Branch**: `033-native-push-routing`
+
+**Created**: 2026-09-29
+
+**Status**: Draft(对话方案已确认;待书面规格审阅)
+
+**Input**: 为现有普通业务通知增加 Android FCM 与 iOS APNs 原生发送能力,以服务端配置切换,重启后生效;交付时继续默认使用 uniPush,不启用原生通道。仅涉及服务端;音视频通话推送不在范围内。
+
+## 背景与边界
+
+当前业务推送通过 `PayPush` 调用用户、商家、骑手三套 uniCloud 云函数,部分路径通过 `PushEvent` 写入 `push_message`。`info_user` 已有 `cid`、`cidType`、`deviceToken`:`cid` 是 uniPush 标识,`deviceToken` 承载 Android FCM token 或 iOS APNs device token,两者不可互用。用户已确认同一账号只允许一台手机登录,新手机登录后旧手机登录状态失效,因此本功能沿用现有单设备字段,不新增推送设备表。
+
+本功能覆盖当前仍在使用的普通业务通知,包括订单、闪送、支付结果及平台/商家通知等现有发送路径;只替换发送通道,不增加新的业务触发时机或文案。不修改已废弃的支付/订单入口,也不改动音视频通话推送。App 端 token 获取、上报及通知展示不在本功能实现范围;服务端以已有上报字段为输入。
+
+## User Scenarios & Testing *(mandatory)*
+
+### User Story 1 - 默认继续使用 uniPush (Priority: P1)
+
+运维部署新版本后,不做任何切换,普通业务通知仍按现有路径发送,不因原生凭据尚未部署而影响服务启动。
+
+**Why this priority**: 先交付原生能力但暂不启用,必须保证当前线上推送行为不变。
+
+**Independent Test**: 不设置通道配置,触发用户、商家、骑手各一条现有业务通知,验证只调用对应 uniCloud 通道。
+
+**Acceptance Scenarios**:
+
+1. **Given** 未配置推送通道或配置为 `uni`,**When** 任一普通业务通知触发,**Then** 仅走现有 uniPush 通道,不调用 FCM/APNs。
+2. **Given** 未部署原生通道凭据,**When** 服务以默认配置启动,**Then** 启动成功且现有 uniPush 可用。
+3. **Given** 已部署原生发送代码,**When** 音视频通话推送触发,**Then** 继续按原有独立流程处理,不受普通业务通知开关影响。
+
+---
+
+### User Story 2 - 配置切换后 Android 走 FCM (Priority: P1)
+
+运维将普通业务推送配置切换为原生并重启服务后,Android 接收方通过对应 App 的 FCM 项目收到现有通知。
+
+**Why this priority**: 这是 Android 原生通知的核心能力,同时必须避免误把 uniPush CID 当作 FCM token。
+
+**Independent Test**: 在原生配置下,分别向用户、商家、骑手 Android 账号触发通知,验证使用各自 Firebase 项目和 `deviceToken`,且未调用 uniCloud。
+
+**Acceptance Scenarios**:
+
+1. **Given** 通道为 `native`、接收方系统为 Android 且有有效 `deviceToken`,**When** 普通业务通知触发,**Then** 通过该接收端对应的 Firebase 项目向 `deviceToken` 发送一次;不得把 `cid` 传给 FCM。
+2. **Given** 用户、商家、骑手分别使用不同 Firebase 项目,**When** 三端各有通知触发,**Then** 每条通知使用对应项目凭据,不串用项目。
+3. **Given** 普通商家或夜市商家是接收方,**When** 原生通知触发,**Then** 均按商家端凭据路由。
+
+---
+
+### User Story 3 - 配置切换后 iOS 走 APNs (Priority: P1)
+
+同一原生配置下,iOS 接收方通过对应 App 的 APNs 普通通知证书收到现有通知。
+
+**Why this priority**: 只有 Android FCM 不满足“三端全切原生”的目标。
+
+**Independent Test**: 在原生配置下,分别向用户、商家、骑手 iOS 账号触发通知,验证使用各自普通推送证书、正确环境与 `deviceToken`,且未调用 uniCloud。
+
+**Acceptance Scenarios**:
+
+1. **Given** 通道为 `native`、接收方系统为 iOS 且有有效 `deviceToken`,**When** 普通业务通知触发,**Then** 通过该接收端对应的 APNs 普通通知证书向 `deviceToken` 发送一次;不得使用 `voIPToken`。
+2. **Given** 运行环境为开发或生产,**When** 发送 iOS 通知,**Then** 选择相应 APNs 环境及证书,不混用开发与生产凭据。
+3. **Given** 原有通知带有订单标识、业务类型或点击跳转所需 payload,**When** 改走 APNs,**Then** 这些业务字段仍可供接收端识别。
+
+---
+
+### User Story 4 - 安全切换、失败隔离与回退 (Priority: P1)
+
+运维能通过修改服务端配置并重启,在 uniPush 与原生通道间切换。单次通知只选择一个通道;发送失败不影响订单等业务操作。
+
+**Why this priority**: 切换期间必须避免重复通知、业务流程被第三方推送故障阻断,以及凭据误入仓库。
+
+**Independent Test**: 用缺失 token、原生服务异常、缺失凭据及配置回退场景验证发送结果、业务结果与消息中心记录。
+
+**Acceptance Scenarios**:
+
+1. **Given** 通道为 `native` 且原生 token 缺失或发送失败,**When** 普通业务通知触发,**Then** 记录可定位的失败信息,不自动补发 uniPush,也不改变原业务操作结果。
+2. **Given** 原路径会通过 `PushEvent` 写入消息中心,**When** 任一通道发送成功、失败或 token 缺失,**Then** 保持原有入库语义,每次业务通知最多写入一条记录,不因切换重复入库。
+3. **Given** 通道从 `native` 改回 `uni` 并重启,**When** 后续通知触发,**Then** 重新使用 `cid` 和 uniPush,不再调用 FCM/APNs。
+4. **Given** 原生模式所需的三端 FCM 或 APNs 凭据配置缺失、无法读取,**When** 服务启动,**Then** 明确报错并拒绝以不完整的原生配置启动;默认 uni 模式不受影响。
+
+### Edge Cases
+
+- `cidType` 为空或不是 `android`/`ios`:原生模式下不猜测平台,不尝试使用错误通道;记录失败,保持业务和原有消息中心行为。
+- `deviceToken` 为空、失效或来自另一 App/项目:不把 `cid` 当作替代 token;记录失败结果,失效 token 的后续处理应避免误删新手机刚更新的 token。
+- 同一账号换手机或 token 刷新:后端始终以当前账号最新上报的 `cidType` 与 `deviceToken` 为发送目标;本功能不改变现有登录互斥机制。
+- 批量发送含不同接收端或系统:按接收端、系统及相应项目/证书分组,不把不同项目 token 混发,也不因部分失败重复发送成功项。
+- 业务代码直接调用底层 uniPush 方法:在范围内的普通业务通知必须纳入统一路由;已废弃代码及音视频通话流程除外。
+
+## Requirements *(mandatory)*
+
+### Functional Requirements
+
+- **FR-001**: 服务端 MUST 提供普通业务推送的全局通道配置,取值为 `uni`/`native`,未配置时默认为 `uni`;修改后重启生效,不新增管理后台实时开关。
+- **FR-002**: 默认 `uni` 模式 MUST 保持现有用户、商家、骑手 uniCloud 发送行为与消息中心入库行为;不得要求原生凭据存在。
+- **FR-003**: `native` 模式 MUST 根据接收端类型和 `cidType` 路由:Android 使用对应 App 的 FCM,iOS 使用对应 App 的 APNs;普通商家和夜市商家均属于商家端。
+- **FR-004**: 原生发送 MUST 只使用 `deviceToken`,uniPush MUST 只使用 `cid`;不得相互复制或替代,`voIPToken` 不得用于普通通知。
+- **FR-005**: 用户、商家、骑手三套 Firebase 项目及 APNs 普通通知证书 MUST 独立配置并正确选择;iOS MUST 区分开发与生产环境。
+- **FR-006**: 原生凭据及证书密码 MUST 从服务端受保护的外部配置读取,不得写入源码、规格、日志或打包进公开构件。
+- **FR-007**: 原生模式的必要凭据不完整时 MUST 在启动阶段明确失败;默认 uni 模式 MUST 可在无原生凭据时正常启动。
+- **FR-008**: 所有当前在用的普通业务推送路径 MUST 使用一致的通道选择规则,包括单发、批量及直接调用现有底层方法的路径;不改变业务触发时机、文案和 payload 语义。
+- **FR-009**: 单次业务通知 MUST 只使用一个外部发送通道;原生发送失败或缺少 token 时 MUST NOT 自动回退到 uniPush,且 MUST NOT 使原业务操作失败。
+- **FR-010**: 对现有会入库的推送,通道切换 MUST NOT 增加或漏掉 `push_message` 记录;消息中心记录与外部发送结果保持解耦。
+- **FR-011**: 实现范围 MUST 排除音视频通话推送、已废弃订单/支付入口及 App 端代码;不新增推送业务场景或改变登录互斥规则。
+- **FR-012**: 本功能 MUST 沿用现有 `info_user` 单设备字段,不新增数据库字段或设备表。
+
+### Key Entities *(include if feature involves data)*
+
+- **接收账号**:已有账号记录,包含接收端类型、`cid`、`cidType`、`deviceToken`;同一账号仅保留当前手机的一组推送标识。
+- **普通业务通知**:已有业务事件生成的标题、正文、payload 与接收方;发送渠道是可替换属性,不改变业务含义。
+- **消息中心记录**:现有 `push_message`,继续按原路径写入,不作为外部发送成功的证明。
+- **通道配置与凭据**:服务端全局 `uni`/`native` 配置,以及三端各自的 FCM/APNs 外部凭据;不属于业务数据库数据。
+
+## Success Criteria *(mandatory)*
+
+### Measurable Outcomes
+
+- **SC-001**: 未配置开关的部署中,受测在用普通业务通知 100% 仍选择原 uniPush 通道,且无原生凭据时服务可启动。
+- **SC-002**: 原生配置下,用户、商家、骑手 × Android、iOS 六种路由组合均选中正确凭据与 `deviceToken`,未把 `cid` 或 `voIPToken` 发送到原生通道。
+- **SC-003**: 受测通知在正常发送、token 缺失和发送异常场景下,单次均不出现双通道外发;现有会入库的路径每次最多生成一条消息记录。
+- **SC-004**: 模拟 FCM/APNs 不可用或 token 无效时,原业务操作结果与不发送推送时一致;失败可通过日志定位到接收端、平台与错误类别,日志不包含完整 token 或凭据。
+- **SC-005**: 测试配置切回 `uni` 并重启后,后续受测通知恢复原 uniPush 发送路径;音视频通话路径保持原行为。
+
+## Assumptions
+
+- 用户已确认同一账号只允许一台手机登录,且新手机登录后旧手机登录状态失效;本功能依赖现有机制,不重新实现登录互斥。
+- App 端会上报真实 FCM/APNs `deviceToken` 与正确的 `cidType`;App 端变更和真机展示不包含在此次服务端交付范围。
+- 原生能力交付后仍保持 `uni` 为默认值;只有运维部署完整凭据、修改服务端配置并重启,才会启用原生通道。
+- 音视频通话使用其他推送流程,不受本次普通业务通知通道开关控制;`voIPToken` 及 VoIP 证书不用于本功能。
+- 证书与 Firebase 服务账号由部署方安全提供;本仓库只包含不涉及密钥内容的配置模板和操作文档。