tasks.md 13 KB

Tasks: 用户端统一订单列表

Input: Design documents from /specs/030-unified-order-list/(spec.md / plan.md / research.md / data-model.md / contracts/api-contract.md / quickstart.md)

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

Tests: spec(FR-010/SC-006)明确要求自动化测试,TDD:先红后绿。

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

构建与测试命令(所有验证任务通用)

export JAVA_HOME="$HOME/.jdks/graalvm-jdk-21.0.12+7.1"
export PATH="$JAVA_HOME/bin:$PATH"
mvn -pl ruoyi-admin -am test -Dtest='XxxTest' -Dsurefire.failIfNoSpecifiedTests=false

已知基线红测试:flash payType 白名单(见项目记忆),与本 feature 无关,不修。


Phase 1: Setup (Shared Infrastructure)

  • T001 基线验证:mvn -pl ruoyi-admin -am compile 通过;git status 工作区干净(test 分支同步状态)(2026-09-21 编译通过;工作区仅本 feature 文档未提交,代码零改动)

Phase 2: Foundational (Blocking Prerequisites)

Purpose: 游标、来源枚举、tab 谓词——三个用户故事共同依赖的纯逻辑件(全部可独立单测)

  • T002 新增 i18n 错误 key:no.unifiedorder.cursor.invalid(游标非法)、no.unifiedorder.param.invalid(tab/source 非法)加到 ruoyi-admin/src/main/resources/i18n/ 全部 6 个语言文件(用 raw 字节模式 perl 脚本追加,沿用 029 教训,勿用 perl -i -pe 直接改写);先 grep -n "no.unifiedorder\|no.user.login.success" messages.properties 找锚点追加(2026-09-21 完成:锚点 no.user.login.success 行后插入,git diff 每文件 +2 行,CR 计数=行数,UTF-8 单次编码)
  • T003 TDD 先红:新建 ruoyi-admin/src/test/java/com/ruoyi/app/unifiedorder/UnifiedCursorTest.java——① 生成与解析往返一致 ② 非法输入(null/垃圾串/负数/越界)抛参数异常 ③ 排序全序比较器(时间 DESC → flash<takeaway → id DESC)④ 各源推进条件布尔(本源序 vs cursor 源序)判定正确(2026-09-21 编译失败红→绿)
  • T004 [P] 新建 ruoyi-admin/src/main/java/com/ruoyi/app/unifiedorder/OrderSource.java(takeaway/flash 枚举 + 字典序)与 UnifiedCursor.java(生成/解析/校验/比较器/各源推进条件),使 T003 转绿(2026-09-21 UnifiedCursorTest 7/7 绿)
  • T005 TDD 先红后绿:新建 UnifiedOrderTabTest.java(两域内存判定全覆盖:外卖 5 tab 条件样本——含货到付款不进 unpaid、payType=1+自取/堂食进 active、售后单从 active/completed/cancelled 剔除、payStatus=2 归 refund;闪送六态映射;all 不过滤),然后新建 ruoyi-admin/src/main/java/com/ruoyi/app/unifiedorder/UnifiedOrderTab.java(枚举内聚 posOrderWrapper()/matches(PosOrder) 与 flashWrapper()/matches(FlashDeliveryOrder),条件常量同一份)转绿。规则照抄 data-model.md §3,不得自行发明(2026-09-21 13/13 绿;flashWrapper 对 unpaid/refund 抛 IllegalStateException 防误用,matches 恒 false)

Checkpoint: 纯逻辑件全绿;US1/US2/US3 可并行开始


Phase 3: User Story 1 - 统一列表浏览与混排 (Priority: P1) 🎯 MVP

Goal: 新接口 /system/unifiedOrder/list 两路查询 + 归并 + 游标滚动 + 来源筛选 + 统一卡片

Independent Test: 构造混排样本(不依赖 tab 语义细节),断言倒序混排、翻页完整、hasMore/nextCursor 正确

Tests for User Story 1(先写、先红)

  • T006 TDD 先红:新建 ruoyi-admin/src/test/java/com/ruoyi/app/unifiedorder/UnifiedOrderQueryServiceTest.java(Mockito mock 两个 mapper,沿用 029 的 ReflectionTestUtils 注入模式):① 两路各查 size+1、归并后恰返回 size 条且时间非递增 ② 第 size+1 行存在 → hasMore=true + nextCursor=末条游标 ③ 滚动多轮遍历与样本全集一致(不漏不重,含同秒跨源并列构造)④ size 缺省 10、钳制 1-50 ⑤ source 单选时只调对应 mapper ⑥ tab=unpaid/refund 时不调闪送 mapper ⑦ 空结果 hasMore=false(2026-09-21 红→绿;构造器注入代替 setField,另加店铺名批量回填用例)
  • T007 TDD 先红:新建 UnifiedOrderQueryControllerTest.java:① 反射断言 @GetMapping("/list") + 每参数 @RequestParam + token @RequestHeader(Controller 规范) ② tab/source 非法值返回 no.unifiedorder.param.invalid ③ cursor 非法返回 no.unifiedorder.cursor.invalid ④ 正常请求透传 Service 结果(2026-09-21 红→绿;加坏 token 防御用例)

