Files
junhong_cmp_fiber/openspec/specs/iot-order/spec.md
huang 590614aecc 清理 OpenSpec 规范文档中的重复标题
移除 IoT 相关规范文档中的重复 "## ADDED Requirements" 标题行:
- iot-agent-commission
- iot-device
- iot-number-card
- iot-order
- iot-package

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-12 16:06:59 +08:00

11 KiB
Raw Blame History

IoT Order Management

Purpose

Manage orders for IoT card packages and number card products, including order creation, payment processing, status tracking, commission triggering, and support for single-card orders, device-level orders, and carrier number card orders.

This capability supports:

  • Unified order entity for package orders and number card orders
  • Order status lifecycle management
  • Multiple payment methods (wallet, online payment, carrier direct payment)
  • Commission triggering on order completion
  • Device-level order commission (counted once regardless of bound card count)
  • Multi-dimensional order querying and filtering

Requirements

Requirement: 订单实体定义

系统 SHALL 定义订单(Order)实体,统一管理两种订单类型:套餐订单、号卡订单。

核心概念:

  • 套餐订单: 用户为 IoT 卡或设备购买套餐的订单,包括单卡套餐订单和设备级套餐订单
  • 号卡订单: 运营商回传的号卡订单,用户直接在上游平台下单,系统只接收订单状态更新

实体字段:

  • id: 订单 ID(主键,BIGINT)
  • order_no: 订单编号(VARCHAR(50),唯一)
  • order_type: 订单类型(INT,1-套餐订单 2-号卡订单)
  • iot_card_id: IoT 卡 ID(BIGINT,可空,单卡套餐订单时有值)
  • device_id: 设备 ID(BIGINT,可空,设备级套餐订单时有值)
  • number_card_id: 号卡 ID(BIGINT,可空,号卡订单时有值)
  • package_id: 套餐 ID(BIGINT,可空,仅当 order_type 为 1 时有值)
  • user_id: 用户 ID(BIGINT,购买用户)
  • agent_id: 代理 ID(BIGINT,可空,通过代理购买时有值)
  • amount: 订单金额(DECIMAL(10,2),元)
  • payment_method: 支付方式(VARCHAR(20),"wallet"-钱包 | "online"-在线支付 | "carrier"-运营商直付)
  • status: 订单状态(INT,1-待支付 2-已支付 3-已完成 4-已取消 5-已退款)
  • carrier_order_id: 运营商订单 ID(VARCHAR(255),可空,仅号卡订单有值)
  • carrier_order_data: 运营商订单原始数据(JSONB,可空)
  • paid_at: 支付时间(TIMESTAMP,可空)
  • completed_at: 完成时间(TIMESTAMP,可空)
  • created_at: 创建时间(TIMESTAMP,自动填充)
  • updated_at: 更新时间(TIMESTAMP,自动填充)

订单类型说明:

  • 单卡套餐订单: order_type 为 1,iot_card_id 有值,device_id 为 NULL
  • 设备级套餐订单: order_type 为 1,device_id 有值,iot_card_id 为 NULL
  • 号卡订单: order_type 为 2,number_card_id 有值,iot_card_iddevice_id 为 NULL

Scenario: 创建单卡套餐购买订单

  • WHEN 用户(ID 为 2001)为 IoT 卡(ID 为 1001)购买套餐(ID 为 3001),金额为 30.00 元
  • THEN 系统创建订单记录,order_type 为 1,iot_card_id 为 1001,device_id 为 NULL,package_id 为 3001,user_id 为 2001,amount 为 30.00,状态为 1(待支付)

Scenario: 创建设备级套餐购买订单

  • WHEN 用户(ID 为 2001)为设备(ID 为 5001,绑定 3 张 IoT 卡)购买套餐(ID 为 3002),金额为 399.00 元
  • THEN 系统创建订单记录,order_type 为 1,device_id 为 5001,iot_card_id 为 NULL,package_id 为 3002,user_id 为 2001,amount 为 399.00,状态为 1(待支付)

Scenario: 创建号卡订单(运营商回传)

  • WHEN Gateway 回传运营商订单,虚拟商品编码对应号卡 ID 为 6001,代理 ID 为 123,订单金额为 30.00 元
  • THEN 系统创建订单记录,order_type 为 2,number_card_id 为 6001,iot_card_id 为 NULL,device_id 为 NULL,agent_id 为 123,amount 为 30.00,payment_method 为 "carrier",状态为 2(已支付)

