feat(notification): add target_mode enum constraint and merge register-notifications script

- Add NotificationTargetMode enum (new_users/exist_users/all_users/user_ids)
- Create Alembic migrations: drop duplicate indexes, add target_mode column
- Merge register-notifications.sh into dev-migrate.sh sync-notifications subcommand
- Shorten notification config path: static/notification/notifications -> static/notifications
- Update registration flow to dispatch notifications by target_mode
- Add is_first_registration to RegisterBonusResult for first-time user detection
- Remove dead code: link_published_notifications_to_user
- Update welcome_points.yaml to target new_users only
- Add 44 unit tests + 1 integration test, all passing
This commit is contained in:
qzl
2026-04-16 17:48:36 +08:00
parent d91064835b
commit c79c773d67
26 changed files with 1011 additions and 49 deletions
+8
View File
@@ -9,6 +9,7 @@ from sqlalchemy.orm import Mapped, mapped_column
from core.db.base import Base, SoftDeleteMixin, TimestampMixin
from core.db.types import json_jsonb
from schemas.enums import NotificationTargetMode
class Notification(TimestampMixin, SoftDeleteMixin, Base):
@@ -18,6 +19,10 @@ class Notification(TimestampMixin, SoftDeleteMixin, Base):
"status IN ('draft', 'published', 'revoked')",
name="ck_notifications_status",
),
CheckConstraint(
"target_mode IN ('new_users', 'exist_users', 'all_users', 'user_ids')",
name="ck_notifications_target_mode",
),
CheckConstraint(
"jsonb_typeof(payload) = 'object'",
name="ck_notifications_payload_object",
@@ -63,6 +68,9 @@ class Notification(TimestampMixin, SoftDeleteMixin, Base):
status: Mapped[str] = mapped_column(
String(16), nullable=False, server_default=text("'published'")
)
target_mode: Mapped[NotificationTargetMode] = mapped_column(
String(32), nullable=False, server_default=text("'all_users'")
)
published_at: Mapped[datetime | None] = mapped_column(
DateTime(timezone=True), nullable=True
)