# Implementation Plan: 用户端统一订单列表 **Branch**: `030-unified-order-list` | **Date**: 2026-09-20 | **Spec**: [spec.md](spec.md) **Input**: Feature specification from `/specs/030-unified-order-list/spec.md` ## Summary 用户端外卖(pos_order)与闪送(flash_delivery_order)订单合并为一个统一列表:Java 读时聚合(否决中间表/视图),新包 `com.ruoyi.app.unifiedorder` 提供独立接口 `GET /system/unifiedOrder/list`,支持 6 tab(沿用外卖现有枚举 all/unpaid/active/completed/cancelled/refund)+ 来源筛选(all/takeaway/dinein/pickup/flash,2026-09-21 扩堂食/自取:takeaway=type0 仅外送、dinein=type2、pickup=type1)+ 游标滚动分页(严格 keyset,同秒跨源决胜:source 字典序 + id DESC)。两路各查 `size+1` 条内存归并;闪送路含收件人视角(`user_id OR receiver_user_id`,role 字段区分);unpaid/refund tab 跳过闪送路。tab 归类做成表驱动谓词(同一份规则生成 SQL 条件与内存判定)。**零数据库结构变更**(唯一条件项:核实/补两表查询索引)。 ## Technical Context **Language/Version**: Java 21(Maven 多模块:ruoyi-admin / ruoyi-system / ruoyi-common) **Primary Dependencies**: Spring Boot、MyBatis-Plus(分页用 `Page<>` 构造,游标模式下 searchCount=false)、Lombok;复用现有 `PosOrderMapper` / `FlashDeliveryOrderMapper`(只读,不新增写路径) **Storage**: 无新表无视图。只读两张源表:`pos_order`(时间字段 `cretim`,Date)、`flash_delivery_order`(时间字段 `create_time`,Date;两表均为秒级 datetime,同秒并列真实存在,决胜规则必须确定性)。条件项:`pos_order(user_id, cretim)`、`flash_delivery_order(user_id, create_time)` 与 `(receiver_user_id)` 索引上线前核实,缺则按流程补(updatesql/sql.md) **Testing**: JUnit 5 + Mockito,沿用 029 建立的 SpringUtils 静态桩 + MockHttpServletRequest 模式;测试位于 `ruoyi-admin/src/test/java/com/ruoyi/app/unifiedorder/`。构建:`JAVA_HOME=%USERPROFILE%\.jdks\graalvm-jdk-21.0.12+7.1` + `mvn -pl ruoyi-admin -am test -Dtest='Xxx' -Dsurefire.failIfNoSpecifiedTests=false` **Target Platform**: Linux 服务器 Spring Boot(越南用户端 uni-app 的后端) **Project Type**: web-service(单体多模块 Maven) **Performance Goals**: 单次滚动请求每源至多一次索引查询(各取 size+1 行,无 count 查询);耗时与现有单域列表同量级 **Constraints**: Controller 参数规范(GET 全 @RequestParam + @RequestHeader token);不触碰 CLAUDE.md 废弃代码;现有两个列表接口零改动;保留 CRLF **Scale/Scope**: 新包约 6 个主文件 + 3 个测试文件 + 1 条条件性索引 SQL;不动任何既有订单写路径 ## Constitution Check *GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.* `.specify/memory/constitution.md` 为空模板;以 CLAUDE.md 项目规范替代核对: | 约束 | 结果 | |---|---| | Controller 请求参数规范(GET 显式 @RequestParam、token @RequestHeader、无 Map 入参) | PASS | | 模块依赖方向 ruoyi-admin → ruoyi-system | PASS — 新包在 ruoyi-admin,仅用 ruoyi-system 实体/Mapper | | 数据库变更管理(不直接执行,写 sql.md) | PASS — 仅条件性索引补充 | | 废弃代码清单不触碰 | PASS — 只读现有表,不参考 PayController/TestTask 等 | | Java 注释安全 | PASS | | 后端错误消息走 MessageUtils | PASS — 非法 cursor 等错误用消息 key | Phase 1 设计后复核:全部 PASS,无违规项。 ## Project Structure ### Documentation (this feature) ```text specs/030-unified-order-list/ ├── plan.md # This file ├── research.md # Phase 0 output ├── data-model.md # Phase 1 output(DTO/cursor/谓词规则) ├── quickstart.md # Phase 1 output ├── contracts/ │ └── api-contract.md # 前端契约 └── tasks.md # Phase 2 output (/speckit-tasks) ``` ### Source Code (repository root) ```text ruoyi-admin/src/main/java/com/ruoyi/app/unifiedorder/ ├── UnifiedOrderQueryController.java # GET /system/unifiedOrder/list ├── UnifiedOrderQueryService.java # 两路查询 + 归并 + 游标推进 + 店铺名批量回填 ├── UnifiedOrderTab.java # tab 枚举 + 表驱动谓词(wrapper 条件构造 & 实体内存判定同源) ├── OrderSource.java # takeaway/dinein/pickup/flash 枚举(字典序决胜依据;dinein/pickup 为 pos_order type 子集) ├── UnifiedCursor.java # 游标:生成/解析/校验(时间_source_id) └── dto/ ├── UnifiedOrderListItem.java # 公共字段 + takeaway 渲染块 + flash 渲染块 └── UnifiedOrderPageView.java # 分页信封:list/hasMore/nextCursor ruoyi-admin/src/test/java/com/ruoyi/app/unifiedorder/ ├── UnifiedOrderTabTest.java # 两域×6 tab×边界样本归类 + wrapper SQL 片段断言 ├── UnifiedCursorTest.java # 生成/解析/非法输入/同秒并列 ├── UnifiedOrderQueryServiceTest.java # 归并排序、翻页完整性、hasMore、tab 跳过闪送路、role 判定 └── UnifiedOrderQueryControllerTest.java # 参数契约 + 组合查询 updatesql/sql.md # 条件性索引补充(标注"核实后执行") (复用只读)ruoyi-system: PosOrderMapper / FlashDeliveryOrderMapper / InfoUser(店铺名回填) ``` **Structure Decision**: 新功能独立成包(不向 700+ 行的 UserOrderController 继续堆叠);两源表的查询直接走各自 MP Mapper(游标模式下 `new Page<>(1, limit, false)` 固定第一页,天然无 count 开销);tab 谓词集中在一个枚举类里同时服务 SQL 构造与内存判定(唯一事实源,FR-007)。 ## Complexity Tracking > **Fill ONLY if Constitution Check has violations that must be justified** 无违规项。