data-model.md 9.0 KB

Data Model: 平台订单状态受控调整

Date: 2026-08-10 Database change: None

1. Existing Persistent Entities

1.1 PosOrder / pos_order

本功能只使用现有字段,不新增列。

Java 字段 数据库列 类型 本功能含义
id id BIGINT 内部订单主键,管理端接口路径参数
ddId dd_id VARCHAR 业务订单号,日志和 OMG 流水关联键
type type BIGINT 0 外送、1 自取、2 堂食
state state BIGINT 0 待处理、1 已接单、2 已出餐、3 已完成、4 已取消
deliveryStatus delivery_status BIGINT NULL 外送:0 待接单、1 骑手已接单、2 配送中、3 已送达
payStatus pay_status BIGINT 0 未支付、1 已支付、2 已退款
afterSaleStatus after_sale_status BIGINT 0 无售后、1~6 为售后流程状态
payType pay_type VARCHAR 1 线下/到付,7 OMG;历史类型不在本功能启用
collectPayment collect_payment VARCHAR 到付标识,保留给既有账单逻辑
qsId qs_id BIGINT NULL 骑手 ID;本功能不提供指派能力
sdTime sd_time DATETIME NULL 实际送达时间
points points INT NULL 退款/取消时需要返还的积分

1.2 PosOrderLog / pos_order_log

字段 用途
ddId 关联业务订单号
operatorType 管理员固定为 1;系统资金日志为 0
operatorId 当前后台管理员 ID
operatorName 当前后台管理员名称
content 状态前后值、动作和原因,最大 512 字
logTime 服务端写入时间

人工操作使用同步日志,保证状态事务回滚时不会残留“成功”日志。

1.3 PosOrderOmgPayment / pos_order_omg_payment

订单层 payStatus 只有 0/1/2;OMG 流水有更细的内部状态:

pay_status 含义 管理端行为
0 未支付 允许查询补单
1 已支付 未完成订单允许退款
2 支付失败 不得人工改为已支付;需要重新发起支付
3 已退款 订单层应补偿为 payStatus=2
4 退款中/结果未知 禁止重复退款,提示等待对账

1.4 PosOrderOmgRefund / pos_order_omg_refund

字段组合 含义
action=R, rtnCode=1 OMG 自动退款成功
action=R, rtnCode!=1 OMG 明确退款失败
action=R, rtnCode=NULL 请求处理中或结果未知,结合 rtnMsg 判断
action=NULL, rtnCode=NULL 支付方式不支持退款 API,等待人工处理
action=NULL, rtnCode=1 管理员确认已在 OMG 后台完成人工退款

同一支付流水只保留一个未决人工待办;已存在时重复请求返回原状态。

2. API-only Models

2.1 AdminOrderActionRequest

所有支付动作与状态动作共享的旧快照和审计字段。

字段 Java 类型 校验
expectedState Long 必填,0~4
expectedDeliveryStatus Long 可空;非空时 0~3
expectedPayStatus Long 必填,0~2
expectedAfterSaleStatus Long 必填,0~6
reason String 必填,trim 后 1~200 字

2.2 AdminOrderStatusUpdateRequest

继承/组合 AdminOrderActionRequest,增加:

字段 Java 类型 校验
targetState Long 可空,非空时 0~4
targetDeliveryStatus Long 可空,非空时 0~3

规则:至少一个目标值与快照不同;未提交的目标保持当前值。外送送达可以只提交 targetDeliveryStatus=3,服务端自动同步 state=3

2.3 AdminOrderStatusContext

allowed*can* 是 UI 提示,不替代写接口校验。

2.4 OmgRefundOutcome

字段 类型 说明
id, ddId Long/String 订单标识
type, payType Long/String 类型判定依据
state, deliveryStatus, payStatus, afterSaleStatus Long 当前快照
sdTime Date 当前送达时间
riderAssigned Boolean qsId != null
allowedOrderStates List 包含当前值及可选下一值
allowedDeliveryStatuses List 非外送为空
canConfirmOfflinePayment Boolean 可确认线下收款
canConfirmOfflineRefund Boolean 可确认线下退款
canReconcileOmg Boolean 可查询 OMG 补单
canRefundOmg Boolean 可发起 OMG 退款
canConfirmManualOmgRefund Boolean 已有待办且可确认人工退款完成
omgPaymentStatus Integer NULL 最新 OMG 流水状态 0~4
manualRefundPending Boolean 存在未决人工退款记录
refundUnknown Boolean OMG 流水为退款中/结果未知
状态 说明 订单层处理
REFUNDED OMG 明确退款成功 同步为已退款/已取消/售后已退款
MANUAL_PENDING 无自动退款 API 保持已支付,显示待人工处理和确认入口
UNKNOWN 请求结果未知 保持已支付,OMG 流水保持退款中
FAILED 网关明确失败 保持已支付,OMG 流水恢复已支付
IDEMPOTENT 已退款或正在处理 返回当前最终状态,无重复副作用

