Files
junhong_cmp_fiber/migrations/000005_create_iot_sim_management_tables.up.sql
huang 034f00e2e7 实现 IoT SIM 管理模块数据模型和数据库结构
- 添加 IoT 核心业务表:运营商、IoT 卡、设备、号卡、套餐、订单等
- 添加分佣系统表:分佣规则、分佣记录、运营商结算等
- 添加轮询和流量管理表:轮询配置、流量使用记录等
- 添加财务和系统管理表:佣金提现、换卡申请等
- 实现完整的 GORM 模型和常量定义
- 添加数据库迁移脚本和详细文档
- 集成 OpenSpec 工作流工具(opsx 命令和 skills)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-12 15:44:23 +08:00

842 lines
39 KiB
SQL

-- IoT SIM 管理系统数据表创建脚本
-- 创建时间: 2026-01-12
-- 说明: 本脚本创建 IoT 卡、设备、号卡、套餐、订单、分佣等核心业务表
-- ========================================
-- 1. 核心业务表
-- ========================================
-- 1.1 运营商表
CREATE TABLE carriers (
id BIGSERIAL PRIMARY KEY,
carrier_code VARCHAR(50) UNIQUE NOT NULL,
carrier_name VARCHAR(100) NOT NULL,
description VARCHAR(500),
status INT NOT NULL DEFAULT 1,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
COMMENT ON TABLE carriers IS '运营商表';
COMMENT ON COLUMN carriers.id IS '主键ID';
COMMENT ON COLUMN carriers.carrier_code IS '运营商编码(CMCC/CUCC/CTCC)';
COMMENT ON COLUMN carriers.carrier_name IS '运营商名称(中国移动/中国联通/中国电信)';
COMMENT ON COLUMN carriers.description IS '运营商描述';
COMMENT ON COLUMN carriers.status IS '状态 1-启用 2-禁用';
COMMENT ON COLUMN carriers.created_at IS '创建时间';
COMMENT ON COLUMN carriers.updated_at IS '更新时间';
-- 插入初始数据
INSERT INTO carriers (carrier_code, carrier_name, status) VALUES
('CMCC', '中国移动', 1),
('CUCC', '中国联通', 1),
('CTCC', '中国电信', 1);
-- 1.2 IoT 卡表
CREATE TABLE iot_cards (
id BIGSERIAL PRIMARY KEY,
iccid VARCHAR(50) UNIQUE NOT NULL,
card_type VARCHAR(50) NOT NULL,
card_category VARCHAR(20) NOT NULL DEFAULT 'normal',
carrier_id BIGINT NOT NULL,
imsi VARCHAR(50),
msisdn VARCHAR(20),
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,
owner_type VARCHAR(20) NOT NULL DEFAULT 'platform',
owner_id BIGINT NOT NULL DEFAULT 0,
activated_at TIMESTAMP,
activation_status INT NOT NULL DEFAULT 0,
real_name_status INT NOT NULL DEFAULT 0,
network_status INT NOT NULL DEFAULT 0,
data_usage_mb BIGINT DEFAULT 0,
enable_polling BOOLEAN DEFAULT TRUE,
last_data_check_at TIMESTAMP,
last_real_name_check_at TIMESTAMP,
last_sync_time TIMESTAMP,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
COMMENT ON TABLE iot_cards IS 'IoT 卡表(物联网卡/流量卡)';
COMMENT ON COLUMN iot_cards.id IS '主键ID';
COMMENT ON COLUMN iot_cards.iccid IS 'ICCID(唯一标识)';
COMMENT ON COLUMN iot_cards.card_type IS '卡类型';
COMMENT ON COLUMN iot_cards.card_category IS '卡业务类型 normal-普通卡 industry-行业卡';
COMMENT ON COLUMN iot_cards.carrier_id IS '运营商ID';
COMMENT ON COLUMN iot_cards.imsi IS 'IMSI';
COMMENT ON COLUMN iot_cards.msisdn IS 'MSISDN(手机号码)';
COMMENT ON COLUMN iot_cards.batch_no IS '批次号';
COMMENT ON COLUMN iot_cards.supplier IS '供应商';
COMMENT ON COLUMN iot_cards.cost_price IS '成本价(元)';
COMMENT ON COLUMN iot_cards.distribute_price IS '分销价(元)';
COMMENT ON COLUMN iot_cards.status IS '状态 1-在库 2-已分销 3-已激活 4-已停用';
COMMENT ON COLUMN iot_cards.owner_type IS '所有者类型 platform-平台 agent-代理 user-用户 device-设备';
COMMENT ON COLUMN iot_cards.owner_id IS '所有者ID';
COMMENT ON COLUMN iot_cards.activated_at IS '激活时间';
COMMENT ON COLUMN iot_cards.activation_status IS '激活状态 0-未激活 1-已激活';
COMMENT ON COLUMN iot_cards.real_name_status IS '实名状态 0-未实名 1-已实名(行业卡可以保持0)';
COMMENT ON COLUMN iot_cards.network_status IS '网络状态 0-停机 1-开机';
COMMENT ON COLUMN iot_cards.data_usage_mb IS '累计流量使用(MB)';
COMMENT ON COLUMN iot_cards.enable_polling IS '是否参与轮询 true-参与 false-不参与';
COMMENT ON COLUMN iot_cards.last_data_check_at IS '最后一次流量检查时间';
COMMENT ON COLUMN iot_cards.last_real_name_check_at IS '最后一次实名检查时间';
COMMENT ON COLUMN iot_cards.last_sync_time IS '最后一次与Gateway同步时间';
COMMENT ON COLUMN iot_cards.created_at IS '创建时间';
COMMENT ON COLUMN iot_cards.updated_at IS '更新时间';
-- IoT 卡表索引
CREATE INDEX idx_iot_cards_carrier ON iot_cards(carrier_id);
CREATE INDEX idx_iot_cards_owner ON iot_cards(owner_type, owner_id, status);
CREATE INDEX idx_iot_cards_batch ON iot_cards(batch_no);
CREATE INDEX idx_iot_cards_activated ON iot_cards(activated_at);
CREATE INDEX idx_iot_cards_category ON iot_cards(card_category);
CREATE INDEX idx_iot_cards_data_check ON iot_cards(enable_polling, activation_status, last_data_check_at);
CREATE INDEX idx_iot_cards_real_name_check ON iot_cards(enable_polling, real_name_status, last_real_name_check_at);
-- 1.3 设备表
CREATE TABLE devices (
id BIGSERIAL PRIMARY KEY,
device_no VARCHAR(100) UNIQUE NOT NULL,
device_name VARCHAR(255),
device_model VARCHAR(100),
device_type VARCHAR(50),
max_sim_slots INT DEFAULT 4,
manufacturer VARCHAR(255),
batch_no VARCHAR(100),
owner_type VARCHAR(20) NOT NULL DEFAULT 'platform',
owner_id BIGINT NOT NULL DEFAULT 0,
status INT NOT NULL DEFAULT 1,
activated_at TIMESTAMP,
device_username VARCHAR(100),
device_password_encrypted VARCHAR(255),
device_api_endpoint VARCHAR(500),
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
COMMENT ON TABLE devices IS '设备表';
COMMENT ON COLUMN devices.id IS '主键ID';
COMMENT ON COLUMN devices.device_no IS '设备编号(唯一标识)';
COMMENT ON COLUMN devices.device_name IS '设备名称';
COMMENT ON COLUMN devices.device_model IS '设备型号';
COMMENT ON COLUMN devices.device_type IS '设备类型';
COMMENT ON COLUMN devices.max_sim_slots IS '最大插槽数量(默认4)';
COMMENT ON COLUMN devices.manufacturer IS '制造商';
COMMENT ON COLUMN devices.batch_no IS '批次号';
COMMENT ON COLUMN devices.owner_type IS '所有者类型 platform-平台 agent-代理 user-用户';
COMMENT ON COLUMN devices.owner_id IS '所有者ID';
COMMENT ON COLUMN devices.status IS '状态 1-在库 2-已分销 3-已激活 4-已停用';
COMMENT ON COLUMN devices.activated_at IS '激活时间';
COMMENT ON COLUMN devices.device_username IS '设备登录用户名';
COMMENT ON COLUMN devices.device_password_encrypted IS '设备登录密码(加密)';
COMMENT ON COLUMN devices.device_api_endpoint IS '设备API端点';
COMMENT ON COLUMN devices.created_at IS '创建时间';
COMMENT ON COLUMN devices.updated_at IS '更新时间';
-- 设备表索引
CREATE INDEX idx_devices_owner ON devices(owner_type, owner_id, status);
-- 1.4 号卡表
CREATE TABLE number_cards (
id BIGSERIAL PRIMARY KEY,
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,
price DECIMAL(10,2),
agent_id BIGINT,
status INT NOT NULL DEFAULT 1,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
COMMENT ON TABLE number_cards IS '号卡表';
COMMENT ON COLUMN number_cards.id IS '主键ID';
COMMENT ON COLUMN number_cards.virtual_product_code IS '虚拟商品编码(用于对应运营商订单)';
COMMENT ON COLUMN number_cards.card_name IS '号卡名称';
COMMENT ON COLUMN number_cards.card_type IS '号卡类型';
COMMENT ON COLUMN number_cards.carrier IS '运营商';
COMMENT ON COLUMN number_cards.data_amount_mb IS '流量额度(MB)';
COMMENT ON COLUMN number_cards.price IS '价格(元)';
COMMENT ON COLUMN number_cards.agent_id IS '代理用户ID';
COMMENT ON COLUMN number_cards.status IS '状态 1-在售 2-下架';
COMMENT ON COLUMN number_cards.created_at IS '创建时间';
COMMENT ON COLUMN number_cards.updated_at IS '更新时间';
-- 号卡表索引
CREATE INDEX idx_number_cards_agent ON number_cards(agent_id, status);
-- 1.5 套餐系列表
CREATE TABLE package_series (
id BIGSERIAL PRIMARY KEY,
series_code VARCHAR(100) UNIQUE NOT NULL,
series_name VARCHAR(255) NOT NULL,
description TEXT,
status INT NOT NULL DEFAULT 1,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
COMMENT ON TABLE package_series IS '套餐系列表';
COMMENT ON COLUMN package_series.id IS '主键ID';
COMMENT ON COLUMN package_series.series_code IS '系列编码';
COMMENT ON COLUMN package_series.series_name IS '系列名称';
COMMENT ON COLUMN package_series.description IS '描述';
COMMENT ON COLUMN package_series.status IS '状态 1-启用 2-禁用';
COMMENT ON COLUMN package_series.created_at IS '创建时间';
COMMENT ON COLUMN package_series.updated_at IS '更新时间';
-- 1.6 套餐表
CREATE TABLE packages (
id BIGSERIAL PRIMARY KEY,
package_code VARCHAR(100) UNIQUE NOT NULL,
package_name VARCHAR(255) NOT NULL,
series_id BIGINT,
package_type VARCHAR(50) NOT NULL,
duration_months INT NOT NULL,
data_type VARCHAR(20),
real_data_mb BIGINT DEFAULT 0,
virtual_data_mb BIGINT DEFAULT 0,
data_amount_mb BIGINT DEFAULT 0,
price DECIMAL(10,2) NOT NULL,
status INT NOT NULL DEFAULT 1,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
COMMENT ON TABLE packages IS '套餐表';
COMMENT ON COLUMN packages.id IS '主键ID';
COMMENT ON COLUMN packages.package_code IS '套餐编码';
COMMENT ON COLUMN packages.package_name IS '套餐名称';
COMMENT ON COLUMN packages.series_id IS '套餐系列ID';
COMMENT ON COLUMN packages.package_type IS '套餐类型 formal-正式套餐 addon-附加套餐';
COMMENT ON COLUMN packages.duration_months IS '套餐时长(月数) 1-月套餐 12-年套餐';
COMMENT ON COLUMN packages.data_type IS '流量类型 real-真流量 virtual-虚流量';
COMMENT ON COLUMN packages.real_data_mb IS '真流量额度(MB)';
COMMENT ON COLUMN packages.virtual_data_mb IS '虚流量额度(MB,用于停机判断)';
COMMENT ON COLUMN packages.data_amount_mb IS '总流量额度(MB)';
COMMENT ON COLUMN packages.price IS '套餐价格(元)';
COMMENT ON COLUMN packages.status IS '状态 1-启用 2-禁用';
COMMENT ON COLUMN packages.created_at IS '创建时间';
COMMENT ON COLUMN packages.updated_at IS '更新时间';
-- 套餐表索引
CREATE INDEX idx_packages_series ON packages(series_id, status);
-- 1.7 代理套餐分配表
CREATE TABLE agent_package_allocations (
id BIGSERIAL PRIMARY KEY,
agent_id BIGINT NOT NULL,
package_id BIGINT NOT NULL,
cost_price DECIMAL(10,2) NOT NULL,
retail_price DECIMAL(10,2) NOT NULL,
status INT NOT NULL DEFAULT 1,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW(),
CONSTRAINT uk_agent_package UNIQUE (agent_id, package_id)
);
COMMENT ON TABLE agent_package_allocations IS '代理套餐分配表';
COMMENT ON COLUMN agent_package_allocations.id IS '主键ID';
COMMENT ON COLUMN agent_package_allocations.agent_id IS '代理用户ID';
COMMENT ON COLUMN agent_package_allocations.package_id IS '套餐ID';
COMMENT ON COLUMN agent_package_allocations.cost_price IS '成本价(元)';
COMMENT ON COLUMN agent_package_allocations.retail_price IS '零售价(元)';
COMMENT ON COLUMN agent_package_allocations.status IS '状态 1-启用 2-禁用';
COMMENT ON COLUMN agent_package_allocations.created_at IS '创建时间';
COMMENT ON COLUMN agent_package_allocations.updated_at IS '更新时间';
-- 1.8 设备-IoT卡绑定关系表
CREATE TABLE device_sim_bindings (
id BIGSERIAL PRIMARY KEY,
device_id BIGINT NOT NULL,
iot_card_id BIGINT NOT NULL,
slot_position INT,
bind_status INT DEFAULT 1,
bind_time TIMESTAMP,
unbind_time TIMESTAMP,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
COMMENT ON TABLE device_sim_bindings IS '设备-IoT卡绑定关系表';
COMMENT ON COLUMN device_sim_bindings.id IS '主键ID';
COMMENT ON COLUMN device_sim_bindings.device_id IS '设备ID';
COMMENT ON COLUMN device_sim_bindings.iot_card_id IS 'IoT卡ID';
COMMENT ON COLUMN device_sim_bindings.slot_position IS '插槽位置(1, 2, 3, 4)';
COMMENT ON COLUMN device_sim_bindings.bind_status IS '绑定状态 1-已绑定 2-已解绑';
COMMENT ON COLUMN device_sim_bindings.bind_time IS '绑定时间';
COMMENT ON COLUMN device_sim_bindings.unbind_time IS '解绑时间';
COMMENT ON COLUMN device_sim_bindings.created_at IS '创建时间';
COMMENT ON COLUMN device_sim_bindings.updated_at IS '更新时间';
-- 设备-IoT卡绑定关系表索引
CREATE INDEX idx_device_sim_bindings_device ON device_sim_bindings(device_id, bind_status);
CREATE INDEX idx_device_sim_bindings_iot_card ON device_sim_bindings(iot_card_id, bind_status);
CREATE UNIQUE INDEX idx_device_sim_bindings_active_card ON device_sim_bindings(iot_card_id) WHERE bind_status = 1;
-- 1.9 订单表
CREATE TABLE orders (
id BIGSERIAL PRIMARY KEY,
order_no VARCHAR(100) UNIQUE NOT NULL,
order_type INT NOT NULL,
iot_card_id BIGINT,
device_id BIGINT,
number_card_id BIGINT,
package_id BIGINT,
user_id BIGINT NOT NULL,
agent_id BIGINT,
amount DECIMAL(10,2) NOT NULL,
payment_method VARCHAR(20),
status INT NOT NULL DEFAULT 1,
carrier_order_id VARCHAR(255),
carrier_order_data JSONB,
paid_at TIMESTAMP,
completed_at TIMESTAMP,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
COMMENT ON TABLE orders IS '订单表';
COMMENT ON COLUMN orders.id IS '主键ID';
COMMENT ON COLUMN orders.order_no IS '订单号(唯一标识)';
COMMENT ON COLUMN orders.order_type IS '订单类型 1-套餐订单 2-号卡订单';
COMMENT ON COLUMN orders.iot_card_id IS 'IoT卡ID(单卡套餐订单时有值)';
COMMENT ON COLUMN orders.device_id IS '设备ID(设备级套餐订单时有值)';
COMMENT ON COLUMN orders.number_card_id IS '号卡ID(号卡订单时有值)';
COMMENT ON COLUMN orders.package_id IS '套餐ID(套餐订单时有值)';
COMMENT ON COLUMN orders.user_id IS '用户ID';
COMMENT ON COLUMN orders.agent_id IS '代理用户ID';
COMMENT ON COLUMN orders.amount IS '订单金额(元)';
COMMENT ON COLUMN orders.payment_method IS '支付方式 wallet-钱包 online-在线支付 carrier-运营商支付';
COMMENT ON COLUMN orders.status IS '状态 1-待支付 2-已支付 3-已完成 4-已取消 5-已退款';
COMMENT ON COLUMN orders.carrier_order_id IS '运营商订单ID';
COMMENT ON COLUMN orders.carrier_order_data IS '运营商订单原始数据(JSON)';
COMMENT ON COLUMN orders.paid_at IS '支付时间';
COMMENT ON COLUMN orders.completed_at IS '完成时间';
COMMENT ON COLUMN orders.created_at IS '创建时间';
COMMENT ON COLUMN orders.updated_at IS '更新时间';
-- 订单表索引
CREATE INDEX idx_orders_user ON orders(user_id, status);
CREATE INDEX idx_orders_agent ON orders(agent_id, status);
CREATE INDEX idx_orders_iot_card ON orders(iot_card_id);
CREATE INDEX idx_orders_device ON orders(device_id);
CREATE INDEX idx_orders_number_card ON orders(number_card_id);
-- ========================================
-- 2. 套餐和轮询相关表
-- ========================================
-- 2.1 套餐使用情况表
CREATE TABLE package_usages (
id BIGSERIAL PRIMARY KEY,
order_id BIGINT NOT NULL,
package_id BIGINT NOT NULL,
usage_type VARCHAR(20) NOT NULL,
iot_card_id BIGINT,
device_id BIGINT,
data_limit_mb BIGINT NOT NULL,
data_usage_mb BIGINT DEFAULT 0,
real_data_usage_mb BIGINT DEFAULT 0,
virtual_data_usage_mb BIGINT DEFAULT 0,
activated_at TIMESTAMP NOT NULL,
expires_at TIMESTAMP NOT NULL,
status INT NOT NULL DEFAULT 1,
last_package_check_at TIMESTAMP,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
COMMENT ON TABLE package_usages IS '套餐使用情况表';
COMMENT ON COLUMN package_usages.id IS '主键ID';
COMMENT ON COLUMN package_usages.order_id IS '订单ID';
COMMENT ON COLUMN package_usages.package_id IS '套餐ID';
COMMENT ON COLUMN package_usages.usage_type IS '使用类型 single_card-单卡套餐 device-设备级套餐';
COMMENT ON COLUMN package_usages.iot_card_id IS 'IoT卡ID(单卡套餐时有值)';
COMMENT ON COLUMN package_usages.device_id IS '设备ID(设备级套餐时有值)';
COMMENT ON COLUMN package_usages.data_limit_mb IS '流量限额(MB)';
COMMENT ON COLUMN package_usages.data_usage_mb IS '已使用流量(MB)';
COMMENT ON COLUMN package_usages.real_data_usage_mb IS '真流量使用(MB)';
COMMENT ON COLUMN package_usages.virtual_data_usage_mb IS '虚流量使用(MB)';
COMMENT ON COLUMN package_usages.activated_at IS '套餐生效时间';
COMMENT ON COLUMN package_usages.expires_at IS '套餐过期时间';
COMMENT ON COLUMN package_usages.status IS '状态 1-生效中 2-已用完 3-已过期';
COMMENT ON COLUMN package_usages.last_package_check_at IS '最后一次套餐流量检查时间';
COMMENT ON COLUMN package_usages.created_at IS '创建时间';
COMMENT ON COLUMN package_usages.updated_at IS '更新时间';
-- 套餐使用情况表索引
CREATE INDEX idx_package_usages_order ON package_usages(order_id);
CREATE INDEX idx_package_usages_package ON package_usages(package_id);
CREATE INDEX idx_package_usages_iot_card ON package_usages(iot_card_id);
CREATE INDEX idx_package_usages_device ON package_usages(device_id);
CREATE INDEX idx_package_usages_check ON package_usages(status, expires_at, last_package_check_at);
-- 2.2 轮询配置表
CREATE TABLE polling_configs (
id BIGSERIAL PRIMARY KEY,
config_name VARCHAR(100) UNIQUE NOT NULL,
description VARCHAR(500),
card_condition VARCHAR(50),
carrier_id BIGINT,
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,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
COMMENT ON TABLE polling_configs IS '轮询配置表';
COMMENT ON COLUMN polling_configs.id IS '主键ID';
COMMENT ON COLUMN polling_configs.config_name IS '配置名称(如 未实名卡、实名卡)';
COMMENT ON COLUMN polling_configs.description IS '配置描述';
COMMENT ON COLUMN polling_configs.card_condition IS '卡状态条件 not_real_name-未实名 real_name-已实名 activated-已激活 suspended-已停用';
COMMENT ON COLUMN polling_configs.carrier_id IS '运营商ID(NULL表示所有运营商)';
COMMENT ON COLUMN polling_configs.real_name_check_enabled IS '是否启用实名检查';
COMMENT ON COLUMN polling_configs.real_name_check_interval IS '实名检查间隔(秒)';
COMMENT ON COLUMN polling_configs.card_data_check_enabled IS '是否启用卡流量检查';
COMMENT ON COLUMN polling_configs.card_data_check_interval IS '卡流量检查间隔(秒)';
COMMENT ON COLUMN polling_configs.package_check_enabled IS '是否启用套餐流量检查';
COMMENT ON COLUMN polling_configs.package_check_interval IS '套餐流量检查间隔(秒)';
COMMENT ON COLUMN polling_configs.priority IS '优先级(数字越小优先级越高)';
COMMENT ON COLUMN polling_configs.status IS '状态 1-启用 2-禁用';
COMMENT ON COLUMN polling_configs.created_at IS '创建时间';
COMMENT ON COLUMN polling_configs.updated_at IS '更新时间';
-- 轮询配置表索引
CREATE INDEX idx_polling_configs_match ON polling_configs(status, card_condition, carrier_id, priority);
-- 2.3 流量使用记录表
CREATE TABLE data_usage_records (
id BIGSERIAL PRIMARY KEY,
iot_card_id BIGINT NOT NULL,
data_usage_mb BIGINT NOT NULL,
data_increase_mb BIGINT DEFAULT 0,
check_time TIMESTAMP NOT NULL,
source VARCHAR(50) DEFAULT 'polling',
created_at TIMESTAMP DEFAULT NOW()
);
COMMENT ON TABLE data_usage_records IS '流量使用记录表';
COMMENT ON COLUMN data_usage_records.id IS '主键ID';
COMMENT ON COLUMN data_usage_records.iot_card_id IS 'IoT卡ID';
COMMENT ON COLUMN data_usage_records.data_usage_mb IS '流量使用量(MB)';
COMMENT ON COLUMN data_usage_records.data_increase_mb IS '相比上次的增量(MB)';
COMMENT ON COLUMN data_usage_records.check_time IS '检查时间';
COMMENT ON COLUMN data_usage_records.source IS '数据来源 polling-轮询 manual-手动 gateway-回调';
COMMENT ON COLUMN data_usage_records.created_at IS '创建时间';
-- 流量使用记录表索引
CREATE INDEX idx_data_usage_records_card_time ON data_usage_records(iot_card_id, check_time DESC);
CREATE INDEX idx_data_usage_records_time ON data_usage_records(check_time);
-- ========================================
-- 3. 分佣相关表
-- ========================================
-- 3.1 代理层级关系表
CREATE TABLE agent_hierarchies (
id BIGSERIAL PRIMARY KEY,
agent_id BIGINT NOT NULL UNIQUE,
parent_agent_id BIGINT,
level INT NOT NULL,
path VARCHAR(500),
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
COMMENT ON TABLE agent_hierarchies IS '代理层级关系表';
COMMENT ON COLUMN agent_hierarchies.id IS '主键ID';
COMMENT ON COLUMN agent_hierarchies.agent_id IS '代理用户ID';
COMMENT ON COLUMN agent_hierarchies.parent_agent_id IS '上级代理用户ID(NULL表示顶级代理)';
COMMENT ON COLUMN agent_hierarchies.level IS '代理层级(1, 2, 3...)';
COMMENT ON COLUMN agent_hierarchies.path IS '代理路径(如: "1/5/12")';
COMMENT ON COLUMN agent_hierarchies.created_at IS '创建时间';
COMMENT ON COLUMN agent_hierarchies.updated_at IS '更新时间';
-- 代理层级关系表索引
CREATE INDEX idx_agent_hierarchies_parent ON agent_hierarchies(parent_agent_id);
-- 3.2 分佣规则表
CREATE TABLE commission_rules (
id BIGSERIAL PRIMARY KEY,
agent_id BIGINT NOT NULL,
business_type VARCHAR(50) NOT NULL,
card_type VARCHAR(50) NOT NULL,
series_id BIGINT,
package_id BIGINT,
commission_type VARCHAR(50) NOT NULL,
commission_mode VARCHAR(20) NOT NULL,
commission_value DECIMAL(10,2) NOT NULL,
unfreeze_days INT DEFAULT 0,
min_activation_for_unfreeze INT DEFAULT 0,
approval_type VARCHAR(20) DEFAULT 'auto',
status INT NOT NULL DEFAULT 1,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
COMMENT ON TABLE commission_rules IS '分佣规则表';
COMMENT ON COLUMN commission_rules.id IS '主键ID';
COMMENT ON COLUMN commission_rules.agent_id IS '代理用户ID';
COMMENT ON COLUMN commission_rules.business_type IS '业务类型';
COMMENT ON COLUMN commission_rules.card_type IS '卡类型 number_card-号卡 iot_card-IoT卡';
COMMENT ON COLUMN commission_rules.series_id IS '套餐系列ID(一次性分佣时用)';
COMMENT ON COLUMN commission_rules.package_id IS '套餐ID(长期分佣时用)';
COMMENT ON COLUMN commission_rules.commission_type IS '分佣类型 one_time-一次性 long_term-长期 combined-组合';
COMMENT ON COLUMN commission_rules.commission_mode IS '分佣模式 fixed-固定金额 percent-百分比';
COMMENT ON COLUMN commission_rules.commission_value IS '分佣值';
COMMENT ON COLUMN commission_rules.unfreeze_days IS '解冻天数';
COMMENT ON COLUMN commission_rules.min_activation_for_unfreeze IS '解冻最小激活量';
COMMENT ON COLUMN commission_rules.approval_type IS '审批类型 auto-自动 manual-人工';
COMMENT ON COLUMN commission_rules.status IS '状态 1-启用 2-禁用';
COMMENT ON COLUMN commission_rules.created_at IS '创建时间';
COMMENT ON COLUMN commission_rules.updated_at IS '更新时间';
-- 分佣规则表索引
CREATE INDEX idx_commission_rules_agent ON commission_rules(agent_id, business_type, card_type);
-- 3.3 阶梯分佣配置表
CREATE TABLE commission_ladder (
id BIGSERIAL PRIMARY KEY,
rule_id BIGINT NOT NULL,
ladder_type VARCHAR(50) NOT NULL,
threshold_value INT NOT NULL,
commission_mode VARCHAR(20) NOT NULL,
commission_value DECIMAL(10,2) NOT NULL,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
COMMENT ON TABLE commission_ladder IS '阶梯分佣配置表';
COMMENT ON COLUMN commission_ladder.id IS '主键ID';
COMMENT ON COLUMN commission_ladder.rule_id IS '分佣规则ID';
COMMENT ON COLUMN commission_ladder.ladder_type IS '阶梯类型 activation-激活量 pickup-提货量 deposit-充值量';
COMMENT ON COLUMN commission_ladder.threshold_value IS '阈值';
COMMENT ON COLUMN commission_ladder.commission_mode IS '分佣模式 fixed-固定金额 percent-百分比';
COMMENT ON COLUMN commission_ladder.commission_value IS '分佣值';
COMMENT ON COLUMN commission_ladder.created_at IS '创建时间';
COMMENT ON COLUMN commission_ladder.updated_at IS '更新时间';
-- 阶梯分佣配置表索引
CREATE INDEX idx_commission_ladder_rule ON commission_ladder(rule_id);
-- 3.4 组合分佣条件表
CREATE TABLE commission_combined_conditions (
id BIGSERIAL PRIMARY KEY,
rule_id BIGINT NOT NULL UNIQUE,
one_time_commission_mode VARCHAR(20),
one_time_commission_value DECIMAL(10,2),
long_term_commission_mode VARCHAR(20),
long_term_commission_value DECIMAL(10,2),
long_term_trigger_time_point TIMESTAMP,
long_term_trigger_package_cycles INT,
long_term_trigger_network_months INT,
long_term_unfreeze_days INT DEFAULT 0,
long_term_min_activation INT DEFAULT 0,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
COMMENT ON TABLE commission_combined_conditions IS '组合分佣条件表';
COMMENT ON COLUMN commission_combined_conditions.id IS '主键ID';
COMMENT ON COLUMN commission_combined_conditions.rule_id IS '分佣规则ID';
COMMENT ON COLUMN commission_combined_conditions.one_time_commission_mode IS '一次性分佣模式 fixed-固定金额 percent-百分比';
COMMENT ON COLUMN commission_combined_conditions.one_time_commission_value IS '一次性分佣值';
COMMENT ON COLUMN commission_combined_conditions.long_term_commission_mode IS '长期分佣模式 fixed-固定金额 percent-百分比';
COMMENT ON COLUMN commission_combined_conditions.long_term_commission_value IS '长期分佣值';
COMMENT ON COLUMN commission_combined_conditions.long_term_trigger_time_point IS '长期分佣触发时间点(如实名后3个月)';
COMMENT ON COLUMN commission_combined_conditions.long_term_trigger_package_cycles IS '长期分佣触发套餐周期数(如10个套餐周期)';
COMMENT ON COLUMN commission_combined_conditions.long_term_trigger_network_months IS '长期分佣触发在网月数(号卡专用)';
COMMENT ON COLUMN commission_combined_conditions.long_term_unfreeze_days IS '长期分佣解冻天数';
COMMENT ON COLUMN commission_combined_conditions.long_term_min_activation IS '长期分佣解冻最小激活量';
COMMENT ON COLUMN commission_combined_conditions.created_at IS '创建时间';
COMMENT ON COLUMN commission_combined_conditions.updated_at IS '更新时间';
-- 3.5 分佣记录表
CREATE TABLE commission_records (
id BIGSERIAL PRIMARY KEY,
agent_id BIGINT NOT NULL,
order_id BIGINT NOT NULL,
rule_id BIGINT NOT NULL,
commission_type VARCHAR(50) NOT NULL,
amount DECIMAL(10,2) NOT NULL,
status INT NOT NULL DEFAULT 1,
unfrozen_at TIMESTAMP,
released_at TIMESTAMP,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
COMMENT ON TABLE commission_records IS '分佣记录表';
COMMENT ON COLUMN commission_records.id IS '主键ID';
COMMENT ON COLUMN commission_records.agent_id IS '代理用户ID';
COMMENT ON COLUMN commission_records.order_id IS '订单ID';
COMMENT ON COLUMN commission_records.rule_id IS '分佣规则ID';
COMMENT ON COLUMN commission_records.commission_type IS '分佣类型 one_time-一次性 long_term-长期';
COMMENT ON COLUMN commission_records.amount IS '分佣金额(元)';
COMMENT ON COLUMN commission_records.status IS '状态 1-已冻结 2-解冻中 3-已发放 4-已失效';
COMMENT ON COLUMN commission_records.unfrozen_at IS '解冻时间';
COMMENT ON COLUMN commission_records.released_at IS '发放时间';
COMMENT ON COLUMN commission_records.created_at IS '创建时间';
COMMENT ON COLUMN commission_records.updated_at IS '更新时间';
-- 分佣记录表索引
CREATE INDEX idx_commission_records_agent ON commission_records(agent_id, status);
CREATE INDEX idx_commission_records_order ON commission_records(order_id);
CREATE INDEX idx_commission_records_rule ON commission_records(rule_id);
-- 3.6 分佣审批表
CREATE TABLE commission_approvals (
id BIGSERIAL PRIMARY KEY,
commission_record_id BIGINT NOT NULL,
approver_id BIGINT,
status INT NOT NULL DEFAULT 1,
reason TEXT,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
COMMENT ON TABLE commission_approvals IS '分佣审批表';
COMMENT ON COLUMN commission_approvals.id IS '主键ID';
COMMENT ON COLUMN commission_approvals.commission_record_id IS '分佣记录ID';
COMMENT ON COLUMN commission_approvals.approver_id IS '审批人用户ID';
COMMENT ON COLUMN commission_approvals.status IS '状态 1-待审批 2-已通过 3-已拒绝';
COMMENT ON COLUMN commission_approvals.reason IS '原因';
COMMENT ON COLUMN commission_approvals.created_at IS '创建时间';
COMMENT ON COLUMN commission_approvals.updated_at IS '更新时间';
-- 分佣审批表索引
CREATE INDEX idx_commission_approvals_record ON commission_approvals(commission_record_id);
CREATE INDEX idx_commission_approvals_status ON commission_approvals(status);
-- 3.7 分佣模板表
CREATE TABLE commission_templates (
id BIGSERIAL PRIMARY KEY,
template_name VARCHAR(255) UNIQUE NOT NULL,
business_type VARCHAR(50) NOT NULL,
card_type VARCHAR(50) NOT NULL,
commission_type VARCHAR(50) NOT NULL,
commission_mode VARCHAR(20) NOT NULL,
commission_value DECIMAL(10,2) NOT NULL,
unfreeze_days INT DEFAULT 0,
min_activation_for_unfreeze INT DEFAULT 0,
approval_type VARCHAR(20) DEFAULT 'auto',
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
COMMENT ON TABLE commission_templates IS '分佣模板表';
COMMENT ON COLUMN commission_templates.id IS '主键ID';
COMMENT ON COLUMN commission_templates.template_name IS '模板名称';
COMMENT ON COLUMN commission_templates.business_type IS '业务类型';
COMMENT ON COLUMN commission_templates.card_type IS '卡类型 number_card-号卡 iot_card-IoT卡';
COMMENT ON COLUMN commission_templates.commission_type IS '分佣类型 one_time-一次性 long_term-长期 combined-组合';
COMMENT ON COLUMN commission_templates.commission_mode IS '分佣模式 fixed-固定金额 percent-百分比';
COMMENT ON COLUMN commission_templates.commission_value IS '分佣值';
COMMENT ON COLUMN commission_templates.unfreeze_days IS '解冻天数';
COMMENT ON COLUMN commission_templates.min_activation_for_unfreeze IS '解冻最小激活量';
COMMENT ON COLUMN commission_templates.approval_type IS '审批类型 auto-自动 manual-人工';
COMMENT ON COLUMN commission_templates.created_at IS '创建时间';
COMMENT ON COLUMN commission_templates.updated_at IS '更新时间';
-- 3.8 号卡运营商结算表
CREATE TABLE carrier_settlements (
id BIGSERIAL PRIMARY KEY,
commission_record_id BIGINT NOT NULL UNIQUE,
agent_id BIGINT NOT NULL,
settlement_month VARCHAR(20) NOT NULL,
settlement_amount DECIMAL(18,2) NOT NULL,
status INT NOT NULL DEFAULT 1,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
COMMENT ON TABLE carrier_settlements IS '号卡运营商结算表';
COMMENT ON COLUMN carrier_settlements.id IS '主键ID';
COMMENT ON COLUMN carrier_settlements.commission_record_id IS '分佣记录ID';
COMMENT ON COLUMN carrier_settlements.agent_id IS '代理用户ID';
COMMENT ON COLUMN carrier_settlements.settlement_month IS '结算月份(如 2026-01)';
COMMENT ON COLUMN carrier_settlements.settlement_amount IS '结算金额(元)';
COMMENT ON COLUMN carrier_settlements.status IS '状态 1-待结算 2-已结算';
COMMENT ON COLUMN carrier_settlements.created_at IS '创建时间';
COMMENT ON COLUMN carrier_settlements.updated_at IS '更新时间';
-- 号卡运营商结算表索引
CREATE INDEX idx_carrier_settlements_agent ON carrier_settlements(agent_id, status);
-- ========================================
-- 4. 财务管理表
-- ========================================
-- 4.1 佣金提现申请表
CREATE TABLE commission_withdrawal_requests (
id BIGSERIAL PRIMARY KEY,
agent_id BIGINT NOT NULL,
amount DECIMAL(18,2) NOT NULL,
fee DECIMAL(18,2) DEFAULT 0,
actual_amount DECIMAL(18,2),
withdrawal_method VARCHAR(20),
account_info JSONB,
status INT DEFAULT 1,
approved_by BIGINT,
approved_at TIMESTAMP,
paid_at TIMESTAMP,
reject_reason TEXT,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
COMMENT ON TABLE commission_withdrawal_requests IS '佣金提现申请表';
COMMENT ON COLUMN commission_withdrawal_requests.id IS '主键ID';
COMMENT ON COLUMN commission_withdrawal_requests.agent_id IS '代理用户ID';
COMMENT ON COLUMN commission_withdrawal_requests.amount IS '提现金额(元)';
COMMENT ON COLUMN commission_withdrawal_requests.fee IS '手续费(元)';
COMMENT ON COLUMN commission_withdrawal_requests.actual_amount IS '实际到账金额(元)';
COMMENT ON COLUMN commission_withdrawal_requests.withdrawal_method IS '提现方式 alipay-支付宝 wechat-微信 bank-银行卡';
COMMENT ON COLUMN commission_withdrawal_requests.account_info IS '收款账户信息(姓名、账号等)';
COMMENT ON COLUMN commission_withdrawal_requests.status IS '状态 1-待审核 2-已通过 3-已拒绝 4-已到账';
COMMENT ON COLUMN commission_withdrawal_requests.approved_by IS '审批人用户ID';
COMMENT ON COLUMN commission_withdrawal_requests.approved_at IS '审批时间';
COMMENT ON COLUMN commission_withdrawal_requests.paid_at IS '到账时间';
COMMENT ON COLUMN commission_withdrawal_requests.reject_reason IS '拒绝原因';
COMMENT ON COLUMN commission_withdrawal_requests.created_at IS '创建时间';
COMMENT ON COLUMN commission_withdrawal_requests.updated_at IS '更新时间';
-- 佣金提现申请表索引
CREATE INDEX idx_commission_withdrawal_requests_agent ON commission_withdrawal_requests(agent_id, status);
CREATE INDEX idx_commission_withdrawal_requests_created ON commission_withdrawal_requests(created_at);
-- 4.2 佣金提现设置表
CREATE TABLE commission_withdrawal_settings (
id BIGSERIAL PRIMARY KEY,
min_withdrawal_amount DECIMAL(10,2),
fee_rate DECIMAL(5,4),
arrival_days INT,
is_active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
COMMENT ON TABLE commission_withdrawal_settings IS '佣金提现设置表';
COMMENT ON COLUMN commission_withdrawal_settings.id IS '主键ID';
COMMENT ON COLUMN commission_withdrawal_settings.min_withdrawal_amount IS '最低提现金额(元)';
COMMENT ON COLUMN commission_withdrawal_settings.fee_rate IS '手续费率(如 0.01 表示 1%)';
COMMENT ON COLUMN commission_withdrawal_settings.arrival_days IS '到账天数';
COMMENT ON COLUMN commission_withdrawal_settings.is_active IS '是否生效(最新一条)';
COMMENT ON COLUMN commission_withdrawal_settings.created_at IS '创建时间';
COMMENT ON COLUMN commission_withdrawal_settings.updated_at IS '更新时间';
-- 4.3 收款商户设置表
CREATE TABLE payment_merchant_settings (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT NOT NULL,
merchant_type VARCHAR(20),
account_name VARCHAR(255),
account_number VARCHAR(255),
bank_name VARCHAR(255),
bank_branch VARCHAR(255),
is_verified BOOLEAN DEFAULT FALSE,
is_default BOOLEAN DEFAULT FALSE,
status INT DEFAULT 1,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
COMMENT ON TABLE payment_merchant_settings IS '收款商户设置表';
COMMENT ON COLUMN payment_merchant_settings.id IS '主键ID';
COMMENT ON COLUMN payment_merchant_settings.user_id IS '用户ID';
COMMENT ON COLUMN payment_merchant_settings.merchant_type IS '商户类型 alipay-支付宝 wechat-微信 bank-银行卡';
COMMENT ON COLUMN payment_merchant_settings.account_name IS '账户名称';
COMMENT ON COLUMN payment_merchant_settings.account_number IS '账号';
COMMENT ON COLUMN payment_merchant_settings.bank_name IS '银行名称(仅银行卡)';
COMMENT ON COLUMN payment_merchant_settings.bank_branch IS '开户行(仅银行卡)';
COMMENT ON COLUMN payment_merchant_settings.is_verified IS '是否已验证';
COMMENT ON COLUMN payment_merchant_settings.is_default IS '是否默认账户';
COMMENT ON COLUMN payment_merchant_settings.status IS '状态 1-启用 2-禁用';
COMMENT ON COLUMN payment_merchant_settings.created_at IS '创建时间';
COMMENT ON COLUMN payment_merchant_settings.updated_at IS '更新时间';
-- 收款商户设置表索引
CREATE INDEX idx_payment_merchant_settings_user ON payment_merchant_settings(user_id, is_default);
CREATE INDEX idx_payment_merchant_settings_type ON payment_merchant_settings(merchant_type, status);
-- ========================================
-- 5. 系统管理表
-- ========================================
-- 5.1 开发能力配置表
CREATE TABLE dev_capability_configs (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT NOT NULL,
app_name VARCHAR(255),
app_id VARCHAR(100) UNIQUE,
app_secret VARCHAR(255),
callback_url VARCHAR(500),
ip_whitelist TEXT,
status INT DEFAULT 1,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
COMMENT ON TABLE dev_capability_configs IS '开发能力配置表';
COMMENT ON COLUMN dev_capability_configs.id IS '主键ID';
COMMENT ON COLUMN dev_capability_configs.user_id IS '用户ID(平台或代理)';
COMMENT ON COLUMN dev_capability_configs.app_name IS '应用名称';
COMMENT ON COLUMN dev_capability_configs.app_id IS '应用ID';
COMMENT ON COLUMN dev_capability_configs.app_secret IS '应用密钥';
COMMENT ON COLUMN dev_capability_configs.callback_url IS '回调地址';
COMMENT ON COLUMN dev_capability_configs.ip_whitelist IS 'IP白名单(多个IP用逗号分隔)';
COMMENT ON COLUMN dev_capability_configs.status IS '状态 1-启用 2-禁用';
COMMENT ON COLUMN dev_capability_configs.created_at IS '创建时间';
COMMENT ON COLUMN dev_capability_configs.updated_at IS '更新时间';
-- 开发能力配置表索引
CREATE INDEX idx_dev_capability_configs_user ON dev_capability_configs(user_id, status);
-- 5.2 换卡申请表
CREATE TABLE card_replacement_requests (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT NOT NULL,
old_iccid VARCHAR(50) NOT NULL,
new_iccid VARCHAR(50),
reason TEXT,
status INT DEFAULT 1,
approved_by BIGINT,
approved_at TIMESTAMP,
completed_at TIMESTAMP,
reject_reason TEXT,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
COMMENT ON TABLE card_replacement_requests IS '换卡申请表';
COMMENT ON COLUMN card_replacement_requests.id IS '主键ID';
COMMENT ON COLUMN card_replacement_requests.user_id IS '申请用户ID';
COMMENT ON COLUMN card_replacement_requests.old_iccid IS '旧卡ICCID';
COMMENT ON COLUMN card_replacement_requests.new_iccid IS '新卡ICCID(审批时填充)';
COMMENT ON COLUMN card_replacement_requests.reason IS '换卡原因';
COMMENT ON COLUMN card_replacement_requests.status IS '状态 1-待处理 2-已通过 3-已拒绝 4-已完成';
COMMENT ON COLUMN card_replacement_requests.approved_by IS '处理人用户ID';
COMMENT ON COLUMN card_replacement_requests.approved_at IS '处理时间';
COMMENT ON COLUMN card_replacement_requests.completed_at IS '完成时间(新卡激活时间)';
COMMENT ON COLUMN card_replacement_requests.reject_reason IS '拒绝原因';
COMMENT ON COLUMN card_replacement_requests.created_at IS '创建时间';
COMMENT ON COLUMN card_replacement_requests.updated_at IS '更新时间';
-- 换卡申请表索引
CREATE INDEX idx_card_replacement_requests_user ON card_replacement_requests(user_id, status);
CREATE INDEX idx_card_replacement_requests_old_iccid ON card_replacement_requests(old_iccid);
CREATE INDEX idx_card_replacement_requests_new_iccid ON card_replacement_requests(new_iccid);