plan.md 6.6 KB

Implementation Plan: 闪送订单状态变更消息推送

Branch: 028-flash-delivery-push | Date: 2026-09-17 | Spec: spec.md

Input: Feature specification from /specs/028-flash-delivery-push/spec.md

Summary

在闪送订单状态变更点接入既有推送通道,并追加新单通知:立即单以 is_display=true 创建后通知骑手;预约单以 is_display=false 创建,由独立 FlashDeliveryAvailabilityTask 每秒检查时间窗,以条件更新原子开放展示并取得唯一通知资格。履约事件推寄件用户、取消事件推接单骑手;骑手候选按在线状态、FLASH 配送类型、车型、距离和接单互斥规则筛选。外卖和闪送分别使用专属新单 i18n key,payload 继续复用 OrderPushBodyDto(pushType=3)。

Technical Context

Language/Version: Java 21(Spring Boot + MyBatis-Plus)

Primary Dependencies: Spring 事务同步(TransactionSynchronizationManager)、AsyncManagerPayPush(uni 云函数推送)、PushEventService(push_message 入库)、fastjson(payload 序列化)

Storage: MySQL(flash_delivery_order 新增 is_display;原子开放预约单;推送消息经 PushEventService 写入既有 push_message

Testing: JUnit 5 + Mockito(spy+doNothing 打断网络发送,验证路由决策;仓库先例 DeliveryOrderNotificationServiceTest

Target Platform: Windows 开发 / Linux 部署的 Spring Boot 服务

Project Type: web-service(多模块 Maven:ruoyi-adminruoyi-system

Performance Goals: 推送异步执行,不增加订单接口耗时(SC-004 零传导)

Constraints: 推送失败不得影响状态流转(FR-009);仅事务提交后触发(FR-011);不新增 Android 通道

Scale/Scope: 通知服务和应用服务增量 + 1 个独立任务类 + Entity/Mapper/XML + 后端 i18n + SQL/spec 测试

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-admincom.ruoyi.app.flashdelivery.service,不进 ruoyi-system
数据库变更须写 updatesql/sql.md is_display、索引和 sys_job 仅记录于 updatesql/sql.md,不直接执行
全栈字段清单 ✅ 更新 Entity、Mapper XML、查询/条件更新及 SQL;该字段不对前端表单开放
Java 注释安全(无 */ 序列) ✅ 实现时遵守
前端 i18n 规范 不涉及前端;后端 6 份资源新增外卖/闪送专属新单 key
CRLF 保留、不格式化整文件 ✅ 实现时遵守

无违规,无需 Complexity Tracking 条目。

Project Structure

Documentation (this feature)

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)

ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/
├── service/
│   ├── FlashDeliveryApplicationService.java   # 修改:状态通知、新单可见性及原子开放
│   └── FlashDeliveryNotificationService.java  # 修改:状态通知与闪送新单骑手路由
└── task/
    └── FlashDeliveryAvailabilityTask.java     # 新增:每秒开放进入预约窗口的订单

ruoyi-admin/src/test/java/com/ruoyi/app/flashdelivery/
└── service/
    ├── FlashDeliveryApplicationServiceTest.java       # 修改:构造器新参 + 推送触发/不触发断言
    └── FlashDeliveryNotificationServiceTest.java      # 新增:接收人判定与跳过逻辑

Structure Decision: 集成和任务仍在 ruoyi-admin;订单字段、候选骑手查询和原子更新落在 ruoyi-system 的既有 Entity/Mapper/XML;不动 Controller 层。

关键设计点(供 tasks 生成引用)

  1. 通知服务 APInotifyAccepted(order) / notifyPickedUp(order) / notifyDelivered(order) / notifyCancelled(order);入参用事务前读到的 FlashDeliveryOrder(只消费 orderNo/userId/riderId 等不可变字段,不依赖其 status 快照)。
  2. 钩子位置
    • acceptwithLockwriteLog(...) 之后、return 之前;
    • pickup/deliver:共用 transitionWithProofwriteLog(...) 之后,按 next 参数分派(PICKED_UP/DELIVERED);
    • userCancel/adminCancelwriteLog(...) 之后,notifyCancelled 内部判 riderId 判空。
  3. 构造器FlashDeliveryApplicationService 追加第 10 个依赖 FlashDeliveryNotificationService;同步更新既有测试的构造调用。
  4. 发送实现runAfterCommituserMapper.selectById 取 cid → AsyncManager.me().executePayPush.userPushHandleLocal / qsPushHandleLocal(title=no.message.push.message,content key 见 research.md D2,body=OrderPushBodyDto.getJson(orderNo, status, -1, 3))。
  5. 新单开放门闩:立即单创建时 isDisplay=true 并在提交后通知;预约单创建时为 false,骑手列表、详情和抢单均以该字段为前置条件。独立任务调用 Mapper 条件更新,在有效时间窗内将 false -> true;只有影响 1 行的执行发送通知。
  6. 独立调度FlashDeliveryAvailabilityTask.openScheduledOrders() 通过 sys_job 使用 0/1 * * * * ? 每秒执行,concurrent='1' 禁止同一任务并发;数据库条件更新继续提供跨实例幂等保护。
  7. 候选骑手:在线、用户类型为骑手、支持 FLASH、车型匹配、配置半径内;加急单排除所有进行中的外卖与闪送,普通单排除进行中的加急闪送;按距离取最近 20 人。
  8. 文案区分:外卖使用 no.message.push.new.food.order,闪送使用 no.message.push.new.flash.order
  9. 不触发清单(对应 FR-010):tipAddcompleteFlashDeliveryAutoCompleteTask.completeDeliveredOrders()cancelExpiredScheduledOrder、预约单创建不调用新单通知。

Complexity Tracking

无 Constitution Check 违规,本节为空。