# Data Model: 用户端统一订单列表 **Date**: 2026-09-20 | **Spec**: [spec.md](spec.md) > 本功能**无新表、无视图、无字段变更**。本文定义组合视图结构、游标格式与 tab 归类谓词的权威规则。 ## 1. 统一列表项 UnifiedOrderListItem(DTO,无落库) ### 公共字段 | 字段 | 类型 | 说明 | |---|---|---| | sourceType | String | `takeaway` / `flash` | | role | String | 仅闪送有意义:`sender` 寄件 / `receiver` 收件;外卖恒 `sender` | | orderId | Long | 源表主键(两表自增 ID 数值可能重叠,必须与 sourceType 联用) | | orderNo | String | 外卖=业务单号 ddId;闪送=orderNo(FD 前缀)——详情跳转键 | | unifiedStatus | String | 归属 tab 同款枚举(unpaid/active/completed/cancelled/refund;all 查询下返回该单实际归属的 tab 值) | | createTime | Long | 下单时间毫秒时间戳(排序与游标基准) | | amount | Long | 订单金额(外卖取现有列表展示金额字段,实现时对齐;闪送 amount) | | currency | String | 币种(闪送 VND;外卖按现有返回,可为空) | | hasAfterSale | Boolean | 外卖售后标记(afterSaleStatus>0);闪送恒 false | ### 外卖渲染块(takeaway,前端复用现有外卖卡片) `type`(0外送/1自取/2堂食)、`state`(0-4)、`deliveryStatus`(0-3,仅外送)、`payStatus`(0/1/2)、`payType`(1-6)、`afterSaleStatus`(0-6)、`storeName`(按 mdId 批量查 pos_store.posName,店铺已删回退订单快照 posName)、`storeAvatar`(pos_store.logo,同回退)、`goodsCount`(food 快照数组 number 求和)、`collectPayment`(到付标记)——数值原值透传,文案由前端渲染。 ### 闪送渲染块(flash,对齐 FlashDeliveryUserOrderListView 字段子集) `status`(六态枚举原值)、`orderVersion`(乐观锁版本,编辑待接单订单须回传)、`serviceType`、`deliveryType`、`vehicleType`、`deliveryMode`+`scheduledPickupStartAt/scheduledPickupEndAt`、`pickupAddress/pickupDetailAddress`、`deliveryAddress/deliveryDetailAddress`、`packageType`、`quantity`、`tipAmount`、`amount`。 ## 2. 游标 UnifiedCursor - **格式**:`{createTimeMillis}_{sourceType}_{orderId}`,如 `1726812345000_flash_1024`。服务端生成,前端视为不透明字符串透传。 - **排序全序**(归并与游标推进的同一规则): 1. `createTime DESC` 2. 同刻:`sourceType` 字典序 ASC(`dinein` < `flash` < `pickup` < `takeaway`) 3. 同刻同源:`orderId DESC` - **来源编码**:混排查询(source=all)的行与游标只有 `takeaway`/`flash`;`dinein`/`pickup` 仅在来源单选查询的游标中出现(堂食/自取是 pos_order.type 子集,单选时同源翻页同秒 id 决胜照常生效)。 - **推进条件**(每源 SQL):本源取 `(t < T) OR (t = T AND 本源序 > cursor源序) OR (t = T AND 本源 = cursor源 AND id < cursorId)`,其中"本源序 > cursor 源序"在单源 SQL 中退化为常量布尔(实现封装在 UnifiedCursor,禁止散落拼接)。 - **校验**:格式非法/数值越界 → 参数错误(消息 key),不静默当首页。 - **有效期**:游标仅是位置标记,无过期概念;源数据变化导致的位置偏移属正常现象(新下单会出现在首页,不影响旧游标正确性)。 ## 3. tab 归类谓词(唯一事实源,SQL 条件与内存判定同源) ### 外卖(pos_order)——照搬 `UserOrderController.orderList:555-582` 现行条件 | tab | 条件 | |---|---| | unpaid | `payStatus=0 AND payType IN (2,3) AND state≠4 AND afterSaleStatus=0`(仅在线支付未付) | | active | `state IN (0,1,2) AND afterSaleStatus=0 AND (payStatus=1 OR (payType IN ('1','4') AND type IN (1,2)))`(2026-09-21:payType 扩入现金 4,随堂食/自取现金下单开放) | | completed | `state=3 AND afterSaleStatus=0` | | cancelled | `state=4 AND afterSaleStatus=0` | | refund | `afterSaleStatus>0` | | all | 无条件(以上皆不过滤) | 不变量提醒(specs/018):payStatus=2 必然 state=4 且 afterSaleStatus=3——refund 条件天然覆盖已退款单。 ### 闪送(flash_delivery_order) | tab | 条件 | |---|---| | unpaid | —(恒空,查询层直接跳过闪送路) | | active | `status IN (WAITING_ACCEPTANCE, ACCEPTED, PICKED_UP, DELIVERED)`(非终态) | | completed | `status = COMPLETED` | | cancelled | `status = CANCELLED` | | refund | —(恒空,同上) | | all | 无条件 | ### 谓词实现约束 - `UnifiedOrderTab` 枚举内聚两域规则:`wrapperFor(posOrder)` / `matches(PosOrder)` 与 `wrapperFor(flash)` / `matches(FlashDeliveryOrder)`;SQL 构造与内存判定使用同一组常量,单测双向校验(构造出的条件查回的样本必须全部通过内存判定)。 - `unifiedStatus` 取值 = 该单在谓词下命中的 tab(all 查询时由内存判定得出)。 ## 4. 查询形态(每源,每次滚动) ```sql -- 外卖路(role 恒 sender) -- 注:PosOrder 实体无 del_flag 字段,且现有 orderList 亦不过滤,为保持行为零漂移不加该条件 SELECT ... FROM pos_order WHERE user_id = ? AND <来源type条件> AND AND <游标推进> ORDER BY cretim DESC, id DESC LIMIT size+1 -- 闪送路(含收件视角,OR 单查询天然去重) SELECT ... FROM flash_delivery_order WHERE (user_id = ? OR receiver_user_id = ?) AND AND <游标推进> ORDER BY create_time DESC, id DESC LIMIT size+1 ``` - 来源 type 条件(2026-09-21 堂食/自取筛选):`source=takeaway → type=0`(仅外送)、`dinein → type=2`、`pickup → type=1`;`source=all` 不加 type 条件(堂食/自取照常混排)。dinein/pickup 是外卖路子集,跳过闪送路。 - 游标推进中的"同刻同源 id < cursorId"与源内 `ORDER BY ... id DESC` 对应;跨源同刻并列由 Java 归并的 source 字典序处理。 - `tab=unpaid/refund`:跳过闪送路;`source` 单选:跳过另一路。 - Java 归并:按全序(§2)取前 size 条,第 size+1 条存在 → `hasMore=true`;`nextCursor` = 末条(t, source, id)。 ## 5. 索引核实清单(上线前置,条件性 SQL,写入 updatesql/sql.md 不直接执行) ```sql -- 2026-09-20 统一订单列表查询索引(030-unified-order-list,核实后执行:已存在则跳过) ALTER TABLE pos_order ADD INDEX idx_user_cretim (user_id, cretim); ALTER TABLE flash_delivery_order ADD INDEX idx_user_ctime (user_id, create_time); ALTER TABLE flash_delivery_order ADD INDEX idx_receiver_ctime (receiver_user_id, create_time); ``` 开发者先在库上 `SHOW INDEX` 核实,缺哪条执行哪条。 ## 6. 错误消息 key(新增,多语言) | key | 用途 | |---|---| | `no.unifiedorder.cursor.invalid` | 游标格式非法 | | `no.unifiedorder.param.invalid` | tab/source 取值非法 |