跳到主要内容

账户交易流水表 (Account Transactions)

account_transactions 表记录用户账户中所有资金变动的详细日志,是财务审计和对账的核心表。

交易类型枚举

系统定义了以下交易类型:

CREATE TYPE tx_type AS ENUM (
'deposit', -- 充值:用户从外部钱包向平台充值
'withdraw', -- 提现:用户从平台提现到外部钱包
'invest', -- 申购:用户申购理财产品(资金冻结)
'interest_return', -- 利息返还:理财产品到期派发利息
'principal_return' -- 本金返还:理财产品到期返还本金
);

表结构

完整的 account_transactions 表定义:

CREATE TABLE account_transactions (
id BIGSERIAL PRIMARY KEY,
user_id INT NOT NULL,
amount NUMERIC(20, 6) NOT NULL,
transaction_type tx_type NOT NULL,
reference_id INT, -- 关联的 investment_id 或外部交易哈希
created_at TIMESTAMPTZ DEFAULT NOW()
);

字段详解

字段类型约束说明
idBIGSERIALPRIMARY KEY流水记录唯一标识(自增主键)
使用 BIGSERIAL 支持海量交易记录
user_idINTNOT NULL用户 ID,关联用户表
amountNUMERIC(20, 6)NOT NULL交易金额(USDT)
正数表示资金增加,负数表示资金减少
transaction_typetx_typeNOT NULL交易类型,见上方枚举说明
reference_idINT-关联业务记录 ID
- 理财相关:关联 user_investments.id
- 充提相关:可存储外部交易哈希或订单号
created_atTIMESTAMPTZDEFAULT NOW()交易发生时间(带时区)

交易类型说明

1. deposit(充值)

用户从外部钱包充值到平台账户:

  • 金额:正数
  • reference_id:可存储链上交易哈希或充值订单ID
  • 示例:用户充值 1000 USDT 到平台

2. withdraw(提现)

用户从平台账户提现到外部钱包:

  • 金额:负数
  • reference_id:可存储链上交易哈希或提现订单ID
  • 示例:用户提现 500 USDT 到个人钱包

3. invest(申购)

用户申购理财产品,资金从可用余额转入理财:

  • 金额:负数(从账户扣除)
  • reference_id:关联 user_investments.id
  • 示例:用户申购 5000 USDT 理财产品

4. interest_return(利息返还)

理财产品到期,派发利息到用户账户:

  • 金额:正数
  • reference_id:关联 user_investments.id
  • 示例:理财产品到期,派发 144 USDT 利息

5. principal_return(本金返还)

理财产品到期,返还本金到用户账户:

  • 金额:正数
  • reference_id:关联 user_investments.id
  • 示例:理财产品到期,返还 5000 USDT 本金

示例数据

示例 1:用户充值

-- 用户 1001 充值 10000 USDT
INSERT INTO account_transactions (
user_id,
amount,
transaction_type,
reference_id,
created_at
) VALUES (
1001,
10000.000000, -- 正数,账户增加
'deposit',
NULL, -- 或存储链上交易哈希
'2026-01-07 10:00:00+08'
);

示例 2:用户申购理财产品

-- 用户 1001 申购 5000 USDT 理财产品(投资记录 ID: 123)
INSERT INTO account_transactions (
user_id,
amount,
transaction_type,
reference_id,
created_at
) VALUES (
1001,
-5000.000000, -- 负数,账户减少
'invest',
123, -- 关联 user_investments.id
'2026-01-10 10:30:00+08'
);

示例 3:理财产品到期结算(本金 + 利息)

-- 理财产品到期,返还本金 5000 USDT
INSERT INTO account_transactions (
user_id,
amount,
transaction_type,
reference_id,
created_at
) VALUES (
1001,
5000.000000, -- 正数,返还本金
'principal_return',
123, -- 关联同一个 investment_id
'2026-01-17 10:00:00+08'
);

-- 派发利息 144 USDT
INSERT INTO account_transactions (
user_id,
amount,
transaction_type,
reference_id,
created_at
) VALUES (
1001,
144.000000, -- 正数,派发利息
'interest_return',
123, -- 关联同一个 investment_id
'2026-01-17 10:00:00+08'
);

示例 4:用户提现

-- 用户 1001 提现 3000 USDT
INSERT INTO account_transactions (
user_id,
amount,
transaction_type,
reference_id,
created_at
) VALUES (
1001,
-3000.000000, -- 负数,账户减少
'withdraw',
NULL, -- 或存储提现订单号
'2026-01-18 14:30:00+08'
);

完整业务流程示例

用户 1001 的完整资金流水

