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.
[ID] [P?] [Story] Descriptionexport 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 无关,不修。
mvn -pl ruoyi-admin -am compile 通过;git status 工作区干净(test 分支同步状态)(2026-09-21 编译通过;工作区仅本 feature 文档未提交,代码零改动)Purpose: 游标、来源枚举、tab 谓词——三个用户故事共同依赖的纯逻辑件(全部可独立单测)
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 单次编码)ruoyi-admin/src/test/java/com/ruoyi/app/unifiedorder/UnifiedCursorTest.java——① 生成与解析往返一致 ② 非法输入(null/垃圾串/负数/越界)抛参数异常 ③ 排序全序比较器(时间 DESC → flash<takeaway → id DESC)④ 各源推进条件布尔(本源序 vs cursor 源序)判定正确(2026-09-21 编译失败红→绿)ruoyi-admin/src/main/java/com/ruoyi/app/unifiedorder/OrderSource.java(takeaway/flash 枚举 + 字典序)与 UnifiedCursor.java(生成/解析/校验/比较器/各源推进条件),使 T003 转绿(2026-09-21 UnifiedCursorTest 7/7 绿)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 可并行开始
Goal: 新接口 /system/unifiedOrder/list 两路查询 + 归并 + 游标滚动 + 来源筛选 + 统一卡片
Independent Test: 构造混排样本(不依赖 tab 语义细节),断言倒序混排、翻页完整、hasMore/nextCursor 正确
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,另加店铺名批量回填用例)UnifiedOrderQueryControllerTest.java:① 反射断言 @GetMapping("/list") + 每参数 @RequestParam + token @RequestHeader(Controller 规范) ② tab/source 非法值返回 no.unifiedorder.param.invalid ③ cursor 非法返回 no.unifiedorder.cursor.invalid ④ 正常请求透传 Service 结果(2026-09-21 红→绿;加坏 token 防御用例)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"内允许)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 值语义)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 崩溃)UnifiedOrder*Test 全绿;mvn -pl ruoyi-admin -am test -Dtest='UnifiedOrder*' 通过(2026-09-21 35/35 绿)Checkpoint: MVP 可演示(全 tab 混排滚动 + 来源筛选,用测试数据)
Goal: 6 tab 归类语义与现有外卖列表一致,且 SQL 条件与内存判定不漂移
Independent Test: 谓词边界样本全量 + wrapper SQL 片段断言
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() 再读参数)UnifiedOrderTab.java 条件构造(常量同源),转绿(无需修正:全部片段断言一次通过,零漂移)UnifiedOrderQueryServiceTest 追加组合用例:tab×source 组合抽查(refund+flash 空路、active+takeaway 只外卖)转绿(2026-09-21 通过)Checkpoint: 归类语义有双保险(内存判定 + SQL 片段断言)
Goal: "我收的"闪送单进统一列表并带角色标记
Independent Test: mock 断言闪送路 wrapper 含 (user_id = ? OR receiver_user_id = ?),role 判定正确
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 基线通过)receiver_user_id OR;UnifiedOrderQueryService 组装 flash 项时判定 role(user_id 命中优先 sender)→ 转绿(2026-09-21 复原转绿,48/48)Checkpoint: 三条故事全部可独立验收
updatesql/sql.md 追加三条索引语句(标注"核实后执行:已存在则跳过",不直接执行)(2026-09-21 追加,CRLF 保持)contracts/api-contract.md(接口/返回/字段块/tab 语义/错误)与实现,偏差改实现或改契约注明(2026-09-21 全对齐;3 处文档偏差已回写:data-model §4 去掉 del_flag(实体无此字段且现有列表不过滤,保持零漂移)、plan.md 测试文件名对齐 UnifiedOrderTabTest、plan.md 补 UnifiedOrderPageView)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 场景待开发者起服务后核对)git status 对照 plan.md Project Structure 一致;交付说明列明:索引核实事项、契约文档移交前端、旧接口保留说明(2026-09-21 结构一致:7 主文件 + 4 测试 + i18n +2 行×6 + sql.md 追加)storeName 修正为店铺名称:UnifiedOrderQueryService 批量回填由「shId 查 InfoUser 昵称」改为「mdId 查 PosStore.posName」,店铺已删/缺失时回退订单快照 posName/logo;新增 storeAvatar(PosStore.logo,同回退)、goodsCount(food 快照数组 number 求和,空/非法为 null);DTO/测试/api-contract/data-model 同步UnifiedOrderTab payType='1' → IN ('1','4')(SQL 与 matches 同源,常量改 OFFLINE_PAYABLE_PAY_TYPES),配合堂食/自取开放用户现金下单(027 spec 2026-09-21 变更记录,commit 31bb384);测试补 payType=4 判定与 wrapper 断言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 同步orderVersion(乐观锁版本,取 flash_delivery_order.version,语义同 FlashDeliveryUserOrderListView):DTO flash 块加字段、flashItem 赋值;测试补 orderVersion 断言;api-contract/data-model 同步T001 → T002 → T003/T004 → T005 → T006-T011(US1 一体)→ T012-T014(US2)→ T015-T016(US3)→ T017-T020
数据层纯逻辑件 + Service/Controller 主链路完成后,统一列表已可端到端演示(全部 tab + 来源筛选 + 滚动)。
*/ 序列and(w -> w.eq(...).or().eq(...)) 嵌套,注意与 tab 条件的括号层级