feat: 实现账号与佣金管理模块
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 4m35s

新增功能:
- 店铺佣金查询:店铺佣金统计、店铺佣金记录列表、店铺提现记录
- 佣金提现审批:提现申请列表、审批通过、审批拒绝
- 提现配置管理:配置列表、新增配置、获取当前生效配置
- 企业管理:企业列表、创建、更新、删除、获取详情
- 企业卡授权:授权列表、批量授权、批量取消授权、统计
- 客户账号管理:账号列表、创建、更新状态、重置密码
- 我的佣金:佣金统计、佣金记录、提现申请、提现记录

数据库变更:
- 扩展 tb_commission_withdrawal_request 新增提现单号等字段
- 扩展 tb_account 新增 is_primary 字段
- 扩展 tb_commission_record 新增 shop_id、balance_after
- 扩展 tb_commission_withdrawal_setting 新增每日提现次数限制
- 扩展 tb_iot_card、tb_device 新增 shop_id 冗余字段
- 新建 tb_enterprise_card_authorization 企业卡授权表
- 新建 tb_asset_allocation_record 资产分配记录表
- 数据迁移:owner_type 枚举值 agent 统一为 shop

测试:
- 新增 7 个单元测试文件覆盖各服务
- 修复集成测试 Redis 依赖问题
This commit is contained in:
2026-01-21 18:20:44 +08:00
parent 1489abe668
commit 91c9bbfeb8
89 changed files with 11958 additions and 159 deletions

View File

@@ -0,0 +1,42 @@
-- 回滚: 账号与佣金管理模块数据模型变更
-- 变更ID: add-commission-model-changes
-- 1. 恢复 owner_type 枚举值 (shop -> agent)
UPDATE tb_iot_card SET owner_type = 'agent' WHERE owner_type = 'shop';
UPDATE tb_device SET owner_type = 'agent' WHERE owner_type = 'shop';
-- 2. 删除新增的表
DROP TABLE IF EXISTS tb_asset_allocation_record;
DROP TABLE IF EXISTS tb_enterprise_card_authorization;
-- 3. 删除 tb_device.shop_id 字段
DROP INDEX IF EXISTS idx_device_shop_id;
ALTER TABLE tb_device DROP COLUMN IF EXISTS shop_id;
-- 4. 删除 tb_iot_card.shop_id 字段
DROP INDEX IF EXISTS idx_iot_card_shop_id;
ALTER TABLE tb_iot_card DROP COLUMN IF EXISTS shop_id;
-- 5. 删除 tb_commission_withdrawal_setting.daily_withdrawal_limit 字段
ALTER TABLE tb_commission_withdrawal_setting DROP COLUMN IF EXISTS daily_withdrawal_limit;
-- 6. 删除 tb_commission_record 新增字段
DROP INDEX IF EXISTS idx_commission_record_shop_id;
ALTER TABLE tb_commission_record
DROP COLUMN IF EXISTS shop_id,
DROP COLUMN IF EXISTS balance_after;
-- 7. 删除 tb_account.is_primary 字段
ALTER TABLE tb_account DROP COLUMN IF EXISTS is_primary;
-- 8. 删除 tb_commission_withdrawal_request 新增字段
DROP INDEX IF EXISTS uk_commission_withdrawal_no;
ALTER TABLE tb_commission_withdrawal_request
DROP COLUMN IF EXISTS withdrawal_no,
DROP COLUMN IF EXISTS applicant_id,
DROP COLUMN IF EXISTS shop_id,
DROP COLUMN IF EXISTS fee_rate,
DROP COLUMN IF EXISTS payment_type,
DROP COLUMN IF EXISTS processor_id,
DROP COLUMN IF EXISTS processed_at,
DROP COLUMN IF EXISTS remark;

View File

