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;
}
最佳实践:状态变更必须使用乐观锁或分布式锁,防止并发导致状态错乱。每次变更都应记录日志,用于对账与问题排查。