Add design, review, and production-readiness documents for the April remediation cycle.\nInclude supporting SQL and supply-api operational design notes so review conclusions and implementation guidance stay versioned together.
102 lines
3.8 KiB
SQL
102 lines
3.8 KiB
SQL
-- ============================================================================
|
||
-- 审计事件表 Schema 迁移 v1 -> v2
|
||
-- 问题: 数据库中的 audit_events 表是旧版 schema,与代码中的 model 不匹配
|
||
--
|
||
-- 旧版 schema (当前生产表):
|
||
-- domain_code, action_code, severity, client_ip, before_data, after_data
|
||
--
|
||
-- 新版 schema (代码期望):
|
||
-- event_id, event_name, event_category, timestamp, timestamp_ms,
|
||
-- action, source_ip, operator_id, operator_type 等
|
||
-- ============================================================================
|
||
|
||
-- 1. 备份现有数据(如果表中有数据)
|
||
CREATE TABLE IF NOT EXISTS audit_events_backup AS
|
||
SELECT * FROM audit_events WHERE 1=0;
|
||
|
||
-- 如果有数据则备份
|
||
INSERT INTO audit_events_backup SELECT * FROM audit_events;
|
||
|
||
-- 2. 删除旧表和约束
|
||
DROP TABLE IF EXISTS audit_events CASCADE;
|
||
|
||
-- 3. 创建新表(使用 partition_strategy_v1.sql 中的定义)
|
||
CREATE TABLE IF NOT EXISTS audit_events (
|
||
id BIGSERIAL,
|
||
event_id VARCHAR(100) NOT NULL,
|
||
event_name VARCHAR(100) NOT NULL,
|
||
event_category VARCHAR(50),
|
||
event_sub_category VARCHAR(50),
|
||
timestamp TIMESTAMPTZ NOT NULL,
|
||
timestamp_ms BIGINT NOT NULL,
|
||
request_id VARCHAR(100),
|
||
idempotency_key VARCHAR(128),
|
||
tenant_id BIGINT,
|
||
object_type VARCHAR(100),
|
||
object_id VARCHAR(100),
|
||
action VARCHAR(100) NOT NULL,
|
||
result_code VARCHAR(50),
|
||
source_ip VARCHAR(50),
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
PRIMARY KEY (id, timestamp)
|
||
) PARTITION BY RANGE (timestamp);
|
||
|
||
-- 4. 创建索引
|
||
CREATE INDEX idx_audit_events_tenant_id ON audit_events(tenant_id);
|
||
CREATE INDEX idx_audit_events_request_id ON audit_events(request_id);
|
||
CREATE INDEX idx_audit_events_created_at ON audit_events(created_at);
|
||
CREATE INDEX idx_audit_events_object ON audit_events(object_type, object_id);
|
||
|
||
-- 5. 创建初始分区(过去12个月 + 未来3个月)
|
||
DO $$
|
||
DECLARE
|
||
i INT;
|
||
target_date DATE;
|
||
partition_name TEXT;
|
||
start_date DATE;
|
||
end_date DATE;
|
||
BEGIN
|
||
-- 过去12个月
|
||
FOR i IN -12..0 LOOP
|
||
target_date := (CURRENT_DATE + (i || ' months')::INTERVAL)::DATE;
|
||
start_date := date_trunc('month', target_date)::DATE;
|
||
end_date := (start_date + INTERVAL '1 month')::DATE;
|
||
partition_name := 'audit_events_' || to_char(start_date, 'YYYY_MM');
|
||
|
||
IF NOT EXISTS (
|
||
SELECT 1 FROM pg_class WHERE relname = partition_name
|
||
) THEN
|
||
EXECUTE format(
|
||
'CREATE TABLE %I PARTITION OF audit_events FOR VALUES FROM (%L) TO (%L)',
|
||
partition_name, start_date, end_date
|
||
);
|
||
RAISE NOTICE 'Created partition: %', partition_name;
|
||
END IF;
|
||
END LOOP;
|
||
|
||
-- 未来3个月
|
||
FOR i IN 1..3 LOOP
|
||
target_date := (CURRENT_DATE + (i || ' months')::INTERVAL)::DATE;
|
||
start_date := date_trunc('month', target_date)::DATE;
|
||
end_date := (start_date + INTERVAL '1 month')::DATE;
|
||
partition_name := 'audit_events_' || to_char(start_date, 'YYYY_MM');
|
||
|
||
IF NOT EXISTS (
|
||
SELECT 1 FROM pg_class WHERE relname = partition_name
|
||
) THEN
|
||
EXECUTE format(
|
||
'CREATE TABLE %I PARTITION OF audit_events FOR VALUES FROM (%L) TO (%L)',
|
||
partition_name, start_date, end_date
|
||
);
|
||
RAISE NOTICE 'Created partition: %', partition_name;
|
||
END IF;
|
||
END LOOP;
|
||
END $$;
|
||
|
||
-- 6. 验证
|
||
SELECT table_name, count(*) as partition_count
|
||
FROM pg_tables
|
||
WHERE table_name LIKE 'audit_events%' AND table_name != 'audit_events' AND table_name != 'audit_events_backup'
|
||
GROUP BY table_name;
|
||
|
||
COMMENT ON TABLE audit_events IS '审计事件表 - 按月分区,保留12个月'; |