spec.md 32 KB

006 - 订单状态流转设计(美团方案)

背景

系统新增自取订单(type=1)和堂食订单(type=2)后,订单不再需要骑手参与。现有 state 字段将支付状态、订单状态、配送状态、退款状态全部混在一起,导致自取/堂食订单无法直接复用。

旧状态定义(待废弃)

旧 state 含义 类别
0 未支付 支付
1 已付款 支付
2 已接单 订单
3 骑手接单 配送
4 配送中 配送
5 已完成 订单
6 申请退款 退款
7 同意退款 退款
8 拒绝退款 退款
9 客服接入 退款
10 作废 订单
11 售后完成 退款
12 送达 配送
13 退款处理中 退款

选定方案:全面重新设计,四字段分离

参考美团做法,将原来混在 state 中的四种关注点拆成四个独立字段:

  • state:订单生命周期(所有类型共用)
  • deliveryStatus:配送追踪(仅外送订单使用)
  • payStatus:支付状态(所有类型共用)
  • afterSaleStatus:售后/退款状态(独立于订单状态,参考美团做法)

同时废弃 diningStatus 字段,出餐动作由 state 状态变更表示。

新字段定义

state(订单状态)— 重新编号

新 state 含义 谁触发 说明
0 待处理 系统创建 订单已创建,等待商家接单
1 已接单 商家 商家已接单,备餐中
2 已出餐 商家 商家出餐完成
3 已完成 系统 外送:骑手送达;自取:用户确认取餐;堂食:备餐完成
4 已取消 系统/商家 作废/取消(全额退款、商家取消、系统超时取消)

state 只有 5 个值,只跟踪订单的核心生命周期。

deliveryStatus(配送状态)— 仅 type=0 外送订单

deliveryStatus 含义 谁触发
0 待接单 系统(商家出餐后设为0,等待骑手)
1 骑手已接单 骑手
2 配送中 骑手(取餐出发)
3 已送达 骑手

自取(type=1)和堂食(type=2)订单 deliveryStatus 为 NULL。

payStatus(支付状态)— 新增字段

payStatus 含义 说明
0 未支付 订单刚创建,等待付款
1 已支付 在线支付成功,或到付外送订单创建时直接设为1
2 已退款 订单退款完成
  • 外送到付(type=0, payType=1):创建时直接设为 payStatus=1(不需要等在线支付)
  • 自取/堂食现金(type=1或2):创建时 payStatus=0,商家收款并完成订单时同步设为 1
  • 在线支付(vnpay/zalopay/银行卡):创建时 payStatus=0,支付回调成功后改为 1
  • 退款完成afterSaleStatus=3(已退款)时,同步把 payStatus 改为 2

设计决策:自取/堂食现金订单 payStatus 在下单时为 0,商家收款+完成时才设为 1。原因:商家需要明确知道"现金是否已收到",收钱和完成是同一个动作。外送订单则不同,到付意味着骑手送货时收钱,所以创建时就确认支付方式。

afterSaleStatus(售后状态)— 新增字段,独立于订单状态

参考美团做法:退款申请本身不改变订单状态,只有全额退款成功才将订单设为已取消。

afterSaleStatus 含义 谁触发 说明
0 无售后 - 订单正常,无退款申请
1 申请中 用户 用户发起退款申请,等待商家处理
2 退款中 系统/商家 商家同意退款,退款正在处理
3 已退款 系统 退款完成,全额退款时同步设 state=4
4 退款拒绝 商家 商家拒绝退款,订单继续正常流转
5 客服介入 客服 客服正在处理争议
6 售后完成 系统 售后流程结束

废弃字段

  • diningStatus:不再使用。"出餐"动作由 state 从 1 变为 2 表示。

各类型状态流转

外送-在线支付 (type=0):
  state:          0 → 1 → 2 → 3
  deliveryStatus:          0 → 1 → 2 → 3
  payStatus:      0 → 1(支付回调)
  afterSaleStatus: 0(无售后时始终为0)
  骑手参与: 是(state 和 deliveryStatus 独立推进)
  完成触发: 骑手点"已送达"