Requirement: 订单状态流转

系统 SHALL 管理订单的状态流转,确保状态变更符合业务规则。

状态定义:

  • 1-待支付: 订单已创建,等待用户支付
  • 2-已支付: 用户已支付,等待系统处理
  • 3-已完成: 订单已完成(激活/发货等)
  • 4-已取消: 订单已取消
  • 5-已退款: 订单已退款

状态流转规则:

  • 待支付(1) → 已支付(2): 用户完成支付
  • 待支付(1) → 已取消(4): 用户取消订单或订单超时
  • 已支付(2) → 已完成(3): 系统完成订单处理(激活/发货)
  • 已支付(2) → 已退款(5): 用户申请退款且审核通过
  • 已完成(3) → 已退款(5): 用户申请退款且审核通过(特殊情况)

Scenario: 用户支付订单

  • WHEN 用户支付待支付订单(ID 为 10001),支付金额为 30.00 元
  • THEN 系统将订单状态从 1(待支付) 变更为 2(已支付),paid_at 记录支付时间

Scenario: 单卡套餐订单完成

  • WHEN 系统处理完单卡套餐订单(ID 为 10001),激活 IoT 卡并分配套餐
  • THEN 系统将订单状态从 2(已支付) 变更为 3(已完成),completed_at 记录完成时间

Scenario: 设备级套餐订单完成

  • WHEN 系统处理完设备级套餐订单(ID 为 10002),为设备绑定的所有 IoT 卡分配套餐
  • THEN 系统将订单状态从 2(已支付) 变更为 3(已完成),completed_at 记录完成时间

Requirement: 订单支付方式

系统 SHALL 支持三种支付方式:钱包支付、在线支付、运营商直付。

支付方式:

  • 钱包支付(wallet): 从用户钱包余额扣款
  • 在线支付(online): 通过第三方支付(微信/支付宝等)
  • 运营商直付(carrier): 用户直接支付给运营商(仅号卡订单)

支付规则:

  • 一次性分佣订单必须使用钱包支付
  • 套餐购买订单可以使用钱包或在线支付
  • 号卡订单必须使用运营商直付

Scenario: 钱包支付订单

  • WHEN 用户使用钱包支付订单(金额为 30.00 元),钱包余额为 50.00 元
  • THEN 系统从钱包扣除 30.00 元,订单状态变更为 2(已支付),payment_method 为 "wallet"

Scenario: 钱包余额不足

  • WHEN 用户使用钱包支付订单(金额为 30.00 元),钱包余额为 20.00 元
  • THEN 系统拒绝支付,返回错误信息"钱包余额不足"

Scenario: 一次性分佣订单强制钱包支付

  • WHEN 用户购买配置了一次性分佣的套餐,尝试使用在线支付
  • THEN 系统拒绝支付,返回错误信息"一次性分佣订单必须使用钱包支付"

Requirement: 订单分佣触发

系统 SHALL 在订单完成时触发分佣计算,根据代理分佣规则创建分佣记录。

触发条件:

  • 订单状态变更为 3(已完成)
  • 订单有 agent_id(通过代理销售)
  • 代理配置了分佣规则

分佣计算规则:

  • 单卡套餐订单: 根据 IoT 卡关联的代理分佣规则计算分佣
  • 设备级套餐订单: 分佣只计算一次(不按设备绑定的 IoT 卡数量倍增)
  • 号卡订单: 下单即冻结分佣,次月通过 Excel 导入解冻

Scenario: 单卡套餐购买订单触发分佣

  • WHEN 代理(ID 为 123)的单卡套餐订单(ID 为 10001)完成,订单金额为 30.00 元,代理配置了 5.00 元一次性分佣
  • THEN 系统创建分佣记录,agent_id 为 123,order_id 为 10001,amount 为 5.00,状态为 1(冻结)

Scenario: 设备级套餐订单触发分佣(只计算一次)

  • WHEN 代理(ID 为 123)的设备级套餐订单(ID 为 10002)完成,设备绑定 3 张 IoT 卡,订单金额为 399.00 元,代理配置了 100.00 元长期分佣
  • THEN 系统创建一条分佣记录,agent_id 为 123,order_id 为 10002,amount 为 100.00,状态为 1(冻结),不是 3 × 100.00

