"""站点写操作应用用例。""" from dataclasses import dataclass from typing import Any, Awaitable, Callable, Mapping, Optional, Protocol from app.application.subscription.delete import AsyncUnitOfWork @dataclass(frozen=True, slots=True) class SiteMutationResult: """描述站点写操作是否成功及兼容提示信息。""" success: bool message: str = "" class SiteMutationRepository(Protocol): """站点写用例需要的最小持久化端口。""" async def get_by_id(self, site_id: int) -> Optional[Any]: """读取指定站点。""" ... async def async_get_by_domain(self, domain: str) -> Optional[Any]: """按域名读取站点。""" ... async def stage_create(self, payload: Mapping[str, Any]) -> None: """暂存新增站点。""" ... async def stage_update(self, site_id: int, payload: Mapping[str, Any]) -> bool: """暂存站点更新并返回目标是否存在。""" ... async def stage_delete(self, site_id: int) -> None: """暂存站点删除。""" ... async def stage_priorities(self, priorities: list[dict]) -> None: """暂存一组站点优先级变更。""" ... async def stage_reset(self) -> None: """暂存清空全部站点。""" ... SiteIndexerLoader = Callable[[str], Awaitable[Optional[dict]]] SiteEventPublisher = Callable[[dict], Awaitable[None]] DomainExtractor = Callable[[str], str] UrlNormalizer = Callable[[str], str] class SiteMutationCommand: """统一执行站点新增、更新、优先级和删除事务。""" def __init__( self, *, repository: SiteMutationRepository, unit_of_work: AsyncUnitOfWork, auth_level_provider: Callable[[], int], indexer_loader: SiteIndexerLoader, domain_extractor: DomainExtractor, url_normalizer: UrlNormalizer, publish_updated: SiteEventPublisher, publish_deleted: SiteEventPublisher, ) -> None: """保存站点验证、持久化、事务和提交后事件端口。""" self._repository = repository self._unit_of_work = unit_of_work self._auth_level_provider = auth_level_provider self._indexer_loader = indexer_loader self._domain_extractor = domain_extractor self._url_normalizer = url_normalizer self._publish_updated = publish_updated self._publish_deleted = publish_deleted async def create(self, payload: Mapping[str, Any]) -> SiteMutationResult: """校验并新增站点,提交成功后发布站点更新事件。""" values = dict(payload) raw_url = values.get("url") if not raw_url: return SiteMutationResult(False, "站点地址不能为空") if self._auth_level_provider() < 2: return SiteMutationResult(False, "用户未通过认证,无法使用站点功能!") domain = self._domain_extractor(raw_url) site_info = await self._indexer_loader(domain) if not site_info: return SiteMutationResult(False, "该站点不支持,请检查站点域名是否正确") if await self._repository.async_get_by_domain(domain): return SiteMutationResult(False, f"{domain} 站点己存在") values.update({ "id": None, "domain": domain, "url": self._url_normalizer(raw_url), "name": site_info.get("name"), "public": 1 if site_info.get("public") else 0, }) await self._repository.stage_create(values) await self._commit() await self._publish_updated({"domain": domain}) return SiteMutationResult(True) async def update(self, payload: Mapping[str, Any]) -> SiteMutationResult: """更新站点并在提交后发布完整的兼容事件载荷。""" values = dict(payload) site_id = values.get("id") if not site_id or not await self._repository.get_by_id(site_id): return SiteMutationResult(False, "站点不存在") values["url"] = self._url_normalizer(values.get("url") or "") values["domain"] = self._domain_extractor(values["url"]) await self._repository.stage_update(site_id, values) await self._commit() await self._publish_updated({ "site_id": site_id, "domain": values["domain"], "name": values.get("name"), "site_url": values["url"], }) return SiteMutationResult(True) async def update_priorities(self, priorities: list[dict]) -> SiteMutationResult: """在同一事务中更新全部站点优先级。""" await self._repository.stage_priorities(priorities) await self._commit() return SiteMutationResult(True) async def delete(self, site_id: int) -> SiteMutationResult: """删除站点,并确保删除事件只在提交成功后发送。""" await self._repository.stage_delete(site_id) await self._commit() await self._publish_deleted({"site_id": site_id}) return SiteMutationResult(True) async def reset(self) -> SiteMutationResult: """清空全部站点,并在提交后发布通配站点删除事件。""" await self._repository.stage_reset() await self._commit() await self._publish_deleted({"site_id": "*"}) return SiteMutationResult(True) async def _commit(self) -> None: """提交当前站点事务,失败时回滚并保留原始异常。""" try: await self._unit_of_work.commit() except Exception: await self._unit_of_work.rollback() raise