外送-到付 (type=0, payType=1):
  state:          0 → 1 → 2 → 3
  deliveryStatus:          0 → 1 → 2 → 3
  payStatus:      1(创建时即确认,骑手送货时收现金)
  afterSaleStatus: 0
  骑手参与: 是
  完成触发: 骑手点"已送达"

自取-现金 (type=1):
  state:     0 → 1 → 2 → 3
  payStatus: 0 ──────────→ 1(商家收款+完成,同一操作)
  afterSaleStatus: 0
  骑手参与: 否(用户到店自取)
  deliveryStatus: 无
  完成触发: 商家点"完成"(收款+出餐)

堂食-现金 (type=2):
  state:     0 → 1 → 2 → 3
  payStatus: 0 ──────────→ 1(商家收款+完成,同一操作)
  afterSaleStatus: 0
  骑手参与: 否
  deliveryStatus: 无
  完成触发: 商家点"完成"(收款+出餐)

售后/退款流转(所有类型,独立状态机)

afterSaleStatus: 0(无售后) → 1(申请中) → 2(退款中) → 3(已退款)
                                         → 4(退款拒绝) → 5(客服介入) → 6(售后完成)

售后与订单状态的联动规则(参考美团)

售后动作 afterSaleStatus state payStatus deliveryStatus 骑手行为
用户申请退款 → 1(申请中) 不变 不变 不变 继续配送
商家同意退款(全额) → 2(退款中) → 4(已取消) → 2(已退款) 废弃 停止配送
商家同意退款(部分) → 3(已退款) 不变 视金额调整 不变 继续配送
商家拒绝退款 → 4(退款拒绝) 不变 不变 不变 继续配送
客服介入 → 5(客服介入) 不变 不变 不变 视客服裁决
用户撤销退款 → 0(无售后) 不变 不变 不变 继续配送

核心原则:退款申请本身不影响订单状态和配送,只有全额退款成功才取消订单。

state=4(已取消)的触发场景

场景 state afterSaleStatus 说明
商家主动取消 → 4 0 未付款或刚接单时商家取消
系统超时自动取消 → 4 0 商家超时未接单
全额退款成功 → 4 3(已退款) 退款完成后订单取消
用户未支付取消 → 4 0 订单超时未支付

旧值 → 新值 数据迁移映射

旧 state 旧含义 新 state 新 payStatus 新 deliveryStatus 新 afterSaleStatus
0 未支付 0 0 NULL 0
1 已付款 0 1 NULL 0
2 已接单 1 1 NULL 0
3 骑手接单 1 1 1 0
4 配送中 1 1 2 0
5 已完成 3 1 3(外送) 0
6 申请退款 1 1 NULL 1
7 同意退款 4 2 NULL 3
8 拒绝退款 1 1 NULL 4
9 客服接入 1 1 NULL 5
10 作废 4 0 NULL 0
11 售后完成 4 2 NULL 6
12 送达 2 1 3 0
13 退款处理中 1 1 NULL 2

注:旧退款状态(6,8,9)迁移时新 state 推算为 1(已接单),因为退款前订单大概率在已接单阶段。历史数据够用即可。

前端展示逻辑

商家端订单列表 Tab

商家端 Tab 筛选条件
待受理 state=0 AND (payStatus=1 OR type IN (1,2))
待出餐 state=1
已出餐 state=2 + afterSaleStatus=0
已完成 state=3 + afterSaleStatus=0
已取消 state=4 + afterSaleStatus=0
退款/售后 afterSaleStatus > 0

设计决策:待受理条件区分了外送和自取/堂食。外送订单必须 payStatus=1(已付款/到付确认)才出现在待受理;自取/堂食订单下单即可见(因为现金是线下收取的,不需要等在线支付)。

用户端订单列表 Tab

