fix(supply-api): realign audit event persistence contract

This commit is contained in:
Your Name
2026-04-20 11:50:20 +08:00
parent 1c088e2dd4
commit 319d9e1989
6 changed files with 438 additions and 19 deletions

View File

@@ -0,0 +1,99 @@
-- Forward-only migration: align supply-api audit_events with the repository contract.
-- This migration is intended for databases that were initialized from the older
-- compact audit_events schema in supply-api/sql/postgresql/partition_strategy_v1.sql.
ALTER TABLE IF EXISTS audit_events
ADD COLUMN IF NOT EXISTS trace_id VARCHAR(64) NOT NULL DEFAULT '',
ADD COLUMN IF NOT EXISTS span_id VARCHAR(64) NOT NULL DEFAULT '',
ADD COLUMN IF NOT EXISTS operator_id BIGINT NOT NULL DEFAULT 0,
ADD COLUMN IF NOT EXISTS operator_type VARCHAR(32) NOT NULL DEFAULT '',
ADD COLUMN IF NOT EXISTS operator_role VARCHAR(64) NOT NULL DEFAULT '',
ADD COLUMN IF NOT EXISTS tenant_type VARCHAR(32) NOT NULL DEFAULT '',
ADD COLUMN IF NOT EXISTS action_detail TEXT NOT NULL DEFAULT '',
ADD COLUMN IF NOT EXISTS credential_type VARCHAR(64) NOT NULL DEFAULT '',
ADD COLUMN IF NOT EXISTS credential_id VARCHAR(255) NOT NULL DEFAULT '',
ADD COLUMN IF NOT EXISTS credential_fingerprint VARCHAR(255) NOT NULL DEFAULT '',
ADD COLUMN IF NOT EXISTS source_type VARCHAR(32) NOT NULL DEFAULT '',
ADD COLUMN IF NOT EXISTS source_region VARCHAR(100) NOT NULL DEFAULT '',
ADD COLUMN IF NOT EXISTS user_agent TEXT NOT NULL DEFAULT '',
ADD COLUMN IF NOT EXISTS target_type VARCHAR(32) NOT NULL DEFAULT '',
ADD COLUMN IF NOT EXISTS target_endpoint TEXT NOT NULL DEFAULT '',
ADD COLUMN IF NOT EXISTS target_direct BOOLEAN NOT NULL DEFAULT FALSE,
ADD COLUMN IF NOT EXISTS result_message TEXT NOT NULL DEFAULT '',
ADD COLUMN IF NOT EXISTS success BOOLEAN NOT NULL DEFAULT FALSE,
ADD COLUMN IF NOT EXISTS before_state JSONB,
ADD COLUMN IF NOT EXISTS after_state JSONB,
ADD COLUMN IF NOT EXISTS security_flags JSONB NOT NULL DEFAULT '{}'::jsonb,
ADD COLUMN IF NOT EXISTS risk_score INTEGER NOT NULL DEFAULT 0,
ADD COLUMN IF NOT EXISTS compliance_tags TEXT[] NOT NULL DEFAULT ARRAY[]::TEXT[],
ADD COLUMN IF NOT EXISTS invariant_rule VARCHAR(255) NOT NULL DEFAULT '',
ADD COLUMN IF NOT EXISTS extensions JSONB,
ADD COLUMN IF NOT EXISTS version INTEGER NOT NULL DEFAULT 1;
UPDATE audit_events
SET
event_category = COALESCE(event_category, ''),
event_sub_category = COALESCE(event_sub_category, ''),
timestamp_ms = COALESCE(timestamp_ms, EXTRACT(EPOCH FROM timestamp) * 1000),
request_id = COALESCE(request_id, ''),
idempotency_key = COALESCE(idempotency_key, ''),
tenant_id = COALESCE(tenant_id, 0),
object_type = COALESCE(object_type, ''),
action = COALESCE(action, ''),
result_code = COALESCE(result_code, ''),
source_ip = COALESCE(source_ip, '');
ALTER TABLE IF EXISTS audit_events
ALTER COLUMN event_category SET DEFAULT '',
ALTER COLUMN event_category SET NOT NULL,
ALTER COLUMN event_sub_category SET DEFAULT '',
ALTER COLUMN event_sub_category SET NOT NULL,
ALTER COLUMN timestamp_ms SET DEFAULT 0,
ALTER COLUMN timestamp_ms SET NOT NULL,
ALTER COLUMN request_id SET DEFAULT '',
ALTER COLUMN request_id SET NOT NULL,
ALTER COLUMN idempotency_key SET DEFAULT '',
ALTER COLUMN idempotency_key SET NOT NULL,
ALTER COLUMN tenant_id SET DEFAULT 0,
ALTER COLUMN tenant_id SET NOT NULL,
ALTER COLUMN object_type SET DEFAULT '',
ALTER COLUMN object_type SET NOT NULL,
ALTER COLUMN result_code SET DEFAULT '',
ALTER COLUMN result_code SET NOT NULL,
ALTER COLUMN source_ip SET DEFAULT '',
ALTER COLUMN source_ip SET NOT NULL;
DO $$
DECLARE
current_type TEXT;
BEGIN
SELECT data_type
INTO current_type
FROM information_schema.columns
WHERE table_schema = 'public'
AND table_name = 'audit_events'
AND column_name = 'object_id';
IF current_type IS NULL THEN
ALTER TABLE audit_events
ADD COLUMN object_id BIGINT NOT NULL DEFAULT 0;
ELSIF current_type <> 'bigint' THEN
UPDATE audit_events
SET object_id = '0'
WHERE object_id IS NULL
OR object_id = ''
OR object_id !~ '^[0-9]+$';
ALTER TABLE audit_events
ALTER COLUMN object_id TYPE BIGINT USING object_id::BIGINT;
END IF;
ALTER TABLE audit_events
ALTER COLUMN object_id SET DEFAULT 0,
ALTER COLUMN object_id SET NOT NULL;
END $$;
CREATE INDEX IF NOT EXISTS idx_audit_events_event_id ON audit_events(event_id);
CREATE INDEX IF NOT EXISTS idx_audit_events_event_name ON audit_events(event_name);
CREATE INDEX IF NOT EXISTS idx_audit_events_trace_id ON audit_events(trace_id);
CREATE INDEX IF NOT EXISTS idx_audit_events_idempotency_key ON audit_events(idempotency_key);

