# IoT SIM 管理系统 - 表结构详细说明

本文档详细说明了 IoT SIM 管理系统的 26 张数据库表的结构、字段定义、索引设计和业务约束。

---

## 目录

1. [核心业务表](#核心业务表)
   - [carriers - 运营商](#1-carriers---运营商)
   - [iot_cards - IoT 卡](#2-iot_cards---iot-卡)
   - [devices - 设备](#3-devices---设备)
   - [number_cards - 号卡](#4-number_cards---号卡)

2. [套餐与流量管理表](#套餐与流量管理表)
   - [package_series - 套餐系列](#5-package_series---套餐系列)
   - [packages - 套餐](#6-packages---套餐)
   - [agent_package_allocations - 代理套餐分配](#7-agent_package_allocations---代理套餐分配)
   - [device_sim_bindings - 设备-IoT卡绑定](#8-device_sim_bindings---设备-iot卡绑定)
   - [package_usages - 套餐使用情况](#9-package_usages---套餐使用情况)
   - [polling_configs - 轮询配置](#10-polling_configs---轮询配置)
   - [data_usage_records - 流量使用记录](#11-data_usage_records---流量使用记录)

3. [订单管理表](#订单管理表)
   - [orders - 订单](#12-orders---订单)

4. [分佣系统表](#分佣系统表)
   - [agent_hierarchies - 代理层级关系](#13-agent_hierarchies---代理层级关系)
   - [commission_rules - 分佣规则](#14-commission_rules---分佣规则)
   - [commission_ladder - 分佣阶梯](#15-commission_ladder---分佣阶梯)
   - [commission_combined_conditions - 组合分佣条件](#16-commission_combined_conditions---组合分佣条件)
   - [commission_records - 分佣记录](#17-commission_records---分佣记录)
   - [commission_approvals - 分佣审批](#18-commission_approvals---分佣审批)
   - [commission_templates - 分佣模板](#19-commission_templates---分佣模板)
   - [carrier_settlements - 运营商结算](#20-carrier_settlements---运营商结算)

5. [财务管理表](#财务管理表)
   - [commission_withdrawal_requests - 提现申请](#21-commission_withdrawal_requests---提现申请)
   - [commission_withdrawal_settings - 提现设置](#22-commission_withdrawal_settings---提现设置)
   - [payment_merchant_settings - 收款商户设置](#23-payment_merchant_settings---收款商户设置)

6. [系统管理表](#系统管理表)
   - [dev_capability_configs - 开发能力配置](#24-dev_capability_configs---开发能力配置)
   - [card_replacement_requests - 换卡申请](#25-card_replacement_requests---换卡申请)

---

## 核心业务表

### 1. carriers - 运营商

**表名**: `carriers`

**用途**: 管理三大运营商信息及其 API 配置

**字段说明**:

| 字段名 | 类型 | 约束 | 说明 |
|--------|------|------|------|
| id | BIGSERIAL | PRIMARY KEY | 运营商ID |
| carrier_code | VARCHAR(50) | UNIQUE, NOT NULL | 运营商编码(CMCC/CUCC/CTCC) |
| carrier_name | VARCHAR(100) | NOT NULL | 运营商名称(中国移动/中国联通/中国电信) |
| api_endpoint | VARCHAR(500) | | API 接口地址 |
| api_credentials | JSONB | | API 凭证(JSON格式) |
| status | INT | NOT NULL, DEFAULT 1 | 状态 1-启用 2-禁用 |
| created_at | TIMESTAMPTZ | NOT NULL, DEFAULT NOW() | 创建时间 |
| updated_at | TIMESTAMPTZ | NOT NULL, DEFAULT NOW() | 更新时间 |

**索引**:
- 主键索引: `id`
- 唯一索引: `carrier_code`

**预置数据**:
```sql
INSERT INTO carriers (carrier_code, carrier_name, status) VALUES
    ('CMCC', '中国移动', 1),
    ('CUCC', '中国联通', 1),
    ('CTCC', '中国电信', 1);
```

**GORM 模型**: `internal/model/carrier.go:5`

---

### 2. iot_cards - IoT 卡

**表名**: `iot_cards`

**用途**: 物联网卡/流量卡的统一管理实体,支持平台自营、代理分销、用户购买等所有权模式

**字段说明**:

| 字段名 | 类型 | 约束 | 说明 |
|--------|------|------|------|
| id | BIGSERIAL | PRIMARY KEY | IoT 卡 ID |
| iccid | VARCHAR(50) | UNIQUE, NOT NULL | ICCID(唯一标识,20位数字) |
| card_type | VARCHAR(50) | NOT NULL | 卡类型 |
| card_category | VARCHAR(20) | NOT NULL, DEFAULT 'normal' | 卡业务类型 normal-普通卡 industry-行业卡 |
| carrier_id | BIGINT | NOT NULL | 运营商ID |
| imsi | VARCHAR(50) | | IMSI |
| msisdn | VARCHAR(20) | | MSISDN(手机号码) |
| batch_no | VARCHAR(100) | | 批次号 |
| supplier | VARCHAR(255) | | 供应商 |
| cost_price | DECIMAL(10,2) | DEFAULT 0 | 成本价(元) |
| distribute_price | DECIMAL(10,2) | DEFAULT 0 | 分销价(元) |
| status | INT | NOT NULL, DEFAULT 1 | 状态 1-在库 2-已分销 3-已激活 4-已停用 |
| owner_type | VARCHAR(20) | NOT NULL, DEFAULT 'platform' | 所有者类型 platform-平台 agent-代理 user-用户 device-设备 |
| owner_id | BIGINT | NOT NULL, DEFAULT 0 | 所有者ID |
| activated_at | TIMESTAMPTZ | | 激活时间 |
| activation_status | INT | NOT NULL, DEFAULT 0 | 激活状态 0-未激活 1-已激活 |
| real_name_status | INT | NOT NULL, DEFAULT 0 | 实名状态 0-未实名 1-已实名(行业卡可以保持0) |
| network_status | INT | NOT NULL, DEFAULT 0 | 网络状态 0-停机 1-开机 |
| data_usage_mb | BIGINT | DEFAULT 0 | 累计流量使用(MB) |
| enable_polling | BOOLEAN | DEFAULT true | 是否参与轮询 true-参与 false-不参与 |
| last_data_check_at | TIMESTAMPTZ | | 最后一次流量检查时间 |
| last_real_name_check_at | TIMESTAMPTZ | | 最后一次实名检查时间 |
| last_sync_time | TIMESTAMPTZ | | 最后一次与Gateway同步时间 |
| created_at | TIMESTAMPTZ | NOT NULL, DEFAULT NOW() | 创建时间 |
| updated_at | TIMESTAMPTZ | NOT NULL, DEFAULT NOW() | 更新时间 |

**索引**:
- 主键索引: `id`
- 唯一索引: `iccid`
- 组合索引: `(carrier_id, status)`
- 组合索引: `(owner_type, owner_id)`
- 单列索引: `batch_no`

**业务约束**:
- `iccid` 必须唯一,通常为 20 位数字
- `card_category = 'industry'` 时,`real_name_status` 可以保持 0 (行业卡无需实名)
- `card_category = 'normal'` 时,必须实名才能激活
- `owner_type` 和 `owner_id` 组合表示所有权
- `enable_polling = false` 的卡不参与轮询检查

**GORM 模型**: `internal/model/iot_card.go:8`

---

### 3. devices - 设备

**表名**: `devices`

**用途**: 设备管理,支持 1-4 张 SIM 卡

**字段说明**:

| 字段名 | 类型 | 约束 | 说明 |
|--------|------|------|------|
| id | BIGSERIAL | PRIMARY KEY | 设备ID |
| device_code | VARCHAR(100) | UNIQUE, NOT NULL | 设备唯一编码 |
| device_name | VARCHAR(255) | NOT NULL | 设备名称 |
| device_type | VARCHAR(100) | | 设备类型 |
| sim_slots | INT | DEFAULT 1 | SIM卡槽数量(1-4) |
| owner_type | VARCHAR(20) | NOT NULL, DEFAULT 'platform' | 所有者类型 platform-平台 agent-代理 user-用户 |
| owner_id | BIGINT | NOT NULL, DEFAULT 0 | 所有者ID |
| status | INT | NOT NULL, DEFAULT 1 | 归属状态 1-在库 2-已分销 |
| activated_at | TIMESTAMPTZ | | 激活时间 |
| created_at | TIMESTAMPTZ | NOT NULL, DEFAULT NOW() | 创建时间 |
| updated_at | TIMESTAMPTZ | NOT NULL, DEFAULT NOW() | 更新时间 |

**索引**:
- 主键索引: `id`
- 唯一索引: `device_code`
- 组合索引: `(owner_type, owner_id)`

**业务约束**:
- `sim_slots` 取值范围 1-4
- 通过 `device_sim_bindings` 表管理设备与 IoT 卡的绑定关系

**GORM 模型**: `internal/model/device.go:5`

---

### 4. number_cards - 号卡

**表名**: `number_cards`

**用途**: 号卡业务(完全独立的业务线),从上游平台下单

**字段说明**:

| 字段名 | 类型 | 约束 | 说明 |
|--------|------|------|------|
| id | BIGSERIAL | PRIMARY KEY | 号卡ID |
| virtual_product_code | VARCHAR(100) | UNIQUE, NOT NULL | 虚拟商品编码(用于对应运营商订单) |
| card_name | VARCHAR(255) | NOT NULL | 号卡名称 |
| card_type | VARCHAR(50) | | 号卡类型 |
| carrier | VARCHAR(50) | | 运营商 |
| data_amount_mb | BIGINT | | 流量额度(MB) |
| price | DECIMAL(10,2) | | 价格(元) |
| agent_id | BIGINT | | 代理用户ID |
| status | INT | NOT NULL, DEFAULT 1 | 状态 1-在售 2-下架 |
| created_at | TIMESTAMPTZ | NOT NULL, DEFAULT NOW() | 创建时间 |
| updated_at | TIMESTAMPTZ | NOT NULL, DEFAULT NOW() | 更新时间 |

**索引**:
- 主键索引: `id`
- 唯一索引: `virtual_product_code`
- 单列索引: `agent_id`

**业务说明**:
- 号卡是完全独立的业务线,与 IoT 卡无关
- 使用虚拟商品编码映射运营商订单
- 从上游平台下单,不涉及本地库存管理

**GORM 模型**: `internal/model/number_card.go:8`

---

## 套餐与流量管理表

### 5. package_series - 套餐系列

**表名**: `package_series`

**用途**: 套餐的分组,用于一次性分佣规则配置

**字段说明**:

| 字段名 | 类型 | 约束 | 说明 |
|--------|------|------|------|
| id | BIGSERIAL | PRIMARY KEY | 套餐系列ID |
| series_code | VARCHAR(100) | UNIQUE, NOT NULL | 系列编码 |
| series_name | VARCHAR(255) | NOT NULL | 系列名称 |
| description | TEXT | | 描述 |
| status | INT | NOT NULL, DEFAULT 1 | 状态 1-启用 2-禁用 |
| created_at | TIMESTAMPTZ | NOT NULL, DEFAULT NOW() | 创建时间 |
| updated_at | TIMESTAMPTZ | NOT NULL, DEFAULT NOW() | 更新时间 |

**索引**:
- 主键索引: `id`
- 唯一索引: `series_code`

**业务说明**:
- 套餐系列用于分组管理套餐
- 分佣规则可以按套餐系列统一配置

**GORM 模型**: `internal/model/package.go:7`

---

### 6. packages - 套餐

**表名**: `packages`

**用途**: 套餐定义,只适用于 IoT 卡,支持真流量/虚流量共存机制

**字段说明**:

| 字段名 | 类型 | 约束 | 说明 |
|--------|------|------|------|
| id | BIGSERIAL | PRIMARY KEY | 套餐ID |
| package_code | VARCHAR(100) | UNIQUE, NOT NULL | 套餐编码 |
| package_name | VARCHAR(255) | NOT NULL | 套餐名称 |
| series_id | BIGINT | | 套餐系列ID |
| package_type | VARCHAR(50) | NOT NULL | 套餐类型 formal-正式套餐 addon-附加套餐 |
| duration_months | INT | NOT NULL | 套餐时长(月数) 1-月套餐 12-年套餐 |
| data_type | VARCHAR(20) | | 流量类型 real-真流量 virtual-虚流量 |
| real_data_mb | BIGINT | DEFAULT 0 | 真流量额度(MB) |
| virtual_data_mb | BIGINT | DEFAULT 0 | 虚流量额度(MB,用于停机判断) |
| data_amount_mb | BIGINT | DEFAULT 0 | 总流量额度(MB) |
| price | DECIMAL(10,2) | NOT NULL | 套餐价格(元) |
| status | INT | NOT NULL, DEFAULT 1 | 状态 1-启用 2-禁用 |
| created_at | TIMESTAMPTZ | NOT NULL, DEFAULT NOW() | 创建时间 |
| updated_at | TIMESTAMPTZ | NOT NULL, DEFAULT NOW() | 更新时间 |

**索引**:
- 主键索引: `id`
- 唯一索引: `package_code`
- 单列索引: `series_id`

**业务约束**:
- `data_amount_mb = real_data_mb + virtual_data_mb`
- 停机判断基于 `virtual_data_mb` (虚流量用完即停机)
- `package_type = 'formal'` 表示正式套餐,`'addon'` 表示附加套餐

**GORM 模型**: `internal/model/package.go:23`

---

### 7. agent_package_allocations - 代理套餐分配

**表名**: `agent_package_allocations`

**用途**: 为直属下级代理分配套餐,设置佣金模式

**字段说明**:

| 字段名 | 类型 | 约束 | 说明 |
|--------|------|------|------|
| id | BIGSERIAL | PRIMARY KEY | 分配ID |
| agent_id | BIGINT | NOT NULL | 代理用户ID |
| package_id | BIGINT | NOT NULL | 套餐ID |
| cost_price | DECIMAL(10,2) | NOT NULL | 成本价(元) |
| retail_price | DECIMAL(10,2) | NOT NULL | 零售价(元) |
| status | INT | NOT NULL, DEFAULT 1 | 状态 1-启用 2-禁用 |
| created_at | TIMESTAMPTZ | NOT NULL, DEFAULT NOW() | 创建时间 |
| updated_at | TIMESTAMPTZ | NOT NULL, DEFAULT NOW() | 更新时间 |

**索引**:
- 主键索引: `id`
- 组合唯一索引: `(agent_id, package_id)`

**业务说明**:
- 平台或上级代理为下级代理分配套餐
- `cost_price` 是代理的采购成本
- `retail_price` 是代理的零售价格
- 佣金 = `retail_price - cost_price`

**GORM 模型**: `internal/model/package.go:48`

---

### 8. device_sim_bindings - 设备-IoT卡绑定

**表名**: `device_sim_bindings`

**用途**: 管理设备与 IoT 卡的多对多绑定关系(1 设备绑定 1-4 张 IoT 卡)

**字段说明**:

| 字段名 | 类型 | 约束 | 说明 |
|--------|------|------|------|
| id | BIGSERIAL | PRIMARY KEY | 绑定ID |
| device_id | BIGINT | NOT NULL | 设备ID |
| iot_card_id | BIGINT | NOT NULL | IoT卡ID |
| slot_position | INT | | 插槽位置(1, 2, 3, 4) |
| bind_status | INT | DEFAULT 1 | 绑定状态 1-已绑定 2-已解绑 |
| bind_time | TIMESTAMPTZ | | 绑定时间 |
| unbind_time | TIMESTAMPTZ | | 解绑时间 |
| created_at | TIMESTAMPTZ | NOT NULL, DEFAULT NOW() | 创建时间 |
| updated_at | TIMESTAMPTZ | NOT NULL, DEFAULT NOW() | 更新时间 |

**索引**:
- 主键索引: `id`
- 组合索引: `(device_id, iot_card_id)`
- 单列索引: `iot_card_id`

**业务约束**:
- 一个设备最多绑定 4 张 IoT 卡
- `slot_position` 取值范围 1-4
- 同一设备的同一插槽位置只能绑定一张激活状态的 IoT 卡

**GORM 模型**: `internal/model/package.go:66`

---

### 9. package_usages - 套餐使用情况

**表名**: `package_usages`

**用途**: 跟踪单卡套餐和设备级套餐的流量使用

**字段说明**:

| 字段名 | 类型 | 约束 | 说明 |
|--------|------|------|------|
| id | BIGSERIAL | PRIMARY KEY | 套餐使用ID |
| order_id | BIGINT | NOT NULL | 订单ID |
| package_id | BIGINT | NOT NULL | 套餐ID |
| usage_type | VARCHAR(20) | NOT NULL | 使用类型 single_card-单卡套餐 device-设备级套餐 |
| iot_card_id | BIGINT | | IoT卡ID(单卡套餐时有值) |
| device_id | BIGINT | | 设备ID(设备级套餐时有值) |
| data_limit_mb | BIGINT | NOT NULL | 流量限额(MB) |
| data_usage_mb | BIGINT | DEFAULT 0 | 已使用流量(MB) |
| real_data_usage_mb | BIGINT | DEFAULT 0 | 真流量使用(MB) |
| virtual_data_usage_mb | BIGINT | DEFAULT 0 | 虚流量使用(MB) |
| activated_at | TIMESTAMPTZ | NOT NULL | 套餐生效时间 |
| expires_at | TIMESTAMPTZ | NOT NULL | 套餐过期时间 |
| status | INT | NOT NULL, DEFAULT 1 | 状态 1-生效中 2-已用完 3-已过期 |
| last_package_check_at | TIMESTAMPTZ | | 最后一次套餐流量检查时间 |
| created_at | TIMESTAMPTZ | NOT NULL, DEFAULT NOW() | 创建时间 |
| updated_at | TIMESTAMPTZ | NOT NULL, DEFAULT NOW() | 更新时间 |

**索引**:
- 主键索引: `id`
- 单列索引: `order_id`
- 单列索引: `iot_card_id`
- 单列索引: `device_id`
- 组合索引: `(status, last_package_check_at)`

**业务约束**:
- `usage_type = 'single_card'` 时,`iot_card_id` 有值,`device_id` 为空
- `usage_type = 'device'` 时,`device_id` 有值,`iot_card_id` 为空
- `data_usage_mb = real_data_usage_mb + virtual_data_usage_mb`
- 轮询检查时更新 `last_package_check_at`

**GORM 模型**: `internal/model/package.go:85`

---

### 10. polling_configs - 轮询配置

**表名**: `polling_configs`

**用途**: 轮询配置,支持梯度轮询策略(实名检查、卡流量检查、套餐流量检查)

**字段说明**:

| 字段名 | 类型 | 约束 | 说明 |
|--------|------|------|------|
| id | BIGSERIAL | PRIMARY KEY | 轮询配置ID |
| config_name | VARCHAR(100) | UNIQUE, NOT NULL | 配置名称(如 未实名卡、实名卡) |
| description | VARCHAR(500) | | 配置描述 |
| card_condition | VARCHAR(50) | | 卡状态条件 not_real_name-未实名 real_name-已实名 activated-已激活 suspended-已停用 |
| carrier_id | BIGINT | | 运营商ID(NULL表示所有运营商) |
| real_name_check_enabled | BOOLEAN | DEFAULT false | 是否启用实名检查 |
| real_name_check_interval | INT | DEFAULT 60 | 实名检查间隔(秒) |
| card_data_check_enabled | BOOLEAN | DEFAULT false | 是否启用卡流量检查 |
| card_data_check_interval | INT | DEFAULT 60 | 卡流量检查间隔(秒) |
| package_check_enabled | BOOLEAN | DEFAULT false | 是否启用套餐流量检查 |
| package_check_interval | INT | DEFAULT 60 | 套餐流量检查间隔(秒) |
| priority | INT | NOT NULL, DEFAULT 100 | 优先级(数字越小优先级越高) |
| status | INT | NOT NULL, DEFAULT 1 | 状态 1-启用 2-禁用 |
| created_at | TIMESTAMPTZ | NOT NULL, DEFAULT NOW() | 创建时间 |
| updated_at | TIMESTAMPTZ | NOT NULL, DEFAULT NOW() | 更新时间 |

**索引**:
- 主键索引: `id`
- 唯一索引: `config_name`
- 单列索引: `carrier_id`
- 单列索引: `priority`

**业务说明**:
- 支持三种独立的轮询检查类型
- 可以按卡状态和运营商配置不同的轮询策略
- 优先级控制多个配置的执行顺序

**GORM 模型**: `internal/model/polling.go:7`

---

### 11. data_usage_records - 流量使用记录

**表名**: `data_usage_records`

**用途**: 记录 IoT 卡每日流量使用情况(历史数据)

**字段说明**:

| 字段名 | 类型 | 约束 | 说明 |
|--------|------|------|------|
| id | BIGSERIAL | PRIMARY KEY | 流量使用记录ID |
| iot_card_id | BIGINT | NOT NULL | IoT卡ID |
| usage_date | DATE | NOT NULL | 使用日期 |
| data_usage_mb | BIGINT | DEFAULT 0 | 流量使用(MB) |
| carrier_sync_data | JSONB | | 运营商同步数据(JSON格式) |
| synced_at | TIMESTAMPTZ | | 同步时间 |
| created_at | TIMESTAMPTZ | NOT NULL, DEFAULT NOW() | 创建时间 |
| updated_at | TIMESTAMPTZ | NOT NULL, DEFAULT NOW() | 更新时间 |

**索引**:
- 主键索引: `id`
- 组合唯一索引: `(iot_card_id, usage_date)`
- 单列索引: `usage_date`

**业务说明**:
- 每日记录一次 IoT 卡的流量使用情况
- `carrier_sync_data` 存储从运营商 Gateway 同步的原始数据
- 用于历史查询和统计分析

**GORM 模型**: `internal/model/data_usage.go:5`

---

## 订单管理表

### 12. orders - 订单

**表名**: `orders`

**用途**: 订单管理,支持两种订单类型:套餐订单(单卡/设备级)、号卡订单

**字段说明**:

| 字段名 | 类型 | 约束 | 说明 |
|--------|------|------|------|
| id | BIGSERIAL | PRIMARY KEY | 订单ID |
| order_no | VARCHAR(100) | UNIQUE, NOT NULL | 订单号(唯一标识) |
| order_type | INT | NOT NULL | 订单类型 1-套餐订单 2-号卡订单 |
| iot_card_id | BIGINT | | IoT卡ID(单卡套餐订单时有值) |
| device_id | BIGINT | | 设备ID(设备级套餐订单时有值) |
| number_card_id | BIGINT | | 号卡ID(号卡订单时有值) |
| package_id | BIGINT | | 套餐ID(套餐订单时有值) |
| user_id | BIGINT | NOT NULL | 用户ID |
| agent_id | BIGINT | | 代理用户ID |
| amount | DECIMAL(10,2) | NOT NULL | 订单金额(元) |
| payment_method | VARCHAR(20) | | 支付方式 wallet-钱包 online-在线支付 carrier-运营商支付 |
| status | INT | NOT NULL, DEFAULT 1 | 状态 1-待支付 2-已支付 3-已完成 4-已取消 5-已退款 |
| carrier_order_id | VARCHAR(255) | | 运营商订单ID |
| carrier_order_data | JSONB | | 运营商订单原始数据(JSON) |
| paid_at | TIMESTAMPTZ | | 支付时间 |
| completed_at | TIMESTAMPTZ | | 完成时间 |
| created_at | TIMESTAMPTZ | NOT NULL, DEFAULT NOW() | 创建时间 |
| updated_at | TIMESTAMPTZ | NOT NULL, DEFAULT NOW() | 更新时间 |

**索引**:
- 主键索引: `id`
- 唯一索引: `order_no`
- 单列索引: `user_id`
- 单列索引: `agent_id`
- 组合索引: `(status, created_at)`

**业务约束**:
- `order_type = 1` (套餐订单):
  - 单卡套餐: `iot_card_id` 有值,`device_id` 为空
  - 设备级套餐: `device_id` 有值,`iot_card_id` 为空
  - `package_id` 必须有值
- `order_type = 2` (号卡订单):
  - `number_card_id` 必须有值
  - `package_id` 为空

**GORM 模型**: `internal/model/order.go:11`

---

## 分佣系统表

### 13. agent_hierarchies - 代理层级关系

**表名**: `agent_hierarchies`

**用途**: 管理代理的树形层级关系

**字段说明**:

| 字段名 | 类型 | 约束 | 说明 |
|--------|------|------|------|
| id | BIGSERIAL | PRIMARY KEY | 层级ID |
| agent_id | BIGINT | UNIQUE, NOT NULL | 代理用户ID |
| parent_agent_id | BIGINT | | 上级代理用户ID |
| agent_level | INT | NOT NULL | 代理层级(1-一级代理, 2-二级代理, ...) |
| agent_path | VARCHAR(500) | | 代理路径(如 /1/2/3/) |
| created_at | TIMESTAMPTZ | NOT NULL, DEFAULT NOW() | 创建时间 |
| updated_at | TIMESTAMPTZ | NOT NULL, DEFAULT NOW() | 更新时间 |

**索引**:
- 主键索引: `id`
- 唯一索引: `agent_id`
- 单列索引: `parent_agent_id`
- 单列索引: `agent_level`

**业务说明**:
- `agent_path` 存储完整的代理路径,便于查询上级链
- 一级代理的 `parent_agent_id` 为 NULL

**GORM 模型**: `internal/model/commission.go:8`

---

### 14. commission_rules - 分佣规则

**表名**: `commission_rules`

**用途**: 定义分佣规则,支持一次性分佣、长期分佣、组合分佣

**字段说明**:

| 字段名 | 类型 | 约束 | 说明 |
|--------|------|------|------|
| id | BIGSERIAL | PRIMARY KEY | 规则ID |
| rule_name | VARCHAR(255) | NOT NULL | 规则名称 |
| rule_type | VARCHAR(50) | NOT NULL | 规则类型 one_time-一次性分佣 long_term-长期分佣 combined-组合分佣 |
| package_series_id | BIGINT | | 套餐系列ID |
| commission_type | VARCHAR(20) | NOT NULL | 分佣方式 fixed-固定金额 percentage-百分比 |
| commission_value | DECIMAL(10,4) | NOT NULL | 分佣值 |
| target_level | INT | | 目标层级(NULL表示所有层级) |
| enable_ladder | BOOLEAN | DEFAULT false | 是否启用阶梯 |
| freeze_days | INT | DEFAULT 0 | 冻结天数(长期分佣) |
| freeze_data_mb | BIGINT | DEFAULT 0 | 冻结流量(MB,长期分佣) |
| unfreeze_mode | VARCHAR(20) | DEFAULT 'auto' | 解冻模式 auto-自动 manual-手动 |
| status | INT | NOT NULL, DEFAULT 1 | 状态 1-启用 2-禁用 |
| created_at | TIMESTAMPTZ | NOT NULL, DEFAULT NOW() | 创建时间 |
| updated_at | TIMESTAMPTZ | NOT NULL, DEFAULT NOW() | 更新时间 |

**索引**:
- 主键索引: `id`
- 单列索引: `package_series_id`
- 组合索引: `(rule_type, status)`

**业务约束**:
- `rule_type = 'one_time'`: 订单完成后立即发放
- `rule_type = 'long_term'`: 订单完成后冻结,满足解冻条件后发放
- `rule_type = 'combined'`: 同时包含一次性和长期分佣,通过 `commission_combined_conditions` 表定义条件
- `unfreeze_mode = 'auto'`: 时间到期 OR 流量达标,满足其一即可自动解冻
- `unfreeze_mode = 'manual'`: 需要人工审批解冻

**GORM 模型**: `internal/model/commission.go:24`

---

### 15. commission_ladder - 分佣阶梯

**表名**: `commission_ladder`

**用途**: 为分佣规则配置阶梯奖励(订单数量越多,分佣越高)

**字段说明**:

| 字段名 | 类型 | 约束 | 说明 |
|--------|------|------|------|
| id | BIGSERIAL | PRIMARY KEY | 阶梯ID |
| rule_id | BIGINT | NOT NULL | 分佣规则ID |
| min_quantity | INT | NOT NULL | 最小数量 |
| max_quantity | INT | | 最大数量(NULL表示无上限) |
| commission_type | VARCHAR(20) | NOT NULL | 分佣方式 fixed-固定金额 percentage-百分比 |
| commission_value | DECIMAL(10,4) | NOT NULL | 分佣值 |
| created_at | TIMESTAMPTZ | NOT NULL, DEFAULT NOW() | 创建时间 |
| updated_at | TIMESTAMPTZ | NOT NULL, DEFAULT NOW() | 更新时间 |

**索引**:
- 主键索引: `id`
- 单列索引: `rule_id`

**业务示例**:
```
1-10 单: 10元/单
11-50 单: 15元/单
51+ 单: 20元/单
```

**GORM 模型**: `internal/model/commission.go:47`

---

### 16. commission_combined_conditions - 组合分佣条件

**表名**: `commission_combined_conditions`

**用途**: 定义组合分佣规则的具体条件(一次性部分 + 长期部分)

**字段说明**:

| 字段名 | 类型 | 约束 | 说明 |
|--------|------|------|------|
| id | BIGSERIAL | PRIMARY KEY | 条件ID |
| rule_id | BIGINT | NOT NULL | 分佣规则ID |
| condition_type | VARCHAR(20) | NOT NULL | 条件类型 one_time-一次性 long_term-长期 |
| commission_type | VARCHAR(20) | NOT NULL | 分佣方式 fixed-固定金额 percentage-百分比 |
| commission_value | DECIMAL(10,4) | NOT NULL | 分佣值 |
| freeze_days | INT | DEFAULT 0 | 冻结天数(长期分佣) |
| freeze_data_mb | BIGINT | DEFAULT 0 | 冻结流量(MB,长期分佣) |
| created_at | TIMESTAMPTZ | NOT NULL, DEFAULT NOW() | 创建时间 |
| updated_at | TIMESTAMPTZ | NOT NULL, DEFAULT NOW() | 更新时间 |

**索引**:
- 主键索引: `id`
- 组合索引: `(rule_id, condition_type)`

**业务说明**:
- 组合分佣规则可以定义多个条件
- 一次性条件立即发放
- 长期条件冻结后按规则解冻

**GORM 模型**: `internal/model/commission.go:68`

---

### 17. commission_records - 分佣记录

**表名**: `commission_records`

**用途**: 记录每笔分佣的详细信息,支持 OR 条件解冻

**字段说明**:

| 字段名 | 类型 | 约束 | 说明 |
|--------|------|------|------|
| id | BIGSERIAL | PRIMARY KEY | 分佣记录ID |
| order_id | BIGINT | NOT NULL | 订单ID |
| agent_id | BIGINT | NOT NULL | 代理用户ID |
| rule_id | BIGINT | NOT NULL | 分佣规则ID |
| commission_type | VARCHAR(50) | NOT NULL | 分佣类型 one_time/long_term/combined |
| commission_amount | DECIMAL(10,2) | NOT NULL | 分佣金额(元) |
| status | INT | NOT NULL, DEFAULT 1 | 状态 1-待发放 2-已发放 3-已冻结 4-已取消 |
| freeze_days | INT | DEFAULT 0 | 冻结天数 |
| freeze_data_mb | BIGINT | DEFAULT 0 | 冻结流量(MB) |
| unfreeze_conditions | JSONB | | 解冻条件(JSON格式,支持OR逻辑) |
| unfrozen_at | TIMESTAMPTZ | | 解冻时间 |
| distributed_at | TIMESTAMPTZ | | 发放时间 |
| created_at | TIMESTAMPTZ | NOT NULL, DEFAULT NOW() | 创建时间 |
| updated_at | TIMESTAMPTZ | NOT NULL, DEFAULT NOW() | 更新时间 |

**索引**:
- 主键索引: `id`
- 单列索引: `order_id`
- 单列索引: `agent_id`
- 组合索引: `(status, unfrozen_at)`

**业务约束**:
- `unfreeze_conditions` 支持 OR 条件:
  ```json
  {
    "time_based": {"days": 30, "deadline": "2025-02-10T00:00:00Z"},
    "data_based": {"data_mb": 1000}
  }
  ```
- 满足时间到期 **OR** 流量达标,任一条件满足即可自动解冻

**GORM 模型**: `internal/model/commission.go:88`

---

### 18. commission_approvals - 分佣审批

**表名**: `commission_approvals`

**用途**: 管理需要手动审批的分佣记录

**字段说明**:

| 字段名 | 类型 | 约束 | 说明 |
|--------|------|------|------|
| id | BIGSERIAL | PRIMARY KEY | 审批ID |
| commission_record_id | BIGINT | UNIQUE, NOT NULL | 分佣记录ID |
| agent_id | BIGINT | NOT NULL | 代理用户ID |
| approval_status | INT | NOT NULL, DEFAULT 1 | 审批状态 1-待审批 2-已通过 3-已拒绝 |
| approver_id | BIGINT | | 审批人ID |
| approval_reason | TEXT | | 审批原因 |
| approved_at | TIMESTAMPTZ | | 审批时间 |
| created_at | TIMESTAMPTZ | NOT NULL, DEFAULT NOW() | 创建时间 |
| updated_at | TIMESTAMPTZ | NOT NULL, DEFAULT NOW() | 更新时间 |

**索引**:
- 主键索引: `id`
- 唯一索引: `commission_record_id`
- 单列索引: `agent_id`
- 组合索引: `(approval_status, created_at)`

**业务说明**:
- 当分佣规则的 `unfreeze_mode = 'manual'` 时,需要创建审批记录
- 审批通过后才能解冻和发放佣金

**GORM 模型**: `internal/model/commission.go:116`

---

### 19. commission_templates - 分佣模板

**表名**: `commission_templates`

**用途**: 快速创建分佣规则的预设模板

**字段说明**:

| 字段名 | 类型 | 约束 | 说明 |
|--------|------|------|------|
| id | BIGSERIAL | PRIMARY KEY | 模板ID |
| template_name | VARCHAR(255) | NOT NULL | 模板名称 |
| template_data | JSONB | NOT NULL | 模板数据(JSON格式) |
| description | TEXT | | 描述 |
| status | INT | NOT NULL, DEFAULT 1 | 状态 1-启用 2-禁用 |
| created_at | TIMESTAMPTZ | NOT NULL, DEFAULT NOW() | 创建时间 |
| updated_at | TIMESTAMPTZ | NOT NULL, DEFAULT NOW() | 更新时间 |

**索引**:
- 主键索引: `id`

**业务说明**:
- `template_data` 存储分佣规则的完整配置
- 可以基于模板快速创建新的分佣规则

**GORM 模型**: `internal/model/commission.go:137`

---

### 20. carrier_settlements - 运营商结算

**表名**: `carrier_settlements`

**用途**: 记录与运营商的月度结算情况

**字段说明**:

| 字段名 | 类型 | 约束 | 说明 |
|--------|------|------|------|
| id | BIGSERIAL | PRIMARY KEY | 结算ID |
| carrier_id | BIGINT | NOT NULL | 运营商ID |
| settlement_month | VARCHAR(7) | NOT NULL | 结算月份(YYYY-MM) |
| total_orders | INT | DEFAULT 0 | 总订单数 |
| total_amount | DECIMAL(10,2) | DEFAULT 0 | 总金额(元) |
| settlement_status | INT | NOT NULL, DEFAULT 1 | 结算状态 1-待结算 2-已结算 3-已支付 |
| settled_at | TIMESTAMPTZ | | 结算时间 |
| paid_at | TIMESTAMPTZ | | 支付时间 |
| created_at | TIMESTAMPTZ | NOT NULL, DEFAULT NOW() | 创建时间 |
| updated_at | TIMESTAMPTZ | NOT NULL, DEFAULT NOW() | 更新时间 |

**索引**:
- 主键索引: `id`
- 组合唯一索引: `(carrier_id, settlement_month)`
- 组合索引: `(settlement_status, settlement_month)`

**业务说明**:
- 按月统计与运营商的交易情况
- 支持结算和支付状态跟踪

**GORM 模型**: `internal/model/commission.go:155`

---

## 财务管理表

### 21. commission_withdrawal_requests - 提现申请

**表名**: `commission_withdrawal_requests`

**用途**: 管理代理用户的佣金提现申请

**字段说明**:

| 字段名 | 类型 | 约束 | 说明 |
|--------|------|------|------|
| id | BIGSERIAL | PRIMARY KEY | 提现申请ID |
| agent_id | BIGINT | NOT NULL | 代理用户ID |
| withdrawal_amount | DECIMAL(10,2) | NOT NULL | 提现金额(元) |
| withdrawal_method | VARCHAR(20) | NOT NULL | 提现方式 bank_card/alipay/wechat |
| account_info | JSONB | NOT NULL | 账户信息(JSON格式) |
| status | INT | NOT NULL, DEFAULT 1 | 状态 1-待审核 2-已通过 3-已拒绝 4-已打款 5-已取消 |
| reviewer_id | BIGINT | | 审核人ID |
| review_reason | TEXT | | 审核原因 |
| reviewed_at | TIMESTAMPTZ | | 审核时间 |
| paid_at | TIMESTAMPTZ | | 打款时间 |
| created_at | TIMESTAMPTZ | NOT NULL, DEFAULT NOW() | 创建时间 |
| updated_at | TIMESTAMPTZ | NOT NULL, DEFAULT NOW() | 更新时间 |

**索引**:
- 主键索引: `id`
- 单列索引: `agent_id`
- 组合索引: `(status, created_at)`

**业务说明**:
- `account_info` 存储提现账户信息:
  ```json
  {
    "account_name": "张三",
    "account_number": "6222021234567890",
    "bank_name": "中国工商银行"
  }
  ```

**GORM 模型**: `internal/model/financial.go:8`

---

### 22. commission_withdrawal_settings - 提现设置

**表名**: `commission_withdrawal_settings`

**用途**: 配置代理用户的提现规则

**字段说明**:

| 字段名 | 类型 | 约束 | 说明 |
|--------|------|------|------|
| id | BIGSERIAL | PRIMARY KEY | 设置ID |
| agent_id | BIGINT | UNIQUE, NOT NULL | 代理用户ID |
| min_withdrawal_amount | DECIMAL(10,2) | DEFAULT 0 | 最小提现金额(元) |
| max_withdrawal_amount | DECIMAL(10,2) | DEFAULT 0 | 最大提现金额(元) |
| withdrawal_fee_rate | DECIMAL(5,4) | DEFAULT 0 | 提现手续费率(小数) |
| auto_approval_enabled | BOOLEAN | DEFAULT false | 是否启用自动审批 |
| created_at | TIMESTAMPTZ | NOT NULL, DEFAULT NOW() | 创建时间 |
| updated_at | TIMESTAMPTZ | NOT NULL, DEFAULT NOW() | 更新时间 |

**索引**:
- 主键索引: `id`
- 唯一索引: `agent_id`

**业务说明**:
- 每个代理可以有独立的提现规则
- `withdrawal_fee_rate` 为 0.005 表示 0.5% 手续费

**GORM 模型**: `internal/model/financial.go:31`

---

### 23. payment_merchant_settings - 收款商户设置

**表名**: `payment_merchant_settings`

**用途**: 配置收款商户信息(支付宝、微信、银行)

**字段说明**:

| 字段名 | 类型 | 约束 | 说明 |
|--------|------|------|------|
| id | BIGSERIAL | PRIMARY KEY | 商户设置ID |
| merchant_name | VARCHAR(255) | NOT NULL | 商户名称 |
| merchant_type | VARCHAR(20) | NOT NULL | 商户类型 alipay/wechat/bank |
| merchant_config | JSONB | NOT NULL | 商户配置(JSON格式) |
| is_default | BOOLEAN | DEFAULT false | 是否默认商户 |
| status | INT | NOT NULL, DEFAULT 1 | 状态 1-启用 2-禁用 |
| created_at | TIMESTAMPTZ | NOT NULL, DEFAULT NOW() | 创建时间 |
| updated_at | TIMESTAMPTZ | NOT NULL, DEFAULT NOW() | 更新时间 |

**索引**:
- 主键索引: `id`
- 组合索引: `(merchant_type, is_default)`

**业务说明**:
- `merchant_config` 存储商户的 API 配置:
  ```json
  {
    "app_id": "2021001234567890",
    "private_key": "...",
    "public_key": "...",
    "notify_url": "https://..."
  }
  ```

**GORM 模型**: `internal/model/financial.go:51`

---

## 系统管理表

### 24. dev_capability_configs - 开发能力配置

**表名**: `dev_capability_configs`

**用途**: 管理系统开发能力配置(如 API 开关、功能权限等)

**字段说明**:

| 字段名 | 类型 | 约束 | 说明 |
|--------|------|------|------|
| id | BIGSERIAL | PRIMARY KEY | 配置ID |
| capability_name | VARCHAR(255) | NOT NULL | 能力名称 |
| capability_code | VARCHAR(100) | UNIQUE, NOT NULL | 能力编码 |
| capability_config | JSONB | NOT NULL | 能力配置(JSON格式) |
| description | TEXT | | 描述 |
| status | INT | NOT NULL, DEFAULT 1 | 状态 1-启用 2-禁用 |
| created_at | TIMESTAMPTZ | NOT NULL, DEFAULT NOW() | 创建时间 |
| updated_at | TIMESTAMPTZ | NOT NULL, DEFAULT NOW() | 更新时间 |

**索引**:
- 主键索引: `id`
- 唯一索引: `capability_code`

**业务说明**:
- 用于配置系统的开发能力开关
- `capability_config` 存储能力的详细配置

**GORM 模型**: `internal/model/system.go:5`

---

### 25. card_replacement_requests - 换卡申请

**表名**: `card_replacement_requests`

**用途**: 管理 IoT 卡的换卡申请流程

**字段说明**:

| 字段名 | 类型 | 约束 | 说明 |
|--------|------|------|------|
| id | BIGSERIAL | PRIMARY KEY | 换卡申请ID |
| old_iot_card_id | BIGINT | NOT NULL | 旧卡ID |
| new_iot_card_id | BIGINT | | 新卡ID |
| user_id | BIGINT | NOT NULL | 用户ID |
| replacement_reason | TEXT | | 换卡原因 |
| status | INT | NOT NULL, DEFAULT 1 | 状态 1-待审核 2-已通过 3-已拒绝 4-已完成 |
| reviewer_id | BIGINT | | 审核人ID |
| reviewed_at | TIMESTAMPTZ | | 审核时间 |
| completed_at | TIMESTAMPTZ | | 完成时间 |
| created_at | TIMESTAMPTZ | NOT NULL, DEFAULT NOW() | 创建时间 |
| updated_at | TIMESTAMPTZ | NOT NULL, DEFAULT NOW() | 更新时间 |

**索引**:
- 主键索引: `id`
- 单列索引: `old_iot_card_id`
- 单列索引: `user_id`
- 组合索引: `(status, created_at)`

**业务流程**:
1. 用户提交换卡申请 (status=1)
2. 管理员审核 (status=2/3)
3. 审核通过后完成换卡 (status=4)

**GORM 模型**: `internal/model/system.go:25`

---

## 数据库设计原则总结

### 1. 无外键约束
- 所有表之间没有数据库级别的外键约束
- 关联关系通过存储关联 ID 字段手动维护
- 提供灵活性和性能优势

### 2. 字段命名规范
- 数据库字段名: 下划线命名法 (snake_case)
- Go 结构体字段名: 驼峰命名法 (PascalCase)
- 所有字段必须添加中文注释

### 3. 数据类型规范
- 主键: `BIGSERIAL`
- 字符串: `VARCHAR(长度)` 或 `TEXT`
- 数值: `INT`, `BIGINT`, `DECIMAL(10,2)`
- 时间: `TIMESTAMPTZ`
- JSON: `JSONB`
- 布尔: `BOOLEAN`

### 4. 索引设计原则
- 所有主键自动创建索引
- 唯一字段创建唯一索引
- 外键字段创建单列索引
- 常用查询条件创建组合索引
- 时间字段按需创建索引

### 5. 时间戳字段
- `created_at`: 创建时间,自动设置
- `updated_at`: 更新时间,自动更新
- 使用 `TIMESTAMPTZ` 类型存储带时区的时间戳

---

**文档版本**: v1.0
**最后更新**: 2026-01-12
**维护人员**: Claude Sonnet 4.5
