-- 创建公共站内通知事实表;不建立外键,接收人与资源引用由应用层显式校验。 CREATE TABLE tb_notification ( id bigserial PRIMARY KEY, event_id varchar(64) NOT NULL, recipient_kind varchar(32) NOT NULL, recipient_id bigint NOT NULL, category varchar(32) NOT NULL, type varchar(100) NOT NULL, severity varchar(16) NOT NULL, title varchar(200) NOT NULL, body text NOT NULL, ref_type varchar(64) NOT NULL DEFAULT '', ref_id varchar(128) NOT NULL DEFAULT '', ref_key varchar(128) NOT NULL DEFAULT '', is_read boolean NOT NULL DEFAULT FALSE, read_at timestamptz, expires_at timestamptz, created_at timestamptz NOT NULL DEFAULT NOW(), CONSTRAINT uq_notification_event_recipient UNIQUE (event_id, recipient_kind, recipient_id), CONSTRAINT ck_notification_recipient_kind CHECK (recipient_kind IN ('account', 'personal_customer')), CONSTRAINT ck_notification_recipient_id CHECK (recipient_id > 0), CONSTRAINT ck_notification_required_text CHECK (event_id <> '' AND type <> '' AND title <> '' AND body <> ''), CONSTRAINT ck_notification_reference CHECK ( (ref_type = '' AND ref_id = '' AND ref_key = '') OR (ref_type <> '' AND (ref_id <> '' OR ref_key <> '')) ), CONSTRAINT ck_notification_category CHECK (category IN ('approval', 'expiry', 'sync', 'system')), CONSTRAINT ck_notification_severity CHECK (severity IN ('info', 'warning', 'error', 'critical')), CONSTRAINT ck_notification_read_time CHECK ((is_read = FALSE AND read_at IS NULL) OR (is_read = TRUE AND read_at IS NOT NULL)) ); CREATE INDEX idx_notification_recipient_unread ON tb_notification (recipient_kind, recipient_id, is_read, created_at DESC, id DESC); CREATE INDEX idx_notification_recipient_category ON tb_notification (recipient_kind, recipient_id, category, created_at DESC, id DESC); CREATE INDEX idx_notification_expires_at ON tb_notification (expires_at, id) WHERE expires_at IS NOT NULL; COMMENT ON TABLE tb_notification IS '公共站内通知事实'; COMMENT ON COLUMN tb_notification.id IS '通知主键ID'; COMMENT ON COLUMN tb_notification.event_id IS '上游稳定事件ID,与接收人共同防重'; COMMENT ON COLUMN tb_notification.recipient_kind IS '接收人类型:account后台账号,personal_customer个人客户'; COMMENT ON COLUMN tb_notification.recipient_id IS '接收人账号或个人客户ID'; COMMENT ON COLUMN tb_notification.category IS '通知分类:审批、临期、同步或系统'; COMMENT ON COLUMN tb_notification.type IS '稳定通知类型编码'; COMMENT ON COLUMN tb_notification.severity IS '通知严重级别:普通、警告、错误或严重'; COMMENT ON COLUMN tb_notification.title IS '发送时固化的纯文本标题'; COMMENT ON COLUMN tb_notification.body IS '发送时固化的纯文本正文'; COMMENT ON COLUMN tb_notification.ref_type IS '受控关联资源类型,空字符串表示无关联资源'; COMMENT ON COLUMN tb_notification.ref_id IS '受控关联资源ID,禁止保存任意URL'; COMMENT ON COLUMN tb_notification.ref_key IS '受控关联资源稳定键,禁止保存任意URL'; COMMENT ON COLUMN tb_notification.is_read IS '是否已被接收人读取'; COMMENT ON COLUMN tb_notification.read_at IS '接收人首次读取时间'; COMMENT ON COLUMN tb_notification.expires_at IS '通知停止展示时间,空值表示不自动过期'; COMMENT ON COLUMN tb_notification.created_at IS '通知创建时间';