Implementation for User Story 1

  • T008 新建 ruoyi-admin/src/main/java/com/ruoyi/app/unifiedorder/dto/UnifiedOrderListItem.java(字段按 data-model.md §1:公共块 + takeaway 块 + flash 块,Lombok @Data)(2026-09-21 完成;另加 dto/UnifiedOrderPageView.java 作分页信封——plan 文件数"约 6"内允许)
  • T009 新建 ruoyi-admin/src/main/java/com/ruoyi/app/unifiedorder/UnifiedOrderQueryService.java:两路 MP new Page<>(1, size+1, false) 查询(外卖 PosOrderMapper:userId+tab 条件+游标推进+cretim DESC,id DESC;闪送 FlashDeliveryOrderMapper:同构)→ Java 按 UnifiedCursor 全序归并 → 截取 size → DTO 组装(外卖批量回填 storeName:收集 shId 批量查 InfoUser 昵称)→ unifiedStatus 用 UnifiedOrderTab.matches 判定(all 查询下也要给)(2026-09-21 完成;UnifiedCursor 补 equals/hashCode 值语义)
  • T010 新建 ruoyi-admin/src/main/java/com/ruoyi/app/unifiedorder/UnifiedOrderQueryController.java:GET /system/unifiedOrder/list,@RequestHeader String token + 显式 @RequestParam(tab/source/cursor/size);userId 解析方式复用 UserOrderController.orderList 现有 token 解析实现;参数校验用 MessageUtils 错误 key(2026-09-21 完成;@Auth+@Anonymous 照抄 orderList,坏 token 返回参数错误而非 500 崩溃)
  • T011 转绿:UnifiedOrder*Test 全绿;mvn -pl ruoyi-admin -am test -Dtest='UnifiedOrder*' 通过(2026-09-21 35/35 绿)

Checkpoint: MVP 可演示(全 tab 混排滚动 + 来源筛选,用测试数据)


Phase 4: User Story 2 - tab 归类与 SQL 条件一致性 (Priority: P2)

Goal: 6 tab 归类语义与现有外卖列表一致,且 SQL 条件与内存判定不漂移

Independent Test: 谓词边界样本全量 + wrapper SQL 片段断言

  • T012 TDD 先红:在 UnifiedOrderTabTest 追加 wrapper 断言用例:对每个 tab 断言 posOrderWrapper().getCustomSqlSegment() / flashWrapper().getCustomSqlSegment() 含预期条件片段(如 unpaid 含 pay_status 与 pay_type IN、refund 含 after_sale_status、闪送 active 含四个状态枚举值),防止 SQL 与 matches() 漂移(2026-09-21 完成,用 getSqlSegment() 求片段;注意 MP paramNameValuePairs 为懒填充,须先 getSqlSegment() 再读参数)
  • T013 若 T012 发现 wrapper 片段与预期不符,修正 UnifiedOrderTab.java 条件构造(常量同源),转绿(无需修正:全部片段断言一次通过,零漂移)
  • T014 在 UnifiedOrderQueryServiceTest 追加组合用例:tab×source 组合抽查(refund+flash 空路、active+takeaway 只外卖)转绿(2026-09-21 通过)

Checkpoint: 归类语义有双保险(内存判定 + SQL 片段断言)


Phase 5: User Story 3 - 闪送收件人视角 (Priority: P3)

Goal: "我收的"闪送单进统一列表并带角色标记

Independent Test: mock 断言闪送路 wrapper 含 (user_id = ? OR receiver_user_id = ?),role 判定正确

  • T015 TDD 先红:UnifiedOrderQueryServiceTest 追加:① 闪送路查询条件含 receiver_user_id OR(捕获 wrapper 断言)② A 寄 B 的单在 B 的查询结果中 role=receiver、A 的结果中 role=sender ③ user_id=receiver_user_id 同一人时结果仅一条且 role=sender ④ 渲染块含 role 字段(2026-09-21 红:临时剥离 OR/role 后 2 用例如期失败,self-send 基线通过)
  • T016 实现:闪送路查询条件加 receiver_user_id OR;UnifiedOrderQueryService 组装 flash 项时判定 role(user_id 命中优先 sender)→ 转绿(2026-09-21 复原转绿,48/48)

Checkpoint: 三条故事全部可独立验收


