refactor: 一次性佣金配置从套餐级别提升到系列级别
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 6m29s

主要变更:
- 新增 tb_shop_series_allocation 表,存储系列级别的一次性佣金配置
- ShopPackageAllocation 移除 one_time_commission_amount 字段
- PackageSeries 新增 enable_one_time_commission 字段控制是否启用一次性佣金
- 新增 /api/admin/shop-series-allocations CRUD 接口
- 佣金计算逻辑改为从 ShopSeriesAllocation 获取一次性佣金金额
- 删除废弃的 ShopSeriesOneTimeCommissionTier 模型
- OpenAPI Tag '系列分配' 和 '单套餐分配' 合并为 '套餐分配'

迁移脚本:
- 000042: 重构佣金套餐模型
- 000043: 简化佣金分配
- 000044: 一次性佣金分配重构
- 000045: PackageSeries 添加 enable_one_time_commission 字段

测试:
- 新增验收测试 (shop_series_allocation, commission_calculation)
- 新增流程测试 (one_time_commission_chain)
- 删除过时的单元测试(已被验收测试覆盖)
This commit is contained in:
2026-02-04 14:28:44 +08:00
parent fba8e9e76b
commit b18ecfeb55
106 changed files with 9899 additions and 6608 deletions

View File

@@ -0,0 +1,95 @@
-- 套餐与佣金模型重构 - 回滚
-- 注意:此回滚会丢失新增字段的数据
-- ============================================
-- 7. ShopSeriesAllocation 表恢复废弃字段
-- ============================================
-- 移除新增的字段
ALTER TABLE tb_shop_series_allocation
DROP COLUMN IF EXISTS one_time_commission_amount;
-- 恢复一次性佣金完整配置字段
ALTER TABLE tb_shop_series_allocation
ADD COLUMN IF NOT EXISTS enable_one_time_commission BOOLEAN DEFAULT false NOT NULL,
ADD COLUMN IF NOT EXISTS one_time_commission_type VARCHAR(20),
ADD COLUMN IF NOT EXISTS one_time_commission_trigger VARCHAR(30),
ADD COLUMN IF NOT EXISTS one_time_commission_threshold BIGINT DEFAULT 0,
ADD COLUMN IF NOT EXISTS one_time_commission_mode VARCHAR(20),
ADD COLUMN IF NOT EXISTS one_time_commission_value BIGINT DEFAULT 0,
ADD COLUMN IF NOT EXISTS enable_force_recharge BOOLEAN DEFAULT false,
ADD COLUMN IF NOT EXISTS force_recharge_amount BIGINT DEFAULT 0,
ADD COLUMN IF NOT EXISTS force_recharge_trigger_type INT DEFAULT 2;
-- ============================================
-- 6. ShopSeriesOneTimeCommissionTier 表移除统计范围
-- ============================================
DO $$
BEGIN
IF EXISTS (
SELECT 1 FROM information_schema.tables
WHERE table_name = 'tb_shop_series_one_time_commission_tier'
) THEN
ALTER TABLE tb_shop_series_one_time_commission_tier
DROP COLUMN IF EXISTS stat_scope;
END IF;
END $$;
-- ============================================
-- 5. PackageSeries 表移除一次性佣金规则配置
-- ============================================
ALTER TABLE tb_package_series
DROP COLUMN IF EXISTS one_time_commission_config;
-- ============================================
-- 4. Device 表移除追踪字段
-- ============================================
ALTER TABLE tb_device
DROP COLUMN IF EXISTS accumulated_recharge_by_series,
DROP COLUMN IF EXISTS first_recharge_triggered_by_series;
-- ============================================
-- 3. IoTCard 表移除追踪字段
-- ============================================
ALTER TABLE tb_iot_card
DROP COLUMN IF EXISTS accumulated_recharge_by_series,
DROP COLUMN IF EXISTS first_recharge_triggered_by_series;
-- ============================================
-- 2. ShopPackageAllocation 表移除字段
-- ============================================
ALTER TABLE tb_shop_package_allocation
DROP COLUMN IF EXISTS one_time_commission_amount;
-- ============================================
-- 1. Package 表恢复废弃字段
-- ============================================
-- 移除虚流量开关字段
ALTER TABLE tb_package
DROP COLUMN IF EXISTS enable_virtual_data;
-- 恢复 cost_price 为 suggested_cost_price
DO $$
BEGIN
IF EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_name = 'tb_package' AND column_name = 'cost_price'
) AND NOT EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_name = 'tb_package' AND column_name = 'suggested_cost_price'
) THEN
ALTER TABLE tb_package RENAME COLUMN cost_price TO suggested_cost_price;
END IF;
END $$;
-- 恢复废弃字段
ALTER TABLE tb_package
ADD COLUMN IF NOT EXISTS price BIGINT DEFAULT 0 NOT NULL,
ADD COLUMN IF NOT EXISTS data_type VARCHAR(20),
ADD COLUMN IF NOT EXISTS data_amount_mb BIGINT DEFAULT 0;