-- 按时间顺序插入流水记录
INSERT INTO account_transactions (user_id, amount, transaction_type, reference_id, created_at) VALUES
-- 1. 充值 10000 USDT
(1001, 10000.000000, 'deposit', NULL, '2026-01-07 10:00:00+08'),

-- 2. 申购理财 5000 USDT(投资记录 ID: 123)
(1001, -5000.000000, 'invest', 123, '2026-01-10 10:30:00+08'),

-- 3. 理财到期,返还本金 5000 USDT
(1001, 5000.000000, 'principal_return', 123, '2026-01-17 10:00:00+08'),

-- 4. 理财到期,派发利息 144 USDT
(1001, 144.000000, 'interest_return', 123, '2026-01-17 10:00:00+08'),

-- 5. 提现 3000 USDT
(1001, -3000.000000, 'withdraw', NULL, '2026-01-18 14:30:00+08');

-- 最终余额:10000 - 5000 + 5000 + 144 - 3000 = 7144 USDT

查询示例

1. 查询用户的交易流水

SELECT 
id,
amount,
transaction_type,
reference_id,
created_at
FROM account_transactions
WHERE user_id = 1001
ORDER BY created_at DESC
LIMIT 50;

2. 计算用户当前余额

-- 通过累加所有交易金额计算余额
SELECT
user_id,
SUM(amount) AS current_balance
FROM account_transactions
WHERE user_id = 1001
GROUP BY user_id;

3. 查询用户的充值记录

SELECT 
id,
amount,
created_at
FROM account_transactions
WHERE user_id = 1001
AND transaction_type = 'deposit'
ORDER BY created_at DESC;

4. 查询用户的理财相关流水

-- 查询所有理财申购和赎回记录
SELECT
at.id,
at.transaction_type,
at.amount,
at.reference_id AS investment_id,
ui.invested_amount,
ui.expected_interest,
at.created_at
FROM account_transactions at
LEFT JOIN user_investments ui ON at.reference_id = ui.id
WHERE at.user_id = 1001
AND at.transaction_type IN ('invest', 'principal_return', 'interest_return')
ORDER BY at.created_at DESC;

5. 统计用户的总利息收入

SELECT 
user_id,
SUM(amount) AS total_interest_earned
FROM account_transactions
WHERE user_id = 1001
AND transaction_type = 'interest_return'
GROUP BY user_id;

6. 查询指定时间段的交易流水

SELECT 
transaction_type,
COUNT(*) AS transaction_count,
SUM(amount) AS total_amount
FROM account_transactions
WHERE user_id = 1001
AND created_at BETWEEN '2026-01-01' AND '2026-01-31'
GROUP BY transaction_type
ORDER BY transaction_count DESC;

7. 查询某笔投资的完整资金流水

-- 查询投资记录 ID 为 123 的所有相关交易
SELECT
id,
transaction_type,
amount,
created_at,
CASE
WHEN transaction_type = 'invest' THEN '申购扣款'
WHEN transaction_type = 'principal_return' THEN '本金返还'
WHEN transaction_type = 'interest_return' THEN '利息派发'
ELSE '其他'
END AS description
FROM account_transactions
WHERE reference_id = 123
ORDER BY created_at ASC;

8. 审计检查:验证投资记录与交易流水一致性

-- 检查投资记录与交易流水的金额是否匹配
SELECT
ui.id AS investment_id,
ui.user_id,
ui.invested_amount,
ui.expected_interest,
ui.actual_interest,
-- 申购扣款
ABS(SUM(CASE WHEN at.transaction_type = 'invest' THEN at.amount ELSE 0 END)) AS invest_tx_amount,
-- 本金返还
SUM(CASE WHEN at.transaction_type = 'principal_return' THEN at.amount ELSE 0 END) AS principal_tx_amount,
-- 利息派发
SUM(CASE WHEN at.transaction_type = 'interest_return' THEN at.amount ELSE 0 END) AS interest_tx_amount,
-- 检查一致性
ui.invested_amount = ABS(SUM(CASE WHEN at.transaction_type = 'invest' THEN at.amount ELSE 0 END)) AS invest_match,
ui.actual_interest = SUM(CASE WHEN at.transaction_type = 'interest_return' THEN at.amount ELSE 0 END) AS interest_match
FROM user_investments ui
LEFT JOIN account_transactions at ON ui.id = at.reference_id
WHERE ui.id = 123
GROUP BY ui.id, ui.user_id, ui.invested_amount, ui.expected_interest, ui.actual_interest;

业务逻辑

申购理财产品时记录流水

-- 在用户申购理财产品时,同时创建投资记录和交易流水

BEGIN;

-- 1. 创建投资记录
INSERT INTO user_investments (user_id, product_id, invested_amount, expected_interest, status)
VALUES (1001, 1, 5000.000000, 144.000000, 'holding')
RETURNING id INTO @investment_id;

