Files
junhong_cmp_fiber/migrations/000199_create_audit_event.up.sql
break b3499adfca 固化七月迭代审计治理进展以隔离线上热修
Constraint: 切换 main 前必须保存当前七月分支全部项目进展,套餐生效提案仅属于 Iteration/7-11。

Rejected: 将七月套餐修复直接移植到 main | 两个分支的可靠投递架构不同。

Confidence: medium

Scope-risk: broad

Directive: 不得将本提交整体 cherry-pick 到 main;main 套餐热修必须基于其纯 Asynq 代码独立实施。

Tested: git diff --check;openspec validate fix-package-activation-starvation --strict。

Not-tested: 按用户要求未运行自动化测试;go build ./... 因当前审计改造中的 Enterprise 模型字面量和 role.recordFailure 参数类型错误未通过。
2026-08-03 09:47:22 +08:00

155 lines
9.7 KiB
PL/PgSQL
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
CREATE TABLE tb_audit_event (
id bigserial PRIMARY KEY,
event_id varchar(64) NOT NULL UNIQUE,
occurred_at timestamptz NOT NULL,
category varchar(64) NOT NULL,
action_code varchar(100) NOT NULL,
action_name varchar(200) NOT NULL,
summary varchar(500) NOT NULL,
actor_kind varchar(32) NOT NULL,
actor_id varchar(128) NOT NULL,
actor_name varchar(200) NOT NULL,
actor_shop_id bigint,
actor_shop_name varchar(200) NOT NULL DEFAULT '',
actor_enterprise_id bigint,
actor_enterprise_name varchar(200) NOT NULL DEFAULT '',
source varchar(32) NOT NULL,
request_path varchar(300) NOT NULL DEFAULT '',
request_method varchar(16) NOT NULL DEFAULT '',
ip_address varchar(64) NOT NULL DEFAULT '',
user_agent varchar(500) NOT NULL DEFAULT '',
scope_type varchar(32) NOT NULL,
scope_id varchar(128) NOT NULL DEFAULT '',
scope_name varchar(200) NOT NULL DEFAULT '',
result varchar(16) NOT NULL,
risk_level varchar(16) NOT NULL,
error_code varchar(100) NOT NULL DEFAULT '',
error_summary varchar(500) NOT NULL DEFAULT '',
request_id varchar(100) NOT NULL DEFAULT '',
correlation_id varchar(100) NOT NULL DEFAULT '',
parent_event_id varchar(64) NOT NULL DEFAULT '',
batch_total integer NOT NULL DEFAULT 0,
success_count integer NOT NULL DEFAULT 0,
fail_count integer NOT NULL DEFAULT 0,
metadata jsonb NOT NULL DEFAULT '{}'::jsonb,
content_hash varchar(64) NOT NULL,
created_at timestamptz NOT NULL DEFAULT NOW(),
CONSTRAINT ck_audit_event_identity CHECK (event_id <> '' AND action_code <> '' AND action_name <> ''),
CONSTRAINT ck_audit_event_result CHECK (result IN ('success', 'failed', 'denied', 'partial', 'unknown')),
CONSTRAINT ck_audit_event_risk CHECK (risk_level IN ('low', 'normal', 'high', 'critical')),
CONSTRAINT ck_audit_event_batch CHECK (
batch_total >= 0 AND success_count >= 0 AND fail_count >= 0
AND success_count + fail_count <= batch_total
),
CONSTRAINT ck_audit_event_metadata CHECK (jsonb_typeof(metadata) = 'object')
);
CREATE TABLE tb_audit_event_resource (
id bigserial PRIMARY KEY,
audit_event_id bigint NOT NULL,
resource_type varchar(64) NOT NULL,
resource_id varchar(128),
resource_key varchar(200) NOT NULL,
display_name varchar(255) NOT NULL DEFAULT '',
relation varchar(16) NOT NULL,
role varchar(64) NOT NULL,
identity_snapshot jsonb NOT NULL DEFAULT '{}'::jsonb,
before_data jsonb NOT NULL DEFAULT '{}'::jsonb,
after_data jsonb NOT NULL DEFAULT '{}'::jsonb,
subject_visibility varchar(24) NOT NULL,
subject_summary varchar(500) NOT NULL DEFAULT '',
subject_data jsonb NOT NULL DEFAULT '{}'::jsonb,
sort_order integer NOT NULL DEFAULT 0,
created_at timestamptz NOT NULL DEFAULT NOW(),
CONSTRAINT ck_audit_resource_identity CHECK (resource_type <> '' AND resource_key <> ''),
CONSTRAINT ck_audit_resource_relation CHECK (relation IN ('primary', 'affected', 'reference')),
CONSTRAINT ck_audit_resource_visibility CHECK (subject_visibility IN ('internal_only', 'subject_result', 'subject_detail')),
CONSTRAINT ck_audit_resource_identity_snapshot CHECK (jsonb_typeof(identity_snapshot) = 'object'),
CONSTRAINT ck_audit_resource_before_data CHECK (jsonb_typeof(before_data) = 'object'),
CONSTRAINT ck_audit_resource_after_data CHECK (jsonb_typeof(after_data) = 'object'),
CONSTRAINT ck_audit_resource_subject_data CHECK (jsonb_typeof(subject_data) = 'object')
);
CREATE UNIQUE INDEX uq_audit_event_resource_role
ON tb_audit_event_resource (audit_event_id, resource_type, resource_key, relation, role);
CREATE INDEX idx_audit_event_occurred ON tb_audit_event (occurred_at DESC, id DESC);
CREATE INDEX idx_audit_event_actor ON tb_audit_event (actor_kind, actor_id, occurred_at DESC, id DESC);
CREATE INDEX idx_audit_event_action_result_risk ON tb_audit_event (action_code, result, risk_level, occurred_at DESC, id DESC);
CREATE INDEX idx_audit_event_scope ON tb_audit_event (scope_type, scope_id, occurred_at DESC, id DESC);
CREATE INDEX idx_audit_event_request ON tb_audit_event (request_id) WHERE request_id <> '';
CREATE INDEX idx_audit_event_correlation ON tb_audit_event (correlation_id, occurred_at DESC, id DESC) WHERE correlation_id <> '';
CREATE INDEX idx_audit_event_parent ON tb_audit_event (parent_event_id) WHERE parent_event_id <> '';
CREATE INDEX idx_audit_resource_event ON tb_audit_event_resource (audit_event_id, sort_order, id);
CREATE INDEX idx_audit_resource_id_timeline
ON tb_audit_event_resource (resource_type, resource_id, created_at DESC, id DESC)
WHERE resource_id IS NOT NULL;
CREATE INDEX idx_audit_resource_key_timeline
ON tb_audit_event_resource (resource_type, resource_key, created_at DESC, id DESC);
CREATE FUNCTION reject_audit_fact_update() RETURNS trigger AS $$
BEGIN
RAISE EXCEPTION '审计事实不可修改;业务修正必须追加新事件';
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER trg_audit_event_immutable
BEFORE UPDATE ON tb_audit_event
FOR EACH ROW EXECUTE FUNCTION reject_audit_fact_update();
CREATE TRIGGER trg_audit_event_resource_immutable
BEFORE UPDATE ON tb_audit_event_resource
FOR EACH ROW EXECUTE FUNCTION reject_audit_fact_update();
COMMENT ON TABLE tb_audit_event IS '统一不可变业务审计事件';
COMMENT ON TABLE tb_audit_event_resource IS '审计事件发生时的独立资源快照';
COMMENT ON COLUMN tb_audit_event.id IS '审计事件数据库主键';
COMMENT ON COLUMN tb_audit_event.event_id IS '对外稳定审计事件ID';
COMMENT ON COLUMN tb_audit_event.occurred_at IS '业务事实实际发生时间';
COMMENT ON COLUMN tb_audit_event.category IS '动作所属稳定业务类别';
COMMENT ON COLUMN tb_audit_event.action_code IS 'Action Registry 注册的稳定动作编码';
COMMENT ON COLUMN tb_audit_event.action_name IS '事件发生时的中文动作名称快照';
COMMENT ON COLUMN tb_audit_event.summary IS '平台内部可读业务摘要';
COMMENT ON COLUMN tb_audit_event.actor_kind IS '真实操作者类型';
COMMENT ON COLUMN tb_audit_event.actor_id IS '真实操作者稳定ID';
COMMENT ON COLUMN tb_audit_event.actor_name IS '事件发生时的操作者名称快照';
COMMENT ON COLUMN tb_audit_event.actor_shop_id IS '操作者所属店铺ID快照';
COMMENT ON COLUMN tb_audit_event.actor_shop_name IS '操作者所属店铺名称快照';
COMMENT ON COLUMN tb_audit_event.actor_enterprise_id IS '操作者所属企业ID快照';
COMMENT ON COLUMN tb_audit_event.actor_enterprise_name IS '操作者所属企业名称快照';
COMMENT ON COLUMN tb_audit_event.source IS '操作入口来源';
COMMENT ON COLUMN tb_audit_event.request_path IS 'HTTP请求路径摘要';
COMMENT ON COLUMN tb_audit_event.request_method IS 'HTTP请求方法';
COMMENT ON COLUMN tb_audit_event.ip_address IS '操作者请求IP';
COMMENT ON COLUMN tb_audit_event.user_agent IS '操作者User-Agent摘要';
COMMENT ON COLUMN tb_audit_event.scope_type IS '事件主要业务范围类型';
COMMENT ON COLUMN tb_audit_event.scope_id IS '事件主要业务范围ID';
COMMENT ON COLUMN tb_audit_event.scope_name IS '事件主要业务范围名称快照';
COMMENT ON COLUMN tb_audit_event.result IS '事件结果success、failed、denied、partial或unknown';
COMMENT ON COLUMN tb_audit_event.risk_level IS 'Action Registry 注册的风险等级';
COMMENT ON COLUMN tb_audit_event.error_code IS '稳定业务错误码';
COMMENT ON COLUMN tb_audit_event.error_summary IS '不含底层敏感信息的错误摘要';
COMMENT ON COLUMN tb_audit_event.request_id IS '同一HTTP请求关联ID';
COMMENT ON COLUMN tb_audit_event.correlation_id IS '跨请求与异步业务链路关联ID';
COMMENT ON COLUMN tb_audit_event.parent_event_id IS '直接父审计事件稳定ID';
COMMENT ON COLUMN tb_audit_event.batch_total IS '批量根事件输入总数';
COMMENT ON COLUMN tb_audit_event.success_count IS '批量根事件成功数';
COMMENT ON COLUMN tb_audit_event.fail_count IS '批量根事件失败数';
COMMENT ON COLUMN tb_audit_event.metadata IS '清理且有界的业务补充参数';
COMMENT ON COLUMN tb_audit_event.content_hash IS '清理并标准化后的事件与资源内容 SHA-256';
COMMENT ON COLUMN tb_audit_event.created_at IS '审计事件数据库写入时间';
COMMENT ON COLUMN tb_audit_event_resource.id IS '审计事件资源数据库主键';
COMMENT ON COLUMN tb_audit_event_resource.audit_event_id IS '审计事件内部 ID仅普通索引不建立外键';
COMMENT ON COLUMN tb_audit_event_resource.resource_type IS 'Resource Registry 注册的资源类型';
COMMENT ON COLUMN tb_audit_event_resource.resource_id IS '可空的资源内部稳定ID';
COMMENT ON COLUMN tb_audit_event_resource.resource_key IS '事件发生时的稳定业务Key';
COMMENT ON COLUMN tb_audit_event_resource.display_name IS '事件发生时的资源显示名称';
COMMENT ON COLUMN tb_audit_event_resource.relation IS '资源关系primary、affected或reference';
COMMENT ON COLUMN tb_audit_event_resource.role IS '资源在本次业务动作中的稳定角色';
COMMENT ON COLUMN tb_audit_event_resource.identity_snapshot IS '事件发生时的资源身份快照';
COMMENT ON COLUMN tb_audit_event_resource.before_data IS '该资源本次操作前的直接业务字段';
COMMENT ON COLUMN tb_audit_event_resource.after_data IS '该资源本次操作后的直接业务字段';
COMMENT ON COLUMN tb_audit_event_resource.subject_visibility IS '主体可见级别';
COMMENT ON COLUMN tb_audit_event_resource.subject_summary IS '代理或企业可见的安全业务结论';
COMMENT ON COLUMN tb_audit_event_resource.subject_data IS '按Action Registry白名单生成的主体业务字段';
COMMENT ON COLUMN tb_audit_event_resource.sort_order IS '同一事件内资源稳定展示顺序';
COMMENT ON COLUMN tb_audit_event_resource.created_at IS '资源快照数据库写入时间';