View File

@@ -0,0 +1,126 @@
-- 套餐与佣金模型重构
-- 重构说明:
-- 1. Package 表:移除废弃字段,新增虚流量开关
-- 2. ShopPackageAllocation 表:新增一次性佣金金额字段
-- 3. IoTCard/Device 表:新增按系列追踪的累计充值和首充状态字段
-- 4. PackageSeries 表:新增一次性佣金规则配置字段
-- 5. ShopSeriesOneTimeCommissionTier 表:新增统计范围字段
-- 6. ShopSeriesAllocation 表:移除完整一次性佣金配置,改为只存金额
-- ============================================
-- 1. Package 表调整
-- ============================================
-- 移除废弃字段
ALTER TABLE tb_package
DROP COLUMN IF EXISTS price,
DROP COLUMN IF EXISTS data_type,
DROP COLUMN IF EXISTS data_amount_mb;
-- 新增虚流量开关字段
ALTER TABLE tb_package
ADD COLUMN IF NOT EXISTS enable_virtual_data BOOLEAN DEFAULT false NOT NULL;
COMMENT ON COLUMN tb_package.enable_virtual_data IS '是否启用虚流量';
-- 重命名 suggested_cost_price 为 cost_price如果存在的话通过 RENAME 实现)
-- 注意PostgreSQL 不支持条件性重命名,此处直接重命名
DO $$
BEGIN
IF EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_name = 'tb_package' AND column_name = 'suggested_cost_price'
) THEN
ALTER TABLE tb_package RENAME COLUMN suggested_cost_price TO cost_price;
END IF;
END $$;
COMMENT ON COLUMN tb_package.cost_price IS '成本价(分为单位)';
-- ============================================
-- 2. ShopPackageAllocation 表新增字段
-- ============================================
ALTER TABLE tb_shop_package_allocation
ADD COLUMN IF NOT EXISTS one_time_commission_amount BIGINT DEFAULT 0 NOT NULL;
COMMENT ON COLUMN tb_shop_package_allocation.one_time_commission_amount IS '该代理能拿到的一次性佣金(分)';
-- ============================================
-- 3. IoTCard 表新增追踪字段
-- ============================================
-- 新增按系列追踪的累计充值字段JSON Map: series_id -> amount
ALTER TABLE tb_iot_card
ADD COLUMN IF NOT EXISTS accumulated_recharge_by_series JSONB DEFAULT '{}';
-- 新增按系列追踪的首充触发状态JSON Map: series_id -> bool
ALTER TABLE tb_iot_card
ADD COLUMN IF NOT EXISTS first_recharge_triggered_by_series JSONB DEFAULT '{}';
COMMENT ON COLUMN tb_iot_card.accumulated_recharge_by_series IS '按套餐系列追踪的累计充值金额JSON Map: series_id -> amount 分)';
COMMENT ON COLUMN tb_iot_card.first_recharge_triggered_by_series IS '按套餐系列追踪的首充触发状态JSON Map: series_id -> bool';
-- ============================================
-- 4. Device 表新增追踪字段
-- ============================================
-- 新增按系列追踪的累计充值字段
ALTER TABLE tb_device
ADD COLUMN IF NOT EXISTS accumulated_recharge_by_series JSONB DEFAULT '{}';
-- 新增按系列追踪的首充触发状态
ALTER TABLE tb_device
ADD COLUMN IF NOT EXISTS first_recharge_triggered_by_series JSONB DEFAULT '{}';
COMMENT ON COLUMN tb_device.accumulated_recharge_by_series IS '按套餐系列追踪的累计充值金额JSON Map: series_id -> amount 分)';
COMMENT ON COLUMN tb_device.first_recharge_triggered_by_series IS '按套餐系列追踪的首充触发状态JSON Map: series_id -> bool';
-- ============================================
-- 5. PackageSeries 表新增一次性佣金规则配置
-- ============================================
ALTER TABLE tb_package_series
ADD COLUMN IF NOT EXISTS one_time_commission_config JSONB;
COMMENT ON COLUMN tb_package_series.one_time_commission_config IS '一次性佣金规则配置JSONenable, trigger_type, threshold, commission_type, commission_amount, validity_type 等)';
-- ============================================
-- 6. ShopSeriesOneTimeCommissionTier 表新增统计范围
-- ============================================
-- 检查表是否存在,存在则添加字段
DO $$
BEGIN
IF EXISTS (
SELECT 1 FROM information_schema.tables
WHERE table_name = 'tb_shop_series_one_time_commission_tier'
) THEN
ALTER TABLE tb_shop_series_one_time_commission_tier
ADD COLUMN IF NOT EXISTS stat_scope VARCHAR(20) DEFAULT 'self' NOT NULL;
COMMENT ON COLUMN tb_shop_series_one_time_commission_tier.stat_scope IS '统计范围 self-仅自己 self_and_sub-自己+下级';
END IF;
END $$;
-- ============================================
-- 7. ShopSeriesAllocation 表移除废弃字段,新增新字段
-- ============================================
-- 移除一次性佣金完整配置字段
ALTER TABLE tb_shop_series_allocation
DROP COLUMN IF EXISTS enable_one_time_commission,
DROP COLUMN IF EXISTS one_time_commission_type,
DROP COLUMN IF EXISTS one_time_commission_trigger,
DROP COLUMN IF EXISTS one_time_commission_threshold,
DROP COLUMN IF EXISTS one_time_commission_mode,
DROP COLUMN IF EXISTS one_time_commission_value,
DROP COLUMN IF EXISTS enable_force_recharge,
DROP COLUMN IF EXISTS force_recharge_amount,
DROP COLUMN IF EXISTS force_recharge_trigger_type;
-- 新增一次性佣金金额字段
ALTER TABLE tb_shop_series_allocation
ADD COLUMN IF NOT EXISTS one_time_commission_amount BIGINT DEFAULT 0 NOT NULL;
COMMENT ON COLUMN tb_shop_series_allocation.one_time_commission_amount IS '给被分配店铺的一次性佣金金额(分)';