用户端 Tab 筛选条件
待付款 payStatus=0 AND type=0(仅外送在线支付订单)
进行中 state IN (0,1,2) AND afterSaleStatus=0 AND (payStatus=1 OR type IN (1,2))
已完成 state=3 + afterSaleStatus=0
已取消 state=4 + afterSaleStatus=0
退款/售后 afterSaleStatus > 0

设计决策:待付款仅显示外送未支付订单。自取/堂食现金订单的 payStatus=0 但不显示在"待付款"(用户无需在线付款),直接进入"进行中"Tab。用户端 Tab 名从"待收货"改为"进行中",因为自取/堂食不存在"收货"概念。

骑手端订单列表 Tab

骑手端 Tab 筛选条件
新任务 type=0 + deliveryStatus=0 + state=2 + afterSaleStatus=0
待取货 deliveryStatus=1 + qsId=当前骑手 + afterSaleStatus=0
配送中 deliveryStatus=2 + qsId=当前骑手 + afterSaleStatus=0
已完成 state=3 + afterSaleStatus=0 + qsId=当前骑手
已取消 state=4 + qsId=当前骑手 + afterSaleStatus=0
退款/售后 afterSaleStatus > 0 + qsId=当前骑手

前端展示给用户的文字(对齐美团精简方案)

用户只关心关键节点,多个内部状态合并为一个展示文字。前端根据字段组合判断,afterSaleStatus 优先。

外送 (type=0) 用户看到的文字

原来的文字 美团方案 对应字段条件
待付款 待支付 payStatus=0
待商家确认 + 商家备餐中 商家已接单 state=0/1, payStatus=1, afterSaleStatus=0
待骑手配送 + 骑手已接单 + 配送中 配送中 state=2, deliveryStatus=0/1/2
已送达 已送达 state=2, deliveryStatus=3
已完成 已完成 state=3
已取消 已取消 state=4, afterSaleStatus=0
退款申请中/退款中/已退款 退款中/已退款 afterSaleStatus=1/2/3

自取 (type=1) 用户看到的文字

美团方案 条件
待支付 payStatus=0
商家已接单 state=0/1
待取餐 state=2
已完成 state=3
已取消 state=4

堂食 (type=2) 用户看到的文字

美团方案 条件
待支付 payStatus=0
商家已接单 state=0/1
备餐完成 state=2
已完成 state=3
已取消 state=4

需要修改的代码

1. 数据库变更

-- 新增 delivery_status 字段
ALTER TABLE pos_order ADD COLUMN delivery_status BIGINT DEFAULT NULL COMMENT '配送状态:0待接单,1骑手已接单,2配送中,3已送达';

-- 新增 pay_status 字段
ALTER TABLE pos_order ADD COLUMN pay_status BIGINT DEFAULT 0 COMMENT '支付状态:0未支付,1已支付,2已退款';

-- 新增 after_sale_status 字段
ALTER TABLE pos_order ADD COLUMN after_sale_status BIGINT DEFAULT 0 COMMENT '售后状态:0无售后,1申请中,2退款中,3已退款,4退款拒绝,5客服介入,6售后完成';

-- 废弃 dining_status 字段
-- ALTER TABLE pos_order DROP COLUMN dining_status;  -- 视情况是否删除

2. 数据迁移

不需要数据迁移。系统未正式使用,历史订单数据由开发者手动清空。新字段(delivery_status, pay_status, after_sale_status)通过 ALTER TABLE 添加后,所有新订单直接使用新值。

注意:如果将来需要迁移历史数据,不能用逐步 UPDATE 的方式(会有值冲突 bug),必须用单条 CASE WHEN 语句原子更新。

3. PosOrder 实体类

  • 新增字段:deliveryStatus(Long)、payStatus(Long)、afterSaleStatus(Long)
  • 废弃但保留的字段diningStatusisAcceptedkefuStatekefuContentkefuRepeatrepeatDdId — 新逻辑不再使用,保留在实体中避免序列化问题
    • diningStatusstate 值变更替代(state=2 即出餐)
    • isAcceptedstate=1(已接单)替代
    • kefuState 等由 afterSaleStatus 替代(客服介入 = afterSaleStatus=5)

