tasks.md 15 KB


description: "Task list for feature 028-flash-delivery-push"

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

Input: Design documents from /specs/028-flash-delivery-push/

Prerequisites: plan.md (required), spec.md (required for user stories), research.md, data-model.md, contracts/push.md

Tests: quickstart.md「自动验证」明确要求单测(FlashDeliveryNotificationServiceTest 新建 + FlashDeliveryApplicationServiceTest 扩展),故包含测试任务且先行(红→绿)。

Organization: Tasks are grouped by user story to enable independent implementation and testing of each story.

Format: [ID] [P?] [Story] Description

  • [P]: Can run in parallel (different files, no dependencies)
  • [Story]: Which user story this task belongs to (e.g., US1, US2, US3)
  • Include exact file paths in descriptions

Path Conventions

  • 主代码:ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/
  • 测试:ruoyi-admin/src/test/java/com/ruoyi/app/flashdelivery/
  • 构建命令(Git Bash):export JAVA_HOME="C:/Users/qmj/.jdks/graalvm-jdk-21.0.7" && export PATH="$JAVA_HOME/bin:$PATH" && mvn -pl ruoyi-admin -am test -Dtest='...'

Phase 1: Setup

Purpose: 确认基线全绿,避免把存量问题带进本特性

  • T001 运行既有闪送单测确认基线全绿:mvn -pl ruoyi-admin -am test -Dtest='FlashDeliveryApplicationServiceTest,FlashDeliveryStateMachineTest,FlashDeliveryControllerContractTest'(JDK21 环境变量见 Path Conventions)——基线 79 测试 1 存量失败(createRejectsUnsupportedPayType,源于在途 payType 白名单注释改动,非本特性范围,全程保持不新增失败即可)

Phase 2: Foundational (Blocking Prerequisites)