View File

@@ -0,0 +1,68 @@
-- 回滚:恢复 ShopSeriesAllocation 层
-- ============================================
-- 1. 重建 ShopSeriesAllocation 表
-- ============================================
CREATE TABLE IF NOT EXISTS tb_shop_series_allocation (
id BIGSERIAL PRIMARY KEY,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW(),
deleted_at TIMESTAMPTZ,
creator BIGINT NOT NULL DEFAULT 0,
updater BIGINT NOT NULL DEFAULT 0,
shop_id BIGINT NOT NULL,
series_id BIGINT NOT NULL,
allocator_shop_id BIGINT NOT NULL DEFAULT 0,
cost_price_markup BIGINT NOT NULL DEFAULT 0,
one_time_commission_amount BIGINT NOT NULL DEFAULT 0,
status INTEGER NOT NULL DEFAULT 1
);
COMMENT ON TABLE tb_shop_series_allocation IS '店铺系列分配表';
COMMENT ON COLUMN tb_shop_series_allocation.shop_id IS '被分配店铺ID';
COMMENT ON COLUMN tb_shop_series_allocation.series_id IS '套餐系列ID';
COMMENT ON COLUMN tb_shop_series_allocation.allocator_shop_id IS '分配者店铺ID0表示平台分配';
COMMENT ON COLUMN tb_shop_series_allocation.cost_price_markup IS '成本价加价(分)';
COMMENT ON COLUMN tb_shop_series_allocation.one_time_commission_amount IS '一次性佣金金额(分)';
CREATE INDEX IF NOT EXISTS idx_shop_series_allocation_shop_id ON tb_shop_series_allocation(shop_id);
CREATE INDEX IF NOT EXISTS idx_shop_series_allocation_series_id ON tb_shop_series_allocation(series_id);
-- ============================================
-- 2. 重建 ShopSeriesAllocationConfig 表
-- ============================================
CREATE TABLE IF NOT EXISTS tb_shop_series_allocation_config (
id BIGSERIAL PRIMARY KEY,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW(),
deleted_at TIMESTAMPTZ,
creator BIGINT NOT NULL DEFAULT 0,
updater BIGINT NOT NULL DEFAULT 0,
allocation_id BIGINT NOT NULL,
config_type VARCHAR(50) NOT NULL,
config_value JSONB
);
COMMENT ON TABLE tb_shop_series_allocation_config IS '店铺系列分配配置表';
-- ============================================
-- 3. 恢复 allocation_id 字段
-- ============================================
ALTER TABLE tb_shop_package_allocation
ADD COLUMN IF NOT EXISTS allocation_id BIGINT NOT NULL DEFAULT 0;
-- ============================================
-- 4. 删除新增的字段
-- ============================================
DROP INDEX IF EXISTS idx_shop_package_allocation_series_id;
DROP INDEX IF EXISTS idx_shop_package_allocation_allocator_shop_id;
ALTER TABLE tb_shop_package_allocation
DROP COLUMN IF EXISTS series_id;
ALTER TABLE tb_shop_package_allocation
DROP COLUMN IF EXISTS allocator_shop_id;