View File

@@ -5,22 +5,48 @@
-- 创建父表
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,
id BIGSERIAL,
event_id VARCHAR(100) NOT NULL,
event_name VARCHAR(100) NOT NULL,
event_category VARCHAR(50) NOT NULL DEFAULT '',
event_sub_category VARCHAR(50) NOT NULL DEFAULT '',
timestamp TIMESTAMPTZ NOT NULL,
timestamp_ms BIGINT NOT NULL DEFAULT 0,
request_id VARCHAR(100) NOT NULL DEFAULT '',
trace_id VARCHAR(64) NOT NULL DEFAULT '',
span_id VARCHAR(64) NOT NULL DEFAULT '',
idempotency_key VARCHAR(128) NOT NULL DEFAULT '',
operator_id BIGINT NOT NULL DEFAULT 0,
operator_type VARCHAR(32) NOT NULL DEFAULT '',
operator_role VARCHAR(64) NOT NULL DEFAULT '',
tenant_id BIGINT NOT NULL DEFAULT 0,
tenant_type VARCHAR(32) NOT NULL DEFAULT '',
object_type VARCHAR(100) NOT NULL DEFAULT '',
object_id BIGINT NOT NULL DEFAULT 0,
action VARCHAR(100) NOT NULL,
action_detail TEXT NOT NULL DEFAULT '',
credential_type VARCHAR(64) NOT NULL DEFAULT '',
credential_id VARCHAR(255) NOT NULL DEFAULT '',
credential_fingerprint VARCHAR(255) NOT NULL DEFAULT '',
source_type VARCHAR(32) NOT NULL DEFAULT '',
source_ip VARCHAR(50) NOT NULL DEFAULT '',
source_region VARCHAR(100) NOT NULL DEFAULT '',
user_agent TEXT NOT NULL DEFAULT '',
target_type VARCHAR(32) NOT NULL DEFAULT '',
target_endpoint TEXT NOT NULL DEFAULT '',
target_direct BOOLEAN NOT NULL DEFAULT FALSE,
result_code VARCHAR(50) NOT NULL DEFAULT '',
result_message TEXT NOT NULL DEFAULT '',
success BOOLEAN NOT NULL DEFAULT FALSE,
before_state JSONB,
after_state JSONB,
security_flags JSONB NOT NULL DEFAULT '{}'::jsonb,
risk_score INTEGER NOT NULL DEFAULT 0,
compliance_tags TEXT[] NOT NULL DEFAULT ARRAY[]::TEXT[],
invariant_rule VARCHAR(255) NOT NULL DEFAULT '',
extensions JSONB,
version INTEGER NOT NULL DEFAULT 1,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id, timestamp)
) PARTITION BY RANGE (timestamp);
@@ -211,7 +237,11 @@ END $$;
-- 在父表上创建索引(会自动继承到分区)
CREATE INDEX IF NOT EXISTS idx_audit_events_tenant_id ON audit_events(tenant_id);
CREATE INDEX IF NOT EXISTS idx_audit_events_event_id ON audit_events(event_id);
CREATE INDEX IF NOT EXISTS idx_audit_events_event_name ON audit_events(event_name);
CREATE INDEX IF NOT EXISTS idx_audit_events_request_id ON audit_events(request_id);
CREATE INDEX IF NOT EXISTS idx_audit_events_trace_id ON audit_events(trace_id);
CREATE INDEX IF NOT EXISTS idx_audit_events_idempotency_key ON audit_events(idempotency_key);
CREATE INDEX IF NOT EXISTS idx_audit_events_created_at ON audit_events(created_at);
CREATE INDEX IF NOT EXISTS idx_audit_events_object ON audit_events(object_type, object_id);