Purpose: 通知服务基础设施 + 应用服务装配,所有 user story 依赖

  • T002 新建 ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/service/FlashDeliveryNotificationService.java@Service;构造器注入 InfoUserMappercom.ruoyi.system.mapper)与 PushEventServicecom.ruoyi.app.utils.event);私有 runAfterCommit(Runnable)(镜像 DeliveryOrderNotificationService.java:156-167);包级可见 sendToUser(FlashDeliveryOrder, String contentKey) / sendToRider(FlashDeliveryOrder, String contentKey)runAfterCommituserMapper.selectByIdInfoUserAsyncManager.me().execute(TimerTask)PayPush.userPushHandleLocal / qsPushHandleLocal,title 固定 no.message.push.message,body=OrderPushBodyDto.getJson(order.getOrderNo(), 状态串, -1, 3),ddId=order.getOrderNo();推送异常捕获仅记日志不上抛
  • T003 FlashDeliveryApplicationService.java:76-94 构造器追加第 10 个依赖 FlashDeliveryNotificationService 并存字段;全仓更新 new FlashDeliveryApplicationService( 调用点(ruoyi-admin/src/test/java/com/ruoyi/app/flashdelivery/service/FlashDeliveryApplicationServiceTest.java 等,先 grep 定位)补 mock 参数;编译并运行 T001 测试集保持全绿

Checkpoint: 通知服务骨架就绪且零行为变化(尚无 notify 入口被调用)


Phase 3: User Story 1 - 寄件用户收到骑手接单通知 (Priority: P1) 🎯 MVP

Goal: 骑手抢单成功后,寄件用户收到「骑手已接单,NO:{orderNo}」推送与消息中心记录

Independent Test: 对一笔 WAITING_ACCEPTANCE 订单执行 accept 成功 → 验证 notifyAccepted 被调用;抢单失败(条件更新 0 行)→ 不调用

Tests for User Story 1(先行,跑红)

  • T004 [US1] 新建 ruoyi-admin/src/test/java/com/ruoyi/app/flashdelivery/service/FlashDeliveryNotificationServiceTest.java:spy 服务 + doNothing 打断 sendToUser/sendToRider(镜像 DeliveryOrderNotificationServiceTest.java 手法),断言 notifyAccepted(order)sendToUser(order, "no.message.push.delivery.personnel.receiving.order");同时在 FlashDeliveryApplicationServiceTest.java 增补:accept 成功路径 verify notifyAcceptedorderMapper.accept 返回 0 时 never 调用

Implementation for User Story 1

  • T005 [US1] 在 FlashDeliveryNotificationService.java 实现 notifyAccepted(FlashDeliveryOrder)(runAfterCommit 后推 userId,状态串 ACCEPTED);在 FlashDeliveryApplicationService.java accept(约 :485 writeLog 之后、return 之前,withLock lambda 内)调用 notificationService.notifyAccepted(target)
  • T006 [US1] 运行 mvn -pl ruoyi-admin -am test -Dtest='FlashDeliveryNotificationServiceTest,FlashDeliveryApplicationServiceTest' 确认由红转绿

Checkpoint: MVP——抢单通知可独立演示(quickstart.md 手动场景 3)


Phase 4: User Story 2 - 寄件用户收到取件与送达通知 (Priority: P1)

Goal: 骑手取件、送达成功后,寄件用户分别收到「骑手配送中」「骑手已送达」推送

Independent Test: 对 ACCEPTED 订单 pickup 成功 → notifyPickedUp;对 PICKED_UP 订单 deliver 成功 → notifyDelivered;任一失败 → 不调用

Tests for User Story 2(先行,跑红)

  • T007 [US2] FlashDeliveryNotificationServiceTest.java 增补:notifyPickedUpsendToUser(order, "no.message.push.delivery.personnel.qspsz.order")notifyDeliveredsendToUser(order, "no.message.push.delivery.personnel.qsysd.order")FlashDeliveryApplicationServiceTest.java 增补:transitionWithProof 成功按 next 分派对应 notify、orderMapper.transitionByRider 返回 0 时两者均 never

Implementation for User Story 2

  • T008 [US2] FlashDeliveryNotificationService.java 实现 notifyPickedUp / notifyDelivered(状态串 PICKED_UP / DELIVERED);FlashDeliveryApplicationService.java transitionWithProof(约 :781 writeLog 之后)按 next 参数分派调用(仅 PICKED_UP/DELIVERED 两值,其它忽略)
  • T009 [US2] 运行 Story 2 测试集确认由红转绿

Checkpoint: 履约进度通知完整(quickstart.md 手动场景 4、5)


Phase 5: User Story 3 - 骑手收到订单取消通知 (Priority: P2)

Goal: 用户/平台取消成功且已有接单骑手时,骑手收到「订单已取消,NO:{orderNo}」推送;未接单取消零推送

Independent Test: ACCEPTED 状态 userCancel/adminCancel 成功 → notifyCancelled;WAITING_ACCEPTANCE 取消(riderId 空)→ 跳过;取消失败 → 不调用

Tests for User Story 3(先行,跑红)

  • T010 [US3] FlashDeliveryNotificationServiceTest.java 增补:notifyCancelled(order) 在 riderId 非空时走 sendToRider(order, "no.message.push.order.cancelled")、riderId 为空时零交互;FlashDeliveryApplicationServiceTest.java 增补:userCancel/adminCancel 成功路径 verify notifyCancelledorderMapper.cancel 返回 0 时 never

Implementation for User Story 3

  • T011 [US3] FlashDeliveryNotificationService.java 实现 notifyCancelled(riderId 判空跳过,状态串 CANCELLED,推骑手端);FlashDeliveryApplicationService.javauserCancel(约 :397)与 adminCancel(约 :622)的 writeLog 之后调用
  • T012 [US3] 运行 Story 3 测试集确认由红转绿

Checkpoint: 取消通知完整(quickstart.md 手动场景 6、7、8)


Phase 6: Polish & Cross-Cutting Concerns

Purpose: 排除事件防回归 + 全量验证

  • T013 反例断言(原始 FR-010):FlashDeliveryApplicationServiceTest.javacompletecancelExpiredScheduledOrdertipAddcreate 增加 notificationService 零交互断言。2026-09-18 需求变更后,create 仅对预约单保持零新单通知;立即单改为通知,见 Phase 7。
  • T014 全量回归:mvn -pl ruoyi-admin -am test 455 测试仅 1 个已知存量失败(createRejectsUnsupportedPayType,在途 payType 特性所致);编译通过;git status 确认本特性改动仅 4 个代码文件(2 新建 2 增量)+ specs/028 + CLAUDE.md + feature.json,其余为并行在途特性(预约单窗口)的既有改动mvn -pl ruoyi-admin -am test(全部模块测试)+ mvn -pl ruoyi-admin -am compile 通过;grep 确认闪送模块外无文件改动(git status 仅闪送相关文件与 specs/028)
  • T015 原始范围快照:当时零数据库变更、零新 i18n key、ruoyi-system 零改动。该结论已被 2026-09-18 新单推送需求取代,见 Phase 7。

Phase 7: 骑手新单通知与预约单原子开放(2026-09-18 追加)

Goal: 立即闪送单创建后可见并通知;预约单创建时隐藏,在有效时间窗内由独立每秒任务原子开放且只通知一次;外卖与闪送新单文案区分。

  • T016 [US4] 先补失败测试:覆盖 isDisplay Mapper 映射、立即/预约创建差异、原子开放成功与重复执行、任务批处理、新单候选路由和新 i18n key。
  • T017 [US4] 在 FlashDeliveryOrder、Mapper/XML 与查询条件中加入 is_display;骑手新任务列表、预接单详情和抢单接口仅允许 is_display=true
  • T018 [US4] 立即单创建为 isDisplay=true 并提交后通知;预约单创建为 false;实现 false -> true 的带状态、无人接单及时间窗条件更新,只有更新成功时通知。
  • T019 [US4] 新增独立 FlashDeliveryAvailabilityTask,批量处理候选预约单并隔离单笔失败;在 updatesql/sql.md 注册每秒 Quartz 调度与禁止同任务并发。
  • T020 [US4] 新增闪送候选骑手查询:在线、FLASH 资质、车型、半径、普通/加急接单互斥,按距离取最近 20 人。
  • T021 [US4] 外卖/闪送新单分别使用 no.message.push.new.food.orderno.message.push.new.flash.order,同步维护 6 份后端 i18n 资源。
  • T022 [US4] 在 updatesql/sql.md 记录 is_display、索引、存量预约单初始化与 sys_job;不直接执行数据库变更。
  • T023 [US4] 更新 spec、plan、tasks、research、data-model、push contract 与 quickstart,说明每秒独立任务和原子展示门闩。
  • T024 [US4] 运行定向测试(ruoyi-system 5 个、ruoyi-admin 14 个,均零失败)、mvn -pl ruoyi-admin -am compilegit diff --check;数据库迁移和真实推送联调仍需部署环境人工验证。

Phase 8: 闪送订单骑手评分(2026-09-18 追加)

Goal: 寄件人对已完成闪送订单的骑手评分(1-5 星,仅一次);闪送分与外卖 pos_order_rating 体系彻底分开,不并骑手总分、不给积分;用户端详情回显评分,平台 PC 端订单详情显示评分。

Background: Bug 工单 #659「骑手评价后要可提交」——用户端已有评价页,后端此前无闪送评价支持。设计决策:评分存 flash_delivery_order.rider_stars 单字段(NULL=未评),新开专用接口带完整校验,骑手身份由后端从订单 rider_id 回填语义(评分条件更新即绑定该订单骑手),星级由服务端校验。

  • T025 [US5] 先补失败测试:FlashDeliveryApplicationServiceTest.java 增补 rateRider 用例(寄件人完成单评价成功、非寄件人拒绝、未完成/无骑手拒绝、已评预检拒绝、并发条件更新 0 行拒绝、星级 null/越界拒绝、userDetail 回显 riderStars);FlashDeliveryControllerContractTest.java 增补 POST /orders/{id}/rate 契约(token→userId 透传 service)
  • T026 [US5] FlashDeliveryOrder 增加 riderStars 字段;FlashDeliveryOrderMapper.xml resultMap 补 rider_stars 映射并新增 rateRider 原子条件更新(status='COMPLETED' AND rider_id IS NOT NULL AND rider_stars IS NULL AND user_id=#{userId} 守卫,防并发重复提交)
  • T027 [US5] 新建 DTO FlashDeliveryRatingRequestLong orderId + Double stars,orderId 放 body 与 addTip/updateAddress 模式一致);FlashDeliveryUserController 新增 POST /system/flashDelivery/orders/rateFlashDeliveryApplicationService.rateRider(星级 1-5 服务端校验、寄件人/完成态/未评过校验、条件更新 0 行报已评);FlashDeliveryOrderDetailView 回显 riderStarsadminDetail 返回完整实体,平台端自动带出)
  • T028 [US5] 后端 i18n 6 份资源新增 flash.delivery.rating.stars.invalid / flash.delivery.rating.not.allowed / flash.delivery.rating.alreadyupdatesql/sql.md 登记 flash_delivery_order.rider_stars 列(不直接执行)
  • T029 [US5] 平台 PC 端 foodie-admin-vue views/flashDelivery/orders/OrderDetail.vue 骑手信息区显示评分(NULL 显示未评价),4 份语言文件(src/api/language/language.*.jsflashDelivery 命名空间新增 key
  • T030 [US5] 回归:定向测试全绿 + mvn -pl ruoyi-admin -am test 维持基线(仅存量已知失败 createRejectsUnsupportedPayType);用户端 App 前端调新接口与五星默认选中由 App 侧另行处理(Bug #659 前端部分)
  • T031 [US5] 用户端详情 GET /orders/{id}rider 用户资料带闪送平均分:新增 selectRiderAverageStarsAVG(rider_stars) 仅统计闪送评价,与外卖分互不影响),无评价回退 4.5 默认展示(与外卖骑手 setQsStar 惯例一致);TDD 含 service 两个用例与 XML 聚合语句断言

Phase Dependencies

  • Setup (Phase 1): 无依赖,立即开始
  • Foundational (Phase 2): 依赖 Phase 1;阻塞全部 user story(T003 之后才能写各 story 的钩子与 verify 断言)
  • User Stories (Phase 3-5): 依赖 Phase 2;三个 story 共享同一对文件(FlashDeliveryApplicationService.java 与两个测试类),必须按 US1 → US2 → US3 顺序串行执行,不可并行
  • Polish (Phase 6): 依赖 Phase 3-5 全部完成
  • New-order availability (Phase 7): 依赖既有通知服务;字段、原子更新、任务、候选骑手查询和推送按 T016 → T023 顺序实现

Within Each User Story

  • 测试任务先行并跑红 → 实现任务 → 验证任务跑绿
  • 每 story 收尾可独立演示(见各 Checkpoint 对应 quickstart.md 场景)

Parallel Opportunities

  • 本特性文件高度集中,仅 T002/T003 与测试内容天然串行;无安全并行窗口(除 Phase 1 与文档工作)

Implementation Strategy

MVP First (User Story 1 Only)

  1. Phase 1 基线 → Phase 2 基础设施(零行为变化)
  2. Phase 3 US1:抢单通知(最高价值单条消息)
  3. STOP and VALIDATE:quickstart.md 场景 3 手动验证 + 单测绿
  4. 继续 US2(取件/送达)、US3(取消)

Incremental Delivery

每个 story 完成即独立可测、可部署的增量;US1 单独上线即可解决"用户不知是否被接单"的核心痛点。


Notes

  • [P] 标记:本特性因文件集中,实际无可并行任务
  • 提交节奏:每 Phase 完成后提交一次(/speckit-git-commit
  • 遵守 plan.md 关键设计点 1-5(不可变字段消费、钩子位置、pushType=3、排除清单)
  • Java 注释不得含 */ 序列;保留 CRLF;不格式化整文件