View File

@@ -0,0 +1,61 @@
-- 简化佣金分配模型:删除 ShopSeriesAllocation 层,使用 ShopPackageAllocation 直接管理
-- 重构原因:业务模型只需要套餐级别的分配,不需要系列级别的中间层
-- ============================================
-- 1. 为 ShopPackageAllocation 添加新字段
-- ============================================
-- 添加 series_id 字段(记录套餐所属系列,便于查询)
ALTER TABLE tb_shop_package_allocation
ADD COLUMN IF NOT EXISTS series_id BIGINT DEFAULT 0 NOT NULL;
COMMENT ON COLUMN tb_shop_package_allocation.series_id IS '套餐系列ID冗余字段便于查询';
-- 添加 allocator_shop_id 字段记录是谁分配的0表示平台分配
ALTER TABLE tb_shop_package_allocation
ADD COLUMN IF NOT EXISTS allocator_shop_id BIGINT DEFAULT 0 NOT NULL;
COMMENT ON COLUMN tb_shop_package_allocation.allocator_shop_id IS '分配者店铺ID0表示平台分配';
-- ============================================
-- 2. 从现有数据迁移 series_id 和 allocator_shop_id
-- ============================================
-- 通过 package 表获取 series_id
UPDATE tb_shop_package_allocation spa
SET series_id = p.series_id
FROM tb_package p
WHERE spa.package_id = p.id AND spa.series_id = 0;
-- 通过 shop_series_allocation 获取 allocator_shop_id
UPDATE tb_shop_package_allocation spa
SET allocator_shop_id = ssa.allocator_shop_id
FROM tb_shop_series_allocation ssa
WHERE spa.allocation_id = ssa.id AND spa.allocator_shop_id = 0;
-- ============================================
-- 3. 删除废弃的 allocation_id 字段
-- ============================================
ALTER TABLE tb_shop_package_allocation
DROP COLUMN IF EXISTS allocation_id;
-- ============================================
-- 4. 添加索引优化查询性能
-- ============================================
CREATE INDEX IF NOT EXISTS idx_shop_package_allocation_series_id
ON tb_shop_package_allocation(series_id);
CREATE INDEX IF NOT EXISTS idx_shop_package_allocation_allocator_shop_id
ON tb_shop_package_allocation(allocator_shop_id);
-- ============================================
-- 5. 删除废弃的表
-- ============================================
-- 删除系列分配配置表
DROP TABLE IF EXISTS tb_shop_series_allocation_config;
-- 删除系列分配表
DROP TABLE IF EXISTS tb_shop_series_allocation;

