from collections.abc import Mapping from typing import Optional from app.dao.parent import ParentDAO from app.service.avatar_storage import AvatarStorageService from app.settings import settings class ParentService: def __init__(self, db, avatar_storage: AvatarStorageService | None = None): self.dao = ParentDAO(db) self.avatar_storage = avatar_storage or AvatarStorageService() def create( self, openid: str, unionid: Optional[str] = None, nickname: Optional[str] = None, avatar_url: Optional[str] = None, ) -> Mapping: user_id = self.dao.upsert(openid, unionid, nickname, avatar_url) return self.get(user_id) def get(self, user_id: int) -> Optional[Mapping]: return self._present_parent(self.dao.get_by_id(user_id)) def update( self, user_id: int, nickname: Optional[str] = None, avatar_url: Optional[str] = None, phone: Optional[str] = None, ) -> Mapping: self.dao.update(user_id, nickname, avatar_url, phone) return self.get(user_id) def upload_avatar( self, *, user_id: int, filename: str | None, content_type: str | None, content: bytes, ) -> Optional[Mapping]: existing = self.dao.get_by_id(user_id) if not existing: return None stored = self.avatar_storage.upload_avatar( user_id=user_id, filename=filename, content_type=content_type, content=content, ) old_avatar_file_key = existing.get("avatar_file_key") try: self.dao.set_avatar_file_key(user_id, stored.file_key) except Exception: try: self.avatar_storage.delete_avatar(stored.file_key) except Exception: pass raise if old_avatar_file_key and old_avatar_file_key != stored.file_key: try: self.avatar_storage.delete_avatar(old_avatar_file_key) except Exception: pass return self.get(user_id) def get_avatar_download(self, user_id: int) -> Optional[dict]: parent = self.dao.get_by_id(user_id) if not parent: return None avatar_file_key = parent.get("avatar_file_key") if avatar_file_key: return { "avatar_url": self.avatar_storage.get_avatar_url(avatar_file_key), "expires_in": settings.cos_avatar_url_expire_seconds, } avatar_url = parent.get("avatar_url") if avatar_url: return { "avatar_url": avatar_url, "expires_in": None, } return None def _present_parent(self, parent: Optional[Mapping]) -> Optional[dict]: if not parent: return None data = dict(parent) avatar_file_key = data.get("avatar_file_key") if avatar_file_key: data["avatar_url"] = self.avatar_storage.get_avatar_url(avatar_file_key) return data