363 lines
16 KiB
MySQL
363 lines
16 KiB
MySQL
|
|
-- Sprint 1: 数据层补全
|
|||
|
|
-- 将数据库从 3 张表升级到 8 张完整表 + audit_log
|
|||
|
|
-- 日期: 2026-05-10
|
|||
|
|
-- 负责人: 宰相
|
|||
|
|
|
|||
|
|
-- ============================================================
|
|||
|
|
-- 一、新表创建
|
|||
|
|
-- ============================================================
|
|||
|
|
|
|||
|
|
-- 1.1 model_provider: 模型厂商表
|
|||
|
|
CREATE TABLE IF NOT EXISTS model_provider (
|
|||
|
|
id BIGSERIAL PRIMARY KEY,
|
|||
|
|
name TEXT NOT NULL UNIQUE, -- "OpenAI", "百度", "DeepSeek"
|
|||
|
|
name_cn TEXT, -- 中文名
|
|||
|
|
country TEXT NOT NULL DEFAULT 'unknown', -- "US" / "CN" / "EU"
|
|||
|
|
website TEXT,
|
|||
|
|
founded_year INTEGER,
|
|||
|
|
description TEXT,
|
|||
|
|
logo_url TEXT,
|
|||
|
|
status TEXT NOT NULL DEFAULT 'active', -- active / deprecated
|
|||
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|||
|
|
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|||
|
|
created_by TEXT DEFAULT 'system',
|
|||
|
|
updated_by TEXT DEFAULT 'system',
|
|||
|
|
deleted_at TIMESTAMP
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
CREATE INDEX IF NOT EXISTS idx_provider_country ON model_provider(country);
|
|||
|
|
CREATE INDEX IF NOT EXISTS idx_provider_status ON model_provider(status);
|
|||
|
|
CREATE INDEX IF NOT EXISTS idx_provider_deleted ON model_provider(deleted_at) WHERE deleted_at IS NOT NULL;
|
|||
|
|
|
|||
|
|
COMMENT ON TABLE model_provider IS '模型厂商/开发商信息';
|
|||
|
|
|
|||
|
|
-- 1.2 operator: 运营商/云平台表
|
|||
|
|
CREATE TABLE IF NOT EXISTS operator (
|
|||
|
|
id BIGSERIAL PRIMARY KEY,
|
|||
|
|
name TEXT NOT NULL UNIQUE, -- "阿里云", "AWS", "OpenRouter"
|
|||
|
|
name_cn TEXT,
|
|||
|
|
country TEXT NOT NULL DEFAULT 'unknown',
|
|||
|
|
website TEXT,
|
|||
|
|
description TEXT,
|
|||
|
|
status TEXT NOT NULL DEFAULT 'active',
|
|||
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|||
|
|
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|||
|
|
created_by TEXT DEFAULT 'system',
|
|||
|
|
updated_by TEXT DEFAULT 'system',
|
|||
|
|
deleted_at TIMESTAMP
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
CREATE INDEX IF NOT EXISTS idx_operator_country ON operator(country);
|
|||
|
|
CREATE INDEX IF NOT EXISTS idx_operator_status ON operator(status);
|
|||
|
|
|
|||
|
|
COMMENT ON TABLE operator IS '模型运营平台/云服务商';
|
|||
|
|
|
|||
|
|
-- 1.3 region_pricing: 区域定价表(替代 model_prices)
|
|||
|
|
CREATE TABLE IF NOT EXISTS region_pricing (
|
|||
|
|
id BIGSERIAL PRIMARY KEY,
|
|||
|
|
model_id BIGINT NOT NULL REFERENCES models(id) ON DELETE CASCADE,
|
|||
|
|
operator_id BIGINT REFERENCES operator(id) ON DELETE SET NULL,
|
|||
|
|
region TEXT NOT NULL DEFAULT 'global', -- global / cn / us / eu
|
|||
|
|
currency TEXT NOT NULL DEFAULT 'USD',
|
|||
|
|
input_price_per_mtok REAL NOT NULL DEFAULT 0,
|
|||
|
|
output_price_per_mtok REAL NOT NULL DEFAULT 0,
|
|||
|
|
request_price REAL, -- 按请求计费
|
|||
|
|
effective_date DATE NOT NULL DEFAULT CURRENT_DATE,
|
|||
|
|
is_free BOOLEAN NOT NULL DEFAULT FALSE,
|
|||
|
|
source_url TEXT,
|
|||
|
|
notes TEXT,
|
|||
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|||
|
|
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|||
|
|
created_by TEXT DEFAULT 'system',
|
|||
|
|
updated_by TEXT DEFAULT 'system',
|
|||
|
|
UNIQUE(model_id, operator_id, region, currency, effective_date),
|
|||
|
|
-- CHECK 约束
|
|||
|
|
CONSTRAINT chk_price_non_negative CHECK (input_price_per_mtok >= 0 AND output_price_per_mtok >= 0),
|
|||
|
|
CONSTRAINT chk_currency_valid CHECK (currency IN ('CNY', 'USD', 'EUR'))
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
CREATE INDEX IF NOT EXISTS idx_region_pricing_model_id ON region_pricing(model_id);
|
|||
|
|
CREATE INDEX IF NOT EXISTS idx_region_pricing_region ON region_pricing(region);
|
|||
|
|
CREATE INDEX IF NOT EXISTS idx_region_pricing_currency ON region_pricing(currency);
|
|||
|
|
CREATE INDEX IF NOT EXISTS idx_region_pricing_is_free ON region_pricing(is_free);
|
|||
|
|
CREATE INDEX IF NOT EXISTS idx_region_pricing_effective ON region_pricing(effective_date);
|
|||
|
|
|
|||
|
|
COMMENT ON TABLE region_pricing IS '模型区域定价信息(含CNY/USD/EUR)';
|
|||
|
|
|
|||
|
|
-- 1.4 pricing_history: 价格变动历史
|
|||
|
|
CREATE TABLE IF NOT EXISTS pricing_history (
|
|||
|
|
id BIGSERIAL PRIMARY KEY,
|
|||
|
|
model_id BIGINT NOT NULL REFERENCES models(id) ON DELETE CASCADE,
|
|||
|
|
region TEXT NOT NULL DEFAULT 'global',
|
|||
|
|
currency TEXT NOT NULL DEFAULT 'USD',
|
|||
|
|
old_input_price REAL,
|
|||
|
|
new_input_price REAL,
|
|||
|
|
old_output_price REAL,
|
|||
|
|
new_output_price REAL,
|
|||
|
|
change_percent REAL, -- 变动百分比
|
|||
|
|
changed_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|||
|
|
reason TEXT, -- 变动原因(如官方公告)
|
|||
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
CREATE INDEX IF NOT EXISTS idx_pricing_history_model ON pricing_history(model_id);
|
|||
|
|
CREATE INDEX IF NOT EXISTS idx_pricing_history_changed ON pricing_history(changed_at);
|
|||
|
|
|
|||
|
|
COMMENT ON TABLE pricing_history IS '模型价格变动历史追踪';
|
|||
|
|
|
|||
|
|
-- 1.5 free_tier: 免费政策库
|
|||
|
|
CREATE TABLE IF NOT EXISTS free_tier (
|
|||
|
|
id BIGSERIAL PRIMARY KEY,
|
|||
|
|
model_id BIGINT NOT NULL REFERENCES models(id) ON DELETE CASCADE,
|
|||
|
|
operator_id BIGINT REFERENCES operator(id) ON DELETE SET NULL,
|
|||
|
|
free_type TEXT NOT NULL DEFAULT 'limited', -- unlimited / limited / trial
|
|||
|
|
max_requests INTEGER, -- 免费请求次数上限
|
|||
|
|
max_tokens BIGINT, -- 免费Token上限
|
|||
|
|
expiration_date DATE, -- 免费政策到期日
|
|||
|
|
description TEXT,
|
|||
|
|
source_url TEXT,
|
|||
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|||
|
|
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|||
|
|
UNIQUE(model_id, operator_id)
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
CREATE INDEX IF NOT EXISTS idx_free_tier_model ON free_tier(model_id);
|
|||
|
|
CREATE INDEX IF NOT EXISTS idx_free_tier_type ON free_tier(free_type);
|
|||
|
|
|
|||
|
|
COMMENT ON TABLE free_tier IS '模型免费政策/额度信息';
|
|||
|
|
|
|||
|
|
-- 1.6 daily_report: 日报存储(替代 report_runs)
|
|||
|
|
CREATE TABLE IF NOT EXISTS daily_report (
|
|||
|
|
id BIGSERIAL PRIMARY KEY,
|
|||
|
|
report_date DATE NOT NULL UNIQUE,
|
|||
|
|
status TEXT NOT NULL DEFAULT 'pending', -- pending / generated / failed
|
|||
|
|
model_count INTEGER, -- 当日模型总数
|
|||
|
|
new_models INTEGER DEFAULT 0, -- 新上线模型数
|
|||
|
|
price_changes INTEGER DEFAULT 0, -- 价格变动数
|
|||
|
|
free_models INTEGER DEFAULT 0, -- 免费模型数
|
|||
|
|
summary_md TEXT, -- Markdown摘要
|
|||
|
|
output_path TEXT,
|
|||
|
|
error_message TEXT,
|
|||
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|||
|
|
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
CREATE INDEX IF NOT EXISTS idx_daily_report_date ON daily_report(report_date);
|
|||
|
|
CREATE INDEX IF NOT EXISTS idx_daily_report_status ON daily_report(status);
|
|||
|
|
|
|||
|
|
COMMENT ON TABLE daily_report IS '每日自动报告运行记录';
|
|||
|
|
|
|||
|
|
-- 1.7 user_subscription: 用户订阅(Phase 2 基础)
|
|||
|
|
CREATE TABLE IF NOT EXISTS user_subscription (
|
|||
|
|
id BIGSERIAL PRIMARY KEY,
|
|||
|
|
user_id TEXT NOT NULL,
|
|||
|
|
subscribe_type TEXT NOT NULL DEFAULT 'daily', -- daily / price_alert / new_model
|
|||
|
|
model_id BIGINT REFERENCES models(id) ON DELETE SET NULL,
|
|||
|
|
provider_id BIGINT REFERENCES model_provider(id) ON DELETE SET NULL,
|
|||
|
|
threshold REAL, -- 价格变动阈值(%)
|
|||
|
|
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
|||
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|||
|
|
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|||
|
|
UNIQUE(user_id, subscribe_type, model_id, provider_id)
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
CREATE INDEX IF NOT EXISTS idx_subscription_user ON user_subscription(user_id);
|
|||
|
|
CREATE INDEX IF NOT EXISTS idx_subscription_active ON user_subscription(is_active);
|
|||
|
|
|
|||
|
|
COMMENT ON TABLE user_subscription IS '用户订阅配置(Phase 2)';
|
|||
|
|
|
|||
|
|
-- 1.8 audit_log: 审计日志【新增】
|
|||
|
|
CREATE TABLE IF NOT EXISTS audit_log (
|
|||
|
|
id BIGSERIAL PRIMARY KEY,
|
|||
|
|
table_name TEXT NOT NULL,
|
|||
|
|
record_id BIGINT NOT NULL,
|
|||
|
|
field_name TEXT,
|
|||
|
|
old_value TEXT,
|
|||
|
|
new_value TEXT,
|
|||
|
|
operation TEXT NOT NULL, -- INSERT / UPDATE / DELETE
|
|||
|
|
operator TEXT DEFAULT 'system', -- 操作人/采集器标识
|
|||
|
|
batch_id TEXT, -- 采集批次号
|
|||
|
|
source_url TEXT,
|
|||
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
CREATE INDEX IF NOT EXISTS idx_audit_table ON audit_log(table_name);
|
|||
|
|
CREATE INDEX IF NOT EXISTS idx_audit_record ON audit_log(record_id);
|
|||
|
|
CREATE INDEX IF NOT EXISTS idx_audit_batch ON audit_log(batch_id);
|
|||
|
|
CREATE INDEX IF NOT EXISTS idx_audit_created ON audit_log(created_at);
|
|||
|
|
|
|||
|
|
COMMENT ON TABLE audit_log IS '数据变更审计日志(血缘追踪)';
|
|||
|
|
|
|||
|
|
-- ============================================================
|
|||
|
|
-- 二、现有表字段扩充
|
|||
|
|
-- ============================================================
|
|||
|
|
|
|||
|
|
-- 2.1 修改 models 表,添加 TECHNICAL_DESIGN 中定义的字段
|
|||
|
|
DO $$
|
|||
|
|
BEGIN
|
|||
|
|
-- 添加 provider_id 外键字段
|
|||
|
|
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='models' AND column_name='provider_id') THEN
|
|||
|
|
ALTER TABLE models ADD COLUMN provider_id BIGINT REFERENCES model_provider(id) ON DELETE SET NULL;
|
|||
|
|
END IF;
|
|||
|
|
|
|||
|
|
-- 添加 version 字段
|
|||
|
|
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='models' AND column_name='version') THEN
|
|||
|
|
ALTER TABLE models ADD COLUMN version TEXT;
|
|||
|
|
END IF;
|
|||
|
|
|
|||
|
|
-- 添加 modality 字段
|
|||
|
|
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='models' AND column_name='modality') THEN
|
|||
|
|
ALTER TABLE models ADD COLUMN modality TEXT NOT NULL DEFAULT 'text';
|
|||
|
|
END IF;
|
|||
|
|
|
|||
|
|
-- 添加 release_date 字段
|
|||
|
|
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='models' AND column_name='release_date') THEN
|
|||
|
|
ALTER TABLE models ADD COLUMN release_date DATE;
|
|||
|
|
END IF;
|
|||
|
|
|
|||
|
|
-- 添加 elo_score 字段
|
|||
|
|
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='models' AND column_name='elo_score') THEN
|
|||
|
|
ALTER TABLE models ADD COLUMN elo_score REAL;
|
|||
|
|
END IF;
|
|||
|
|
|
|||
|
|
-- 添加 benchmark_scores 字段
|
|||
|
|
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='models' AND column_name='benchmark_scores') THEN
|
|||
|
|
ALTER TABLE models ADD COLUMN benchmark_scores JSONB;
|
|||
|
|
END IF;
|
|||
|
|
|
|||
|
|
-- 添加 data_confidence 字段
|
|||
|
|
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='models' AND column_name='data_confidence') THEN
|
|||
|
|
ALTER TABLE models ADD COLUMN data_confidence TEXT DEFAULT 'official';
|
|||
|
|
END IF;
|
|||
|
|
|
|||
|
|
-- 添加 retrieved_at 字段
|
|||
|
|
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='models' AND column_name='retrieved_at') THEN
|
|||
|
|
ALTER TABLE models ADD COLUMN retrieved_at TIMESTAMP;
|
|||
|
|
END IF;
|
|||
|
|
|
|||
|
|
-- 添加 batch_id 字段(血缘追踪)
|
|||
|
|
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='models' AND column_name='batch_id') THEN
|
|||
|
|
ALTER TABLE models ADD COLUMN batch_id TEXT;
|
|||
|
|
END IF;
|
|||
|
|
|
|||
|
|
-- 添加 collector_version 字段
|
|||
|
|
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='models' AND column_name='collector_version') THEN
|
|||
|
|
ALTER TABLE models ADD COLUMN collector_version TEXT DEFAULT 'v1.0';
|
|||
|
|
END IF;
|
|||
|
|
|
|||
|
|
-- 添加 source_url 字段
|
|||
|
|
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='models' AND column_name='source_url') THEN
|
|||
|
|
ALTER TABLE models ADD COLUMN source_url TEXT;
|
|||
|
|
END IF;
|
|||
|
|
|
|||
|
|
-- 添加审计字段
|
|||
|
|
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='models' AND column_name='created_by') THEN
|
|||
|
|
ALTER TABLE models ADD COLUMN created_by TEXT DEFAULT 'system';
|
|||
|
|
END IF;
|
|||
|
|
|
|||
|
|
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='models' AND column_name='updated_by') THEN
|
|||
|
|
ALTER TABLE models ADD COLUMN updated_by TEXT DEFAULT 'system';
|
|||
|
|
END IF;
|
|||
|
|
|
|||
|
|
IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='models' AND column_name='deleted_at') THEN
|
|||
|
|
ALTER TABLE models ADD COLUMN deleted_at TIMESTAMP;
|
|||
|
|
END IF;
|
|||
|
|
END $$;
|
|||
|
|
|
|||
|
|
-- 2.2 为 models 表添加 CHECK 约束
|
|||
|
|
DO $$
|
|||
|
|
BEGIN
|
|||
|
|
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname='chk_models_context_length') THEN
|
|||
|
|
ALTER TABLE models ADD CONSTRAINT chk_models_context_length CHECK (context_length IS NULL OR context_length <= 10000000);
|
|||
|
|
END IF;
|
|||
|
|
|
|||
|
|
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname='chk_models_modality') THEN
|
|||
|
|
ALTER TABLE models ADD CONSTRAINT chk_models_modality CHECK (modality IN ('text', 'vision', 'audio', 'video', 'code', 'multimodal'));
|
|||
|
|
END IF;
|
|||
|
|
|
|||
|
|
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname='chk_models_data_confidence') THEN
|
|||
|
|
ALTER TABLE models ADD CONSTRAINT chk_models_data_confidence CHECK (data_confidence IN ('official', 'inferred', 'unverified', 'stale'));
|
|||
|
|
END IF;
|
|||
|
|
END $$;
|
|||
|
|
|
|||
|
|
-- 2.3 创建 models 表的新索引
|
|||
|
|
CREATE INDEX IF NOT EXISTS idx_models_provider_id ON models(provider_id);
|
|||
|
|
CREATE INDEX IF NOT EXISTS idx_models_modality ON models(modality);
|
|||
|
|
CREATE INDEX IF NOT EXISTS idx_models_data_confidence ON models(data_confidence);
|
|||
|
|
CREATE INDEX IF NOT EXISTS idx_models_retrieved_at ON models(retrieved_at);
|
|||
|
|
CREATE INDEX IF NOT EXISTS idx_models_batch_id ON models(batch_id);
|
|||
|
|
|
|||
|
|
-- ============================================================
|
|||
|
|
-- 三、审计触发器(自动更新 updated_at)
|
|||
|
|
-- ============================================================
|
|||
|
|
|
|||
|
|
-- 3.1 创建触发器函数
|
|||
|
|
CREATE OR REPLACE FUNCTION update_updated_at_column()
|
|||
|
|
RETURNS TRIGGER AS $$
|
|||
|
|
BEGIN
|
|||
|
|
NEW.updated_at = CURRENT_TIMESTAMP;
|
|||
|
|
RETURN NEW;
|
|||
|
|
END;
|
|||
|
|
$$ language 'plpgsql';
|
|||
|
|
|
|||
|
|
-- 3.2 为所有业务表创建触发器
|
|||
|
|
DO $$
|
|||
|
|
DECLARE
|
|||
|
|
tbl TEXT;
|
|||
|
|
tables TEXT[] := ARRAY['models', 'model_provider', 'operator', 'region_pricing',
|
|||
|
|
'pricing_history', 'free_tier', 'daily_report',
|
|||
|
|
'user_subscription'];
|
|||
|
|
BEGIN
|
|||
|
|
FOREACH tbl IN ARRAY tables
|
|||
|
|
LOOP
|
|||
|
|
IF NOT EXISTS (SELECT 1 FROM pg_trigger WHERE tgname = tbl || '_updated_at') THEN
|
|||
|
|
EXECUTE format('CREATE TRIGGER %I_updated_at BEFORE UPDATE ON %I FOR EACH ROW EXECUTE FUNCTION update_updated_at_column()', tbl, tbl);
|
|||
|
|
END IF;
|
|||
|
|
END LOOP;
|
|||
|
|
END $$;
|
|||
|
|
|
|||
|
|
-- ============================================================
|
|||
|
|
-- 四、数据迁移(model_prices → region_pricing)
|
|||
|
|
-- ============================================================
|
|||
|
|
|
|||
|
|
-- 4.1 先创建默认 operator(OpenRouter)
|
|||
|
|
INSERT INTO operator (name, name_cn, country, description)
|
|||
|
|
VALUES ('OpenRouter', 'OpenRouter', 'US', 'OpenRouter API聚合平台')
|
|||
|
|
ON CONFLICT (name) DO NOTHING;
|
|||
|
|
|
|||
|
|
-- 4.2 迁移 model_prices 数据到 region_pricing
|
|||
|
|
INSERT INTO region_pricing (
|
|||
|
|
model_id, operator_id, region, currency,
|
|||
|
|
input_price_per_mtok, output_price_per_mtok,
|
|||
|
|
is_free, effective_date, source_url, created_at
|
|||
|
|
)
|
|||
|
|
SELECT
|
|||
|
|
mp.model_id,
|
|||
|
|
(SELECT id FROM operator WHERE name = 'OpenRouter' LIMIT 1),
|
|||
|
|
'global',
|
|||
|
|
mp.currency,
|
|||
|
|
COALESCE(mp.input_price_per_mtok, 0),
|
|||
|
|
COALESCE(mp.output_price_per_mtok, 0),
|
|||
|
|
COALESCE(m.is_free, FALSE),
|
|||
|
|
COALESCE(mp.effective_date, CURRENT_DATE),
|
|||
|
|
mp.source_url,
|
|||
|
|
mp.created_at
|
|||
|
|
FROM model_prices mp
|
|||
|
|
JOIN models m ON mp.model_id = m.id
|
|||
|
|
ON CONFLICT (model_id, operator_id, region, currency, effective_date) DO NOTHING;
|
|||
|
|
|
|||
|
|
-- ============================================================
|
|||
|
|
-- 五、迁移 report_runs → daily_report
|
|||
|
|
-- ============================================================
|
|||
|
|
|
|||
|
|
INSERT INTO daily_report (report_date, status, output_path, error_message, created_at)
|
|||
|
|
SELECT report_date, status, output_path, error_message, created_at
|
|||
|
|
FROM report_runs
|
|||
|
|
ON CONFLICT (report_date) DO NOTHING;
|
|||
|
|
|
|||
|
|
-- ============================================================
|
|||
|
|
-- 六、完成标记
|
|||
|
|
-- ============================================================
|
|||
|
|
|
|||
|
|
SELECT 'Sprint 1 Schema Migration Complete' AS status;
|