muse-agent-example/db/ddl/96-example参考作品授权快照.sql
zizi 2666d50a8e 修复: 收紧授权用途与导入任务匹配
阻断允许用途与禁止用途冲突,并让授权快照外层与内层保持一致。
导入任务按参考作品原文件唯一匹配,避免无关成功任务误阻断回放。
2026-07-19 22:19:47 +08:00

99 lines
5.8 KiB
PL/PgSQL
Raw 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.

-- example_reference_authorization_snapshot:参考作品用途授权的不可变快照。
-- 本表只允许 INSERT;授权变化通过新增版本表达,禁止覆盖或删除历史证据。
CREATE FUNCTION example_jsonb_text_arrays_disjoint(left_values JSONB, right_values JSONB)
RETURNS BOOLEAN
LANGUAGE sql
IMMUTABLE
STRICT
AS $$
SELECT NOT EXISTS (
SELECT 1
FROM jsonb_array_elements_text(left_values) AS left_value(value)
JOIN jsonb_array_elements_text(right_values) AS right_value(value) USING (value)
);
$$;
CREATE TABLE example_reference_authorization_snapshot (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
reference_work_id BIGINT NOT NULL, -- → example_reference_work.id(软引用)
work_id BIGINT NOT NULL, -- → muse_content_work.id(软引用)
knowledge_document_id BIGINT NOT NULL, -- → muse_knowledge_document.id(软引用)
import_task_id BIGINT NOT NULL, -- → muse_content_import_task.id(软引用)
source_file VARCHAR(500) NOT NULL, -- 授权对应的原文件名
source_hash VARCHAR(71) NOT NULL, -- sha256:<64位小写十六进制>
source_version VARCHAR(96) NOT NULL, -- raw-file-v1:sha256:<64位小写十六进制>
snapshot_version VARCHAR(160) NOT NULL, -- 授权快照业务版本,不使用物理主键充当合同版本
copyright_status VARCHAR(30) NOT NULL, -- licensed/public_domain/research_only/unauthorized
source_status VARCHAR(30) NOT NULL DEFAULT 'active',
allowed_purpose JSONB NOT NULL DEFAULT '[]', -- 允许用途数组,例如 ["offline_evaluation"]
forbidden_purpose JSONB NOT NULL DEFAULT '[]', -- 明确禁止用途数组,例如 ["external_distribution"]
authorization_basis VARCHAR(40) NOT NULL, -- user_authorization/public_domain_record/license_contract
authorized_by VARCHAR(128) NOT NULL, -- 授权主体或登记责任人
authorization_evidence JSONB NOT NULL, -- 授权原文摘要、时间和证据定位,不存敏感全文
display_summary VARCHAR(500) NOT NULL, -- 面向审计面的脱敏摘要
checked_at TIMESTAMPTZ NOT NULL, -- 本次授权核验时间
expires_at TIMESTAMPTZ, -- 到期即阻断
revalidation_at TIMESTAMPTZ, -- 到点必须重验
creator VARCHAR(64) NOT NULL DEFAULT '',
create_time TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
tenant_id BIGINT NOT NULL DEFAULT 0,
CONSTRAINT uk_example_reference_auth_work_version UNIQUE (tenant_id, work_id, snapshot_version),
CONSTRAINT chk_example_reference_auth_source_hash
CHECK (source_hash ~ '^sha256:[0-9a-f]{64}$'),
CONSTRAINT chk_example_reference_auth_source_version
CHECK (source_version = 'raw-file-v1:' || source_hash),
CONSTRAINT chk_example_reference_auth_copyright
CHECK (copyright_status IN ('research_only','public_domain','licensed','unauthorized')),
CONSTRAINT chk_example_reference_auth_source_status
CHECK (source_status IN ('active','stale','revoked','delisted','recalled','blocked','owner_missing','unauthorized')),
CONSTRAINT chk_example_reference_auth_allowed_purpose
CHECK (jsonb_typeof(allowed_purpose) = 'array'),
CONSTRAINT chk_example_reference_auth_forbidden_purpose
CHECK (jsonb_typeof(forbidden_purpose) = 'array'),
CONSTRAINT chk_example_reference_auth_purpose_disjoint
CHECK (example_jsonb_text_arrays_disjoint(allowed_purpose, forbidden_purpose)),
CONSTRAINT chk_example_reference_auth_basis
CHECK (authorization_basis IN ('user_authorization','public_domain_record','license_contract')),
CONSTRAINT chk_example_reference_auth_basis_copyright
CHECK (
(authorization_basis <> 'user_authorization' OR copyright_status = 'research_only')
AND (authorization_basis <> 'public_domain_record' OR copyright_status = 'public_domain')
AND (authorization_basis <> 'license_contract' OR copyright_status = 'licensed')
),
CONSTRAINT chk_example_reference_auth_user_purpose
CHECK (
authorization_basis <> 'user_authorization'
OR allowed_purpose = '["offline_evaluation"]'::jsonb
),
CONSTRAINT chk_example_reference_auth_evidence
CHECK (jsonb_typeof(authorization_evidence) = 'object'),
CONSTRAINT chk_example_reference_auth_purpose
CHECK (
(copyright_status = 'unauthorized' AND jsonb_array_length(allowed_purpose) = 0)
OR (copyright_status <> 'unauthorized' AND jsonb_array_length(allowed_purpose) > 0)
),
CONSTRAINT chk_example_reference_auth_recheck
CHECK (expires_at IS NOT NULL OR revalidation_at IS NOT NULL),
CONSTRAINT chk_example_reference_auth_expiry_order
CHECK (expires_at IS NULL OR expires_at > checked_at),
CONSTRAINT chk_example_reference_auth_revalidation_order
CHECK (revalidation_at IS NULL OR revalidation_at > checked_at)
);
CREATE INDEX idx_example_reference_auth_latest
ON example_reference_authorization_snapshot(tenant_id, work_id, checked_at DESC, id DESC);
-- 授权快照是审计证据;撤销、到期或用途变化都必须插入新版本,不能改写旧记录。
CREATE FUNCTION reject_example_reference_authorization_snapshot_mutation()
RETURNS TRIGGER
LANGUAGE plpgsql
AS $$
BEGIN
RAISE EXCEPTION 'example_reference_authorization_snapshot 是 append-only 表,禁止 UPDATE/DELETE';
END;
$$;
CREATE TRIGGER trg_example_reference_auth_append_only
BEFORE UPDATE OR DELETE ON example_reference_authorization_snapshot
FOR EACH ROW EXECUTE FUNCTION reject_example_reference_authorization_snapshot_mutation();