View File

@@ -0,0 +1,54 @@
-- 回滚:恢复原有结构
-- ============================================
-- 1. 恢复 tb_shop_package_allocation 字段
-- ============================================
-- 恢复 series_id 字段
ALTER TABLE tb_shop_package_allocation
ADD COLUMN IF NOT EXISTS series_id BIGINT NOT NULL DEFAULT 0;
COMMENT ON COLUMN tb_shop_package_allocation.series_id IS '套餐系列ID冗余字段便于查询';
-- 恢复 one_time_commission_amount 字段
ALTER TABLE tb_shop_package_allocation
ADD COLUMN IF NOT EXISTS one_time_commission_amount BIGINT NOT NULL DEFAULT 0;
COMMENT ON COLUMN tb_shop_package_allocation.one_time_commission_amount IS '该代理能拿到的一次性佣金(分)';
-- 删除 series_allocation_id 字段
DROP INDEX IF EXISTS idx_shop_package_allocation_series_allocation_id;
ALTER TABLE tb_shop_package_allocation
DROP COLUMN IF EXISTS series_allocation_id;
-- ============================================
-- 2. 恢复 tb_shop_series_one_time_commission_tier 表
-- ============================================
CREATE TABLE IF NOT EXISTS tb_shop_series_one_time_commission_tier (
id BIGSERIAL PRIMARY KEY,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW(),
deleted_at TIMESTAMPTZ,
creator BIGINT NOT NULL DEFAULT 0,
updater BIGINT NOT NULL DEFAULT 0,
allocation_id BIGINT NOT NULL,
tier_type VARCHAR(50) NOT NULL,
threshold_value BIGINT NOT NULL DEFAULT 0,
commission_mode VARCHAR(20) NOT NULL DEFAULT 'fixed',
commission_value BIGINT NOT NULL DEFAULT 0,
stat_scope VARCHAR(50) NOT NULL DEFAULT 'self',
status INT NOT NULL DEFAULT 1
);
CREATE INDEX IF NOT EXISTS idx_shop_series_otc_tier_allocation_id
ON tb_shop_series_one_time_commission_tier(allocation_id);
COMMENT ON TABLE tb_shop_series_one_time_commission_tier IS '一次性佣金梯度配置表';
-- ============================================
-- 3. 删除 tb_shop_series_allocation 表
-- ============================================
DROP TABLE IF EXISTS tb_shop_series_allocation;

View File

