Files
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

10 KiB
Raw Permalink Blame History

IoT Package Management

Purpose

Manage IoT packages (data plans) for IoT cards and devices, including package definitions, real/virtual data coexistence, single-card packages, device-level packages with shared data pools, and agent package allocation.

This capability supports:

  • Package entity definition with real and virtual data types
  • Formal packages and addon packages (data top-ups)
  • Single-card package purchases
  • Device-level package purchases with shared data pool across all bound cards
  • Agent package allocation with retail pricing
  • Commission calculation (counted once for device-level packages regardless of card count)

Requirements

Requirement: 套餐实体定义

系统 SHALL 定义套餐(Package)实体,包含套餐的基本属性、定价、流量配置。

核心概念: 套餐只适用于 IoT 卡(ICCID),用户可以为单张 IoT 卡购买套餐,也可以为设备购买套餐(套餐分配到设备绑定的所有 IoT 卡,流量设备级共享)。

实体字段:

  • id: 套餐 ID(主键,BIGINT)
  • package_code: 套餐编码(VARCHAR(50),唯一)
  • package_name: 套餐名称(VARCHAR(255))
  • series_id: 套餐系列 ID(BIGINT,关联 package_series 表,用于组织套餐分组和配置一次性分佣)
  • package_type: 套餐类型(VARCHAR(20),"formal"-正式套餐 | "addon"-加油包)
  • duration_months: 套餐时长(INT,月数,1-月套餐 12-年套餐,加油包为 0)
  • real_data_mb: 真流量额度(BIGINT,MB 为单位,可选)
  • virtual_data_mb: 虚流量额度(BIGINT,MB 为单位,用于停机判断,可选)
  • data_amount_mb: 总流量额度(BIGINT,MB 为单位,real_data_mb + virtual_data_mb)
  • price: 套餐价格(DECIMAL(10,2),元)
  • status: 套餐状态(INT,1-上架 2-下架)
  • created_at: 创建时间(TIMESTAMP,自动填充)
  • updated_at: 更新时间(TIMESTAMP,自动填充)

套餐类型说明:

  • 正式套餐(formal): 每张 IoT 卡只能有一个有效的正式套餐,购买新的正式套餐会替换旧的
  • 加油包(addon): 每张 IoT 卡可以购买多个加油包,与正式套餐共存

Scenario: 创建月套餐

  • WHEN 平台创建月套餐,套餐编码为 "PKG-M-001",套餐名称为 "月套餐 10GB",套餐系列 ID 为 1,类型为正式套餐,时长为 1 个月,真流量为 10240 MB,虚流量为 0,价格为 30.00 元
  • THEN 系统创建套餐记录,package_code 为 "PKG-M-001",series_id 为 1,package_type 为 "formal",duration_months 为 1,real_data_mb 为 10240,virtual_data_mb 为 0,data_amount_mb 为 10240,price 为 30.00

Scenario: 创建年套餐

  • WHEN 平台创建年套餐,套餐编码为 "PKG-Y-001",套餐名称为 "年套餐 120GB",套餐系列 ID 为 1,类型为正式套餐,时长为 12 个月,真流量为 122880 MB,虚流量为 0,价格为 300.00 元
  • THEN 系统创建套餐记录,package_code 为 "PKG-Y-001",series_id 为 1,package_type 为 "formal",duration_months 为 12,real_data_mb 为 122880,virtual_data_mb 为 0,data_amount_mb 为 122880,price 为 300.00

Scenario: 创建流量加油包

  • WHEN 平台创建加油包,套餐编码为 "PKG-ADD-001",套餐名称为 "流量包 5GB",套餐系列 ID 为 2,类型为加油包,时长为 0,真流量为 5120 MB,虚流量为 0,价格为 10.00 元
  • THEN 系统创建套餐记录,package_code 为 "PKG-ADD-001",series_id 为 2,package_type 为 "addon",duration_months 为 0,real_data_mb 为 5120,virtual_data_mb 为 0,data_amount_mb 为 5120,price 为 10.00

Requirement: 套餐流量类型和真虚流量共存

系统 SHALL 支持真流量和虚流量两种流量类型,两者可以共存于同一套餐中。