4. MyBatis Mapper(PosOrderMapper.xml)

  • resultMap 新增 delivery_statuspay_statusafter_sale_status 映射
  • insert/update 新增对应字段

5. PosOrderShOprateController(商家操作)

  • 接单state 从 0 改为 1
  • 出餐state 从 1 改为 2
    • 外送订单:额外设置 deliveryStatus=0(等待骑手接单)
    • 自取/堂食订单:只改 state=2
  • 完成(自取/堂食)state 从 2 改为 3,同时 payStatus 从 0 改为 1(收现金+完成,一个操作)
    • 仅限 type=1(自取)或 type=2(堂食)订单

6. PosOrderQsOprateController(骑手操作)

  • acceptOrder:校验 type=0 且 afterSaleStatus=0,设置 deliveryStatus=1
  • pickupOrder:校验 type=0 且 afterSaleStatus=0,设置 deliveryStatus=2
  • deliverOrder:校验 type=0,设置 deliveryStatus=3,同时设置 state=3
  • 所有骑手操作需检查 afterSaleStatus,有活跃退款申请时允许操作但需提示

7. 订单完成机制

参考美团做法:商家/骑手操作是主触发器,用户确认是可选的,自动完成是兜底。

类型 完成触发者 操作 自动完成兜底
外送 骑手 骑手点"已送达" → state=3, deliveryStatus=3 送达后 N 小时自动完成
自取 商家 商家点"完成" → state=3, payStatus=1 出餐后 N 小时自动完成
堂食 商家 商家点"完成" → state=3, payStatus=1 出餐后 N 小时自动完成
  • 用户确认:可提供可选的"确认取餐"按钮,用户点了也设 state=3,但不点也不影响
  • 定时任务兜底:商家出餐(state=2)后超时未完成,系统自动设为 state=3

8. 订单创建(UserOrderController)

  • 创建订单时设置 state=0afterSaleStatus=0deliveryStatus=NULL(所有类型),根据类型和支付方式设置 payStatus
    • 外送到付(type=0, payType=1):payStatus=1
    • 自取/堂食现金(type=1或2):payStatus=0(商家收款时设为1)
    • 在线支付:payStatus=0

9. 支付回调

  • 支付成功后:payStatus 从 0 改为 1

10. 售后/退款操作(OrderAppealController 等)

本次不实现完整退款流程。系统尚未接入在线支付,暂无退款场景。afterSaleStatus 字段预留,但只实现以下两个操作:

  • 用户取消订单(未接单前):state → 4,afterSaleStatus 保持 0
  • 商家取消订单:state → 4,afterSaleStatus 保持 0

以下完整退款流程留待接入在线支付后实现:

  • 用户申请退款:afterSaleStatus → 1,state 不变
  • 商家同意退款(全额):afterSaleStatus → 2 → 3,state → 4,payStatus → 2
  • 商家同意退款(部分):afterSaleStatus → 3,state 不变
  • 商家拒绝退款:afterSaleStatus → 4,state 不变
  • 客服介入:afterSaleStatus → 5
  • 售后完成:afterSaleStatus → 6
  • 用户撤销退款:afterSaleStatus → 0

11. 旧订单列表查询方法(废弃)

以下旧方法在数据迁移后自然废弃,不再修改。新方法见第 15 节。

  • PosOrderController.getstoreorderlist() — 商家端订单列表(废弃,由 PosOrderShOprateController.orderList 替代)
  • PosOrderController.getorderlist() — 用户端订单列表(废弃,由 UserOrderController.orderList 替代)
  • PosOrderController.getqsorderlist() — 骑手端订单列表(废弃,由 PosOrderQsOprateController.orderList 替代)
  • PosOrderController.getqishouorderlist() — 骑手抢单列表(废弃,由 PosOrderQsOprateController.orderList 的 newTask Tab 替代)
  • ShindexController — 商家首页(废弃)
  • TableQrcodeController — 堂食二维码订单(废弃)
  • PosOrderMapper — 旧 SQL 中的 state 条件不再维护

