3. 支付订单模型设计:订单核心字段定义、订单状态机设计、订单生命周期管理
支付订单模型是聚合支付系统的核心数据实体,它承载了交易流转的全部信息。一个设计良好的订单模型,需要同时满足业务可扩展性、状态可追溯性以及生命周期可管理性。本节将从核心字段、状态机与生命周期三个维度展开。
3.1 订单核心字段定义
支付订单字段可划分为基础信息、金额与币种、渠道与路由、时间与状态、安全与扩展五大类。以下为聚合支付场景下的核心字段清单:
| 分类 | 字段名 | 类型 | 说明 |
|---|---|---|---|
| 基础信息 | order_id | varchar(64) | 全局唯一订单号,通常由发单系统生成 |
| merchant_id | varchar(32) | 商户标识,用于分账与对账 | |
| out_trade_no | varchar(64) | 商户侧订单号,用于商户系统幂等 | |
| 金额与币种 | total_amount | bigint | 订单总金额,单位:分 |
| currency | varchar(3) | ISO 4217 标准币种代码,如 CNY | |
| discount_amount | bigint | 优惠金额,用于营销活动 | |
| 渠道与路由 | channel_code | varchar(32) | 实际支付渠道,如 wxpay、alipay |
| route_strategy | varchar(16) | 路由策略标识,记录智能路由决策结果 | |
| 时间与状态 | order_status | tinyint | 订单状态码,对应状态机定义 |
| pay_at | datetime | 实际支付完成时间,用于对账 | |
| 安全与扩展 | signature | varchar(256) | 订单签名,防篡改校验 |
| extra_info | json | 扩展字段,存储渠道回传的附加数据 |
设计要点:金额字段统一使用
bigint 以分为单位存储,避免浮点数精度丢失。签名字段应覆盖 order_id、total_amount、currency 等关键字段,确保数据完整性。
3.2 支付订单状态机设计
状态机是订单模型的行为骨架。聚合支付场景下,订单状态需要覆盖创建、支付中、成功、失败、退款等核心节点,并处理超时、撤销等边界情况。
以下为推荐的状态机定义:
| 状态码 | 状态名称 | 可流转至 | 触发条件 |
|---|---|---|---|
| 0 | 待支付 | 1, 4, 5 | 订单创建成功,等待用户支付 |
| 1 | 支付中 | 2, 3, 4 | 用户已发起支付,渠道处理中 |
| 2 | 支付成功 | 6 | 渠道返回支付成功通知 |
| 3 | 支付失败 | 0, 5 | 渠道返回明确失败或超时 |
| 4 | 已关闭 | — | 用户主动取消或系统超时关闭 |
| 5 | 已撤销 | — | 支付中订单被商户撤销 |
| 6 | 退款中 | 7, 8 | 商户发起退款请求 |
| 7 | 退款成功 | — | 渠道返回退款成功 |
| 8 | 退款失败 | 6 | 渠道返回退款失败,可重试 |
状态机约束:支付成功(2)为终态,不可回退至待支付或支付中。退款流程独立于主支付流程,但必须关联原订单号。
以下 SVG 展示了核心状态流转路径:
3.3 订单生命周期管理
订单生命周期管理关注订单从创建到终态的全过程治理,包括超时处理、幂等保障、对账闭环与数据归档。
3.3.1 生命周期阶段划分
- 创建阶段:接收商户下单请求,校验签名与参数,生成订单号,状态置为待支付。
- 支付阶段:调用渠道接口,处理异步通知,更新状态为支付中/成功/失败。
- 完成阶段:支付成功触发后续业务(如分账、发货),订单进入终态。
- 售后阶段:退款流程独立运行,但关联原订单生命周期。
- 归档阶段:终态订单超过保留期后,迁移至历史表或冷存储。
3.3.2 超时与补偿机制
| 场景 | 超时阈值 | 处理动作 |
|---|---|---|
| 待支付超时 | 30 分钟 | 系统自动关闭订单,状态置为已关闭 |
| 支付中无响应 | 5 分钟 | 触发渠道查询,若仍无结果则标记为失败 |
| 异步通知延迟 | 24 小时 | 启动对账任务,以渠道对账单为准修正状态 |
注意:支付中状态不可无限等待。建议引入定时任务扫描支付中超时订单,主动调用渠道查询接口,避免订单卡死。
3.3.3 生命周期状态迁移图
以下 SVG 展示了订单从创建到归档的完整生命周期路径:
3.3.4 代码示例:订单状态变更接口
以下为订单状态变更的核心逻辑片段,包含状态校验与持久化:
// 订单状态变更方法
public boolean updateOrderStatus(String orderId, int fromStatus, int toStatus) {
// 1. 校验状态机规则
if (!stateMachine.isValidTransition(fromStatus, toStatus)) {
log.warn("非法状态转换: {} -> {}", fromStatus, toStatus);
return false;
}
// 2. 乐观锁更新,防止并发
String sql = "UPDATE payment_order SET order_status = ?, version = version + 1 " +
"WHERE order_id = ? AND order_status = ? AND version = ?";
int rows = jdbcTemplate.update(sql, toStatus, orderId, fromStatus, currentVersion);
if (rows == 0) {
log.error("订单状态更新失败,可能已被其他线程修改: {}", orderId);
return false;
}
// 3. 记录状态变更日志
insertStatusLog(orderId, fromStatus, toStatus);
return true;
}
最佳实践:状态变更必须使用乐观锁或分布式锁,防止并发导致状态错乱。每次变更都应记录日志,用于对账与问题排查。