2026-04-10 18:50:08 +08:00
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
import uuid
|
|
|
|
|
from datetime import datetime
|
|
|
|
|
|
2026-04-28 17:20:17 +08:00
|
|
|
from sqlalchemy import CheckConstraint, DateTime, Index, String, text
|
2026-04-10 18:50:08 +08:00
|
|
|
from sqlalchemy.dialects.postgresql import UUID
|
|
|
|
|
from sqlalchemy.orm import Mapped, mapped_column
|
|
|
|
|
|
|
|
|
|
from core.db.base import Base, SoftDeleteMixin, TimestampMixin
|
2026-04-28 17:20:17 +08:00
|
|
|
from core.db.types import json_jsonb as jsonb
|
2026-04-16 17:48:36 +08:00
|
|
|
from schemas.enums import NotificationTargetMode
|
2026-04-10 18:50:08 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class Notification(TimestampMixin, SoftDeleteMixin, Base):
|
|
|
|
|
__tablename__ = "notifications"
|
|
|
|
|
__table_args__ = (
|
|
|
|
|
CheckConstraint(
|
|
|
|
|
"status IN ('draft', 'published', 'revoked')",
|
|
|
|
|
name="ck_notifications_status",
|
|
|
|
|
),
|
2026-04-16 17:48:36 +08:00
|
|
|
CheckConstraint(
|
|
|
|
|
"target_mode IN ('new_users', 'exist_users', 'all_users', 'user_ids')",
|
|
|
|
|
name="ck_notifications_target_mode",
|
|
|
|
|
),
|
2026-04-10 18:50:08 +08:00
|
|
|
CheckConstraint(
|
|
|
|
|
"jsonb_typeof(payload) = 'object'",
|
|
|
|
|
name="ck_notifications_payload_object",
|
|
|
|
|
),
|
|
|
|
|
Index(
|
|
|
|
|
"ix_notifications_status_created_at",
|
|
|
|
|
"status",
|
|
|
|
|
"created_at",
|
|
|
|
|
),
|
|
|
|
|
Index(
|
|
|
|
|
"ix_notifications_published_at",
|
|
|
|
|
"published_at",
|
|
|
|
|
),
|
2026-04-10 19:23:38 +08:00
|
|
|
Index(
|
|
|
|
|
"uq_notifications_source_source_key",
|
|
|
|
|
"source",
|
|
|
|
|
"source_key",
|
|
|
|
|
unique=True,
|
|
|
|
|
postgresql_where=text("source_key IS NOT NULL"),
|
|
|
|
|
),
|
2026-04-10 18:50:08 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
id: Mapped[uuid.UUID] = mapped_column(
|
|
|
|
|
UUID(as_uuid=True), primary_key=True, default=uuid.uuid4
|
|
|
|
|
)
|
|
|
|
|
type: Mapped[str] = mapped_column(
|
|
|
|
|
String(32), nullable=False, server_default=text("'system'")
|
|
|
|
|
)
|
2026-04-10 19:23:38 +08:00
|
|
|
source: Mapped[str] = mapped_column(
|
|
|
|
|
String(32), nullable=False, server_default=text("'manual'")
|
|
|
|
|
)
|
|
|
|
|
source_key: Mapped[str | None] = mapped_column(String(128), nullable=True)
|
|
|
|
|
source_version: Mapped[int | None] = mapped_column(nullable=True)
|
|
|
|
|
content_hash: Mapped[str | None] = mapped_column(String(64), nullable=True)
|
2026-04-28 17:20:17 +08:00
|
|
|
title: Mapped[dict[str, str]] = mapped_column(
|
2026-05-21 16:26:58 +08:00
|
|
|
jsonb,
|
|
|
|
|
nullable=False,
|
|
|
|
|
server_default=text("'{}'::jsonb"),
|
|
|
|
|
default=dict,
|
2026-04-28 17:20:17 +08:00
|
|
|
)
|
|
|
|
|
body: Mapped[dict[str, str]] = mapped_column(
|
2026-05-21 16:26:58 +08:00
|
|
|
jsonb,
|
|
|
|
|
nullable=False,
|
|
|
|
|
server_default=text("'{}'::jsonb"),
|
|
|
|
|
default=dict,
|
2026-04-28 17:20:17 +08:00
|
|
|
)
|
2026-04-10 18:50:08 +08:00
|
|
|
payload: Mapped[dict[str, object]] = mapped_column(
|
2026-04-28 17:20:17 +08:00
|
|
|
jsonb,
|
2026-04-10 18:50:08 +08:00
|
|
|
nullable=False,
|
|
|
|
|
server_default=text("'{}'::jsonb"),
|
|
|
|
|
default=dict,
|
|
|
|
|
)
|
|
|
|
|
status: Mapped[str] = mapped_column(
|
|
|
|
|
String(16), nullable=False, server_default=text("'published'")
|
|
|
|
|
)
|
2026-04-16 17:48:36 +08:00
|
|
|
target_mode: Mapped[NotificationTargetMode] = mapped_column(
|
|
|
|
|
String(32), nullable=False, server_default=text("'all_users'")
|
|
|
|
|
)
|
2026-04-10 18:50:08 +08:00
|
|
|
published_at: Mapped[datetime | None] = mapped_column(
|
|
|
|
|
DateTime(timezone=True), nullable=True
|
|
|
|
|
)
|
|
|
|
|
revoked_at: Mapped[datetime | None] = mapped_column(
|
|
|
|
|
DateTime(timezone=True), nullable=True
|
|
|
|
|
)
|