技术设计文档
版本: v1.0 创建日期: 2026-01-13 分支:
feature/points-system作者: AXBlade Team
目录
架构设计
事件驱动流程
交易积分流程
┌──────────────────────────────────────────────────────────────────────┐
│ Trade Event Flow → Points │
└──────────────────────────────────────────────────────────────────────┘
MatchingEngine
│
│ TradeEvent via broadcast::channel
▼
┌──────────────────┐
│ Trade Listener │ ◄─── 已存在于 main.rs
└────────┬─────────┘
│
│ After persist_trade() success
▼
┌──────────────────┐ ┌────────────────────────── ──────┐
│ PointsService │────▶│ 1. calculate_trading_points() │
│ │ │ - Query user's tier │
│ │ │ - Calculate: vol × tier × α │
│ │ │ - Record points_event │
│ │ │ - Update summary (atomic) │
│ │ └────────────────────────────────┘
│ │ ┌────────────────────────────────┐
│ │────▶│ 2. calculate_referral_points() │
│ │ │ - Query referral relation │
│ │ │ - Calculate referrer points │
│ │ │ - Record event for referrer │
│ │ └────────────────────────────────┘
└──────────────────┘
│
│ Async (non-blocking)
▼
┌──────────────────┐
│ Update Cache │ (invalidate user_points, tier)
└──────────────────┘
持仓积分流程
┌──────────────────────────────────────────────────────────────────────┐
│ Holding Points Background Task (Hourly) │
└──────────────────────────────────────────────────────────────────────┘
Tokio Scheduler (每小时)
│
▼
┌────────────────────────────┐
│ calculate_holding_points() │
└──────────┬─────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ Batch Process All Active Positions │
│ 1. Query: SELECT * FROM positions WHERE status='open' │
│ 2. For each position: │
│ - value = size × mark_price │
│ - hours = (now - last_calc_time).hours() │
│ - points = value × hours × 0.00001 │
│ - Insert points_event │
│ - Update user_points_summary │
│ - Update position.last_calc_time = now │
└─────────────────────────────────────────────────────────┘
集成点
与现有系统的集成点:
| 现有系统模块 | 集成方式 | 集成点 |
|---|---|---|
| MatchingEngine | 监听 TradeEvent | 计算交易积分 + 推荐积分 |
| PositionService | 查询活跃仓位 | 定时计算持仓积分 |
| ReferralService | 查询推荐关系 | 分配推荐积分 |
| BlockchainService | 监听质押事件 | 记录质押 + 计算质押积分 |
| Database | 共享连接池 | 所有数据持久化 |
| CacheManager | 统一缓存层 | 积分/Tier缓存 |
数据流向
实时数据流:
Trade → Points Event → Update Summary → Invalidate Cache → Broadcast Update
定时数据流:
Scheduler → Batch Calculate → Insert Events → Update Summaries → Refresh Cache
查询数据流:
API Request → Check Cache → Miss → Query DB → Update Cache → Return
数据库设计
表结构设计
Epoch配置表
CREATE TABLE IF NOT EXISTS points_epochs (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
epoch_number INT NOT NULL UNIQUE,
start_time TIMESTAMPTZ NOT NULL,
end_time TIMESTAMPTZ NOT NULL,
duration_days INT NOT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'pending',
-- 状态: pending(待开始), active(进行中), ended(已结束), settled(已结算)
config JSONB,
-- 存储当期配置:
-- {
-- "tiers": [{"name": "T1", "min": 0, "max": 99999, "multiplier": 1.0}, ...],
-- "point_rates": {"trading": 0.0001, "pnl": 0.001, ...}
-- }
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_epoch_status ON points_epochs(status);
CREATE INDEX idx_epoch_time ON points_epochs(start_time, end_time);
用户积分汇总表
CREATE TABLE IF NOT EXISTS user_points_summary (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_address VARCHAR(42) NOT NULL,
epoch_number INT NOT NULL,
-- 5种积分类型
trading_points DECIMAL(20, 2) NOT NULL DEFAULT 0,
pnl_points DECIMAL(20, 2) NOT NULL DEFAULT 0,
holding_points DECIMAL(20, 2) NOT NULL DEFAULT 0,
referral_points DECIMAL(20, 2) NOT NULL DEFAULT 0,
staking_points DECIMAL(20, 2) NOT NULL DEFAULT 0,
-- 汇总
total_points DECIMAL(20, 2) NOT NULL DEFAULT 0,
-- 交易统计
trading_volume DECIMAL(30, 6) NOT NULL DEFAULT 0,
trade_count INT NOT NULL DEFAULT 0,
realized_pnl DECIMAL(30, 6) NOT NULL DEFAULT 0,
-- Tier信息
tier VARCHAR(10), -- T1, T2, T3, T4
tier_multiplier DECIMAL(5, 2) DEFAULT 1.0,
-- 推荐统计
referral_count INT NOT NULL DEFAULT 0,
referral_volume DECIMAL(30, 6) NOT NULL DEFAULT 0,
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT unique_user_epoch UNIQUE(user_address, epoch_number)
);
CREATE INDEX idx_user_epoch ON user_points_summary(user_address, epoch_number);
CREATE INDEX idx_epoch_total_points ON user_points_summary(epoch_number, total_points DESC);
CREATE INDEX idx_epoch_trading_points ON user_points_summary(epoch_number, trading_points DESC);
CREATE INDEX idx_epoch_tier ON user_points_summary(epoch_number, tier);
积分事件明细表 (TimescaleDB)
CREATE TABLE IF NOT EXISTS points_events (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_address VARCHAR(42) NOT NULL,
epoch_number INT NOT NULL,
point_type VARCHAR(20) NOT NULL,
-- 类型: trading, pnl, holding, referral, staking
points DECIMAL(20, 2) NOT NULL,
-- 关联数据
related_trade_id UUID, -- 关联交易ID
related_order_id UUID, -- 关联订单ID
related_position_id UUID, -- 关联仓位ID
referrer_address VARCHAR(42), -- 推荐人地址(仅referral类型)
-- 元数据
metadata JSONB,
-- 示例:
-- Trading: {"volume": 100000, "tier": "T2", "multiplier": 1.1}
-- PnL: {"realized_pnl": 5000, "position_size": 10}
-- Holding: {"position_value": 50000, "hours": 24}
-- Referral: {"referee": "0x...", "referee_volume": 10000}
-- Staking: {"staking_amount": 10000, "days": 1}
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- 转换为TimescaleDB hypertable (时序优化)
SELECT create_hypertable('points_events', 'created_at', if_not_exists => TRUE);
CREATE INDEX idx_user_events ON points_events(user_address, created_at DESC);
CREATE INDEX idx_epoch_events ON points_events(epoch_number, created_at);
CREATE INDEX idx_point_type ON points_events(point_type, created_at);
CREATE INDEX idx_related_trade ON points_events(related_trade_id) WHERE related_trade_id IS NOT NULL;
-- TimescaleDB 压缩策略 (7天后压缩,节省存储)
SELECT add_compression_policy('points_events', INTERVAL '7 days');
-- 数据保留策略 (90天后删除)
SELECT add_retention_policy('points_events', INTERVAL '90 days');
交易量Tier配置表
CREATE TABLE IF NOT EXISTS trading_tier_config (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tier_name VARCHAR(10) NOT NULL, -- T1, T2, T3, T4
min_volume DECIMAL(30, 6) NOT NULL,
max_volume DECIMAL(30, 6), -- NULL表示无上限
multiplier DECIMAL(5, 2) NOT NULL,
epoch_number INT, -- NULL表示全局默认
is_active BOOLEAN NOT NULL DEFAULT true,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT unique_tier_epoch UNIQUE(tier_name, epoch_number)
);
CREATE INDEX idx_tier_epoch ON trading_tier_config(epoch_number, is_active);
CREATE INDEX idx_tier_volume ON trading_tier_config(min_volume, max_volume);
-- 插入默认Tier配置
INSERT INTO trading_tier_config (tier_name, min_volume, max_volume, multiplier) VALUES
('T1', 0, 99999.99, 1.0),
('T2', 100000, 499999.99, 1.1),
('T3', 500000, 999999.99, 1.3),
('T4', 1000000, NULL, 1.5);
质押记录表
CREATE TABLE IF NOT EXISTS points_staking (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_address VARCHAR(42) NOT NULL,
amount DECIMAL(30, 6) NOT NULL,
token_address VARCHAR(42) NOT NULL, -- 质押的代币地址
start_time TIMESTAMPTZ NOT NULL DEFAULT NOW(),
end_time TIMESTAMPTZ,
status VARCHAR(20) NOT NULL DEFAULT 'active',
-- 状态: active(质押中), withdrawn(已提取)
tx_hash VARCHAR(66), -- 质押交易hash
withdraw_tx_hash VARCHAR(66), -- 提取交易hash
last_calculated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
-- 上次计算积分的时间(用于增量计算)
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_user_staking ON points_staking(user_address, status);
CREATE INDEX idx_staking_status ON points_staking(status, start_time);
CREATE INDEX idx_staking_time ON points_staking(start_time, end_time);