# Implementation Plan: 闪送订单状态变更消息推送 **Branch**: `028-flash-delivery-push` | **Date**: 2026-09-17 | **Spec**: [spec.md](spec.md) **Input**: Feature specification from `/specs/028-flash-delivery-push/spec.md` ## Summary 在闪送订单五个状态变更点(骑手抢单、取件、送达、用户取消、平台取消)接入既有推送通道:新建 `FlashDeliveryNotificationService`(镜像 `DeliveryOrderNotificationService` 的 afterCommit + AsyncManager + PayPush 模式),由 `FlashDeliveryApplicationService` 在条件更新成功后调用。履约事件推寄件用户、取消事件推接单骑手;复用现有 i18n 文案 key 与 `OrderPushBodyDto` payload(pushType=3)。零新表、零新接口、零新翻译(详见 [research.md](research.md))。 ## Technical Context **Language/Version**: Java 21(Spring Boot + MyBatis-Plus) **Primary Dependencies**: Spring 事务同步(`TransactionSynchronizationManager`)、`AsyncManager`、`PayPush`(uni 云函数推送)、`PushEventService`(push_message 入库)、fastjson(payload 序列化) **Storage**: MySQL(只读 `flash_delivery_order`、`info_user`;只写既有 `push_message`,经由 PushEventService) **Testing**: JUnit 5 + Mockito(`spy`+`doNothing` 打断网络发送,验证路由决策;仓库先例 `DeliveryOrderNotificationServiceTest`) **Target Platform**: Windows 开发 / Linux 部署的 Spring Boot 服务 **Project Type**: web-service(多模块 Maven:`ruoyi-admin` → `ruoyi-system`) **Performance Goals**: 推送异步执行,不增加订单接口耗时(SC-004 零传导) **Constraints**: 推送失败不得影响状态流转(FR-009);仅事务提交后触发(FR-011);不新增 Android 通道 **Scale/Scope**: 1 个新服务类 + 1 个应用服务的 5 个方法加钩子 + 2 个测试类 ## Constitution Check *GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.* `.specify/memory/constitution.md` 为未定制的占位模板,无项目级原则可引用;改按仓库 `CLAUDE.md` 约束逐条核对: | 约束 | 结论 | |------|------| | Controller 参数规范 | 不涉及(无新 Controller/接口改动) | | 模块边界:集成代码放 ruoyi-admin | ✅ 通知服务放 `ruoyi-admin` 的 `com.ruoyi.app.flashdelivery.service`,不进 ruoyi-system | | 数据库变更须写 updatesql/sql.md | ✅ 本特性零数据库变更 | | 全栈字段清单 | ✅ 无新字段 | | Java 注释安全(无 `*/` 序列) | ✅ 实现时遵守 | | 前端 i18n 规范 | 不涉及(复用后端既有文案 key,无前端改动) | | CRLF 保留、不格式化整文件 | ✅ 实现时遵守 | 无违规,无需 Complexity Tracking 条目。 ## Project Structure ### Documentation (this feature) ```text specs/028-flash-delivery-push/ ├── plan.md # This file (/speckit-plan command output) ├── research.md # Phase 0 output (/speckit-plan command) ├── data-model.md # Phase 1 output (/speckit-plan command) ├── quickstart.md # Phase 1 output (/speckit-plan command) ├── contracts/ │ └── push.md # 推送 payload 契约(Phase 1 output) └── tasks.md # Phase 2 output (/speckit-tasks command - NOT created by /speckit-plan) ``` ### Source Code (repository root) ```text ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/ └── service/ ├── FlashDeliveryApplicationService.java # 修改:构造器 + 5 个方法的成功路径加通知钩子 └── FlashDeliveryNotificationService.java # 新增:推送路由(afterCommit + AsyncManager + PayPush) ruoyi-admin/src/test/java/com/ruoyi/app/flashdelivery/ └── service/ ├── FlashDeliveryApplicationServiceTest.java # 修改:构造器新参 + 推送触发/不触发断言 └── FlashDeliveryNotificationServiceTest.java # 新增:接收人判定与跳过逻辑 ``` **Structure Decision**: 完全落在闪送模块既有目录结构内,不新增包、不动 `ruoyi-system`、不动 Controller 层。 ## 关键设计点(供 tasks 生成引用) 1. **通知服务 API**:`notifyAccepted(order)` / `notifyPickedUp(order)` / `notifyDelivered(order)` / `notifyCancelled(order)`;入参用事务前读到的 `FlashDeliveryOrder`(只消费 orderNo/userId/riderId 等不可变字段,不依赖其 status 快照)。 2. **钩子位置**: - `accept`:`withLock` 内 `writeLog(...)` 之后、`return` 之前; - `pickup`/`deliver`:共用 `transitionWithProof` 的 `writeLog(...)` 之后,按 `next` 参数分派(PICKED_UP/DELIVERED); - `userCancel`/`adminCancel`:`writeLog(...)` 之后,`notifyCancelled` 内部判 `riderId` 判空。 3. **构造器**:`FlashDeliveryApplicationService` 追加第 10 个依赖 `FlashDeliveryNotificationService`;同步更新既有测试的构造调用。 4. **发送实现**:`runAfterCommit` → `userMapper.selectById` 取 cid → `AsyncManager.me().execute` → `PayPush.userPushHandleLocal / qsPushHandleLocal`(title=`no.message.push.message`,content key 见 research.md D2,body=`OrderPushBodyDto.getJson(orderNo, status, -1, 3)`)。 5. **不触发清单**(对应 FR-010):`create`、`tipAdd`、`complete`、`FlashDeliveryAutoCompleteTask`、`cancelExpiredScheduledOrder` 一律不调用通知服务。 ## Complexity Tracking > 无 Constitution Check 违规,本节为空。