from collections.abc import Mapping from typing import Optional from sqlalchemy import text from sqlalchemy.exc import IntegrityError from app.dao import BaseDAO from app.db_compat import inserted_primary_key class ParentDAO(BaseDAO): def create( self, openid: str, unionid: Optional[str] = None, nickname: Optional[str] = None, avatar_url: Optional[str] = None, ) -> int: result = self.db.execute( text( """ INSERT INTO parents (openid, unionid, nickname, avatar_url, status) VALUES (:openid, :unionid, :nickname, :avatar_url, 1) """ ), {"openid": openid, "unionid": unionid, "nickname": nickname, "avatar_url": avatar_url}, ) user_id = inserted_primary_key(result) self.commit() return user_id def get_by_id(self, user_id: int) -> Optional[Mapping]: return ( self.db.execute( text("SELECT * FROM parents WHERE user_id = :user_id"), {"user_id": user_id}, ) .mappings() .first() ) def get_by_openid(self, openid: str) -> Optional[Mapping]: return ( self.db.execute( text("SELECT * FROM parents WHERE openid = :openid"), {"openid": openid}, ) .mappings() .first() ) def update( self, user_id: int, nickname: Optional[str] = None, avatar_url: Optional[str] = None, phone: Optional[str] = None, ) -> None: self.db.execute( text( """ UPDATE parents SET nickname = COALESCE(:nickname, nickname), avatar_url = COALESCE(:avatar_url, avatar_url), phone = COALESCE(:phone, phone) WHERE user_id = :user_id """ ), {"user_id": user_id, "nickname": nickname, "avatar_url": avatar_url, "phone": phone}, ) self.commit() def set_avatar_file_key(self, user_id: int, avatar_file_key: str) -> None: self.db.execute( text( """ UPDATE parents SET avatar_file_key = :avatar_file_key, avatar_url = NULL WHERE user_id = :user_id """ ), {"user_id": user_id, "avatar_file_key": avatar_file_key}, ) self.commit() def update_from_wechat_login( self, user_id: int, unionid: Optional[str] = None, nickname: Optional[str] = None, avatar_url: Optional[str] = None, ) -> None: self.db.execute( text( """ UPDATE parents SET unionid = CASE WHEN unionid IS NULL AND :unionid IS NOT NULL THEN :unionid ELSE unionid END, nickname = COALESCE(:nickname, nickname), avatar_url = COALESCE(:avatar_url, avatar_url) WHERE user_id = :user_id """ ), { "user_id": user_id, "unionid": unionid, "nickname": nickname, "avatar_url": avatar_url, }, ) self.commit() def upsert( self, openid: str, unionid: Optional[str] = None, nickname: Optional[str] = None, avatar_url: Optional[str] = None, ) -> int: existing = self.get_by_openid(openid) if existing: self.update_from_wechat_login(existing["user_id"], unionid, nickname, avatar_url) return int(existing["user_id"]) try: return self.create(openid, unionid, nickname, avatar_url) except IntegrityError: self.db.rollback() existing = self.get_by_openid(openid) if not existing: raise if unionid or nickname or avatar_url: self.update_from_wechat_login(existing["user_id"], unionid, nickname, avatar_url) return int(existing["user_id"])