-- 2. 记录资金流水(扣款)
INSERT INTO account_transactions (user_id, amount, transaction_type, reference_id)
VALUES (1001, -5000.000000, 'invest', @investment_id);

-- 3. 更新产品已售额度
UPDATE financial_products
SET current_amount = current_amount + 5000.000000
WHERE id = 1;

COMMIT;

理财产品到期结算时记录流水

-- 到期时,返还本金和利息,并记录两条流水

BEGIN;

-- 1. 更新投资记录状态
UPDATE user_investments
SET status = 'redeemed',
actual_interest = expected_interest,
redeemed_at = NOW()
WHERE id = 123;

-- 2. 记录本金返还流水
INSERT INTO account_transactions (user_id, amount, transaction_type, reference_id)
SELECT user_id, invested_amount, 'principal_return', 123
FROM user_investments WHERE id = 123;

-- 3. 记录利息派发流水
INSERT INTO account_transactions (user_id, amount, transaction_type, reference_id)
SELECT user_id, actual_interest, 'interest_return', 123
FROM user_investments WHERE id = 123;

COMMIT;

索引建议

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

-- 用户ID索引,用于查询用户的所有流水
CREATE INDEX idx_account_tx_user_id ON account_transactions(user_id);

-- 交易类型索引,用于按类型筛选
CREATE INDEX idx_account_tx_type ON account_transactions(transaction_type);

-- 时间索引,用于按时间范围查询
CREATE INDEX idx_account_tx_created_at ON account_transactions(created_at);

-- 关联ID索引,用于查询特定业务的流水
CREATE INDEX idx_account_tx_reference_id ON account_transactions(reference_id)
WHERE reference_id IS NOT NULL;

-- 复合索引,用于查询用户在特定时间段的流水
CREATE INDEX idx_account_tx_user_time ON account_transactions(user_id, created_at DESC);

-- 复合索引,用于按用户和类型查询
CREATE INDEX idx_account_tx_user_type ON account_transactions(user_id, transaction_type);

数据分区建议

由于 account_transactions 表数据量会快速增长,建议使用时间分区:

-- 创建按月分区的表(PostgreSQL 10+)
CREATE TABLE account_transactions (
id BIGSERIAL,
user_id INT NOT NULL,
amount NUMERIC(20, 6) NOT NULL,
transaction_type tx_type NOT NULL,
reference_id INT,
created_at TIMESTAMPTZ DEFAULT NOW(),
PRIMARY KEY (id, created_at)
) PARTITION BY RANGE (created_at);

-- 创建月度分区
CREATE TABLE account_transactions_2026_01 PARTITION OF account_transactions
FOR VALUES FROM ('2026-01-01') TO ('2026-02-01');

CREATE TABLE account_transactions_2026_02 PARTITION OF account_transactions
FOR VALUES FROM ('2026-02-01') TO ('2026-03-01');

-- 继续创建后续月份的分区...

触发器建议

防止修改历史流水

-- 创建触发器防止修改或删除已有的交易流水
CREATE OR REPLACE FUNCTION prevent_transaction_modification()
RETURNS TRIGGER AS $$
BEGIN
RAISE EXCEPTION 'Transaction records are immutable and cannot be modified or deleted';
RETURN NULL;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER trigger_prevent_update
BEFORE UPDATE ON account_transactions
FOR EACH ROW
EXECUTE FUNCTION prevent_transaction_modification();

CREATE TRIGGER trigger_prevent_delete
BEFORE DELETE ON account_transactions
FOR EACH ROW
EXECUTE FUNCTION prevent_transaction_modification();

相关表

注意事项

  1. 不可变性:交易流水一旦创建,不应被修改或删除,这是审计的基本要求
  2. 精度控制amount 使用 NUMERIC(20, 6) 保证计算精度
  3. 金额符号约定
    • 正数:资金流入(充值、利息、本金返还)
    • 负数:资金流出(提现、申购)
  4. 时区处理created_at 使用 TIMESTAMPTZ 确保跨时区一致性
  5. 原子性:每笔业务操作都应在事务中同时创建业务记录和交易流水
  6. 数据一致性
    • 用户余额 = SUM(amount) WHERE user_id = X
    • 每笔投资应有对应的 invest, principal_return, interest_return 流水
  7. 性能优化
    • 使用分区表管理历史数据
    • 定期归档旧数据到冷存储
    • 必要时可以缓存用户余额以提高查询性能
  8. reference_id 使用规范
    • 理财相关:存储 user_investments.id
    • 充提相关:可存储外部交易哈希或订单ID
    • 确保可追溯到具体业务场景