feat(notifications): TODO 7 — draft decision toast + email + bell badge

This commit is contained in:
Александр Зимин
2026-05-14 14:40:35 +00:00
parent 50d263745a
commit 9050e2427e
21 changed files with 1992 additions and 82 deletions
@@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.27.xsd">
<!--
notification_read_state — tracks which notification_log rows have been read
by a given user, enabling the "unread count" and per-item read flag in
GET /api/v1/me/notifications.
Design decisions:
- Separate table keeps notification_log append-only (no mutable columns there).
- PK = (user_id, notification_id) — no surrogate id needed; natural composite
is unique and small enough for index coverage.
- user_id stores Keycloak subject (VARCHAR 128, matching user_display_cache.sub).
- notification_id references notification_log.id (BIGINT). FK not declared to
avoid blocking DELETE on notification_log retention sweeps.
- ON CONFLICT DO NOTHING in INSERT — idempotent re-read.
-->
<changeSet id="0024-1-notification-read-state" author="ordinis">
<comment>notification_read_state — per-user read tracking for notification_log rows</comment>
<createTable tableName="notification_read_state">
<!-- Keycloak subject — same shape as user_display_cache.sub -->
<column name="user_id" type="VARCHAR(128)">
<constraints nullable="false"/>
</column>
<!-- References notification_log.id — no FK to allow independent retention sweep -->
<column name="notification_id" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="read_at" type="TIMESTAMPTZ" defaultValueComputed="NOW()">
<constraints nullable="false"/>
</column>
</createTable>
<sql>
ALTER TABLE notification_read_state
ADD CONSTRAINT notification_read_state_pkey
PRIMARY KEY (user_id, notification_id);
</sql>
<!-- Index for "how many unread does user X have" query:
SELECT COUNT(*) FROM notification_log nl
WHERE nl.recipient = ?
AND NOT EXISTS (SELECT 1 FROM notification_read_state rs
WHERE rs.user_id = ? AND rs.notification_id = nl.id)
-->
<createIndex indexName="idx_notif_read_state_user" tableName="notification_read_state">
<column name="user_id"/>
</createIndex>
</changeSet>
</databaseChangeLog>
@@ -33,5 +33,6 @@
<include file="changes/0021-notifications.xml" relativeToChangelogFile="true"/>
<include file="changes/0022-schema-workflow.xml" relativeToChangelogFile="true"/>
<include file="changes/0023-user-display-cache.xml" relativeToChangelogFile="true"/>
<include file="changes/0024-notification-read-state.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>