固化七月迭代审计治理进展以隔离线上热修

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 参数类型错误未通过。
This commit is contained in:
2026-08-03 09:47:22 +08:00
parent cf2ff0ac1c
commit b3499adfca
114 changed files with 16961 additions and 2782 deletions

View File

@@ -0,0 +1,18 @@
BEGIN;
LOCK TABLE tb_audit_event, tb_audit_event_resource IN ACCESS EXCLUSIVE MODE;
DO $$
BEGIN
IF EXISTS (SELECT 1 FROM tb_audit_event LIMIT 1)
OR EXISTS (SELECT 1 FROM tb_audit_event_resource LIMIT 1) THEN
RAISE EXCEPTION '统一审计表已存在事实,禁止删表回滚;请停止生产者后向前修复';
END IF;
END
$$;
DROP TABLE tb_audit_event_resource;
DROP TABLE tb_audit_event;
DROP FUNCTION reject_audit_fact_update();
COMMIT;

View File

@@ -0,0 +1,154 @@
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 '资源快照数据库写入时间';

View File

@@ -0,0 +1,4 @@
DROP INDEX IF EXISTS idx_outbox_event_parent;
ALTER TABLE tb_outbox_event
DROP COLUMN IF EXISTS parent_event_id;

View File

@@ -0,0 +1,9 @@
ALTER TABLE tb_outbox_event
ADD COLUMN parent_event_id varchar(64) NOT NULL DEFAULT '';
CREATE INDEX idx_outbox_event_parent
ON tb_outbox_event (parent_event_id)
WHERE parent_event_id <> '';
COMMENT ON TABLE tb_outbox_event IS '公共可靠事件 Outbox';
COMMENT ON COLUMN tb_outbox_event.parent_event_id IS '直接触发该可靠事件的真实审计事件ID空字符串表示无已落库父事件';

View File

@@ -0,0 +1,17 @@
DO $$
BEGIN
IF EXISTS (
SELECT 1
FROM tb_integration_log
WHERE length(correlation_id) > 64
) THEN
RAISE EXCEPTION 'tb_integration_log.correlation_id 已存在超过64字符的业务链路ID禁止缩短字段回滚';
END IF;
END;
$$;
ALTER TABLE tb_integration_log
ALTER COLUMN correlation_id TYPE varchar(64);
COMMENT ON TABLE tb_integration_log IS '外部集成调用、回调及未发送尝试记录';
COMMENT ON COLUMN tb_integration_log.correlation_id IS '跨事务和异步链路关联ID最长64字符';

View File

@@ -0,0 +1,5 @@
ALTER TABLE tb_integration_log
ALTER COLUMN correlation_id TYPE varchar(100);
COMMENT ON TABLE tb_integration_log IS '外部集成调用、回调及未发送尝试记录';
COMMENT ON COLUMN tb_integration_log.correlation_id IS '跨请求、异步任务与业务后续步骤的关联ID最长100字符';

View File

@@ -0,0 +1,25 @@
-- 回滚外部集成调查组合索引,并恢复原有技术序列与业务链路索引定义。
DROP INDEX idx_integration_log_result_created;
DROP INDEX idx_integration_log_provider_created;
DROP INDEX idx_integration_log_external_created;
DROP INDEX idx_integration_log_audit_event_created;
DROP INDEX idx_integration_log_trigger;
CREATE INDEX idx_integration_log_trigger
ON tb_integration_log (trigger_series, attempt);
DROP INDEX idx_integration_log_correlation;
CREATE INDEX idx_integration_log_correlation
ON tb_integration_log (correlation_id, created_at ASC)
WHERE correlation_id IS NOT NULL;
COMMENT ON TABLE tb_integration_log IS '外部集成调用、回调及未发送尝试记录';
COMMENT ON COLUMN tb_integration_log.id IS '外部集成记录主键ID';
COMMENT ON COLUMN tb_integration_log.result IS '尝试结果pending仅为内部执行态其余为公开终态';
COMMENT ON COLUMN tb_integration_log.provider IS '外部服务提供方稳定编码';
COMMENT ON COLUMN tb_integration_log.external_id IS '外部系统返回的业务或请求标识';
COMMENT ON COLUMN tb_integration_log.audit_event_id IS '关联审计事件ID仅代码显式维护且不建立外键';
COMMENT ON COLUMN tb_integration_log.trigger_series IS '同一次外部操作显式技术尝试序列的稳定标识';
COMMENT ON COLUMN tb_integration_log.attempt IS '同一技术尝试序列内的单调尝试序号';
COMMENT ON COLUMN tb_integration_log.correlation_id IS '跨请求、异步任务与业务后续步骤的关联ID最长100字符';
COMMENT ON COLUMN tb_integration_log.created_at IS '外部集成记录创建时间';

View File

@@ -0,0 +1,35 @@
-- 为外部集成调查中心增加受控组合查询索引,不为 JSONB 摘要增加任意搜索能力。
CREATE INDEX idx_integration_log_result_created
ON tb_integration_log (result, created_at DESC, id DESC);
CREATE INDEX idx_integration_log_provider_created
ON tb_integration_log (provider, created_at DESC, id DESC);
CREATE INDEX idx_integration_log_external_created
ON tb_integration_log (external_id, created_at DESC, id DESC)
WHERE external_id IS NOT NULL;
CREATE INDEX idx_integration_log_audit_event_created
ON tb_integration_log (audit_event_id, created_at DESC, id DESC)
WHERE audit_event_id IS NOT NULL;
DROP INDEX idx_integration_log_trigger;
CREATE INDEX idx_integration_log_trigger
ON tb_integration_log (trigger_series, attempt, created_at, id)
WHERE trigger_series IS NOT NULL;
DROP INDEX idx_integration_log_correlation;
CREATE INDEX idx_integration_log_correlation
ON tb_integration_log (correlation_id, created_at DESC, id DESC)
WHERE correlation_id IS NOT NULL;
COMMENT ON TABLE tb_integration_log IS '外部集成调用、回调及未发送尝试记录';
COMMENT ON COLUMN tb_integration_log.id IS '外部集成记录主键ID';
COMMENT ON COLUMN tb_integration_log.result IS '尝试结果pending仅为内部执行态其余为公开终态';
COMMENT ON COLUMN tb_integration_log.provider IS '外部服务提供方稳定编码';
COMMENT ON COLUMN tb_integration_log.external_id IS '外部系统返回的业务或请求标识';
COMMENT ON COLUMN tb_integration_log.audit_event_id IS '关联审计事件ID仅代码显式维护且不建立外键';
COMMENT ON COLUMN tb_integration_log.trigger_series IS '同一次外部操作显式技术尝试序列的稳定标识';
COMMENT ON COLUMN tb_integration_log.attempt IS '同一技术尝试序列内的单调尝试序号';
COMMENT ON COLUMN tb_integration_log.correlation_id IS '跨请求、异步任务与业务后续步骤的关联ID最长100字符';
COMMENT ON COLUMN tb_integration_log.created_at IS '外部集成记录创建时间';