refactor: unify durable event topics

This commit is contained in:
jxxghp
2026-08-24 06:55:50 +08:00
parent 8e669d415e
commit 138a48770c
11 changed files with 172 additions and 64 deletions
+41 -1
View File
@@ -2,15 +2,43 @@
from __future__ import annotations
from collections.abc import Callable
from collections.abc import Callable, Mapping
from dataclasses import dataclass
from datetime import datetime, timedelta, timezone
from types import MappingProxyType
from typing import Any, Protocol, TypeVar
from app.schemas.types import EventType
T = TypeVar("T")
SUBSCRIBE_ADDED_TOPIC = "subscribe.added"
SUBSCRIBE_MODIFIED_TOPIC = "subscribe.modified"
SUBSCRIBE_DELETED_TOPIC = "subscribe.deleted"
DOWNLOAD_ADDED_TOPIC = "download.added"
TRANSFER_COMPLETED_TOPIC = "transfer.completed"
TRANSFER_FAILED_TOPIC = "transfer.failed"
DURABLE_EVENT_TOPICS: Mapping[EventType, str] = MappingProxyType({
EventType.SubscribeAdded: SUBSCRIBE_ADDED_TOPIC,
EventType.SubscribeModified: SUBSCRIBE_MODIFIED_TOPIC,
EventType.SubscribeDeleted: SUBSCRIBE_DELETED_TOPIC,
EventType.DownloadAdded: DOWNLOAD_ADDED_TOPIC,
EventType.TransferComplete: TRANSFER_COMPLETED_TOPIC,
EventType.TransferFailed: TRANSFER_FAILED_TOPIC,
})
def durable_event_topic(event_type: EventType) -> str:
"""返回 durable-required 事件唯一登记的 outbox topic。"""
try:
return DURABLE_EVENT_TOPICS[event_type]
except KeyError as error:
raise ValueError(f"事件 {event_type.name} 未登记 durable topic") from error
@dataclass(frozen=True, slots=True)
class OutboxIntent:
"""与业务事务一起暂存的版本化副作用意图。"""
@@ -33,6 +61,18 @@ class ClaimedOutboxMessage:
attempt: int
def validate_durable_event_handlers(
handlers: Mapping[str, Callable[[ClaimedOutboxMessage], None]],
) -> None:
"""拒绝缺少任一 durable-required 事件恢复 handler 的 dispatcher。"""
missing = set(DURABLE_EVENT_TOPICS.values()) - set(handlers)
if missing:
raise RuntimeError(
"Outbox dispatcher 缺少 durable 事件 handler: "
+ ", ".join(sorted(missing))
)
class OutboxRepository(Protocol):
"""outbox 写入、claim 和终态更新所需的最小端口。"""
+2 -1
View File
@@ -12,6 +12,7 @@ from app.application.outbox import (
OutboxIntent,
SyncOutboxTransaction,
SyncUnitOfWork,
SUBSCRIBE_DELETED_TOPIC,
)
from app.schemas.event import SubscribeDeletedEventData
@@ -251,7 +252,7 @@ def _build_deletion_effects(
report_payload=report_payload,
event_intent=OutboxIntent(
event_key=event_key,
topic="subscribe.deleted",
topic=SUBSCRIBE_DELETED_TOPIC,
payload=event_payload,
),
report_intent=OutboxIntent(
+6 -2
View File
@@ -3,7 +3,11 @@
from datetime import datetime, timezone
from typing import Any, Callable, Protocol
from app.application.outbox import AsyncOutboxTransaction, OutboxIntent
from app.application.outbox import (
AsyncOutboxTransaction,
OutboxIntent,
SUBSCRIBE_DELETED_TOPIC,
)
from app.application.subscription.delete import (
AsyncUnitOfWork,
SubscribeDeletedPublisher,
@@ -89,7 +93,7 @@ class DeleteSubscriptionsByIdentityCommand:
await self._outbox.stage(
OutboxIntent(
event_key=event_payload["idempotency_key"],
topic="subscribe.deleted",
topic=SUBSCRIBE_DELETED_TOPIC,
payload=event_payload,
),
now,
+6 -2
View File
@@ -7,7 +7,11 @@ from datetime import datetime, timezone
from typing import Any, Protocol
from uuid import uuid4
from app.application.outbox import AsyncOutboxTransaction, OutboxIntent
from app.application.outbox import (
AsyncOutboxTransaction,
OutboxIntent,
SUBSCRIBE_MODIFIED_TOPIC,
)
from app.schemas.event import SubscribeModifiedEventData
@@ -143,7 +147,7 @@ class SubscriptionMutationService:
await self._outbox.stage(
OutboxIntent(
event_key=event_key,
topic="subscribe.modified",
topic=SUBSCRIBE_MODIFIED_TOPIC,
payload=event_payload,
),
datetime.now(timezone.utc),
+2 -2
View File
@@ -19,7 +19,7 @@ from collections.abc import Awaitable, Callable
from datetime import datetime, timezone
from typing import Mapping, Optional, Protocol, Tuple
from app.application.outbox import OutboxIntent
from app.application.outbox import OutboxIntent, SUBSCRIBE_ADDED_TOPIC
from app.domain.context import MediaInfo, MusicInfo
from app.schemas.media import resolve_media_identity
from app.schemas.types import MUSIC_ENTITY_ALBUM, MediaType
@@ -243,7 +243,7 @@ def _subscribe_added_intents(
intents: list[OutboxIntent] = [
OutboxIntent(
event_key=event_key,
topic="subscribe.added",
topic=SUBSCRIBE_ADDED_TOPIC,
payload=event_payload,
),
]