跳到主要内容

理财产品期数表 (Financial Products)

financial_products 表用于管理平台发行的各期理财产品,支持期数化管理和完整的生命周期状态跟踪。

产品状态枚举

系统定义了以下产品状态类型:

CREATE TYPE product_status AS ENUM (
'pending', -- 待发布:产品已创建但未开始申购
'active', -- 申购中:正在募集资金
'sold_out', -- 已售罄:达到总额度上限
'settling', -- 结算中:到期正在进行收益结算
'finished' -- 已完成:结算完成,产品结束
);

表结构

完整的 financial_products 表定义:

CREATE TABLE financial_products (
id SERIAL PRIMARY KEY,
period_name VARCHAR(100) NOT NULL, -- 第N期理财
period_number INT UNIQUE NOT NULL, -- 期数编号,如1, 2, 3...
annual_yield NUMERIC(10, 4) NOT NULL, -- 年化收益率 (例: 1.5000 = 150%)
period_yield NUMERIC(10, 4) NOT NULL, -- 当期利率 (例: 0.0500 = 5%)
duration_days INT DEFAULT 7, -- 理财期限(天)

total_capacity NUMERIC(20, 6) NOT NULL, -- 总额度 (20万U)
current_amount NUMERIC(20, 6) DEFAULT 0, -- 已售金额
individual_min_amount NUMERIC(20, 6) DEFAULT 100, -- 个人起购限额
individual_max_amount NUMERIC(20, 6), -- 个人最高限额 (1万U)

start_time TIMESTAMPTZ NOT NULL, -- 申购开始时间
end_time TIMESTAMPTZ NOT NULL, -- 申购结束时间
settlement_time TIMESTAMPTZ NOT NULL, -- 到期结算时间

status product_status DEFAULT 'pending', -- 状态
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);

字段详解

基本信息

字段类型约束说明
idSERIALPRIMARY KEY理财产品唯一标识(自增主键)
period_nameVARCHAR(100)NOT NULL期数名称,如"第1期理财"、"第2期理财"
period_numberINTUNIQUE, NOT NULL期数编号,从 1 开始递增,确保唯一性

收益配置

字段类型约束说明
annual_yieldNUMERIC(10, 4)NOT NULL年化收益率,以小数表示
例:1.5000 表示 150% 年化收益率
period_yieldNUMERIC(10, 4)NOT NULL当期实际收益率,以小数表示
例:0.0500 表示本期 5% 收益率
duration_daysINTDEFAULT 7理财期限(天数),默认为 7 天

收益率计算关系period_yield = annual_yield × (duration_days / 365)

额度管理

字段类型约束说明
total_capacityNUMERIC(20, 6)NOT NULL总募集额度,单位:USDT
例:200000.000000 表示 20 万 U
current_amountNUMERIC(20, 6)DEFAULT 0当前已售金额,实时更新
individual_min_amountNUMERIC(20, 6)DEFAULT 100个人最低申购金额,默认 100 U
individual_max_amountNUMERIC(20, 6)-个人最高申购限额
例:10000.000000 表示单人最多购买 1 万 U

时间管理

字段类型约束说明
start_timeTIMESTAMPTZNOT NULL申购开始时间(带时区)
end_timeTIMESTAMPTZNOT NULL申购结束时间(带时区)
settlement_timeTIMESTAMPTZNOT NULL到期结算时间,通常为 end_time + duration_days

状态和审计

字段类型约束说明
statusproduct_statusDEFAULT 'pending'产品当前状态,见上方状态枚举说明
created_atTIMESTAMPTZDEFAULT NOW()记录创建时间
updated_atTIMESTAMPTZDEFAULT NOW()记录最后更新时间

产品生命周期

理财产品的典型状态流转:

pending → active → sold_out → settling → finished
↓ ↓
└──── (未售罄) ───────┘
  1. pending(待发布):产品创建完成,等待申购开始
  2. active(申购中):到达 start_time,开放用户申购
  3. sold_out(已售罄)current_amount >= total_capacity 时自动变更
  4. settling(结算中):到达 settlement_time,系统开始计算和分配收益
  5. finished(已完成):收益结算完成,产品周期结束

示例数据

示例 1:第 1 期理财产品

INSERT INTO financial_products (
period_name,
period_number,
annual_yield,
period_yield,
duration_days,
total_capacity,
individual_min_amount,
individual_max_amount,
start_time,
end_time,
settlement_time,
status
) VALUES (
'第1期理财',
1,
1.5000, -- 150% 年化收益率
0.0288, -- 约 2.88% 当期收益 (150% × 7/365)
7, -- 7天期
200000.000000, -- 总额度 20万U
100.000000, -- 起购 100U
10000.000000, -- 最高 1万U
'2026-01-10 10:00:00+08', -- 申购开始
'2026-01-17 10:00:00+08', -- 申购结束
'2026-01-24 10:00:00+08', -- 结算时间(7天后)
'pending'
);

