实现 IoT SIM 管理模块数据模型和数据库结构
- 添加 IoT 核心业务表:运营商、IoT 卡、设备、号卡、套餐、订单等 - 添加分佣系统表:分佣规则、分佣记录、运营商结算等 - 添加轮询和流量管理表:轮询配置、流量使用记录等 - 添加财务和系统管理表:佣金提现、换卡申请等 - 实现完整的 GORM 模型和常量定义 - 添加数据库迁移脚本和详细文档 - 集成 OpenSpec 工作流工具(opsx 命令和 skills) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
304
openspec/specs/iot-card/spec.md
Normal file
304
openspec/specs/iot-card/spec.md
Normal file
@@ -0,0 +1,304 @@
|
||||
# IoT Card Management
|
||||
|
||||
## Purpose
|
||||
|
||||
Manage IoT cards (SIM cards) for the IoT management system, including inventory management, distribution, activation, status tracking, and Gateway integration.
|
||||
|
||||
This capability supports:
|
||||
- IoT card entity definition and lifecycle management
|
||||
- Platform self-operation and agent distribution models
|
||||
- Integration with Gateway project for real-time status synchronization
|
||||
- Batch import and multi-dimensional querying
|
||||
- Support for normal cards (require real-name verification) and industry cards (no real-name required)
|
||||
|
||||
## Requirements
|
||||
|
||||
### Requirement: IoT 卡实体定义
|
||||
|
||||
系统 SHALL 定义 IoT 卡(IotCard)实体,包含 IoT 卡(物联网卡/流量卡/SIM卡)的商品属性、状态属性、所有权信息和 Gateway 集成字段。
|
||||
|
||||
**核心概念**: IoT 卡 = 物联网卡 = SIM 卡 = 网卡 = 流量卡(同一个东西,不同叫法)。系统使用 ICCID 作为 IoT 卡的唯一标识。
|
||||
|
||||
**卡业务类型**:
|
||||
- **普通卡(normal)**: 需要实名认证才能激活使用,遵循运营商实名制要求
|
||||
- **行业卡(industry)**: 不需要实名认证,可以直接激活使用,适用于企业/行业客户批量采购场景
|
||||
|
||||
**实体字段**:
|
||||
|
||||
**商品属性**:
|
||||
- `id`: IoT 卡 ID(主键,BIGINT)
|
||||
- `iccid`: ICCID(VARCHAR(50),唯一,国际移动用户识别码,IoT卡的唯一标识)
|
||||
- `card_type`: 卡类型(VARCHAR(50),如 "4G"、"5G"、"NB-IoT")
|
||||
- `card_category`: 卡业务类型(VARCHAR(20),枚举值:"normal"-普通卡 | "industry"-行业卡,默认 "normal")
|
||||
- `carrier_id`: 运营商 ID(BIGINT,关联 carriers 表,如中国移动、中国联通、中国电信)
|
||||
- `imsi`: IMSI(VARCHAR(50),可选,国际移动用户识别码)
|
||||
- `msisdn`: 手机号码(VARCHAR(20),可选)
|
||||
- `batch_no`: 批次号(VARCHAR(100),用于批量导入追溯)
|
||||
- `supplier`: 供应商名称(VARCHAR(255),可选)
|
||||
- `cost_price`: 成本价(DECIMAL(10,2),平台进货价)
|
||||
- `distribute_price`: 分销价(DECIMAL(10,2),分销给代理的价格,仅当 owner_type 为 agent 时有值)
|
||||
|
||||
**所有权和状态**:
|
||||
- `status`: IoT 卡状态(INT,1-在库 2-已分销 3-已激活 4-已停用)
|
||||
- `owner_type`: 所有者类型(VARCHAR(20),"platform"-平台自营 | "agent"-代理商 | "user"-用户 | "device"-设备)
|
||||
- `owner_id`: 所有者 ID(BIGINT,platform 时为 0,agent/user/device 时为对应的 ID)
|
||||
- `activated_at`: 激活时间(TIMESTAMP,可空)
|
||||
|
||||
**Gateway 集成字段**(从 Gateway 项目同步):
|
||||
- `activation_status`: 激活状态(INT,0-未激活 1-已激活)
|
||||
- `real_name_status`: 实名状态(INT,0-未实名 1-已实名)
|
||||
- `network_status`: 网络状态(INT,0-停机 1-开机)
|
||||
- `data_usage_mb`: 累计流量使用(BIGINT,MB 为单位,默认 0)
|
||||
- `last_sync_time`: 最后一次与 Gateway 同步时间(TIMESTAMP,可空)
|
||||
|
||||
**轮询控制字段**:
|
||||
- `enable_polling`: 是否参与轮询(BOOLEAN,默认 true,用于控制是否对该卡进行定时轮询)
|
||||
- `last_data_check_at`: 最后一次卡流量检查时间(TIMESTAMP,可空,记录上次轮询卡流量的时间)
|
||||
- `last_real_name_check_at`: 最后一次实名检查时间(TIMESTAMP,可空,记录上次轮询实名状态的时间)
|
||||
|
||||
**系统字段**:
|
||||
- `created_at`: 创建时间(TIMESTAMP,自动填充)
|
||||
- `updated_at`: 更新时间(TIMESTAMP,自动填充)
|
||||
|
||||
#### Scenario: 创建平台自营 IoT 卡
|
||||
|
||||
- **WHEN** 平台批量导入 IoT 卡数据,ICCID 为 "89860123456789012345"
|
||||
- **THEN** 系统创建 IoT 卡记录,`owner_type` 为 "platform",`owner_id` 为 0,状态为 1(在库),`activation_status` 为 0(未激活)
|
||||
|
||||
#### Scenario: 平台分销 IoT 卡给代理
|
||||
|
||||
- **WHEN** 平台将在库 IoT 卡分销给代理商(用户 ID 为 123),设置分销价为 50.00 元
|
||||
- **THEN** 系统将 IoT 卡状态从 1(在库) 变更为 2(已分销),`owner_type` 变更为 "agent",`owner_id` 设置为 123,`distribute_price` 设置为 50.00
|
||||
|
||||
#### Scenario: IoT 卡绑定到设备
|
||||
|
||||
- **WHEN** 用户将 IoT 卡(ICCID 为 "8986...")绑定到设备(ID 为 1001)
|
||||
- **THEN** 系统在 `device_sim_bindings` 表创建绑定记录,IoT 卡的 `owner_type` 变更为 "device",`owner_id` 变更为 1001
|
||||
|
||||
#### Scenario: IoT 卡直接销售给用户
|
||||
|
||||
- **WHEN** 平台或代理将 IoT 卡直接销售给用户(用户 ID 为 2001)
|
||||
- **THEN** 系统创建套餐订单记录,IoT 卡的 `owner_type` 变更为 "user",`owner_id` 变更为 2001
|
||||
|
||||
#### Scenario: 行业卡无需实名认证
|
||||
|
||||
- **WHEN** 创建卡业务类型为 "industry"(行业卡)的 IoT 卡
|
||||
- **THEN** 系统允许该卡在 `real_name_status` 为 0(未实名)的情况下激活使用,不强制要求实名认证
|
||||
|
||||
#### Scenario: 普通卡需要实名认证
|
||||
|
||||
- **WHEN** 创建卡业务类型为 "normal"(普通卡)的 IoT 卡
|
||||
- **THEN** 系统要求该卡必须先完成实名认证(`real_name_status` 为 1)才能激活使用
|
||||
|
||||
---
|
||||
|
||||
### Requirement: IoT 卡状态流转
|
||||
|
||||
系统 SHALL 管理 IoT 卡的状态流转,确保状态变更符合业务规则。
|
||||
|
||||
**状态定义**:
|
||||
- **1-在库**: IoT 卡在平台库存中,未分销
|
||||
- **2-已分销**: IoT 卡已分销给代理商,代理可销售
|
||||
- **3-已激活**: IoT 卡已被终端用户激活使用
|
||||
- **4-已停用**: IoT 卡已停用,不可使用
|
||||
|
||||
**状态流转规则**:
|
||||
- 在库(1) → 已分销(2): 平台分销给代理
|
||||
- 在库(1) → 已激活(3): 平台自营直接销售给用户并激活
|
||||
- 已分销(2) → 已激活(3): 代理销售给用户并激活
|
||||
- 已激活(3) → 已停用(4): 用户或平台主动停用
|
||||
- 已停用(4) → 已激活(3): 用户或平台主动复机(仅在符合业务规则时)
|
||||
|
||||
#### Scenario: 代理销售 IoT 卡给用户
|
||||
|
||||
- **WHEN** 代理商销售已分销 IoT 卡给终端用户并激活
|
||||
- **THEN** 系统将 IoT 卡状态从 2(已分销) 变更为 3(已激活),`activated_at` 记录激活时间,`activation_status` 从 Gateway 同步后变更为 1
|
||||
|
||||
#### Scenario: 平台自营销售 IoT 卡
|
||||
|
||||
- **WHEN** 平台直接销售在库 IoT 卡给终端用户并激活
|
||||
- **THEN** 系统将 IoT 卡状态从 1(在库) 变更为 3(已激活),`owner_type` 保持 "platform",`activated_at` 记录激活时间
|
||||
|
||||
#### Scenario: 停用已激活 IoT 卡
|
||||
|
||||
- **WHEN** 用户或平台停用已激活 IoT 卡
|
||||
- **THEN** 系统将 IoT 卡状态从 3(已激活) 变更为 4(已停用),通过 Gateway API 执行停机操作
|
||||
|
||||
---
|
||||
|
||||
### Requirement: IoT 卡平台自营和代理分销
|
||||
|
||||
系统 SHALL 支持 IoT 卡的平台自营销售和代理分销两种模式,通过 `owner_type` 和 `owner_id` 区分所有者。
|
||||
|
||||
**平台自营**:
|
||||
- `owner_type` 为 "platform"
|
||||
- `owner_id` 为 0
|
||||
- 平台直接销售给终端用户
|
||||
- 销售价格由平台自主定价
|
||||
|
||||
**代理分销**:
|
||||
- `owner_type` 为 "agent"
|
||||
- `owner_id` 为代理用户 ID
|
||||
- 代理商可以销售给终端用户或下级代理
|
||||
- 分销价格由平台设置(`distribute_price`),代理商可在分销价基础上加价(但不能超过 2 倍)
|
||||
|
||||
#### Scenario: 查询平台自营 IoT 卡库存
|
||||
|
||||
- **WHEN** 查询平台自营 IoT 卡库存
|
||||
- **THEN** 系统返回 `owner_type` 为 "platform" 且 `status` 为 1(在库) 的 IoT 卡列表
|
||||
|
||||
#### Scenario: 查询代理分销 IoT 卡库存
|
||||
|
||||
- **WHEN** 代理商(用户 ID 为 123)查询自己的 IoT 卡库存
|
||||
- **THEN** 系统返回 `owner_type` 为 "agent" 且 `owner_id` 为 123 且 `status` 为 2(已分销) 的 IoT 卡列表
|
||||
|
||||
#### Scenario: 代理加价销售 IoT 卡套餐
|
||||
|
||||
- **WHEN** 代理商为已分销 IoT 卡设置套餐售价
|
||||
- **THEN** 系统校验套餐售价不超过分销价的 2 倍,校验通过后允许销售
|
||||
|
||||
---
|
||||
|
||||
### Requirement: IoT 卡批量导入
|
||||
|
||||
系统 SHALL 支持批量导入 IoT 卡数据,用于初始化库存或补充库存。
|
||||
|
||||
**导入字段**:
|
||||
- ICCID(必填)
|
||||
- 卡类型(必填,如 "4G"、"5G"、"NB-IoT")
|
||||
- 卡业务类型(可选,枚举值 "normal" | "industry",默认 "normal")
|
||||
- 运营商 ID(必填,从 carriers 表中选择)
|
||||
- IMSI(可选)
|
||||
- 手机号码(可选)
|
||||
- 供应商(可选)
|
||||
- 成本价(必填)
|
||||
- 批次号(必填)
|
||||
|
||||
**导入规则**:
|
||||
- ICCID 必须唯一,重复 ICCID 将被拒绝
|
||||
- 导入的 IoT 卡默认状态为 1(在库),所有者为平台(`owner_type` 为 "platform",`owner_id` 为 0)
|
||||
- 导入成功后记录操作日志
|
||||
|
||||
#### Scenario: 批量导入 IoT 卡成功
|
||||
|
||||
- **WHEN** 平台上传包含 100 条 IoT 卡数据的 CSV 文件
|
||||
- **THEN** 系统创建 100 条 IoT 卡记录,状态为 1(在库),所有者为平台,返回导入成功消息
|
||||
|
||||
#### Scenario: 批量导入包含重复 ICCID
|
||||
|
||||
- **WHEN** 平台上传的 CSV 文件中包含已存在的 ICCID
|
||||
- **THEN** 系统拒绝重复 ICCID 的 IoT 卡,返回错误信息并列出重复 ICCID,其他有效 IoT 卡正常导入
|
||||
|
||||
---
|
||||
|
||||
### Requirement: IoT 卡查询和筛选
|
||||
|
||||
系统 SHALL 支持多维度查询和筛选 IoT 卡,包括状态、所有者、批次号、卡类型等。
|
||||
|
||||
**查询条件**:
|
||||
- ICCID(精确匹配或模糊匹配)
|
||||
- IoT 卡状态(单选或多选)
|
||||
- 所有者类型(platform | agent | user | device)
|
||||
- 所有者 ID(仅当所有者类型为 agent/user/device 时有效)
|
||||
- 批次号(精确匹配)
|
||||
- 卡类型(单选或多选)
|
||||
- 运营商 ID(单选或多选,从 carriers 表选择)
|
||||
- 激活状态(0-未激活 | 1-已激活)
|
||||
- 实名状态(0-未实名 | 1-已实名)
|
||||
- 网络状态(0-停机 | 1-开机)
|
||||
- 是否参与轮询(true | false)
|
||||
- 激活时间范围(开始时间 - 结束时间)
|
||||
- 创建时间范围(开始时间 - 结束时间)
|
||||
|
||||
**分页**:
|
||||
- 默认每页 20 条,最大每页 100 条
|
||||
- 返回总记录数和总页数
|
||||
|
||||
#### Scenario: 查询特定批次的在库 IoT 卡
|
||||
|
||||
- **WHEN** 平台查询批次号为 "BATCH-2025-001" 且状态为 1(在库) 的 IoT 卡
|
||||
- **THEN** 系统返回符合条件的 IoT 卡列表,包含 ICCID、类型、运营商、成本价等信息
|
||||
|
||||
#### Scenario: 代理查询自己的已分销 IoT 卡
|
||||
|
||||
- **WHEN** 代理商(用户 ID 为 123)查询自己的已分销 IoT 卡
|
||||
- **THEN** 系统返回 `owner_type` 为 "agent" 且 `owner_id` 为 123 且 `status` 为 2(已分销) 的 IoT 卡列表
|
||||
|
||||
#### Scenario: 分页查询 IoT 卡
|
||||
|
||||
- **WHEN** 平台查询在库 IoT 卡,指定每页 50 条,查询第 2 页
|
||||
- **THEN** 系统返回第 51-100 条 IoT 卡记录,以及总记录数和总页数
|
||||
|
||||
---
|
||||
|
||||
### Requirement: Gateway 集成
|
||||
|
||||
系统 SHALL 预留 IoT 卡状态相关字段,用于后续与 Gateway 项目集成。
|
||||
|
||||
**集成字段**:
|
||||
- `activation_status`: 激活状态(从 Gateway 同步)
|
||||
- `real_name_status`: 实名状态(从 Gateway 同步)
|
||||
- `network_status`: 网络状态(从 Gateway 同步)
|
||||
- `data_usage_mb`: 累计流量使用(从 Gateway 同步)
|
||||
- `last_sync_time`: 最后同步时间
|
||||
|
||||
**集成说明**:
|
||||
- 本阶段只设计数据模型字段,不实现 Gateway HTTP 客户端代码
|
||||
- 后续 Service 层将调用 Gateway API 获取 IoT 卡状态并更新这些字段
|
||||
- Gateway 使用 AES 加密 + MD5 签名的统一传输协议(参考 design.md)
|
||||
|
||||
**Gateway API 功能**:
|
||||
- 查询 IoT 卡状态(激活状态、实名状态、网络状态)
|
||||
- 查询流量详情(累计流量使用、剩余流量)
|
||||
- 停复机操作(停机、复机)
|
||||
- 实名认证操作
|
||||
|
||||
#### Scenario: 预留 Gateway 集成字段
|
||||
|
||||
- **WHEN** 创建 IoT 卡记录
|
||||
- **THEN** 系统初始化 Gateway 相关字段为默认值:`activation_status` 为 0,`real_name_status` 为 0,`network_status` 为 0,`data_usage_mb` 为 0,`last_sync_time` 为空
|
||||
|
||||
#### Scenario: 从 Gateway 同步 IoT 卡状态
|
||||
|
||||
- **WHEN** Service 层调用 Gateway API 查询 IoT 卡状态
|
||||
- **THEN** 系统更新 IoT 卡的 `activation_status`、`real_name_status`、`network_status`、`data_usage_mb` 和 `last_sync_time` 字段
|
||||
|
||||
---
|
||||
|
||||
### Requirement: IoT 卡数据校验
|
||||
|
||||
系统 SHALL 对 IoT 卡数据进行校验,确保数据完整性和一致性。
|
||||
|
||||
**校验规则**:
|
||||
- ICCID(iccid):必填,长度 19-20 字符,唯一
|
||||
- 卡类型(card_type):必填,长度 1-50 字符
|
||||
- 卡业务类型(card_category):必填,枚举值 "normal"(普通卡) | "industry"(行业卡),默认 "normal"
|
||||
- 运营商 ID(carrier_id):必填,≥ 1,必须是有效的运营商 ID
|
||||
- 成本价(cost_price):必填,≥ 0,最多 2 位小数
|
||||
- 分销价(distribute_price):可选,≥ 0,最多 2 位小数,≥ 成本价
|
||||
- 所有者类型(owner_type):必填,枚举值 "platform" | "agent" | "user" | "device"
|
||||
- 所有者 ID(owner_id):必填,≥ 0,当 owner_type 为 "platform" 时必须为 0
|
||||
- 激活状态(activation_status):必填,枚举值 0(未激活) | 1(已激活)
|
||||
- 实名状态(real_name_status):必填,枚举值 0(未实名) | 1(已实名),当 card_category 为 "industry"(行业卡)时可以保持 0
|
||||
- 网络状态(network_status):必填,枚举值 0(停机) | 1(开机)
|
||||
- 轮询开关(enable_polling):必填,布尔值 true | false
|
||||
|
||||
#### Scenario: 创建 IoT 卡时 ICCID 格式错误
|
||||
|
||||
- **WHEN** 平台创建 IoT 卡,ICCID 长度为 15(小于 19)
|
||||
- **THEN** 系统拒绝创建,返回错误信息"ICCID 长度必须为 19-20 字符"
|
||||
|
||||
#### Scenario: 创建 IoT 卡时 ICCID 重复
|
||||
|
||||
- **WHEN** 平台创建 IoT 卡,ICCID 为已存在的 "89860123456789012345"
|
||||
- **THEN** 系统拒绝创建,返回错误信息"ICCID 已存在"
|
||||
|
||||
#### Scenario: 创建 IoT 卡时成本价为负数
|
||||
|
||||
- **WHEN** 平台创建 IoT 卡,成本价为 -10.00
|
||||
- **THEN** 系统拒绝创建,返回错误信息"成本价必须 ≥ 0"
|
||||
|
||||
#### Scenario: 创建 IoT 卡时分销价低于成本价
|
||||
|
||||
- **WHEN** 平台创建 IoT 卡,成本价为 50.00,分销价为 40.00
|
||||
- **THEN** 系统拒绝创建,返回错误信息"分销价不能低于成本价"
|
||||
Reference in New Issue
Block a user