# 用户通知 eventScene Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:executing-plans for inline execution; use superpowers:subagent-driven-development only if the user selects that method. Steps use checkbox syntax for tracking. **Goal:** 只在既有用户订单通知中新增 eventScene,供前端控制前台语音,并交付接入文档。 **Architecture:** 使用现有 OrderPushBodyDto 增加一个可选场景及用户专用 JSON 工厂,实际成功动作明确传入枚举。用户通知入库与外部发送复用同一个 body;旧工厂和商家/骑手通知不增加场景。App、音频、云函数及声音配置均不修改。 **Tech Stack:** Java 21、Spring Boot、既有 JSON 工具、事务同步、JUnit 5/Mockito、uniPush/FCM/APNs。 **Spec:** [spec.md](spec.md) **Date / Branch / Progress:** 2026-10-09 / 当前 test-202609v2 / 已验证(本期后端测试、构建及审查修复通过;全量回归 1 项基线失败另记,未部署)。 ## Global Constraints - 原分支工作,不创建 worktree、不切换/合并/改写其他分支。 - 只新增 eventScene,不生成事件 ID,不传语音语种、接收账号或时间,不提供替代字段/额外接口。 - 不修改 App、原生插件、云函数、PC、数据库结构、声音/通知渠道、支付退款或配送状态机。 - 保留 ddId/state/type/pushType;不占用 type=0,不靠中文正文或跨入口的通用 state 判断场景。 - 只覆盖当前实际用户通知,不新增闪送用户取消、超时、退款等通知时机。 - 文字保留当前 Locale;前端按自身设置选音频,不能保证文字/声音语种一致。 - 无设备地址仍写用户消息;推送失败不改变业务结果,事务回滚不发成功通知。 - 关键代码写注释,保留文件编码和换行;集成代码放 ruoyi-admin。 - 前端采用统一接收入口;本期没有足够字段保证精准去重、乱序识别或跨账号过滤,不在文档承诺这些能力。 ## Review Focus 1. 旧 JSON 和新 JSON 的差异仅 eventScene;Task 1 精确断言键集合、旧字段值。 2. 同一动作通知用户/商家/骑手时不能污染非用户 body;Task 2/3 分别捕获参数。 3. 无 CID/token 时不能跳过用户消息记录;Task 2/4 断言入库一次、外发零次。 4. 自配送直接送达、闪送部分取件、失败/回滚不能虚构成功场景;Task 2 断言调用次数。 5. 推送异常和默认声音不能因场景改动改变订单结果或渠道;Task 3/4 回归;客户端去重等限制由文档交接。 ## Technical Context / Constitution Check push_message.body 已保存完整 JSON,FCM/APNs 已转发完整 payload;无需新表、SQL、顶层字段或云函数改动。.specify/memory/constitution.md 是未填写模板,按 AGENTS.md 和用户最新范围执行,无需例外。 本次实现及本地验证不运行真实支付、数据库操作或生产推送,不读取凭据。已取消此前 UserNotificationPayload 类、事件 ID/语言映射封装和用户推送重载的计划,不改 PayPush 本地化逻辑。 ## Project Structure / Interfaces 后端根目录 D:/QtwCode/foodie/foodie-server;以下生产路径以 ruoyi-admin/src/main/java/com/ruoyi/app/ 为基准,测试路径以 ruoyi-admin/src/test/java/com/ruoyi/app/ 为基准。 - 新建 push/UserNotificationScene.java:七个允许值,枚举名称为协议值。 - 修改 order/dto/OrderPushBodyDto.java:新增可选 String eventScene;新增静态工厂: `getUserJson(String ddId, String state, int type, int pushType, UserNotificationScene scene): String`。 - 工厂使用原构造方式,scene 非空才设置场景。旧 getJson/getMap 保持输出,null 场景不输出该 key,不新增其他字段。 - 闪送用户方法改为 `sendToUser(InfoUser recipient, FlashDeliveryOrder order, String status, String contentKey, UserNotificationScene scene): void`。 - 自配送用户方法改为 `pushSelfDeliveryStatus(PosOrder order, String contentKey, String stateStr, UserNotificationScene scene): void`,按原事务存在与否在成功提交/持久化后调度。 - 其他现有用户包装签名不变,收到带 eventScene 的 body 后照常入库/发送,不重新解释场景。 - 文档集中在本目录 spec.md、plan.md、tasks.md、frontend-integration.md、checklists/requirements.md。 ## Task 1: 单字段场景契约(US2/US3) **Files:** 新建 push/UserNotificationScene.java、测试 order/dto/OrderPushBodyDtoTest.java;修改 order/dto/OrderPushBodyDto.java。 **Interfaces:** 产出上述 getUserJson 和七场景枚举;不再创建额外 payload 封装类。 - [x] 写测试 `preservesLegacyFieldsAndAddsOnlyEventScene`:旧四字段原值不变,新键集合仅多 eventScene;`oldFactoryOmitsEventScene` 断言旧 getJson/getMap 无新增 key;覆盖枚举七值及 null 场景。 - [x] 运行 Task 1 定向测试,确认因新工厂/场景缺失的预期失败;实现可选字段、用户工厂和 enum,并加兼容注释。 - [x] 重跑要求零失败,核对 DTO 序列化没有额外 null 字段,检查差异和暂存范围后独立本地提交,不推送。 ## Task 2: 外卖、闪送、自配送和出餐(US1/US4) **Files:** 修改 order/PosOrderQsOprateController.java、order/PosOrderController.java、order/PosOrderShOprateController.java、flashdelivery/service/FlashDeliveryNotificationService.java。扩展测试 order/PosOrderQsOprateControllerTest.java、order/PosOrderAdminStatusControllerTest.java、order/PosOrderShOprateControllerTest.java、flashdelivery/service/FlashDeliveryNotificationServiceTest.java、flashdelivery/service/FlashDeliveryMultiPickupTest.java。 **Interfaces:** 使用 Task 1 getUserJson,仅用户 body 带场景。现代 deliveryStatus 1/2/3 的成功动作对应 RIDER_ACCEPTED/ORDER_DELIVERING/ORDER_DELIVERED;现有有效旧入口内部 state 3/4/12 分别明确对应这三个动作,不改 state 值。 - [x] 写并运行预期失败测试:成功三节点场景正确、无地址消息不丢、失败/回滚零成功通知、部分取件零派送、全部取件一次派送、自配送直接完成仅送达、非用户 body 无场景。 - [x] 成功动作传明确枚举;自配送商家接单 MERCHANT_ACCEPTED、出餐 MERCHANT_READY、开始/完成 ORDER_DELIVERING/ORDER_DELIVERED,不误播骑手接单。 - [x] 移除仅针对用户入库的地址前置阻断,外发仍由原通道判断;存在事务时 afterCommit,无事务时持久化成功后发送,不扩张支付/发票/账单事务。 - [x] 跑 Task 2 测试捕获实际调用参数,零失败后审阅并独立本地提交,不推送。 ## Task 3: 现有支付成功与取消(US3/US4) **Files:** 修改 pay/LinePayOrderNotificationService.java、omgpay/OmgPaymentNotifyService.java、order/DeliveryOrderNotificationService.java;扩展同目录对应三份现有测试。 **Interfaces:** 用户专用 getUserJson body 复用于 PublisherEvent/BusinessPushService;商家/骑手用原始独立 body。 - [x] 写并运行预期失败测试:用户支付 PAYMENT_SUCCEEDED、用户取消 ORDER_CANCELLED;type=0 原值不变,同动作非用户 payload 无 eventScene,无地址仍入库。 - [x] 仅改现有提交后用户通知,不改 OMG/LINE 验签、核销、幂等、退款、查询或通知对象;不新增闪送用户取消。016 plan 已读。 - [x] 跑三组定向测试,包括重复支付不新增成功通知,检查差异后独立本地提交,不推送。 ## Task 4: 兼容验证和文档交接(全部故事) **Files:** 扩展测试 push/BusinessPushServiceTest.java、push/FcmPushSenderTest.java、push/ApnsPushSenderTest.java、utils/event/PushEventListenerTest.java,收口本目录文档。 **Interfaces:** 验证现有完整 payload 保留 eventScene,不增加顶层字段、不改声音。只有测试确证 uni 用户 GET 编码破坏 JSON 时,才在 utils/PayPush.java 修该路径参数编码,不改云函数。 - [x] 写并运行通道/入库测试:完整 JSON 场景保持、body 一致、缺地址仅入库、通道异常不补发或影响业务、旧 payload 和 default sound/qsRing/pushsound.caf 不变。 - [x] 对照最终代码收口接入文档;明确仅一个新增字段、前端选语言、无事件 ID 等限制,记录后端与 App 验证边界。 - [x] 跑下方定向测试和模块构建、diff 和暂存检查,零失败后本地提交。不要自动推送、部署或生产试发。 ## Verification Commands 执行时每 Task 缩小同一 -Dtest 列表,先证明预期失败再通过;既有通道透传只增加回归测试,不改发送实现。控制器公共场景/事务夹具集中在 UserOrderNotificationSceneTest。 ```powershell $env:JAVA_HOME = 'C:/Users/99029/.jdks/graalvm-jdk-21.0.12+7.1' $env:PATH = "$env:JAVA_HOME/bin;$env:PATH" mvn -pl ruoyi-admin -am '-Dtest=OrderPushBodyDtoTest,UserOrderNotificationSceneTest,PosOrderQsOprateControllerTest,PosOrderShOprateControllerTest,PosOrderAdminStatusControllerTest,FlashDeliveryNotificationServiceTest,FlashDeliveryMultiPickupTest,LinePayOrderNotificationServiceTest,OmgPaymentNotifyServiceTest,DeliveryOrderNotificationServiceTest,BusinessPushServiceTest,FcmPushSenderTest,ApnsPushSenderTest,PushEventListenerTest' '-Dsurefire.failIfNoSpecifiedTests=false' test mvn -pl ruoyi-admin -am -DskipTests package git diff --check ``` 预期测试零失败/零错误、两次 BUILD SUCCESS、diff 检查退出码 0。Java 环境只对当前进程生效,不改全局配置。实际云端场景透传及 App 真机声音由联调核验,未执行必须单独记录。 ## Dependencies / Handoff Task 1 → Task 2 → Task 3 → Task 4;详单见 tasks.md。用户已确认继续,按本计划顺序实施;不创建分支或 worktree。收口时依执行计划技能安排一次只读整体审查,不分派实现任务。 ## 最终执行结果 - 实际业务动作明确传场景;共用旧资料编辑通知的二参 chuCan 不带场景,真正出餐入口调用私有三参版本传 MERCHANT_READY。 - 定向测试 152/152、独立商家/场景测试 33/33、修复后模块 package BUILD SUCCESS,差异格式检查通过。 - 一次只读整体审查仅发现上述误分类 Important,已按失败回归修复;无 Critical/Minor,不重新派发审查。 - 扩展全模块测试的 admin 947 项中有 1 项基线泰文键集合失败,不修范围外语言包、不推送或部署。App/云端与真机验收交接前端。完整记录及实施取舍见 tasks.md。