流量类型定义:

  • 真流量(real_data_mb): 实际可用的流量,可在运营商网络中使用
  • 虚流量(virtual_data_mb): 虚拟流量,用于停机判断(虚流量用完后停机,即使真流量还有剩余)
  • 总流量(data_amount_mb): 真流量 + 虚流量的总和

重要规则:

  • 真流量和虚流量可以同时存在于一个套餐中
  • 停机判断基于虚流量(虚流量用完后停机)
  • 套餐可以只有真流量、只有虚流量、或两者都有

Scenario: 创建真虚流量共存的套餐

  • WHEN 平台创建套餐,真流量为 8000 MB,虚流量为 2000 MB
  • THEN 系统创建套餐记录,real_data_mb 为 8000,virtual_data_mb 为 2000,data_amount_mb 为 10000

Scenario: 创建纯真流量套餐

  • WHEN 平台创建套餐,真流量为 10240 MB,虚流量为 0
  • THEN 系统创建套餐记录,real_data_mb 为 10240,virtual_data_mb 为 0,data_amount_mb 为 10240

Scenario: 创建纯虚流量套餐

  • WHEN 平台创建套餐,真流量为 0,虚流量为 10240 MB
  • THEN 系统创建套餐记录,real_data_mb 为 0,virtual_data_mb 为 10240,data_amount_mb 为 10240

Scenario: 虚流量用完停机

  • WHEN 套餐的虚流量为 2000 MB,用户已使用 2000 MB 虚流量,但真流量还剩余 5000 MB
  • THEN 系统判断虚流量已用完,触发停机操作,即使真流量还有剩余

Requirement: 单卡套餐购买

系统 SHALL 支持用户为单张 IoT 卡购买套餐。

购买规则:

  • 每张 IoT 卡只能有一个有效的正式套餐
  • 购买新的正式套餐会替换旧的正式套餐
  • 可以同时购买多个加油包
  • 套餐购买后创建套餐订单记录

Scenario: 为 IoT 卡购买正式套餐

  • WHEN 用户为 IoT 卡(ICCID 为 "8986...")购买月套餐(套餐 ID 为 1001),价格为 30.00 元
  • THEN 系统创建套餐订单,order_type 为 1(套餐订单),iot_card_id 为 IoT 卡 ID,package_id 为 1001,amount 为 30.00

Scenario: 为 IoT 卡购买加油包

  • WHEN 用户为 IoT 卡购买流量加油包(套餐 ID 为 2001),价格为 10.00 元
  • THEN 系统创建套餐订单,IoT 卡的正式套餐保持不变,加油包作为额外套餐生效

Scenario: 购买新正式套餐替换旧套餐

  • WHEN 用户为 IoT 卡购买新的月套餐,该 IoT 卡已有月套餐
  • THEN 系统创建新订单,旧的正式套餐失效,新套餐生效

Requirement: 设备级套餐购买和流量共享

系统 SHALL 支持用户为设备购买套餐,套餐分配到设备绑定的所有 IoT 卡,流量设备级共享。

设备套餐业务规则:

  • 用户为设备购买套餐时,套餐会分配到设备绑定的所有 IoT 卡(1-4 张)
  • 套餐的流量是设备级别共享的(例如 3000G/月共享,不管用哪张卡)
  • 分佣只计算一次(不按卡数倍增)
  • 订单表通过 device_id 字段关联设备,通过 device_sim_bindings 表查找绑定的所有 IoT 卡
  • 设备购买的套餐不受单卡套餐限制(设备套餐和单卡套餐独立管理)

流量共享机制:

  • 设备绑定的所有 IoT 卡共享套餐流量池
  • 任意一张 IoT 卡使用流量都会从共享池扣除
  • 流量池耗尽后,所有绑定的 IoT 卡都无法使用

订单记录:

  • 订单表 device_id 字段记录设备 ID(设备级套餐订单)
  • 订单表 iot_card_id 字段为 NULL(不关联具体 IoT 卡)
  • 通过 device_sim_bindings 表查询设备绑定的所有 IoT 卡

