You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

728 lines
25 KiB

-- AmazingData 金融数据服务平台 - 数据库初始化脚本
-- PostgreSQL 15+
-- 执行命令: psql -U postgres -d amazing_data -f init.sql
-- ============================================
-- 1. 创建扩展
-- ============================================
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE EXTENSION IF NOT EXISTS "pg_trgm"; -- 用于模糊搜索
-- ============================================
-- 2. 用户表
-- ============================================
CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
username VARCHAR(50) UNIQUE NOT NULL,
password_hash VARCHAR(255) NOT NULL,
is_active BOOLEAN DEFAULT TRUE,
is_superuser BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
COMMENT ON TABLE users IS '系统用户表';
COMMENT ON COLUMN users.password_hash IS 'bcrypt加密的密码';
-- 默认管理员用户由应用启动时自动创建 (admin/admin123)
-- ============================================
-- 3. SDK配置表
-- ============================================
CREATE TABLE IF NOT EXISTS sdk_configs (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
username VARCHAR(100) NOT NULL,
password VARCHAR(255) NOT NULL,
host VARCHAR(100) NOT NULL,
port INTEGER NOT NULL DEFAULT 8080,
local_path VARCHAR(255) DEFAULT './amazing_data_cache/',
is_active BOOLEAN DEFAULT TRUE,
is_default BOOLEAN DEFAULT FALSE,
description TEXT,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
COMMENT ON TABLE sdk_configs IS 'AmazingData SDK配置表';
COMMENT ON COLUMN sdk_configs.password IS 'SDK登录密码建议加密存储';
-- 确保只有一个默认配置
CREATE OR REPLACE FUNCTION ensure_single_default_sdk()
RETURNS TRIGGER AS $$
BEGIN
IF NEW.is_default THEN
UPDATE sdk_configs SET is_default = FALSE WHERE id != NEW.id;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
DROP TRIGGER IF EXISTS trg_ensure_single_default_sdk ON sdk_configs;
CREATE TRIGGER trg_ensure_single_default_sdk
BEFORE INSERT OR UPDATE ON sdk_configs
FOR EACH ROW
EXECUTE FUNCTION ensure_single_default_sdk();
-- ============================================
-- 4. 系统配置表
-- ============================================
CREATE TABLE IF NOT EXISTS system_configs (
id SERIAL PRIMARY KEY,
config_name VARCHAR(100),
config_key VARCHAR(100) UNIQUE NOT NULL,
config_value TEXT NOT NULL,
current_db_type VARCHAR(50),
is_active BOOLEAN DEFAULT TRUE,
description TEXT,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
COMMENT ON TABLE system_configs IS '系统配置键值表';
INSERT INTO system_configs (config_key, config_value, description) VALUES
('cache.default_period', 'daily', '默认K线周期'),
('cache.default_days', '365', '默认查询天数'),
('cache.auto_cleanup_days', '7', '实时数据自动清理天数'),
('cache.batch_size', '100', '批量缓存每次处理代码数'),
('cache.missing_threshold', '0.1', '数据缺失判断阈值(10%)'),
('realtime.subscribe_interval', '1000', '实时订阅推送间隔(ms)'),
('jwt.expire_hours', '24', 'JWT Token过期时间(小时)')
ON CONFLICT (config_key) DO NOTHING;
-- ============================================
-- 5. 股票代码信息表
-- ============================================
CREATE TABLE IF NOT EXISTS stock_info (
id SERIAL PRIMARY KEY,
code VARCHAR(20) UNIQUE NOT NULL,
symbol VARCHAR(100) NOT NULL,
security_status INTEGER,
pre_close DECIMAL(12, 4),
high_limited DECIMAL(12, 4),
low_limited DECIMAL(12, 4),
price_tick DECIMAL(10, 4),
exchange VARCHAR(10), -- SH, SZ, BJ
industry VARCHAR(50),
list_date DATE,
is_active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
COMMENT ON TABLE stock_info IS '股票基础信息表';
CREATE INDEX IF NOT EXISTS idx_stock_info_code ON stock_info(code);
CREATE INDEX IF NOT EXISTS idx_stock_info_exchange ON stock_info(exchange);
CREATE INDEX IF NOT EXISTS idx_stock_info_symbol ON stock_info USING gin(symbol gin_trgm_ops);
-- ============================================
-- 6. 期货代码信息表
-- ============================================
CREATE TABLE IF NOT EXISTS future_info (
id SERIAL PRIMARY KEY,
code VARCHAR(20) UNIQUE NOT NULL,
symbol VARCHAR(100) NOT NULL,
underlying VARCHAR(20), -- 标的代码
contract_month VARCHAR(10),
pre_close DECIMAL(12, 4),
high_limited DECIMAL(12, 4),
low_limited DECIMAL(12, 4),
price_tick DECIMAL(10, 4),
exchange VARCHAR(10) DEFAULT 'CFE', -- CFE: 中金所
list_date DATE,
expire_date DATE,
is_active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
COMMENT ON TABLE future_info IS '期货基础信息表';
CREATE INDEX IF NOT EXISTS idx_future_info_code ON future_info(code);
CREATE INDEX IF NOT EXISTS idx_future_info_underlying ON future_info(underlying);
-- ============================================
-- 7. 股票日线数据表
-- ============================================
CREATE TABLE IF NOT EXISTS stock_kline_daily (
id BIGSERIAL PRIMARY KEY,
code VARCHAR(20) NOT NULL,
trade_date DATE NOT NULL,
open DECIMAL(12, 4) NOT NULL,
high DECIMAL(12, 4) NOT NULL,
low DECIMAL(12, 4) NOT NULL,
close DECIMAL(12, 4) NOT NULL,
volume BIGINT NOT NULL,
amount DECIMAL(18, 4) NOT NULL,
adj_factor DECIMAL(12, 6), -- 复权因子
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
UNIQUE(code, trade_date)
);
COMMENT ON TABLE stock_kline_daily IS '股票日线K线数据';
CREATE INDEX IF NOT EXISTS idx_stock_daily_code_date ON stock_kline_daily(code, trade_date);
CREATE INDEX IF NOT EXISTS idx_stock_daily_trade_date ON stock_kline_daily(trade_date);
CREATE INDEX IF NOT EXISTS idx_stock_daily_code ON stock_kline_daily(code);
-- 分区表(按年分区,可选)
-- 如果需要分区,可以创建子表
-- CREATE TABLE stock_kline_daily_2024 PARTITION OF stock_kline_daily
-- FOR VALUES FROM ('2024-01-01') TO ('2025-01-01');
-- ============================================
-- 8. 股票分钟数据表
-- ============================================
CREATE TABLE IF NOT EXISTS stock_kline_min (
id BIGSERIAL PRIMARY KEY,
code VARCHAR(20) NOT NULL,
period_type VARCHAR(10) NOT NULL CHECK (period_type IN ('min1', 'min5', 'min15', 'min30', 'min60')),
trade_datetime TIMESTAMP NOT NULL,
open DECIMAL(12, 4) NOT NULL,
high DECIMAL(12, 4) NOT NULL,
low DECIMAL(12, 4) NOT NULL,
close DECIMAL(12, 4) NOT NULL,
volume BIGINT NOT NULL,
amount DECIMAL(18, 4) NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
UNIQUE(code, period_type, trade_datetime)
);
COMMENT ON TABLE stock_kline_min IS '股票分钟K线数据';
CREATE INDEX IF NOT EXISTS idx_stock_min_code_period_datetime ON stock_kline_min(code, period_type, trade_datetime);
CREATE INDEX IF NOT EXISTS idx_stock_min_trade_datetime ON stock_kline_min(trade_datetime);
-- ============================================
-- 9. 期货日线数据表
-- ============================================
CREATE TABLE IF NOT EXISTS future_kline_daily (
id BIGSERIAL PRIMARY KEY,
code VARCHAR(20) NOT NULL,
trade_date DATE NOT NULL,
open DECIMAL(12, 4) NOT NULL,
high DECIMAL(12, 4) NOT NULL,
low DECIMAL(12, 4) NOT NULL,
close DECIMAL(12, 4) NOT NULL,
volume BIGINT NOT NULL,
amount DECIMAL(18, 4) NOT NULL,
settle DECIMAL(12, 4),
open_interest BIGINT,
pre_settle DECIMAL(12, 4),
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
UNIQUE(code, trade_date)
);
COMMENT ON TABLE future_kline_daily IS '期货日线K线数据';
CREATE INDEX IF NOT EXISTS idx_future_daily_code_date ON future_kline_daily(code, trade_date);
CREATE INDEX IF NOT EXISTS idx_future_daily_trade_date ON future_kline_daily(trade_date);
-- ============================================
-- 10. 期货分钟数据表
-- ============================================
CREATE TABLE IF NOT EXISTS future_kline_min (
id BIGSERIAL PRIMARY KEY,
code VARCHAR(20) NOT NULL,
period_type VARCHAR(10) NOT NULL CHECK (period_type IN ('min1', 'min5', 'min15', 'min30', 'min60')),
trade_datetime TIMESTAMP NOT NULL,
open DECIMAL(12, 4) NOT NULL,
high DECIMAL(12, 4) NOT NULL,
low DECIMAL(12, 4) NOT NULL,
close DECIMAL(12, 4) NOT NULL,
volume BIGINT NOT NULL,
amount DECIMAL(18, 4) NOT NULL,
settle DECIMAL(12, 4),
open_interest BIGINT,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
UNIQUE(code, period_type, trade_datetime)
);
COMMENT ON TABLE future_kline_min IS '期货分钟K线数据';
CREATE INDEX IF NOT EXISTS idx_future_min_code_period_datetime ON future_kline_min(code, period_type, trade_datetime);
-- ============================================
-- 11. 指数日线数据表
-- ============================================
CREATE TABLE IF NOT EXISTS index_kline_daily (
id BIGSERIAL PRIMARY KEY,
code VARCHAR(20) NOT NULL,
trade_date DATE NOT NULL,
open DECIMAL(12, 4),
high DECIMAL(12, 4),
low DECIMAL(12, 4),
close DECIMAL(12, 4),
volume BIGINT,
amount DECIMAL(18, 4),
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
UNIQUE(code, trade_date)
);
COMMENT ON TABLE index_kline_daily IS '指数日线数据';
CREATE INDEX IF NOT EXISTS idx_index_daily_code_date ON index_kline_daily(code, trade_date);
-- ============================================
-- 12. 实时快照数据表 (TTL 7天)
-- ============================================
CREATE TABLE IF NOT EXISTS realtime_snapshot (
id BIGSERIAL PRIMARY KEY,
code VARCHAR(20) NOT NULL,
security_type VARCHAR(20) NOT NULL, -- stock, future, index, etf, kzz, option
trade_time TIMESTAMP NOT NULL,
pre_close DECIMAL(12, 4),
last DECIMAL(12, 4),
open DECIMAL(12, 4),
high DECIMAL(12, 4),
low DECIMAL(12, 4),
close DECIMAL(12, 4),
volume BIGINT,
amount DECIMAL(18, 4),
-- 盘口数据
ask_price1 DECIMAL(12, 4),
ask_price2 DECIMAL(12, 4),
ask_price3 DECIMAL(12, 4),
ask_price4 DECIMAL(12, 4),
ask_price5 DECIMAL(12, 4),
ask_volume1 INTEGER,
ask_volume2 INTEGER,
ask_volume3 INTEGER,
ask_volume4 INTEGER,
ask_volume5 INTEGER,
bid_price1 DECIMAL(12, 4),
bid_price2 DECIMAL(12, 4),
bid_price3 DECIMAL(12, 4),
bid_price4 DECIMAL(12, 4),
bid_price5 DECIMAL(12, 4),
bid_volume1 INTEGER,
bid_volume2 INTEGER,
bid_volume3 INTEGER,
bid_volume4 INTEGER,
bid_volume5 INTEGER,
-- 期货特有字段
settle DECIMAL(12, 4),
open_interest BIGINT,
pre_settle DECIMAL(12, 4),
average_price DECIMAL(12, 4),
-- 状态
trading_phase_code VARCHAR(10),
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
expires_at TIMESTAMP WITH TIME ZONE NOT NULL -- 过期时间
);
COMMENT ON TABLE realtime_snapshot IS '实时快照数据自动清理7天前数据';
CREATE INDEX IF NOT EXISTS idx_snapshot_code_time ON realtime_snapshot(code, trade_time);
CREATE INDEX IF NOT EXISTS idx_snapshot_expires ON realtime_snapshot(expires_at);
-- 创建自动清理过期数据的触发器
CREATE OR REPLACE FUNCTION auto_cleanup_expired_snapshots()
RETURNS void AS $$
BEGIN
DELETE FROM realtime_snapshot
WHERE expires_at < NOW();
END;
$$ LANGUAGE plpgsql;
-- ============================================
-- 13. 历史快照数据表
-- ============================================
CREATE TABLE IF NOT EXISTS history_snapshot (
id BIGSERIAL PRIMARY KEY,
code VARCHAR(20) NOT NULL,
security_type VARCHAR(20) NOT NULL,
trade_date DATE NOT NULL,
trade_time TIME NOT NULL,
pre_close DECIMAL(12, 4),
last DECIMAL(12, 4),
open DECIMAL(12, 4),
high DECIMAL(12, 4),
low DECIMAL(12, 4),
close DECIMAL(12, 4),
volume BIGINT,
amount DECIMAL(18, 4),
ask_price1 DECIMAL(12, 4),
ask_volume1 INTEGER,
bid_price1 DECIMAL(12, 4),
bid_volume1 INTEGER,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
UNIQUE(code, trade_date, trade_time)
);
COMMENT ON TABLE history_snapshot IS '历史快照数据';
CREATE INDEX IF NOT EXISTS idx_hist_snapshot_code_date ON history_snapshot(code, trade_date);
-- ============================================
-- 14. 财务数据表 - 资产负债表
-- ============================================
CREATE TABLE IF NOT EXISTS finance_balance_sheet (
id BIGSERIAL PRIMARY KEY,
code VARCHAR(20) NOT NULL,
report_date DATE NOT NULL,
report_type INTEGER, -- 1:年报, 2:中报, 3:季报
statement_type INTEGER, -- 报表类型
-- 资产
total_assets DECIMAL(18, 4),
total_cur_assets DECIMAL(18, 4),
total_noncur_assets DECIMAL(18, 4),
currency_cap DECIMAL(18, 4),
notes_receivable DECIMAL(18, 4),
acct_receivable DECIMAL(18, 4),
inventory DECIMAL(18, 4),
fix_assets DECIMAL(18, 4),
-- 负债
total_liab DECIMAL(18, 4),
total_cur_liab DECIMAL(18, 4),
total_noncur_liab DECIMAL(18, 4),
notes_payable DECIMAL(18, 4),
acct_payable DECIMAL(18, 4),
st_borrowing DECIMAL(18, 4),
lt_loan DECIMAL(18, 4),
-- 权益
tot_share_equity DECIMAL(18, 4),
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
UNIQUE(code, report_date, statement_type)
);
COMMENT ON TABLE finance_balance_sheet IS '资产负债表';
CREATE INDEX IF NOT EXISTS idx_balance_code_date ON finance_balance_sheet(code, report_date);
-- ============================================
-- 15. 财务数据表 - 现金流量表
-- ============================================
CREATE TABLE IF NOT EXISTS finance_cash_flow (
id BIGSERIAL PRIMARY KEY,
code VARCHAR(20) NOT NULL,
report_date DATE NOT NULL,
report_type INTEGER,
statement_type INTEGER,
net_cash_flows_opera_act DECIMAL(18, 4),
net_cash_flows_inv_act DECIMAL(18, 4),
net_cash_flows_fin_act DECIMAL(18, 4),
net_incr_cash_and_cash_equ DECIMAL(18, 4),
cash_recp_sg_and_rs DECIMAL(18, 4),
cash_pay_goods_services DECIMAL(18, 4),
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
UNIQUE(code, report_date, statement_type)
);
COMMENT ON TABLE finance_cash_flow IS '现金流量表';
CREATE INDEX IF NOT EXISTS idx_cashflow_code_date ON finance_cash_flow(code, report_date);
-- ============================================
-- 16. 财务数据表 - 利润表
-- ============================================
CREATE TABLE IF NOT EXISTS finance_income (
id BIGSERIAL PRIMARY KEY,
code VARCHAR(20) NOT NULL,
report_date DATE NOT NULL,
report_type INTEGER,
statement_type INTEGER,
tot_opera_rev DECIMAL(18, 4),
opera_rev DECIMAL(18, 4),
tot_opera_cost DECIMAL(18, 4),
opera_profit DECIMAL(18, 4),
total_profit DECIMAL(18, 4),
net_pro_incl_min_int_inc DECIMAL(18, 4),
basic_eps DECIMAL(12, 6),
diluted_eps DECIMAL(12, 6),
rd_exp DECIMAL(18, 4),
selling_exp DECIMAL(18, 4),
admin_exp DECIMAL(18, 4),
fin_exp DECIMAL(18, 4),
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
UNIQUE(code, report_date, statement_type)
);
COMMENT ON TABLE finance_income IS '利润表';
CREATE INDEX IF NOT EXISTS idx_income_code_date ON finance_income(code, report_date);
-- ============================================
-- 17. 交易日历表
-- ============================================
CREATE TABLE IF NOT EXISTS trading_calendar (
id SERIAL PRIMARY KEY,
market VARCHAR(10) NOT NULL, -- SH, SZ, BJ, CFE
trade_date DATE NOT NULL,
is_trading_day BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
UNIQUE(market, trade_date)
);
COMMENT ON TABLE trading_calendar IS '交易日历';
CREATE INDEX IF NOT EXISTS idx_calendar_market_date ON trading_calendar(market, trade_date);
CREATE INDEX IF NOT EXISTS idx_calendar_date ON trading_calendar(trade_date);
-- ============================================
-- 18. 缓存任务表
-- ============================================
CREATE TABLE IF NOT EXISTS cache_tasks (
id SERIAL PRIMARY KEY,
task_name VARCHAR(200) NOT NULL,
task_type VARCHAR(50) NOT NULL CHECK (task_type IN ('detect_missing', 'detect_all_missing', 'cache_data', 'cache_all_data', 'sync_data', 'fill_missing_data', 'batch_fill_date')),
security_type VARCHAR(20) NOT NULL, -- stock, future, index
period_type VARCHAR(10), -- daily, min1, min5, etc.
start_date DATE NOT NULL,
end_date DATE NOT NULL,
code_list TEXT, -- 逗号分隔的代码列表NULL表示全部
status VARCHAR(20) DEFAULT 'pending' CHECK (status IN ('pending', 'running', 'completed', 'failed', 'cancelled')),
progress DECIMAL(5, 2) DEFAULT 0, -- 0-100
total_count INTEGER DEFAULT 0,
success_count INTEGER DEFAULT 0,
error_count INTEGER DEFAULT 0,
error_message TEXT,
created_by INTEGER REFERENCES users(id),
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
started_at TIMESTAMP WITH TIME ZONE,
completed_at TIMESTAMP WITH TIME ZONE
);
COMMENT ON TABLE cache_tasks IS '数据缓存任务';
CREATE INDEX IF NOT EXISTS idx_cache_tasks_status ON cache_tasks(status);
CREATE INDEX IF NOT EXISTS idx_cache_tasks_created ON cache_tasks(created_at);
-- ============================================
-- 19. 缓存任务详情表
-- ============================================
CREATE TABLE IF NOT EXISTS cache_task_details (
id BIGSERIAL PRIMARY KEY,
task_id INTEGER NOT NULL REFERENCES cache_tasks(id) ON DELETE CASCADE,
code VARCHAR(20) NOT NULL,
trade_date DATE NOT NULL,
expected_count INTEGER DEFAULT 0,
actual_count INTEGER DEFAULT 0,
is_missing BOOLEAN DEFAULT FALSE,
status VARCHAR(20) DEFAULT 'pending', -- pending, success, failed, skipped
error_message TEXT,
processed_at TIMESTAMP WITH TIME ZONE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
COMMENT ON TABLE cache_task_details IS '缓存任务详细记录';
CREATE INDEX IF NOT EXISTS idx_cache_details_task ON cache_task_details(task_id);
CREATE INDEX IF NOT EXISTS idx_cache_details_code ON cache_task_details(code);
-- ============================================
-- 20. API测试日志表
-- ============================================
CREATE TABLE IF NOT EXISTS api_test_logs (
id BIGSERIAL PRIMARY KEY,
test_name VARCHAR(200) NOT NULL,
api_category VARCHAR(50) NOT NULL, -- base_data, stock, future, realtime, finance, etc.
api_endpoint VARCHAR(200) NOT NULL,
request_method VARCHAR(10) NOT NULL,
request_params JSONB,
response_data JSONB,
status_code INTEGER,
execution_time_ms INTEGER,
is_success BOOLEAN DEFAULT FALSE,
error_message TEXT,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
COMMENT ON TABLE api_test_logs IS 'API接口测试日志';
CREATE INDEX IF NOT EXISTS idx_test_logs_category ON api_test_logs(api_category);
CREATE INDEX IF NOT EXISTS idx_test_logs_endpoint ON api_test_logs(api_endpoint);
CREATE INDEX IF NOT EXISTS idx_test_logs_created ON api_test_logs(created_at);
-- ============================================
-- 21. 数据同步日志表
-- ============================================
CREATE TABLE IF NOT EXISTS data_sync_logs (
id BIGSERIAL PRIMARY KEY,
code VARCHAR(20) NOT NULL,
security_type VARCHAR(20) NOT NULL,
period_type VARCHAR(10),
start_date DATE,
end_date DATE,
record_count INTEGER DEFAULT 0,
source VARCHAR(50) DEFAULT 'sdk', -- sdk, manual, import
status VARCHAR(20) DEFAULT 'success',
error_message TEXT,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
COMMENT ON TABLE data_sync_logs IS '数据同步日志';
CREATE INDEX IF NOT EXISTS idx_sync_logs_code ON data_sync_logs(code);
CREATE INDEX IF NOT EXISTS idx_sync_logs_created ON data_sync_logs(created_at);
-- ============================================
-- 22. 创建视图 - 数据缓存状态概览
-- ============================================
CREATE OR REPLACE VIEW v_cache_status AS
SELECT
code,
'stock' as security_type,
'daily' as period_type,
COUNT(*) as record_count,
MIN(trade_date) as min_date,
MAX(trade_date) as max_date
FROM stock_kline_daily
GROUP BY code
UNION ALL
SELECT
code,
'future' as security_type,
'daily' as period_type,
COUNT(*) as record_count,
MIN(trade_date) as min_date,
MAX(trade_date) as max_date
FROM future_kline_daily
GROUP BY code;
COMMENT ON VIEW v_cache_status IS '数据缓存状态概览视图';
-- ============================================
-- 23. 创建函数 - 获取交易日列表
-- ============================================
CREATE OR REPLACE FUNCTION get_trading_days(
p_market VARCHAR(10),
p_start_date DATE,
p_end_date DATE
)
RETURNS TABLE (trade_date DATE) AS $$
BEGIN
RETURN QUERY
SELECT tc.trade_date
FROM trading_calendar tc
WHERE tc.market = p_market
AND tc.trade_date BETWEEN p_start_date AND p_end_date
AND tc.is_trading_day = TRUE
ORDER BY tc.trade_date;
END;
$$ LANGUAGE plpgsql;
COMMENT ON FUNCTION get_trading_days IS '获取指定区间的交易日列表';
-- ============================================
-- 24. 创建函数 - 计算数据缺失率
-- ============================================
CREATE OR REPLACE FUNCTION calculate_missing_ratio(
p_code VARCHAR(20),
p_security_type VARCHAR(20),
p_period_type VARCHAR(10),
p_start_date DATE,
p_end_date DATE
)
RETURNS TABLE (
expected_count INTEGER,
actual_count INTEGER,
missing_count INTEGER,
missing_ratio DECIMAL(5, 4)
) AS $$
DECLARE
v_expected INTEGER;
v_actual INTEGER;
v_market VARCHAR(10);
BEGIN
-- 确定市场
v_market := CASE
WHEN p_security_type = 'future' THEN 'CFE'
WHEN RIGHT(p_code, 3) = '.SH' THEN 'SH'
WHEN RIGHT(p_code, 3) = '.SZ' THEN 'SZ'
WHEN RIGHT(p_code, 3) = '.BJ' THEN 'BJ'
ELSE 'SH'
END;
-- 计算期望数据条数
SELECT COUNT(*) INTO v_expected
FROM get_trading_days(v_market, p_start_date, p_end_date);
-- 计算实际数据条数
IF p_security_type = 'stock' AND p_period_type = 'daily' THEN
SELECT COUNT(*) INTO v_actual
FROM stock_kline_daily
WHERE code = p_code AND trade_date BETWEEN p_start_date AND p_end_date;
ELSIF p_security_type = 'future' AND p_period_type = 'daily' THEN
SELECT COUNT(*) INTO v_actual
FROM future_kline_daily
WHERE code = p_code AND trade_date BETWEEN p_start_date AND p_end_date;
ELSE
v_actual := 0;
END IF;
RETURN QUERY
SELECT
v_expected,
v_actual,
GREATEST(0, v_expected - v_actual),
CASE
WHEN v_expected > 0 THEN (v_expected - v_actual)::DECIMAL / v_expected
ELSE 0
END;
END;
$$ LANGUAGE plpgsql;
COMMENT ON FUNCTION calculate_missing_ratio IS '计算指定代码在指定区间的数据缺失率';
-- ============================================
-- 25. 创建触发器 - 自动更新updated_at
-- ============================================
CREATE OR REPLACE FUNCTION update_updated_at_column()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = CURRENT_TIMESTAMP;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
-- 为用户表创建触发器
DROP TRIGGER IF EXISTS trg_users_updated_at ON users;
CREATE TRIGGER trg_users_updated_at
BEFORE UPDATE ON users
FOR EACH ROW
EXECUTE FUNCTION update_updated_at_column();
-- 为SDK配置表创建触发器
DROP TRIGGER IF EXISTS trg_sdk_configs_updated_at ON sdk_configs;
CREATE TRIGGER trg_sdk_configs_updated_at
BEFORE UPDATE ON sdk_configs
FOR EACH ROW
EXECUTE FUNCTION update_updated_at_column();
-- 为系统配置表创建触发器
DROP TRIGGER IF EXISTS trg_system_configs_updated_at ON system_configs;
CREATE TRIGGER trg_system_configs_updated_at
BEFORE UPDATE ON system_configs
FOR EACH ROW
EXECUTE FUNCTION update_updated_at_column();
-- ============================================
-- 26. 创建分区表(可选,用于大数据量场景)
-- ============================================
-- 如果需要按月分区,可以使用以下命令创建分区表结构
-- 注意这需要PostgreSQL 10+
/*
-- 股票分钟数据按月分区示例
CREATE TABLE stock_kline_min_partitioned (
LIKE stock_kline_min INCLUDING ALL
) PARTITION BY RANGE (trade_datetime);
-- 创建各月分区
CREATE TABLE stock_kline_min_202401 PARTITION OF stock_kline_min_partitioned
FOR VALUES FROM ('2024-01-01') TO ('2024-02-01');
CREATE TABLE stock_kline_min_202402 PARTITION OF stock_kline_min_partitioned
FOR VALUES FROM ('2024-02-01') TO ('2024-03-01');
-- ... 更多分区
*/
-- ============================================
-- 初始化完成
-- ============================================
SELECT 'Database initialization completed successfully!' as status;