Scenario: 号卡订单触发分佣

  • WHEN 代理(ID 为 123)的号卡订单(ID 为 10003)创建,订单金额为 30.00 元,代理配置了长期分佣
  • THEN 系统创建分佣记录,agent_id 为 123,order_id 为 10003,状态为 1(冻结),等待次月通过 Excel 导入解冻

Requirement: 订单查询和筛选

系统 SHALL 支持多维度查询和筛选订单。

查询条件:

  • 订单编号(精确匹配)
  • 订单类型(1-套餐订单 2-号卡订单)
  • 订单状态(单选或多选)
  • IoT 卡 ID(精确匹配)
  • 设备 ID(精确匹配)
  • 号卡 ID(精确匹配)
  • 用户 ID(精确匹配)
  • 代理 ID(精确匹配)
  • 支付方式(单选或多选)
  • 创建时间范围(开始时间 - 结束时间)
  • 支付时间范围(开始时间 - 结束时间)
  • 完成时间范围(开始时间 - 结束时间)

分页:

  • 默认每页 20 条,最大每页 100 条
  • 返回总记录数和总页数

Scenario: 查询用户的所有订单

  • WHEN 用户(ID 为 2001)查询自己的所有订单
  • THEN 系统返回 user_id 为 2001 的所有订单列表,按创建时间倒序排列

Scenario: 查询代理的订单

  • WHEN 代理(ID 为 123)查询自己的订单,筛选已完成的套餐订单
  • THEN 系统返回 agent_id 为 123 且 order_type 为 1 且 status 为 3(已完成) 的订单列表

Scenario: 查询 IoT 卡的订单历史

  • WHEN 运营人员查询 IoT 卡(ID 为 1001)的所有订单
  • THEN 系统返回 iot_card_id 为 1001 的所有订单列表,包含套餐购买记录

Scenario: 查询设备的订单历史

  • WHEN 运营人员查询设备(ID 为 5001)的所有订单
  • THEN 系统返回 device_id 为 5001 的所有设备级套餐订单列表

Requirement: 订单数据校验

系统 SHALL 对订单数据进行校验,确保数据完整性和一致性。

校验规则:

  • 订单编号(order_no):必填,长度 1-50 字符,唯一
  • 订单类型(order_type):必填,枚举值 1(套餐订单) | 2(号卡订单)
  • IoT 卡 ID(iot_card_id):套餐订单时 iot_card_id 和 device_id 二选一
  • 设备 ID(device_id):套餐订单时 iot_card_id 和 device_id 二选一
  • 号卡 ID(number_card_id):号卡订单时必填
  • 套餐 ID(package_id):套餐订单时必填
  • 用户 ID(user_id):必填,≥ 1
  • 订单金额(amount):必填,≥ 0,最多 2 位小数
  • 支付方式(payment_method):必填,枚举值 "wallet" | "online" | "carrier"
  • 状态(status):必填,枚举值 1-5

Scenario: 创建订单时金额为负数

  • WHEN 创建订单,金额为 -10.00
  • THEN 系统拒绝创建,返回错误信息"订单金额必须 ≥ 0"

Scenario: 创建订单时订单编号重复

  • WHEN 创建订单,订单编号为已存在的 "ORD-2025-001"
  • THEN 系统拒绝创建,返回错误信息"订单编号已存在"

Scenario: 创建套餐订单时未关联 IoT 卡或设备

  • WHEN 创建套餐订单,iot_card_iddevice_id 都为 NULL
  • THEN 系统拒绝创建,返回错误信息"套餐订单必须关联 IoT 卡或设备"

Scenario: 创建套餐订单时同时关联 IoT 卡和设备

  • WHEN 创建套餐订单,iot_card_id 为 1001,device_id 为 5001
  • THEN 系统拒绝创建,返回错误信息"套餐订单不能同时关联 IoT 卡和设备"

Scenario: 创建号卡订单时未关联号卡

  • WHEN 创建号卡订单,number_card_id 为 NULL
  • THEN 系统拒绝创建,返回错误信息"号卡订单必须关联号卡"