Scenario: 为设备购买套餐

  • WHEN 用户为设备(ID 为 1001,绑定 3 张 IoT 卡)购买年套餐,价格为 399.00 元,流量为 3000G/月
  • THEN 系统创建套餐订单,order_type 为 1(套餐订单),device_id 为 1001,iot_card_id 为 NULL,amount 为 399.00,套餐分配到 3 张绑定的 IoT 卡

Scenario: 设备流量共享

  • WHEN 设备(绑定 3 张 IoT 卡)购买套餐 3000G/月,其中一张 IoT 卡使用 1000G 流量
  • THEN 流量池剩余 2000G,其他两张 IoT 卡可以使用剩余的 2000G

Scenario: 设备套餐分佣只计算一次

  • WHEN 设备(绑定 3 张 IoT 卡)购买套餐,长期佣金为 100.00 元
  • THEN 系统创建一条分佣记录,金额为 100.00 元(不是 3 × 100.00 元)

Requirement: 套餐分配给代理

系统 SHALL 支持将套餐分配给代理商,代理可以在平台设置的成本价基础上加价销售。

分配规则:

  • 平台为套餐设置成本价(分配给代理的价格)
  • 代理可以在成本价基础上加价,但不能超过成本价的 2 倍
  • 分配记录存储在 agent_package_allocations

agent_package_allocations 表:

  • id: 分配记录 ID(主键,BIGINT)
  • agent_id: 代理用户 ID(BIGINT)
  • package_id: 套餐 ID(BIGINT)
  • cost_price: 成本价(DECIMAL(10,2),平台给代理的价格)
  • retail_price: 零售价(DECIMAL(10,2),代理设置的终端销售价格)
  • status: 分配状态(INT,1-有效 2-无效)
  • created_at: 创建时间(TIMESTAMP,自动填充)
  • updated_at: 更新时间(TIMESTAMP,自动填充)

Scenario: 平台分配套餐给代理

  • WHEN 平台将套餐(ID 为 1001)分配给代理(用户 ID 为 123),成本价为 25.00 元
  • THEN 系统创建分配记录,agent_id 为 123,package_id 为 1001,cost_price 为 25.00,状态为 1(有效)

Scenario: 代理设置零售价

  • WHEN 代理(用户 ID 为 123)为套餐(ID 为 1001)设置零售价为 30.00 元
  • THEN 系统更新分配记录,retail_price 为 30.00

Scenario: 代理零售价超过 2 倍成本价

  • WHEN 代理设置零售价为 60.00 元,成本价为 25.00 元(2 倍为 50.00 元)
  • THEN 系统拒绝设置,返回错误信息"零售价不能超过成本价的 2 倍"

Requirement: 套餐数据校验

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

校验规则:

  • 套餐编码(package_code):必填,长度 1-50 字符,唯一
  • 套餐名称(package_name):必填,长度 1-255 字符
  • 套餐系列 ID(series_id):必填,≥ 1,必须是有效的套餐系列 ID
  • 套餐类型(package_type):必填,枚举值 "formal" | "addon"
  • 套餐时长(duration_months):必填,≥ 0(正式套餐 ≥ 1,加油包为 0)
  • 真流量额度(real_data_mb):可选,≥ 0
  • 虚流量额度(virtual_data_mb):可选,≥ 0
  • 总流量额度(data_amount_mb):必填,≥ 0,必须等于 real_data_mb + virtual_data_mb
  • 套餐价格(price):必填,≥ 0,最多 2 位小数
  • 状态(status):必填,枚举值 1(上架) | 2(下架)

Scenario: 创建套餐时价格为负数

  • WHEN 平台创建套餐,价格为 -10.00
  • THEN 系统拒绝创建,返回错误信息"套餐价格必须 ≥ 0"

Scenario: 创建套餐时套餐编码重复

  • WHEN 平台创建套餐,套餐编码为已存在的 "PKG-M-001"
  • THEN 系统拒绝创建,返回错误信息"套餐编码已存在"

Scenario: 创建正式套餐时时长为 0

  • WHEN 平台创建正式套餐,套餐类型为 "formal",时长为 0
  • THEN 系统拒绝创建,返回错误信息"正式套餐时长必须 ≥ 1"