|
|
@@ -4,24 +4,26 @@
|
|
|
|
|
|
**Created**: 2026-10-09
|
|
|
|
|
|
-**Status**: 实现中(specify:书面规格待用户审阅;尚未进入 plan/tasks/implement)
|
|
|
+**Status**: 实现中(单字段方案已确认并实施;最终构建与审查收口中,未部署)
|
|
|
|
|
|
-**工作分支**:后端沿用 `test-202609v2`;用户 App 沿用本地当前 `master`。不创建分支或 worktree,不切换、合并或改写其他分支。
|
|
|
+**工作分支**:后端沿用 `test-202609v2`。不创建分支或 worktree,不切换、合并或改写其他分支;不修改任何 App 仓库。
|
|
|
|
|
|
**Input**: 用户要求现有用户消息通知同时提供简短语音提醒,例如“骑手已接单”“您的订单已派送”“您的订单已送达”;已同意沿用现有通知、随 App 打包音频、前台统一播放、事件去重以及消息中心独立入库,采用 spec-kit 流程实施。2026-10-09 用户进一步收缩范围:本期只做前台语音,不做后台/锁屏语音,不修改云函数。
|
|
|
|
|
|
+**最新责任边界(2026-10-09)**:App 改动由前端人员完成;本任务只实现后端 eventScene、定向验证及前端接入文档。用户已明确取消此前计划的 eventId、voiceLanguage、recipientUserId、eventTime,不以改名、隐藏字段或额外接口替代。不制作音频、不修改 App/原生插件或云函数;客户端播放条件是交接要求,不计为本任务已验证能力。
|
|
|
+
|
|
|
## 已确认方案及范围
|
|
|
|
|
|
-- 在同一条既有业务通知中增加场景信息和语音,不额外发送第二条语音消息。
|
|
|
-- 后端、用户 App 的前台统一接收和播放,以及为前台分流必需的 Android/iOS 接收代码在范围内。用户本次批准的跨端方案包含 App 配套,覆盖此前其他任务“不修改 App”的局部要求。
|
|
|
+- 在同一条既有用户业务通知中只新增 eventScene,供前端选择短语音,不另发语音消息。此前事件唯一标识和语言元数据方案已被用户最新要求取消。
|
|
|
+- 只修改后端通知代码、定向测试和本 feature 文档。前端接入文档覆盖前台统一接收、播放、Android/iOS 分流、资源及验收要求,实际 App 实现由前端人员负责。
|
|
|
- 云函数不修改、不部署;后台和锁屏不新增语音、不调整自定义 sound 或通知渠道,保持原有通知行为。用户恢复 App 或点击通知时也不补播后台事件。
|
|
|
-- 平台与商家 PC 不改;商家和骑手 App 的既有新单铃声不改。
|
|
|
-- 默认 `uni` 通道不变;新增场景、语言及事件标识沿现有 payload 传递,已有 FCM/APNs 也保留这些信息供前台接收。不自动启用原生通道,不改凭据、通知声音或设备登录互斥。
|
|
|
+- 用户/商家/骑手 App 及其原生插件、平台和商家 PC 均不修改;商家和骑手的既有新单铃声不改。
|
|
|
+- 默认 uni 通道不变;eventScene 沿现有完整 payload 传递,FCM/APNs 保留完整 JSON。不新增语言、用户、时间、事件标识,不改凭据、通知声音或设备登录互斥。
|
|
|
- 只给当前实际向用户发送的通知增加语音,不新增取消、退款、预约超时等缺失的业务通知,不修改支付/退款或配送状态机。
|
|
|
- 不使用通话、PushKit、VoIP token 或音频后台保活来绕过系统通知限制。
|
|
|
- 不新增消息表、音频表、设备表或用户设置页;场景与事件信息保存在现有消息 `body`。若计划阶段证明需要新增数据库字段,必须先说明原因,迁移 SQL 仅登记,不执行。
|
|
|
|
|
|
-## 现有代码事实(只读检查,非部署验证)
|
|
|
+## 实施前代码事实(非部署验证)
|
|
|
|
|
|
- `PayPush` 用户业务包装分别发布 `PushEvent` 和调用 `BusinessPushService`;监听器写入 `push_message`,外部发送成功不代表消息中心入库成功,反之亦然。
|
|
|
- `BusinessPushProperties` 和 `application.yml` 默认 `uni`;FCM/APNs 已有发送代码。未读取线上环境变量,不声称线上已启用原生通道。
|
|
|
@@ -39,7 +41,7 @@
|
|
|
|
|
|
**Why this priority**: 接单、开始配送和送达是截图明确要求的三个节点。
|
|
|
|
|
|
-**Independent Test**: 用户 App 保持前台,分别在外卖和闪送真实成功的接单、取餐/取件、送达节点检查用户通知事件与声音。
|
|
|
+**Independent Test**: 后端定向测试捕获外卖、闪送、自配送通知,验证成功场景及失败/回滚零通知;实际播放由前端按接入文档另行真机验收。
|
|
|
|
|
|
**Acceptance Scenarios**:
|
|
|
|
|
|
@@ -49,17 +51,17 @@
|
|
|
4. **Given** 外卖为商家自配送,**When** 商家开始配送或确认送达,**Then** 使用同样的派送/送达短语音,不误播“骑手已接单”。
|
|
|
5. **Given** 抢单、取件或送达失败,或者事务回滚,**When** 请求返回,**Then** 不生成该次成功状态通知或语音事件。
|
|
|
|
|
|
-### User Story 2 - 只在前台播放且不重复 (Priority: P1)
|
|
|
+### User Story 2 - 前端有明确的前台语音接入契约 (Priority: P1)
|
|
|
|
|
|
-通知在 App 前台由统一接收逻辑播放;后台/锁屏保持原有通知,不启动语音播放器,不依赖 JavaScript 在后台持续运行。
|
|
|
+后端仅给出 eventScene,文档指导前端用自己的语言设置选择音频、在 App 前台统一接收和播放;后台/锁屏保持原有通知。未增加事件 ID,不能承诺跨回调精确去重或仅凭本 payload 区分重复/合法再次发生的同场景动作。
|
|
|
|
|
|
-**Independent Test**: Android 和 iOS 前台收到同一场景只播一次;后台、锁屏、点击通知和恢复 App 均不新增或补播语音。
|
|
|
+**Independent Test**: 契约测试校验只有 eventScene 为新增字段;文档列出前台统一接收、后台/锁屏不新增播放、历史不补播,以及没有额外字段时的能力限制。
|
|
|
|
|
|
**Acceptance Scenarios**:
|
|
|
|
|
|
1. **Given** 用户 App 在前台,**When** uniPush 或原生通知到达,**Then** 统一处理场景并播放一次,不叠加普通铃声、第二条本地通知或另一个监听器的语音。
|
|
|
2. **Given** App 在后台或锁屏,**When** 通知正常到达,**Then** 保持原有系统通知行为,不设置新的订单语音声音,不启动前台播放器或通话。
|
|
|
-3. **Given** 同一通知到达多个回调,**When** 客户端处理,**Then** 使用相同事件标识避免重复展示/播报。
|
|
|
+3. **Given** 同一通知到达多个回调,**When** 客户端处理,**Then** 前端选定唯一处理入口并复用 SDK 已有消息标识(若存在);本期不提供服务端事件 ID,也不保证仅靠订单号/场景可精确去重。
|
|
|
4. **Given** 用户点击通知、打开消息列表或恢复历史数据,**When** 详情导航或消息加载,**Then** 不再次播报历史事件。
|
|
|
5. **Given** 系统限制发声、当前账号不匹配或 App 已转后台,**When** 前台播放器处理事件,**Then** 不强制发声、不为订单语音申请后台保活。前台媒体播放与系统通知声音设置不完全相同,具体平台行为需单独验证。
|
|
|
|
|
|
@@ -67,7 +69,7 @@
|
|
|
|
|
|
已有商家出餐、支付成功、商家自配送接单及已存在的用户取消通知也有独立短语音,不能全部套用接单音频。
|
|
|
|
|
|
-**Independent Test**: 按下表各事件和四语言抽检;核对语音语言、文字通知、订单归属及通知对象。
|
|
|
+**Independent Test**: 按下表各事件和当前后端语言抽检,核对元数据、文字、订单归属及通知对象;音频文案与资源由前端按文档实现。
|
|
|
|
|
|
| 事件语义 | 简体中文语音文案 | 现有触发范围 |
|
|
|
| --- | --- | --- |
|
|
|
@@ -81,8 +83,8 @@
|
|
|
|
|
|
**Acceptance Scenarios**:
|
|
|
|
|
|
-1. **Given** 用户语言为简中、繁中、英文或越南语,**When** 受支持的业务通知触发,**Then** 文字和音频使用同一种语言;不播订单号、联系方式或地址。
|
|
|
-2. **Given** 用户语言未知或没有该语种音频,**When** 通知触发,**Then** 使用明确的默认语种策略;不得展示原始 i18n key 或引用不存在的资源。
|
|
|
+1. **Given** 用户有 App 语言设置,**When** 前台收到受支持场景,**Then** 前端按自身设置选择本地短音频;后端文字仍沿用当前 Locale。本期没有语言字段,不承诺两者语言绝对一致,也不改变后端语言设置。
|
|
|
+2. **Given** App 语言未知或音频缺失,**When** 前台处理通知,**Then** 由前端采用自身默认语种或仅保留文字;不得引用不存在的资源。
|
|
|
3. **Given** 商家或骑手是通知接收人,**When** 同一业务动作发生,**Then** 其原有铃声和 payload 行为不因用户语音改造而改变。
|
|
|
|
|
|
### User Story 4 - 不影响消息中心及业务操作 (Priority: P1)
|
|
|
@@ -101,12 +103,12 @@
|
|
|
|
|
|
- 自配送允许跳过开始配送直接确认送达:只通知实际成功的送达事件,不补造“开始配送”。
|
|
|
- 平台/旧入口使用的数字状态不同于当前外卖 deliveryStatus 和闪送字符串状态:语音事件从实际动作产生,不按一个通用 state 映射。
|
|
|
-- 短时间连续多个事件或多笔订单:前台避免重叠播放;不同订单事件不得被一个全局布尔标记误去重;已被更新状态替代的待播事件不倒序播报。
|
|
|
+- 短时间连续通知或多笔订单:前端采用单一入口和串行播放器;订单号加场景不是可靠事件 ID,同场景再次发生时不能盲目永久去重。没有事件时间/版本,不能仅凭 payload 完整判别迟到、乱序或历史。
|
|
|
- App 原生接收回调和 uniPush 回调可能同时到达:必须有明确的通知展示/声音责任方。
|
|
|
- App 转后台时停止前台播放并清理待播队列;恢复前台不补播后台收到的消息或被清理的事件。
|
|
|
- 前台音频须在实际 App 包内且播放器可访问;本期不要求打包为系统通知声音,也不创建场景/语言通知渠道。
|
|
|
- 老版本或缺少音频时保持普通文字通知,不导致接收逻辑失败;发布前须真机验证,不能仅凭 payload 构造测试判定兼容。
|
|
|
-- 未登录/已换账号时不播另一账号的前台缓存事件;设备通知仍遵守现有单设备 token 与会话机制。
|
|
|
+- 未登录/已换账号时前端停止和清理队列;保留现有单设备 token 与会话机制。本期无接收用户字段,payload 自身不能判断迟到通知属于哪个账号。
|
|
|
|
|
|
## Requirements
|
|
|
|
|
|
@@ -115,51 +117,57 @@
|
|
|
- **FR-001**: MUST 沿用现有用户业务通知,每个成功事件只构造同一条含文字和语音信息的通知,不另发语音通知。
|
|
|
- **FR-002**: MUST 明确标识上表七种事件语义;只覆盖实际存在的用户通知,不新增触发时机或收件人。
|
|
|
- **FR-003**: MUST 保留历史 ddId/state/type/pushType 契约;新增场景与语音语义不得占用旧 type=0,也不得通过匹配中文正文判断。
|
|
|
-- **FR-004**: MUST 为一次业务事件生成并复用事件唯一标识,同一通知的入库、通道发送和所有客户端回调携带一致标识;转发不得重新生成。
|
|
|
-- **FR-005**: MUST 为受支持场景提供简中、繁中、英文、越南语简短音频,随用户 App 打包,不在每次通知时在线合成/下载,不播敏感或冗长订单信息。
|
|
|
-- **FR-006**: MUST 以接收用户语言决定文字和语音;已有其他语言无音频时明确回落,避免缺失资源或非法文件名。
|
|
|
-- **FR-007**: MUST 仅在 App 前台由统一接收逻辑播放一次;后台/锁屏不启动语音,保持原有通知行为,转后台停止播放并清理队列,恢复时不补播。
|
|
|
-- **FR-008**: MUST 在默认 uniPush、已有 FCM/APNs payload 中保留场景、语言及事件标识供前台处理;当前通道选择、声音配置、失败不回退、端别凭据隔离保持不变。
|
|
|
+- **FR-004**: MUST 只新增 eventScene 一个字段;取消 eventId、voiceLanguage、recipientUserId、eventTime,不增加替代字段、声音 URL 或语音接口。入库和发送仍复用同一业务 body,但不生成事件 ID。
|
|
|
+- **FR-005**: MUST 提供 eventScene 与短语音文案映射接入文档;语种选择、音频制作与打包由前端负责,建议本地预制、不播敏感或冗长订单信息。本任务不交付音频。
|
|
|
+- **FR-006**: MUST 保持后端文字的当前用户 Locale 机制,不新增语音语言字段;接入文档明确前端按自身语言选资源,并注明不能保证与通知文字语种完全一致。
|
|
|
+- **FR-007**: MUST 在文档明确前端仅在 App 前台统一播放一次;后台/锁屏不启动语音,保持原有通知行为,转后台停止播放并清理队列,恢复时不补播。本任务通过后端元数据支持该规则,不实现客户端播放器。
|
|
|
+- **FR-008**: MUST 在默认 uniPush 和既有 FCM/APNs 的完整 payload 中保留 eventScene;通道选择、声音配置、失败不回退、端别凭据隔离保持不变。
|
|
|
- **FR-009**: MUST 不修改或部署云函数,不传递新的自定义通知 sound、不新建通知声音渠道;前台从受控的场景/语言映射选择本地音频,不接受任意文件路径或声音 URL。若现有云端不能保留 payload,报告限制并请求方向。
|
|
|
- **FR-010**: MUST 保留消息中心独立入库;本范围内有有效用户但无设备地址不能跳过记录生成。
|
|
|
- **FR-011**: MUST 在实际业务状态成功且事务提交后生成成功事件,失败/回滚不发;自配送及出餐等现有非统一提交后路径需按本范围收口。
|
|
|
- **FR-012**: MUST 隔离推送与播放失败,不改变支付、配送、取消等原业务结果,不把入库成功等同于外部到达或播放成功。
|
|
|
-- **FR-013**: MUST 对重复回调去重,并正确区分订单/事件;通知点击、消息列表、历史恢复不触发补播。
|
|
|
+- **FR-013**: MUST 文档明确前端采用统一接收入口、不在点击/消息列表/恢复时补播;可利用 SDK 已有消息标识,但本期不提供服务端事件 ID、不承诺跨回调精确去重。
|
|
|
- **FR-014**: MUST 将普通业务推送与通话消息分流,不调用 CallKit/PushKit 或复用 voIPToken 来实现订单语音。
|
|
|
-- **FR-015**: MUST 保持原有系统通知权限和声音设置,不通过强制音量、通话或后台保活绕过限制;前台播放器的权限、静音和音频中断行为在 Android/iOS 分别验证,不把媒体播放与通知声音开关视为相同能力。
|
|
|
-- **FR-016**: MUST 保持旧 App 的文字通知、已知跳转字段及普通声音回退;对支持新功能的 App,点击按 pushType 路由外卖/闪送详情,不把闪送订单号当外卖查询。
|
|
|
-- **FR-017**: MUST 仅修改本功能必需代码,补充关键场景、前台生命周期、去重、事务及兼容注释,不改云函数、后台/锁屏声音、废弃支付入口、商家/骑手新单语音或 PC 页面。
|
|
|
+- **FR-015**: MUST 不修改服务端系统通知声音和渠道配置,文档提醒前端不得强制音量、通话或后台保活绕过限制,须分别核验 Android/iOS 权限、静音和音频中断;不能把媒体播放与通知声音开关视为相同能力。
|
|
|
+- **FR-016**: MUST 保持旧 App 的文字通知和已知跳转字段;文档规定新 App 点击按 pushType 区分外卖/闪送,由前端对接其实际详情页,不把闪送订单号当外卖查询。实际页面或路由不在本任务实现范围。
|
|
|
+- **FR-017**: MUST 仅修改必需后端代码并补充场景、事务及兼容注释,不改 App、原生插件、云函数、后台/锁屏声音、废弃支付入口、商家/骑手新单语音或 PC 页面。
|
|
|
- **FR-018**: MUST 按 specify → plan → tasks → implement 记录进度,原分支开发,不创建或切换分支;实际测试、构建、音频资源及真机验证分别记录,不将模拟检查报告为完整功能验收。
|
|
|
|
|
|
### Key Entities
|
|
|
|
|
|
-- **业务通知事件**:关联原订单、接收用户、成功动作、事件唯一标识、语言与短语音场景;不是一份新的订单状态。
|
|
|
+- **通知场景**:既有用户通知的实际成功动作,由 eventScene 描述;不是订单状态,不包含新增事件唯一标识、时间、用户或语种。
|
|
|
- **消息中心记录**:已有 push_message;新增场景信息放在 JSON body,列表读取不会自动播放。
|
|
|
-- **本地语音资源**:事件与语言确定的短音频;Android/iOS 前台播放器可访问,文件格式、时长、内容及打包结果分别验证,不作为本期系统通知声音。
|
|
|
-- **播放/通知去重状态**:客户端区分订单、事件与账号;不是 isRead,不改变消息已读状态。
|
|
|
+- **本地语音资源(前端负责)**:事件与语言确定的短音频;前端验证播放器可访问、文案、时长及打包结果,本任务仅描述接入要求。
|
|
|
+- **播放控制(前端负责)**:依赖前台状态、统一接收入口和客户端/SDK 已有信息;不改变 isRead,本期不额外提供去重、账号或排序元数据。
|
|
|
|
|
|
## Success Criteria
|
|
|
|
|
|
### Measurable Outcomes
|
|
|
|
|
|
- **SC-001**: 上表七种实际用户通知在受测成功动作中均有正确事件分类;失败动作/事务回滚对应成功事件数量为 0。
|
|
|
-- **SC-002**: 每种受支持语言的通知文字与语音内容匹配;所有发布音频均已实际生成、可解码、语句正确且在最终打包资源中可访问。
|
|
|
-- **SC-003**: 在权限和声音允许、消息已到设备的条件下,前台闲置播放器在收到有效事件后 2 秒内开始一次播报;后台/锁屏保持原有通知且不启动新增语音,恢复 App 不补播。
|
|
|
-- **SC-004**: 同一事件重复进入原生/uniPush 回调、点击通知、恢复 App、打开历史消息的测试中,前台不重复播报;一次正常事件没有两条展示通知或叠加声音。
|
|
|
+- **SC-002**: 新增场景契约的字段集合只有 eventScene,七种允许值完整;后端原文字本地化保持不变,前端文档无不存在的音频 URL 或被取消字段依赖。
|
|
|
+- **SC-003**: 前端接入文档明确有效前台事件在闲置播放器收到后 2 秒内开始播报的目标,以及后台/锁屏不新增播报、恢复不补播规则;实际耗时及声音效果交由前端真机验收。
|
|
|
+- **SC-004**: 同一条后端通知的入库/外发 body 内容一致;文档说明单一前台入口和历史不补播要求,同时明确没有事件 ID/时间/用户/语种字段的限制,不宣称实现精准去重、乱序判断或跨账号校验。
|
|
|
- **SC-005**: 缺失设备地址或外部通道异常时,范围内受测事件仍产生一条用户消息记录,业务结果不受影响,不出现双通道补发。
|
|
|
-- **SC-006**: 商家/骑手通知、音视频通话与四个历史 payload 字段的定向回归无行为改变;旧 App 普通通知回退和新 App 外卖/闪送点击路径完成真机核验。
|
|
|
+- **SC-006**: 商家/骑手服务端通知、通道配置与四个历史 payload 字段的定向回归无行为改变;App、原生插件、PC、云函数源码零改动,客户端声音与跳转真机核验单独交接。
|
|
|
|
|
|
## Assumptions / Dependencies
|
|
|
|
|
|
-- 本规格以仓库源码为依据;实际服务器环境变量、现有云端 payload 透传、签名 App 和最终打包音频尚未验证。云函数与后台/锁屏语音已由用户明确排除,不作为本期交付项。
|
|
|
-- 本轮不切换 App 的 master 分支。若该 checkout 并非用户维护的目标 App 源码,需在跨端实施前由用户指定路径/分支,不能自行切换。
|
|
|
-- 音频必须是可交付的实际资源,不能以空文件、占位文件或待提供说明代替完成;若本机无法制作/核听所需语种,应记录资源依赖并请求用户提供,不宣称闭环完成。
|
|
|
-- 音频合成如需外部付费服务或上传数据,须先说明并取得相应授权;预制资源不包含订单号、姓名、手机号、地址。
|
|
|
+- 2026-10-09 用户已确认书面规格,并随后明确 App 由前端人员实现,本任务只交付后端与接入文档;本地 App checkout 缺少闪送页不再阻断后端工作,不新增页面、不修改或切换 App 仓库。
|
|
|
+- 后端文字沿用 LocaleUtils 的 zh_CN/zh_TW/en_US/th_TH 和繁中默认,不恢复已移除的 vi。本期不传音频语种,前端用自身语言设置;客户端资源与翻译由前端负责。
|
|
|
+- 本规格以仓库源码为依据;实际服务器环境变量、现有云端 payload 透传、签名 App 和最终音频尚未验证。云函数及后台/锁屏语音已排除;App 音频、实现、打包和真机由前端负责。
|
|
|
+- 音频文件或客户端验收没有完成,不妨碍本任务完成后端代码及文档交付,但必须明确“后端已验证”不等于“App 语音已上线”。
|
|
|
- 不做远端部署、数据库操作或生产推送试发;这些操作需要用户明确要求。代码与本地验证完成后仍单独列出真机/部署未验证项。
|
|
|
-- 新规格补充 028 的用户通知与 033 的通道路由;不将其旧版“仅服务端/App 不在范围”的边界套用到本次已批准的 App 配套,也不改变旧任务的历史验收状态。
|
|
|
+- 新规格补充 028 的用户通知与 033 的通道路由;不改变旧任务历史验收状态,本期 App 责任以用户最新要求为准。
|
|
|
|
|
|
## 平台能力参考
|
|
|
|
|
|
- [DCloud 服务端推送 API](https://doc.dcloud.net.cn/uniCloud/uni-cloud-push/api.html):本期沿用 payload 传递,不启用自定义 sound 或离线语音提醒。
|
|
|
- [Apple UNNotificationSound](https://developer.apple.com/documentation/usernotifications/unnotificationsound) 与 [Android 通知渠道](https://developer.android.com/develop/ui/compose/notifications/channels):系统通知声音是独立能力,本期维持现状,不增加资源或渠道适配。
|
|
|
- [Firebase Android 消息接收](https://firebase.google.com/docs/cloud-messaging/android/receive-messages):notification + data 在后台由系统展示,不能假定 JS/前台 onMessageReceived 会立即执行。
|
|
|
+
|
|
|
+## 范围变更记录
|
|
|
+
|
|
|
+2026-10-09 用户确认 eventScene 的播放场景用途,并明确其他新增字段不要。已精简规格、计划、任务和接入文档:只保留一个新增场景字段,撤销对服务端事件 ID、语种、接收用户和时间的依赖。产品代码尚未开始。
|
|
|
+
|
|
|
+2026-10-09 用户确认继续后在原分支实施:用户专用 JSON 工厂仅增加 eventScene;骑手、自配送、出餐、LINE/OMG 支付和现有外卖取消接入七场景。无地址仍走消息入库,提交后通知与通知准备失败隔离;商家/骑手 body 保持原样。公共控制器测试集中在 UserOrderNotificationSceneTest,覆盖实际参数、回滚、失败、直接送达及旧状态兼容。未新增 SQL、云函数、App 或 PC 修改。
|