@@ -0,0 +1,77 @@
-- 重构一次性佣金分配:恢复系列分配表,移除套餐分配中的佣金字段
-- 原因:一次性佣金是系列级概念,不应存储在套餐分配中
-- ============================================
-- 1. 创建 tb_shop_series_allocation 表
-- ============================================
CREATE TABLE IF NOT EXISTS tb_shop_series_allocation (
id BIGSERIAL PRIMARY KEY,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW(),
deleted_at TIMESTAMPTZ,
creator BIGINT NOT NULL DEFAULT 0,
updater BIGINT NOT NULL DEFAULT 0,
shop_id BIGINT NOT NULL,
series_id BIGINT NOT NULL,
allocator_shop_id BIGINT NOT NULL DEFAULT 0,
one_time_commission_amount BIGINT NOT NULL DEFAULT 0,
enable_one_time_commission BOOLEAN NOT NULL DEFAULT false,
one_time_commission_trigger VARCHAR(50),
one_time_commission_threshold BIGINT NOT NULL DEFAULT 0,
enable_force_recharge BOOLEAN NOT NULL DEFAULT false,
force_recharge_amount BIGINT NOT NULL DEFAULT 0,
force_recharge_trigger_type INT NOT NULL DEFAULT 2,
status INT NOT NULL DEFAULT 1
);
-- 索引
CREATE INDEX IF NOT EXISTS idx_shop_series_allocation_shop_id
ON tb_shop_series_allocation(shop_id);
CREATE INDEX IF NOT EXISTS idx_shop_series_allocation_series_id
ON tb_shop_series_allocation(series_id);
CREATE INDEX IF NOT EXISTS idx_shop_series_allocation_allocator_shop_id
ON tb_shop_series_allocation(allocator_shop_id);
CREATE UNIQUE INDEX IF NOT EXISTS idx_shop_series_allocation_shop_series
ON tb_shop_series_allocation(shop_id, series_id) WHERE deleted_at IS NULL;
-- 注释
COMMENT ON TABLE tb_shop_series_allocation IS '店铺系列分配表 - 管理系列级一次性佣金配置';
COMMENT ON COLUMN tb_shop_series_allocation.shop_id IS '被分配店铺ID';
COMMENT ON COLUMN tb_shop_series_allocation.series_id IS '套餐系列ID';
COMMENT ON COLUMN tb_shop_series_allocation.allocator_shop_id IS '分配者店铺ID0表示平台分配';
COMMENT ON COLUMN tb_shop_series_allocation.one_time_commission_amount IS '该代理能拿的一次性佣金金额上限(分)';
COMMENT ON COLUMN tb_shop_series_allocation.enable_one_time_commission IS '是否启用一次性佣金';
COMMENT ON COLUMN tb_shop_series_allocation.one_time_commission_trigger IS '一次性佣金触发类型 first_recharge-首次充值 accumulated_recharge-累计充值';
COMMENT ON COLUMN tb_shop_series_allocation.one_time_commission_threshold IS '一次性佣金触发阈值(分)';
COMMENT ON COLUMN tb_shop_series_allocation.enable_force_recharge IS '是否启用强制充值';
COMMENT ON COLUMN tb_shop_series_allocation.force_recharge_amount IS '强制充值金额(分)';
COMMENT ON COLUMN tb_shop_series_allocation.force_recharge_trigger_type IS '强充触发类型 1-单次充值 2-累计充值';
COMMENT ON COLUMN tb_shop_series_allocation.status IS '状态 1-启用 2-禁用';
-- ============================================
-- 2. 修改 tb_shop_package_allocation 表
-- ============================================
-- 添加 series_allocation_id 字段(如果不存在)
ALTER TABLE tb_shop_package_allocation
ADD COLUMN IF NOT EXISTS series_allocation_id BIGINT;
COMMENT ON COLUMN tb_shop_package_allocation.series_allocation_id IS '关联的系列分配ID';
CREATE INDEX IF NOT EXISTS idx_shop_package_allocation_series_allocation_id
ON tb_shop_package_allocation(series_allocation_id);
-- 删除 one_time_commission_amount 字段(佣金配置移到系列分配)
ALTER TABLE tb_shop_package_allocation
DROP COLUMN IF EXISTS one_time_commission_amount;
-- 删除 series_id 字段(通过 series_allocation_id 关联)
ALTER TABLE tb_shop_package_allocation
DROP COLUMN IF EXISTS series_id;
-- ============================================
-- 3. 删除 tb_shop_series_one_time_commission_tier 表(未使用)
-- ============================================
DROP TABLE IF EXISTS tb_shop_series_one_time_commission_tier;

View File

@@ -0,0 +1,4 @@
DROP INDEX IF EXISTS idx_package_series_enable_one_time_commission;
ALTER TABLE tb_package_series
DROP COLUMN IF EXISTS enable_one_time_commission;

View File

@@ -0,0 +1,10 @@
-- 修复tb_package_series 表添加 enable_one_time_commission 字段
-- 该字段在 000042 迁移中被删除,但按新设计需要在系列级启用/禁用一次性佣金
ALTER TABLE tb_package_series
ADD COLUMN IF NOT EXISTS enable_one_time_commission BOOLEAN NOT NULL DEFAULT false;
COMMENT ON COLUMN tb_package_series.enable_one_time_commission IS '是否启用一次性佣金顶层字段支持SQL索引';
CREATE INDEX IF NOT EXISTS idx_package_series_enable_one_time_commission
ON tb_package_series(enable_one_time_commission);