@@ -0,0 +1,185 @@
-- 迁移: 账号与佣金管理模块数据模型变更
-- 变更ID: add-commission-model-changes
-- 说明:
-- 1. 扩展现有表字段
-- 2. 创建企业卡授权表和资产分配记录表
-- 3. 统一 owner_type 枚举值 (agent -> shop)
-- ========================================
-- 1. 扩展现有表字段
-- ========================================
-- 1.1 tb_commission_withdrawal_request 佣金提现申请表新增字段
ALTER TABLE tb_commission_withdrawal_request
ADD COLUMN IF NOT EXISTS withdrawal_no VARCHAR(50),
ADD COLUMN IF NOT EXISTS applicant_id BIGINT,
ADD COLUMN IF NOT EXISTS shop_id BIGINT,
ADD COLUMN IF NOT EXISTS fee_rate BIGINT DEFAULT 0,
ADD COLUMN IF NOT EXISTS payment_type VARCHAR(20) DEFAULT 'manual',
ADD COLUMN IF NOT EXISTS processor_id BIGINT,
ADD COLUMN IF NOT EXISTS processed_at TIMESTAMP WITH TIME ZONE,
ADD COLUMN IF NOT EXISTS remark TEXT;
-- 添加唯一索引(提现单号)
CREATE UNIQUE INDEX IF NOT EXISTS uk_commission_withdrawal_no
ON tb_commission_withdrawal_request(withdrawal_no) WHERE deleted_at IS NULL AND withdrawal_no IS NOT NULL;
-- 添加字段注释
COMMENT ON COLUMN tb_commission_withdrawal_request.withdrawal_no IS '提现单号唯一格式W + 时间戳 + 随机数)';
COMMENT ON COLUMN tb_commission_withdrawal_request.applicant_id IS '申请人账号ID';
COMMENT ON COLUMN tb_commission_withdrawal_request.shop_id IS '店铺ID冗余字段';
COMMENT ON COLUMN tb_commission_withdrawal_request.fee_rate IS '手续费比率基点100=1%,快照)';
COMMENT ON COLUMN tb_commission_withdrawal_request.payment_type IS '放款类型manual=人工打款)';
COMMENT ON COLUMN tb_commission_withdrawal_request.processor_id IS '处理人ID';
COMMENT ON COLUMN tb_commission_withdrawal_request.processed_at IS '处理时间';
COMMENT ON COLUMN tb_commission_withdrawal_request.remark IS '备注';
-- 1.2 tb_account 账号表新增字段
ALTER TABLE tb_account
ADD COLUMN IF NOT EXISTS is_primary BOOLEAN DEFAULT FALSE;
COMMENT ON COLUMN tb_account.is_primary IS '是否为店铺主账号(默认 false';
-- 1.3 tb_commission_record 佣金记录表新增字段
ALTER TABLE tb_commission_record
ADD COLUMN IF NOT EXISTS shop_id BIGINT,
ADD COLUMN IF NOT EXISTS balance_after BIGINT DEFAULT 0;
-- 添加索引
CREATE INDEX IF NOT EXISTS idx_commission_record_shop_id ON tb_commission_record(shop_id);
COMMENT ON COLUMN tb_commission_record.shop_id IS '店铺ID佣金主要跟着店铺走';
COMMENT ON COLUMN tb_commission_record.balance_after IS '入账后佣金余额(分)';
-- 1.4 tb_commission_withdrawal_setting 提现设置表新增字段
ALTER TABLE tb_commission_withdrawal_setting
ADD COLUMN IF NOT EXISTS daily_withdrawal_limit INT DEFAULT 3;
COMMENT ON COLUMN tb_commission_withdrawal_setting.daily_withdrawal_limit IS '每日提现次数限制';
-- 1.5 tb_iot_card 物联网卡表新增字段
ALTER TABLE tb_iot_card
ADD COLUMN IF NOT EXISTS shop_id BIGINT;
-- 添加索引
CREATE INDEX IF NOT EXISTS idx_iot_card_shop_id ON tb_iot_card(shop_id);
COMMENT ON COLUMN tb_iot_card.shop_id IS '店铺ID冗余字段方便查询';
-- 1.6 tb_device 设备表新增字段
ALTER TABLE tb_device
ADD COLUMN IF NOT EXISTS shop_id BIGINT;
-- 添加索引
CREATE INDEX IF NOT EXISTS idx_device_shop_id ON tb_device(shop_id);
COMMENT ON COLUMN tb_device.shop_id IS '店铺ID冗余字段方便查询';
-- ========================================
-- 2. 创建新表
-- ========================================
-- 2.1 tb_enterprise_card_authorization 企业卡授权表
CREATE TABLE IF NOT EXISTS tb_enterprise_card_authorization (
id BIGSERIAL PRIMARY KEY,
enterprise_id BIGINT NOT NULL,
iot_card_id BIGINT NOT NULL,
shop_id BIGINT NOT NULL,
authorized_by BIGINT NOT NULL,
authorized_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
status INT DEFAULT 1,
creator BIGINT NOT NULL,
updater BIGINT NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
deleted_at TIMESTAMP WITH TIME ZONE
);
-- 添加唯一约束(企业+卡唯一)
CREATE UNIQUE INDEX IF NOT EXISTS uk_enterprise_card
ON tb_enterprise_card_authorization(enterprise_id, iot_card_id) WHERE deleted_at IS NULL;
-- 添加索引
CREATE INDEX IF NOT EXISTS idx_enterprise_card_auth_enterprise_id ON tb_enterprise_card_authorization(enterprise_id);
CREATE INDEX IF NOT EXISTS idx_enterprise_card_auth_iot_card_id ON tb_enterprise_card_authorization(iot_card_id);
CREATE INDEX IF NOT EXISTS idx_enterprise_card_auth_shop_id ON tb_enterprise_card_authorization(shop_id);
CREATE INDEX IF NOT EXISTS idx_enterprise_card_auth_status ON tb_enterprise_card_authorization(status);
-- 添加表注释
COMMENT ON TABLE tb_enterprise_card_authorization IS '企业卡授权表,记录企业被授权可见的卡';
COMMENT ON COLUMN tb_enterprise_card_authorization.enterprise_id IS '企业ID';
COMMENT ON COLUMN tb_enterprise_card_authorization.iot_card_id IS 'IoT卡ID';
COMMENT ON COLUMN tb_enterprise_card_authorization.shop_id IS '店铺ID授权方';
COMMENT ON COLUMN tb_enterprise_card_authorization.authorized_by IS '授权人ID';
COMMENT ON COLUMN tb_enterprise_card_authorization.authorized_at IS '授权时间';
COMMENT ON COLUMN tb_enterprise_card_authorization.status IS '状态 1=有效 0=已回收';
COMMENT ON COLUMN tb_enterprise_card_authorization.creator IS '创建人ID';
COMMENT ON COLUMN tb_enterprise_card_authorization.updater IS '更新人ID';
-- 2.2 tb_asset_allocation_record 资产分配记录表
CREATE TABLE IF NOT EXISTS tb_asset_allocation_record (
id BIGSERIAL PRIMARY KEY,
allocation_no VARCHAR(50) NOT NULL,
allocation_type VARCHAR(20) NOT NULL,
asset_type VARCHAR(20) NOT NULL,
asset_id BIGINT NOT NULL,
asset_identifier VARCHAR(50) NOT NULL,
from_owner_type VARCHAR(20),
from_owner_id BIGINT,
to_owner_type VARCHAR(20) NOT NULL,
to_owner_id BIGINT NOT NULL,
related_device_id BIGINT,
related_card_ids JSONB,
operator_id BIGINT NOT NULL,
remark TEXT,
creator BIGINT NOT NULL,
updater BIGINT NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
deleted_at TIMESTAMP WITH TIME ZONE
);
-- 添加唯一约束(分配单号唯一)
CREATE UNIQUE INDEX IF NOT EXISTS uk_asset_allocation_no
ON tb_asset_allocation_record(allocation_no) WHERE deleted_at IS NULL;
-- 添加索引
CREATE INDEX IF NOT EXISTS idx_asset_allocation_type ON tb_asset_allocation_record(allocation_type);
CREATE INDEX IF NOT EXISTS idx_asset_allocation_asset_type ON tb_asset_allocation_record(asset_type);
CREATE INDEX IF NOT EXISTS idx_asset_allocation_asset_id ON tb_asset_allocation_record(asset_id);
CREATE INDEX IF NOT EXISTS idx_asset_allocation_from_owner ON tb_asset_allocation_record(from_owner_type, from_owner_id);
CREATE INDEX IF NOT EXISTS idx_asset_allocation_to_owner ON tb_asset_allocation_record(to_owner_type, to_owner_id);
-- 添加表注释
COMMENT ON TABLE tb_asset_allocation_record IS '资产分配记录表,记录卡/设备在平台和代理商之间的流转历史';
COMMENT ON COLUMN tb_asset_allocation_record.allocation_no IS '分配单号(唯一)';
COMMENT ON COLUMN tb_asset_allocation_record.allocation_type IS '分配类型 allocate=分配 recall=回收';
COMMENT ON COLUMN tb_asset_allocation_record.asset_type IS '资产类型 iot_card=物联网卡 device=设备';
COMMENT ON COLUMN tb_asset_allocation_record.asset_id IS '资产ID';
COMMENT ON COLUMN tb_asset_allocation_record.asset_identifier IS '资产标识符ICCID或设备号';
COMMENT ON COLUMN tb_asset_allocation_record.from_owner_type IS '来源所有者类型';
COMMENT ON COLUMN tb_asset_allocation_record.from_owner_id IS '来源所有者ID';
COMMENT ON COLUMN tb_asset_allocation_record.to_owner_type IS '目标所有者类型';
COMMENT ON COLUMN tb_asset_allocation_record.to_owner_id IS '目标所有者ID';
COMMENT ON COLUMN tb_asset_allocation_record.related_device_id IS '关联设备ID';
COMMENT ON COLUMN tb_asset_allocation_record.related_card_ids IS '关联卡ID列表';
COMMENT ON COLUMN tb_asset_allocation_record.operator_id IS '操作人ID';
COMMENT ON COLUMN tb_asset_allocation_record.remark IS '备注';
COMMENT ON COLUMN tb_asset_allocation_record.creator IS '创建人ID';
COMMENT ON COLUMN tb_asset_allocation_record.updater IS '更新人ID';
-- ========================================
-- 3. 数据迁移 - owner_type 枚举统一
-- ========================================
-- 3.1 更新 tb_iot_card 表 owner_type='agent' 为 'shop'
UPDATE tb_iot_card SET owner_type = 'shop' WHERE owner_type = 'agent';
-- 3.2 更新 tb_device 表 owner_type='agent' 为 'shop'
UPDATE tb_device SET owner_type = 'shop' WHERE owner_type = 'agent';
-- 3.3 填充 tb_iot_card.shop_id 字段owner_type='shop' 时等于 owner_id
UPDATE tb_iot_card SET shop_id = owner_id WHERE owner_type = 'shop' AND shop_id IS NULL;
-- 3.4 填充 tb_device.shop_id 字段owner_type='shop' 时等于 owner_id
UPDATE tb_device SET shop_id = owner_id WHERE owner_type = 'shop' AND shop_id IS NULL;