Feature Specification: 用户端统一订单列表(外卖+闪送合并)
Feature Branch: 030-unified-order-list
Created: 2026-09-20
Status: Draft
Input: User description: "用户端订单列表现在是外卖订单和闪送订单分开的。现在需要做到一起,都放到同样的列表中。列表分为全部 待付款 进行中 已完成 已取消 退款/售后;要求也能外卖、闪送筛选。"(方案经头脑风暴确认为 Java 读时聚合:不用中间表、不用视图、零数据库结构变更)
User Scenarios & Testing (mandatory)
User Story 1 - 统一列表浏览与混排 (Priority: P1)
用户打开订单页,看到的不再需要区分"外卖订单""闪送订单"两个入口,而是一个按下单时间倒序混排的统一列表:外卖单和闪送单交替出现,各自用现有的卡片样式渲染(外卖卡显示店铺/商品/配送信息,闪送卡显示取收地址/物品/小费)。列表支持无限滚动加载;顶部支持来源筛选(全部/外卖/堂食/自取/闪送,2026-09-21 扩堂食自取:外卖=仅外送单)。点击任意一单跳转到该单现有的详情页。
Why this priority: 这是功能的核心价值——"像淘宝京东一样所有订单一个列表";没有它其余无意义。
Independent Test: 构造同一用户名下不同时间的外卖单与闪送单,验证混排顺序严格按时间倒序、两种卡片字段齐全、来源筛选生效、滚动到底 hasMore=false。
Acceptance Scenarios:
- Given 用户名下同时存在外卖单与闪送单,When 打开统一列表(tab=全部,来源=全部),Then 两类订单按下单时间倒序混排,卡片按来源用各自样式渲染
- Given 滚动到列表底部,When 继续上拉,When 服务端按游标返回下一页,Then 所有订单在全部翻页中恰好出现一次(不漏不重)
- Given 来源筛选选择"外卖"(或"堂食"/"自取"/"闪送"),When 查询,Then 仅返回该来源订单(外卖=外送单不含堂食自取、堂食/自取反之),滚动分页同样完整
- Given 点击列表项,When 前端跳转,Then 凭列表提供的订单键直达该来源现有详情页
User Story 2 - 六个状态 tab 的正确归类 (Priority: P2)
列表提供 6 个 tab:全部 / 待付款 / 进行中 / 已完成 / 已取消 / 退款·售后。归类规则:外卖沿用现有列表的判定语义(在线支付未付=待付款;履约中=进行中;state=3=已完成;state=4=已取消;有售后记录=退款·售后);闪送按状态机映射(非终态=进行中;已完成=已完成;已取消=已取消;闪送无待付款、无售后,这两个 tab 天然只含外卖单)。
Why this priority: tab 是用户找单的主要手段;语义与现有列表不一致会造成"这边看得到那边看不到"的困惑。
Independent Test: 构造覆盖每个 tab 的两域订单样本(含货到付款单、售后中的单、闪送各状态单),逐 tab 断言归属。
Acceptance Scenarios:
- Given 外卖在线支付单已创建未支付,When 查待付款 tab,Then 该单出现;闪送单永不出现于此 tab
- Given 闪送单处于待接单/已接单/已取件/已送达任一状态,When 查进行中 tab,Then 该单出现;已完成的闪送单只出现在已完成 tab
- Given 外卖单处于售后流程(售后标记>0),When 查进行中/已完成/已取消 tab,Then 该单均不出现,仅出现在退款·售后 tab
- Given 货到付款的外卖单(创建即视为已付),When 查待付款 tab,Then 不出现(沿用现有语义:待付款仅限在线支付未付)
User Story 3 - 闪送"我收的"订单进列表 (Priority: P3)
闪送存在收件人视角(现有闪送列表有"我发的/我收的")。统一列表将"我收的"闪送单一并纳入:同一闪送单会出现在寄件人与收件人两个账号的列表中,列表项携带角色标记(寄件/收件),前端据此渲染"我收的"标签。用户自己寄给自己的单只出现一条(判寄件人角色)。
Why this priority: 增量视角,保证统一列表后用户不丢失原闪送列表的"我收的"能力;不阻塞主流程。
Independent Test: 构造 A 寄给 B 的闪送单,验证 A、B 两账号的统一列表均含该单且角色标记分别为寄件、收件;构造 A 寄给 A 的单,验证 A 列表仅一条且角色为寄件。
Acceptance Scenarios:
- Given 闪送单寄件人=A 收件人=B,When B 查统一列表,Then 该单出现且角色标记=收件
- Given 同上,When A 查统一列表,Then 该单出现且角色标记=寄件
- Given 闪送单寄件人=收件人=A,When A 查统一列表,Then 该单仅出现一次,角色标记=寄件
Edge Cases
- 滚动加载的同秒跨源并列:外卖单与闪送单下单时间同一秒时,排序必须有确定规则(来源序+单ID决胜),翻页不漏不重。
- 待付款/退款·售后 tab + 闪送:闪送侧恒为空集(正确语义,非错误);查询实现上应直接跳过闪送路,不发无效查询。
- 游标参数非法(格式错误/伪造):返回明确的参数错误,不按首页静默处理(避免用户端死循环)。
- 每页条数:缺省 10,服务端钳制 1-50,超范围取边界值。
- 售后中的外卖单:从进行中/已完成/已取消 tab 剔除,只归退款·售后(沿用现有列表语义)。
- 空列表用户:新用户无任何订单,各 tab 返回空列表 + hasMore=false,不报错。
- 旧接口回归:现有外卖列表接口、闪送列表接口(其它页面仍在用)行为完全不变。
- 列表项与详情页一致性:列表提供的订单键(外卖业务单号 / 闪送订单ID)必须能直达现有详情接口。
Requirements (mandatory)
Functional Requirements
- FR-001: 系统 MUST 提供统一订单列表查询接口:登录用户按下单时间倒序查看名下全部外卖与闪送订单的混排结果,支持无限滚动(游标分页:请求携带上一页返回的 cursor,响应含 list/hasMore/nextCursor)。
- FR-002: 列表 MUST 提供 6 个 tab 筛选(all/unpaid/active/completed/cancelled/refund,枚举名与现有外卖列表一致)且归类满足:外卖沿用现有判定(在线支付未付且无售后→待付款;履约中且无售后→进行中;已完成且无售后→已完成;已取消且无售后→已取消;售后标记>0→退款·售后);闪送按状态映射(非终态→进行中;已完成→已完成;已取消→已取消;待付款/退款·售后 tab 闪送侧为空)。
- FR-003: 列表 MUST 支持来源筛选(all/takeaway/dinein/pickup/flash 五值;2026-09-21 扩堂食/自取:takeaway=仅外送单 type=0、dinein=堂食 type=2、pickup=自取 type=1、all 不限 type 混排),与 tab 可自由组合。
- FR-004: 游标分页 MUST 保证翻页完整性:全部翻页遍历中每个订单恰好出现一次,含同秒跨源并列场景(确定性决胜规则)。
- FR-005: 统一列表项 MUST 携带:来源类型、角色标记(仅闪送:寄件/收件)、订单标识与业务单号(详情跳转键)、统一状态(tab 同款枚举)、下单时间、金额与币种、售后标记,以及两个来源各自的卡片渲染字段块(外卖块含订单类型/状态族原值/店铺名等;闪送块对齐现有闪送列表卡片字段子集)。
- FR-006: 闪送订单 MUST 同时按寄件人与收件人视角纳入列表并以角色字段区分;寄件人=收件人的单仅出现一条(判寄件人)。
- FR-007: 外卖 tab 判定规则 MUST 与现有外卖列表接口语义一致(同一谓词来源,避免两处规则漂移)。
- FR-008: 现有外卖列表接口与闪送列表接口 MUST 保持行为完全不变(零回归)。
- FR-009: 本功能 MUST NOT 引入任何数据库结构变更(不建中间表、不建视图);上线前仅核实既有索引(外卖:用户+下单时间;闪送:寄件人+下单时间、收件人)是否覆盖查询,若缺按《数据库变更管理》流程补。
- FR-010: 服务层自动化测试 MUST 覆盖:tab 归类(两域×6 tab×边界样本)、游标归并与翻页完整性(含同秒并列)、接口参数契约。
- FR-011: 单次滚动请求 MUST 在每个来源至多一次索引查询内完成(两路各取一页归并);待付款/退款·售后 tab MUST NOT 发起闪送查询。
Key Entities (include if feature involves data)
- 统一列表项(组合视图,无新表):公共字段(来源类型/角色/订单标识/业务单号/统一状态/时间/金额/币种/售后标记)+ 外卖渲染块 + 闪送渲染块。只读组合,数据仍存于两张源表。
- 游标(Cursor):
下单时间_来源_单ID 不透明字符串,服务端生成、前端透传,用于严格 keyset 分页。
- tab 归类谓词(表驱动):同一份规则既生成数据库查询条件又做内存判定,是归类语义的唯一事实源。
- 复用实体(只读):外卖订单(含订单状态/配送状态/支付状态/售后标记四字段族)、闪送订单(状态机六态 + 角色两字段)。本功能不修改任何源表数据与写入路径。
Success Criteria (mandatory)
Measurable Outcomes
- SC-001: 用户在单一列表按时间倒序看到全部外卖与闪送订单,滚动到底后无遗漏(与两域订单总数一致)。
- SC-002: 任一 tab 与来源筛选组合下,翻页遍历每个订单恰好出现一次(自动化用例验证)。
- SC-003: 混排正确性可验证:返回列表内任意相邻两项的下单时间非递增。
- SC-004: 现有外卖/闪送两个列表接口回归零差异(原测试全绿 + 行为对照)。
- SC-005: 单次滚动请求的耗时与现有单域列表查询处于同一量级,不因合并明显变慢。
- SC-006: 全部新增自动化测试通过,覆盖 SC-002/SC-003 的可判定断言。
Assumptions
- 前端为滚动加载(已确认),接口只提供游标分页,不提供页码模式;首页请求不带 cursor。
- tab 枚举与来源枚举返回 code,展示文案由前端 i18n 渲染(与现有列表一致的分工)。
- 卡片渲染复用现有两种样式,后端字段块与现有列表接口返回等价信息(含店铺名批量回填)。
- 不做 tab 数量角标(本期未提出;后续可按需增加计数接口)。
- 闪送无支付状态与售后概念(spec 024 FR-058),故待付款/退款·售后两 tab 闪送侧为空是正确行为。
- 商家端、骑手端订单列表不在本功能范围;统一列表只服务用户端 App。
- 架构决策(头脑风暴已确认):读时聚合,否决中间表(双写一致性风险、写入点过多)与数据库视图(状态映射埋 SQL 不可测、变更需人工 DDL);将来若演进为订单搜索服务,仅替换读实现,接口契约不变。