12. 定时任务(TestTask.java)

  • 旧自动完成逻辑(state=12→5)不再需要:骑手"已送达"操作直接设 state=3 + deliveryStatus=3,不存在需要从"送达"自动转为"已完成"的中间状态
  • 新增自动完成兜底:查 state=2 AND afterSaleStatus=0 且超时未完成,自动设 state=3(外送同时设 deliveryStatus=3,自取/堂食同时设 payStatus=1
  • 退款处理逻辑:原来查 state=13 改为查 afterSaleStatus=2(本次不实现)
  • 作废逻辑:原来设 state=10 改为设 state=4

13. 骑手约束逻辑

  • 防止骑手同时接多单:原来查 state IN (3,4) 改为查 deliveryStatus IN (1,2)
  • RiderPositionMapper 排除有活跃订单的骑手:条件同步更新
  • 有退款申请的订单(afterSaleStatus>0)仍允许骑手操作,直到全额退款成功

14. 推送通知

  • 推送消息中的 state 值需对应新值
  • 根据 type + state + deliveryStatus + afterSaleStatus 组合发送不同内容
  • 退款相关推送应基于 afterSaleStatus 而非 state

15. 新订单列表接口设计

设计原则

  • 旧方法不动:PosOrderController 中旧的 getstoreorderlist/getorderlist/getqsorderlist/getqishouorderlist 等方法保留代码不删,数据迁移后自然废弃
  • 新方法写在三个 Controller 中:用户端写 UserOrderController,骑手端写 PosOrderQsOprateController,商家端写 PosOrderShOprateController
  • 使用 LambdaQueryWrapper:三个端统一用 LambdaQueryWrapper 拼查询条件
  • Tab 参数:前端传 tab 字符串参数(如 pendingactivecompleted),后端映射到四字段查询条件
  • 状态展示文字由前端计算:后端只返回 state/deliveryStatus/payStatus/afterSaleStatus/type 字段,前端根据这些字段组合自行决定显示文案
  • 分页返回 total:新接口统一返回 { records, total, page, size } 格式

前置条件

以下变更必须在列表接口开发前完成:

  1. PosOrder 实体添加 deliveryStatuspayStatusafterSaleStatus 字段
  2. PosOrderMapper.xml resultMap 添加三个新字段映射,insert/update 语句同步更新
  3. 数据库执行 ALTER TABLE 添加三个新列(第1节 SQL 脚本)
  4. 历史订单数据手动清空(第2节:不需要迁移脚本)
  5. PosOrder 已有 longitude(经度)和 latitude(纬度)字段,骑手距离排序可直接使用

15.1 用户端订单列表

位置UserOrderController.java(路径前缀 /system/userOrder接口GET /system/userOrder/orderList

参数

参数 类型 必填 说明
token Header 用户身份
page int 页码,从1开始
size int 每页条数
tab String Tab 标识
type String 订单类型筛选:0 外送 / 1 自取 / 2 堂食,不传则查所有类型

Tab 映射

tab 值 查询条件 说明
unpaid payStatus=0 AND type=0 待付款(仅外送在线支付订单)
active state IN (0,1,2) AND afterSaleStatus=0 AND (payStatus=1 OR type IN (1,2)) 进行中
completed state=3 AND afterSaleStatus=0 已完成
cancelled state=4 AND afterSaleStatus=0 已取消
refund afterSaleStatus > 0 退款/售后

返回格式:复用旧接口 getorderlist() 的返回格式。PosOrder 全字段自动序列化(含 deliveryStatuspayStatusafterSaleStatus 新字段),额外附加以下关联数据:

附加字段 来源 说明
ddId PosOrder.ddId 转为字符串
shanghu InfoUser(shId) 商家用户信息
store PosStore(mdId) 门店信息
shaddress InfoAddress(shdzId) 或 JSON解析 收货地址
user InfoUser(userId) 用户信息
food PosOrder.food JSON 解析为商品列表
parentRemarks OrderParent 父订单备注

骑手信息只返回 PosOrder 自带的 qsIdqsImg,不做额外 InfoUser 查询。

返回示例

{
  "code": 200,
  "msg": "操作成功",
  "data": {
    "records": [
      {
        "id": 1,
        "ddId": "202605150001",
        "type": 0,
        "state": 1,
        "deliveryStatus": null,
        "payStatus": 1,
        "afterSaleStatus": 0,
        "qsId": null,
        "qsImg": null,
        "mdId": 100,
        "amount": 50000,
        "cretim": "2026-05-15 12:00:00",
        "shanghu": { "userId": 50, "nickName": "商家A" },
        "store": { "id": 100, "mdName": "测试门店" },
        "shaddress": { "id": 1, "address": "..." },
        "user": { "userId": 1, "nickName": "用户A" },
        "food": [{ "foodName": "菜品1", "num": 2 }],
        "parentRemarks": ""
      }
    ],
    "total": 100,
    "page": 1,
    "size": 10
  }
}

15.2 商家端订单列表

位置PosOrderShOprateController.java(路径前缀 /system/orderShOprate接口GET /system/orderShOprate/orderList

参数

参数 类型 必填 说明
token Header 商家身份
page int 页码,从1开始
size int 每页条数
tab String Tab 标识
mdId String 门店ID
type String 订单类型筛选:0 外送 / 1 自取 / 2 堂食,不传则显示全部类型

Tab 映射

tab 值 查询条件 说明
pending state=0 AND (payStatus=1 OR type IN (1,2)) AND mdId=? 待受理
preparing state=1 AND mdId=? 待出餐
ready state=2 AND afterSaleStatus=0 AND mdId=? 已出餐
completed state=3 AND afterSaleStatus=0 AND mdId=? 已完成
cancelled state=4 AND afterSaleStatus=0 AND mdId=? 已取消
refund afterSaleStatus > 0 AND mdId=? 退款/售后

返回格式:PosOrder 全字段序列化 + 新字段自动包含。返回 { records, total, page, size } 格式。

15.3 骑手端订单列表

位置PosOrderQsOprateController.java(路径前缀 /system/orderQsOprate接口GET /system/orderQsOprate/orderList

参数

参数 类型 必填 说明
token Header 骑手身份(JwtUtil.getusid(token) 即为 qsId)
page int 页码,从1开始
size int 每页条数
tab String Tab 标识
longitude BigDecimal 条件必填 骑手当前经度(newTask Tab 必填)
latitude BigDecimal 条件必填 骑手当前纬度(newTask Tab 必填)

Tab 映射

tab 值 查询条件 排序
newTask type=0 AND deliveryStatus=0 AND state=2 AND afterSaleStatus=0 距离 ASC(使用 PosOrder.longitude/latitude 计算到骑手的距离)
toPickup deliveryStatus=1 AND qsId=当前骑手ID AND afterSaleStatus=0 时间 ASC
delivering deliveryStatus=2 AND qsId=当前骑手ID AND afterSaleStatus=0 时间 ASC
completed state=3 AND afterSaleStatus=0 AND qsId=当前骑手ID 时间 DESC
cancelled state=4 AND qsId=当前骑手ID AND afterSaleStatus=0 时间 DESC
refund afterSaleStatus > 0 AND qsId=当前骑手ID 时间 DESC

距离排序实现

wrapper.last("ORDER BY ST_Distance_Sphere(point(longitude, latitude), " +
             "point(" + lng + ", " + lat + ")) ASC");

返回格式:PosOrder 全字段序列化 + 新字段自动包含。返回 { records, total, page, size } 格式。

15.4 设计决策记录

以下问题在需求讨论中确认,记录备查:

# 问题 决策 原因
1 旧方法如何处理 保留代码不删,数据迁移后自然废弃 系统未正式使用,旧方法不再维护
2 Tab 参数格式 用有意义的字符串(pending/active 等) 参考美团做法,语义清晰,与旧接口 z01 模式完全独立
3 用户端返回格式 复用旧接口格式 + 新字段自动包含 前端切换成本低,新字段通过 PosOrder 序列化自动包含
4 骑手信息 只返回 qsId + qsImg 和旧接口一致,不做额外 InfoUser 查询
5 已取消订单归属 三个端都新增 cancelled Tab 参考美团做法,已取消订单(state=4, afterSaleStatus=0)需要独立 Tab 承接,否则无任何 Tab 显示
6 商家端 Tab 数量 从 4 个扩充到 6 个(加 completed + cancelled) 参考美团商家端有"已完成"和"已取消"分类
7 骑手距离排序 用 LambdaQueryWrapper.last() 追加原始 SQL PosOrder 已有 longitude/latitude 字段,无需 JOIN PosStore
8 状态展示文字 前端根据 type+state+deliveryStatus+afterSaleStatus 组合自行计算 后端只返回数据,不返回 statusText
9 分页格式 返回 { records, total, page, size } 前端需要 total 做分页控件
10 类型筛选 用户端和商家端都支持可选的 type 参数 商家可能需要区分外送/自取/堂食订单
11 数据迁移策略 四步骤一次性执行(加字段+填充+迁移state) 系统未正式使用,无需分步兼容
12 自取/堂食现金 payStatus 创建时 payStatus=0,商家收款+完成时设为1 商家需要明确知道现金是否已收到,收钱和完成是同一动作
13 待受理 Tab 条件 state=0 AND (payStatus=1 OR type IN (1,2)) 自取/堂食下单即可见,外送需先付款
14 用户端"待付款"Tab payStatus=0 AND type=0 自取/堂食现金无需在线付款,不显示在待付款
15 订单完成机制 商家/骑手操作为主触发,用户确认为可选,定时任务兜底 参考美团:不依赖用户点确认
16 数据迁移 不需要,历史数据手动清空 系统未正式使用
17 退款流程 本次不实现,afterSaleStatus 字段预留 尚未接入在线支付,无退款场景
18 旧字段处理 diningStatus/isAccepted/kefuState 等保留但废弃 新逻辑不再使用,避免序列化问题
19 商家操作步骤 接单(state 0→1)和出餐(state 1→2)分两步 参考美团商家端流程

16. 平台管理端(foodie-admin-vue)调整

16.1 订单列表页(src/views/system/order/index.vue)

位置E:\QtwCode\foodie\foodie-admin-vue\src\views\system\order\index.vue 后端接口/system/order/list(保留现有接口,后端需支持新的筛选参数)

筛选条件变更

原来用单个下拉框筛选旧 state(0-12),改为四个独立下拉框:

筛选字段 下拉选项 默认
state 全部 / 待处理(0) / 已接单(1) / 已出餐(2) / 已完成(3) / 已取消(4) 全部
payStatus 全部 / 未支付(0) / 已支付(1) / 已退款(2) 全部
deliveryStatus 全部 / 待接单(0) / 骑手已接单(1) / 配送中(2) / 已送达(3) / 不适用(NULL) 全部
afterSaleStatus 全部 / 无售后(0) / 申请中(1) / 退款中(2) / 已退款(3) / 退款拒绝(4) / 客服介入(5) / 售后完成(6) 全部

保留现有的订单号、用户ID、订单类型、创建时间筛选条件不变。

状态列变更

原来单个 el-tag 显示旧 state,改为两个 tag 组合显示——主状态 + 副状态:

| 订单状态列 |
|-----------|
| [待处理] [已支付]          ← state=0, payStatus=1 |
| [已接单]                  ← state=1 |
| [已出餐] [配送中]          ← state=2, deliveryStatus=2 |
| [已出餐] [待骑手]          ← state=2, deliveryStatus=0 |
| [已完成]                  ← state=3 |
| [已取消]                  ← state=4, afterSaleStatus=0 |
| [已取消] [已退款]          ← state=4, afterSaleStatus=3 |
| [已接单] [退款申请中]      ← state=1, afterSaleStatus=1 |

规则:

  • 主 tag:根据 state 显示(待处理/已接单/已出餐/已完成/已取消)
  • 副 tag:根据订单类型和附加状态显示
    • 外送(type=0):显示 deliveryStatus(待骑手/骑手已接单/配送中/已送达)
    • 所有类型:显示 payStatus(仅未支付/已退款时显示,已支付不显示避免冗余)
    • 所有类型:显示 afterSaleStatus(仅 >0 时显示)
  • 已取消的订单主 tag 用红色(type="danger")

修改订单弹窗

原来用 sys_order_type 字典的 radio 选择旧 state 值。改为:

  • 只能修改 state 字段(0-4 的下拉选择)
  • 平台管理员通常不应该直接改状态,如确需保留此功能,下拉选项为:待处理(0) / 已接单(1) / 已出餐(2) / 已完成(3) / 已取消(4)

16.2 订单详情弹窗

进度条变更

原来7步进度条(未支付→已付款→已接单→取餐中→配送中→已完成→异常),改为4步:

待处理(0) → 已接单(1) → 已出餐(2) → 已完成(3)
  • 已取消(state=4)的订单不显示进度条,改为显示红色 已取消 标签
  • :active 绑定 orxiangq.state(0-3 直接对应步骤索引)

新增状态信息区域

在进度条下方,增加三个状态信息行(仅在对应值非默认时显示):

显示条件 内容
payStatus != 1 支付状态:未支付(payStatus=0) / 已退款(payStatus=2)
type == 0 且 deliveryStatus != null 配送状态:待接单(0) / 骑手已接单(1) / 配送中(2) / 已送达(3)
afterSaleStatus > 0 售后状态:申请中(1) / 退款中(2) / 已退款(3) / 退款拒绝(4) / 客服介入(5) / 售后完成(6)

16.3 后端接口调整

现有 /system/order/list 接口需支持新的筛选参数:

  • 新增 statepayStatusdeliveryStatusafterSaleStatus 查询参数
  • 多个筛选条件同时传入时为 AND 关系
  • deliveryStatus 需支持 NULL 筛选(自取/堂食订单)
  • sys_order_type 字典可废弃

16.4 字典数据

新增以下字典或在前端硬编码(推荐前端硬编码,因为值固定不变):

字段 选项
state [{value:0, label:'待处理'}, {value:1, label:'已接单'}, {value:2, label:'已出餐'}, {value:3, label:'已完成'}, {value:4, label:'已取消'}]
payStatus [{value:0, label:'未支付'}, {value:1, label:'已支付'}, {value:2, label:'已退款'}]
deliveryStatus [{value:0, label:'待接单'}, {value:1, label:'骑手已接单'}, {value:2, label:'配送中'}, {value:3, label:'已送达'}]
afterSaleStatus [{value:0, label:'无售后'}, {value:1, label:'申请中'}, {value:2, label:'退款中'}, {value:3, label:'已退款'}, {value:4, label:'退款拒绝'}, {value:5, label:'客服介入'}, {value:6, label:'售后完成'}]

推荐前端硬编码而非字典表,因为这些值由代码逻辑定义,不会由运营人员修改。

美团订单完成机制参考

美团各类型订单的完成机制:

  • 外送:骑手点"已送达"后启动自动完成倒计时(通常24小时),到期自动标记已完成。用户可提前手动确认收货,但不点确认不影响完成。
  • 自取:商家点"出餐完成"后启动倒计时,到期自动完成。用户到店取餐可在 App 中扫码确认,但非必须。
  • 堂食:商家出餐后自动倒计时完成。无需用户额外操作。

核心原则:商家/骑手操作是主触发器,用户确认是可选的,自动完成是兜底。 用户不确认不会导致订单永远"挂着"。

参考资源