3. State Invariants

以下不变量必须在每个写入口成立:

  1. type IN (1,2) → deliveryStatus IS NULL
  2. deliveryStatus=3 → type=0 AND state=3 AND sdTime IS NOT NULL
  3. state=3 → payStatus=1 AND afterSaleStatus=0(本期不允许完成后退款)。
  4. payStatus=2 → state=4 AND afterSaleStatus=3(本功能处理的全额退款)。
  5. payType=7 AND payStatus=1 必须存在 OMG 已支付流水。
  6. payType=7 AND payStatus=2 必须存在 OMG 自动退款成功记录或管理员明确确认人工退款完成;人工待办不提前置 2。
  7. state IN (3,4)payStatus=2 为本功能终态,普通状态接口不能重新打开。
  8. afterSaleStatus>0 时,普通订单/配送状态接口不得变更数据。

4. Order State Transitions

4.1 Normal order state

Current Target Additional conditions Result
0 1 OMG 订单必须已支付;无售后 state=1
1 2 无售后 state=2;外送若配送为空则初始化为 0
2 3 已支付;外送已送达,自取/堂食无配送状态 state=3 + 完成副作用
0 4 未支付、无售后 state=4
1 4 未支付、无售后 state=4

其他跳转全部拒绝。已支付订单取消必须使用退款动作。

4.2 Delivery state

Current Target Conditions Result
NULL 0 外送,state=2 等待骑手接单
0 1 外送,已有 qsIdstate=2 骑手已接单
1 2 外送,已有 qsIdstate=2 配送中
2 3 外送,已有 qsId,已支付,无售后 配送已送达 + 订单完成 + 送达时间 + 账单

配送状态不得跳级或回退。相同值视为无变化。

5. Payment State Transitions

5.1 Offline payType="1"

0 --confirm-payment--> 1
1 --confirm-refund--> 2
  • 收款不自动完成订单;完成仍要满足订单/配送规则。
  • 退款原子同步 state=4afterSaleStatus=3

5.2 OMG payType="7"

order.payStatus 0 --callback/query + ledger CAS--> 1
order.payStatus 1 --real refund success--> 2

管理员请求体不能携带目标 payStatus。OMG 流水状态 4 期间订单仍显示已支付,并额外显示“退款结果待核对”。

对于无退款 API 的支付方式:创建未决人工记录时订单和支付流水保持已支付;管理员完成外部人工退款并二次确认后,支付流水通过既有 1→4→3 CAS,新增 action=NULL, rtnCode=1 的完成记录,再同步订单三状态。

6. Composite Snapshot CAS

逻辑条件:

UPDATE pos_order
SET ...
WHERE id = :id
  AND state = :expectedState
  AND pay_status = :expectedPayStatus
  AND after_sale_status = :expectedAfterSaleStatus
  AND delivery_status <=> :expectedDeliveryStatus;

实现使用 MyBatis-Plus 条件构造器参数化生成;上面的 MySQL <=> 仅用于说明 NULL-safe 语义,不要求新增 XML SQL。

CAS 失败时:

  • 不写订单日志;
  • 不生成账单、不返积分、不推送;
  • 返回业务冲突并要求重新加载 status-context

7. Audit Content

日志内容保持可读且不超过 512 字:

平台调整[订单/配送]:state 2→3,delivery 2→3,pay 1→1,afterSale 0→0;原因:骑手端漏操作
平台确认线下收款:pay 0→1;原因:门店已收现金
平台发起OMG补单:pay 0→1;原因:用户提供付款凭证
平台OMG退款成功:state 1→4,pay 1→2,afterSale 0→3;原因:商家无法履约

不得记录 OMG HashKey/HashIV、token、完整回调原文或其他秘密。

8. Database Migration

无数据库迁移,不修改 updatesql/sql.md。如测试发现现有生产表 pos_order_log.content 不是 512 字,应暂停实现并单独提出迁移评审,不在本功能中自动执行 DDL。