示例 2:第 2 期理财产品(高收益短期)

INSERT INTO financial_products (
period_name,
period_number,
annual_yield,
period_yield,
duration_days,
total_capacity,
individual_min_amount,
individual_max_amount,
start_time,
end_time,
settlement_time,
status
) VALUES (
'第2期理财',
2,
2.0000, -- 200% 年化收益率
0.0548, -- 约 5.48% 当期收益 (200% × 10/365)
10, -- 10天期
500000.000000, -- 总额度 50万U
500.000000, -- 起购 500U
50000.000000, -- 最高 5万U
'2026-01-20 10:00:00+08',
'2026-01-30 10:00:00+08',
'2026-02-09 10:00:00+08', -- 结算时间(10天后)
'pending'
);

查询示例

获取所有进行中的理财产品

SELECT 
period_name,
period_number,
annual_yield,
period_yield,
duration_days,
total_capacity,
current_amount,
(total_capacity - current_amount) AS remaining_amount,
ROUND((current_amount / total_capacity * 100), 2) AS sold_percentage,
start_time,
end_time,
status
FROM financial_products
WHERE status = 'active'
ORDER BY period_number DESC;

获取即将到期需要结算的产品

SELECT 
id,
period_name,
settlement_time,
current_amount,
period_yield
FROM financial_products
WHERE status IN ('active', 'sold_out')
AND settlement_time <= NOW() + INTERVAL '1 day'
ORDER BY settlement_time ASC;

计算某期产品的总收益

-- 假设第1期产品已售 15万U
SELECT
period_name,
current_amount,
period_yield,
ROUND(current_amount * period_yield, 6) AS total_interest
FROM financial_products
WHERE period_number = 1;

-- 结果示例:
-- period_name: '第1期理财'
-- current_amount: 150000.000000
-- period_yield: 0.0288
-- total_interest: 4320.000000 (需要支付的总利息)

业务规则

1. 申购限制

  • 用户单次申购金额必须满足:individual_min_amount <= 申购金额 <= individual_max_amount
  • 用户对同一期产品的累计申购不能超过 individual_max_amount
  • 产品总申购量不能超过 total_capacity

2. 状态自动转换

系统应在以下时机自动更新产品状态:

-- 申购开始时: pending → active
UPDATE financial_products
SET status = 'active', updated_at = NOW()
WHERE status = 'pending'
AND start_time <= NOW();

-- 达到额度上限: active → sold_out
UPDATE financial_products
SET status = 'sold_out', updated_at = NOW()
WHERE status = 'active'
AND current_amount >= total_capacity;

-- 到期开始结算: active/sold_out → settling
UPDATE financial_products
SET status = 'settling', updated_at = NOW()
WHERE status IN ('active', 'sold_out')
AND settlement_time <= NOW();

3. 触发器建议

建议为表添加自动更新 updated_at 的触发器:

CREATE OR REPLACE FUNCTION update_modified_column()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = NOW();
RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER update_financial_products_modtime
BEFORE UPDATE ON financial_products
FOR EACH ROW
EXECUTE FUNCTION update_modified_column();

索引建议

为提高查询性能,建议添加以下索引:

-- 期数编号已有 UNIQUE 约束,会自动创建索引
-- CREATE UNIQUE INDEX idx_period_number ON financial_products(period_number);

-- 状态索引,用于快速筛选不同状态的产品
CREATE INDEX idx_product_status ON financial_products(status);

-- 时间索引,用于定时任务查询
CREATE INDEX idx_start_time ON financial_products(start_time);
CREATE INDEX idx_settlement_time ON financial_products(settlement_time);

-- 复合索引,用于查询活跃和未售罄的产品
CREATE INDEX idx_status_time ON financial_products(status, start_time, end_time);

相关表

注意事项

  1. 精度问题:收益率字段使用 NUMERIC(10, 4) 保证计算精度,避免浮点数误差
  2. 时区处理:所有时间字段使用 TIMESTAMPTZ 类型,确保跨时区一致性
  3. 并发控制:申购时需要使用事务和行锁,防止超售:
    BEGIN;
    SELECT * FROM financial_products WHERE id = $1 FOR UPDATE;
    -- 检查额度并更新 current_amount
    COMMIT;
  4. 数据一致性current_amount 应该与 investment_records 表中该期产品的总申购额保持一致