1. 订单状态机基础实现

订单状态机基础实现 学习卡片

任务目标

实现一个订单状态机,覆盖订单从创建到完成的完整生命周期。状态包括:CREATED(已创建)、PAID(已支付)、SHIPPED(已发货)、COMPLETED(已完成)。支持通过事件触发合法状态转换,并拒绝非法转换。

状态转换规则

当前状态触发事件目标状态
CREATEDPAYPAID
PAIDSHIPSHIPPED
SHIPPEDCOMPLETECOMPLETED

任何其他状态-事件组合均为非法转换,应抛出异常或返回错误。

完整实现代码

// order_state_machine.js
// 运行环境:Node.js 12+,无需第三方依赖

class OrderStateMachine {
  // 状态定义
  static STATES = {
    CREATED: 'CREATED',
    PAID: 'PAID',
    SHIPPED: 'SHIPPED',
    COMPLETED: 'COMPLETED'
  };

  // 合法转换表:{ 当前状态: { 事件: 目标状态 } }
  static TRANSITIONS = {
    [OrderStateMachine.STATES.CREATED]: {
      PAY: OrderStateMachine.STATES.PAID
    },
    [OrderStateMachine.STATES.PAID]: {
      SHIP: OrderStateMachine.STATES.SHIPPED
    },
    [OrderStateMachine.STATES.SHIPPED]: {
      COMPLETE: OrderStateMachine.STATES.COMPLETED
    }
  };

  constructor(orderId) {
    this.orderId = orderId;
    this.state = OrderStateMachine.STATES.CREATED; // 初始状态
    this.history = []; // 状态变更历史
  }

  // 触发事件,返回是否转换成功
  trigger(event) {
    const currentTransitions = OrderStateMachine.TRANSITIONS[this.state];
    const nextState = currentTransitions ? currentTransitions[event] : undefined;

    if (!nextState) {
      throw new Error(
        `非法转换: 订单 ${this.orderId} 当前状态 ${this.state} 不能响应事件 ${event}`
      );
    }

    this.history.push({
      from: this.state,
      to: nextState,
      event: event,
      timestamp: new Date().toISOString()
    });

    this.state = nextState;
    return true;
  }

  // 获取当前状态
  getState() {
    return this.state;
  }

  // 获取完整历史记录
  getHistory() {
    return this.history;
  }
}

// ========== 最小调用入口与验证 ==========
function runDemo() {
  const order = new OrderStateMachine('ORD-2024-001');
  const results = [];

  // 1. 正常流程:创建 → 支付 → 发货 → 完成
  results.push(`初始状态: ${order.getState()}`);

  order.trigger('PAY');
  results.push(`支付后: ${order.getState()}`);

  order.trigger('SHIP');
  results.push(`发货后: ${order.getState()}`);

  order.trigger('COMPLETE');
  results.push(`完成后: ${order.getState()}`);

  // 2. 非法转换验证:已完成订单再次支付应报错
  try {
    order.trigger('PAY');
    results.push('非法转换未被拦截(错误)');
  } catch (e) {
    results.push(`非法转换被正确拦截: ${e.message}`);
  }

  // 3. 输出历史记录
  results.push('历史记录:');
  order.getHistory().forEach(h => {
    results.push(`  ${h.from} --${h.event}--> ${h.to} @ ${h.timestamp}`);
  });

  return results.join('\n');
}

// 执行演示
console.log(runDemo());

运行方式

node order_state_machine.js

验证方法与通过标准

运行上述命令后,观察控制台输出,满足以下条件即判定实现正确:

  • 输出依次包含 初始状态: CREATED、支付后: PAID、发货后: SHIPPED、完成后: COMPLETED,证明正常状态链路完整走通。
  • 输出包含 非法转换被正确拦截: 非法转换: 订单 ORD-2024-001 当前状态 COMPLETED 不能响应事件 PAY,证明非法事件被拒绝。
  • 历史记录中列出3条转换记录,每条包含 from --event--> to 格式及时间戳。

预期输出(与代码逻辑严格一致)

初始状态: CREATED
支付后: PAID
发货后: SHIPPED
完成后: COMPLETED
非法转换被正确拦截: 非法转换: 订单 ORD-2024-001 当前状态 COMPLETED 不能响应事件 PAY
历史记录:
  CREATED --PAY--> PAID @ 2024-01-01T00:00:00.000Z
  PAID --SHIP--> SHIPPED @ 2024-01-01T00:00:00.000Z
  SHIPPED --COMPLETE--> COMPLETED @ 2024-01-01T00:00:00.000Z

(时间戳为运行时动态生成,格式如上所示,具体值以实际执行为准。)

核心设计说明

状态机采用查表法实现:TRANSITIONS 静态对象定义了所有合法转换路径,trigger() 方法先查表判断目标状态是否存在,不存在则抛出异常。这种实现方式将状态转换规则集中管理,新增状态或事件时只需修改转换表,无需改动业务逻辑代码。历史记录数组完整追踪每次状态变更,便于审计与调试。