Phase 6: Polish & Cross-Cutting Concerns

  • T017 按 data-model.md §5 在 updatesql/sql.md 追加三条索引语句(标注"核实后执行:已存在则跳过",不直接执行)(2026-09-21 追加,CRLF 保持)
  • T018 契约一致性核对:逐条比对 contracts/api-contract.md(接口/返回/字段块/tab 语义/错误)与实现,偏差改实现或改契约注明(2026-09-21 全对齐;3 处文档偏差已回写:data-model §4 去掉 del_flag(实体无此字段且现有列表不过滤,保持零漂移)、plan.md 测试文件名对齐 UnifiedOrderTabTest、plan.md 补 UnifiedOrderPageView)
  • T019 全量验证:mvn -pl ruoyi-admin -am test -Dtest='UnifiedOrder*' 全绿 + mvn -pl ruoyi-admin -am test 全模块(旧接口零回归,基线红除外)+ compile 通过 + quickstart 手动场景核对;确认未触碰废弃代码清单文件(2026-09-21:UnifiedOrder* 48/48 绿;全模块 552 测试唯一失败=已知基线红 createRejectsUnsupportedPayType,零新增失败;未修改任何既有 Java 文件;quickstart 手动 curl 场景待开发者起服务后核对)
  • T020 收尾:git status 对照 plan.md Project Structure 一致;交付说明列明:索引核实事项、契约文档移交前端、旧接口保留说明(2026-09-21 结构一致:7 主文件 + 4 测试 + i18n +2 行×6 + sql.md 追加)

Phase 7: 追加需求(2026-09-21)——外卖卡片店铺信息修正

  • T021 外卖 storeName 修正为店铺名称:UnifiedOrderQueryService 批量回填由「shId 查 InfoUser 昵称」改为「mdId 查 PosStore.posName」,店铺已删/缺失时回退订单快照 posName/logo;新增 storeAvatar(PosStore.logo,同回退)、goodsCount(food 快照数组 number 求和,空/非法为 null);DTO/测试/api-contract/data-model 同步
  • T022 ACTIVE 谓词线下收款分支扩入现金:UnifiedOrderTab payType='1' → IN ('1','4')(SQL 与 matches 同源,常量改 OFFLINE_PAYABLE_PAY_TYPES),配合堂食/自取开放用户现金下单(027 spec 2026-09-21 变更记录,commit 31bb384);测试补 payType=4 判定与 wrapper 断言

Phase 8: 追加需求(2026-09-21)——来源筛选扩堂食/自取

  • T023 source 扩枚举 dinein(堂食 type=2)/pickup(自取 type=1),takeaway 收窄为仅外送单(type=0),all 不限 type 照常混排:OrderSource 增枚举值+posOrderType;UnifiedOrderQueryService 外卖路按查询源加 type 过滤、游标推进源随查询源(dinein/pickup 单选翻页同秒 id 决胜)、单选跳过闪送路;游标编码单选时为 dinein/pickup,混排仍只产生 takeaway/flash;测试补 type 谓词/路由/游标推进;api-contract/data-model/quickstart 同步
  • T024 闪送条目返回 orderVersion(乐观锁版本,取 flash_delivery_order.version,语义同 FlashDeliveryUserOrderListView):DTO flash 块加字段、flashItem 赋值;测试补 orderVersion 断言;api-contract/data-model 同步

Dependencies & Execution Order

Phase Dependencies

  • Phase 1:无依赖
  • Phase 2:依赖 Phase 1;阻塞所有故事(cursor/谓词是地基)
  • US1 (Phase 3):依赖 Phase 2;US2/US3 都在其 Service 之上追加测试与实现
  • US2 (Phase 4):依赖 Phase 2(谓词);与 US1 的 Service 部分耦合(wrapper 断言)
  • US3 (Phase 5):依赖 Phase 3 的 Service 存在
  • Polish (Phase 6):依赖全部故事完成

单人执行顺序(推荐)

T001 → T002 → T003/T004 → T005 → T006-T011(US1 一体)→ T012-T014(US2)→ T015-T016(US3)→ T017-T020

Parallel Opportunities

  • Phase 2 内 T004(cursor 文件)与 T005(tab 文件)不同文件可并行
  • T006 与 T007(两个测试文件)可并行写

Implementation Strategy

MVP First (Phase 1-3)

数据层纯逻辑件 + Service/Controller 主链路完成后,统一列表已可端到端演示(全部 tab + 来源筛选 + 滚动)。

Incremental Delivery

  1. Phase 2 → 纯逻辑件(可先行 review)
  2. Phase 3 → MVP 混排列表
  3. Phase 4 → 归类双保险
  4. Phase 5 → 收件视角补齐
  5. Phase 6 → 索引/契约/回归/交付

Notes

  • 所有新文件 Lombok + 现有注释风格;保留 CRLF;Java 注释无 */ 序列
  • 不修改任何既有订单写路径与旧列表接口(FR-008 回归靠现有测试守护)
  • 错误消息一律 MessageUtils;tab/source 枚举校验在 Controller 层
  • 闪送路 wrapper 的 OR 条件用 MP and(w -> w.eq(...).or().eq(...)) 嵌套,注意与 tab 条件的括号层级