diff --git a/services/gacha_log_rank/__init__.py b/services/gacha_log_rank/__init__.py new file mode 100644 index 0000000..6e5da71 --- /dev/null +++ b/services/gacha_log_rank/__init__.py @@ -0,0 +1 @@ +"""Gacha Log Rank Service""" diff --git a/services/gacha_log_rank/cache.py b/services/gacha_log_rank/cache.py new file mode 100644 index 0000000..fcd948b --- /dev/null +++ b/services/gacha_log_rank/cache.py @@ -0,0 +1,37 @@ +from typing import TYPE_CHECKING + +from gram_core.base_service import BaseService +from gram_core.dependence.redisdb import RedisDB + +__all__ = ("GachaLogRankCache",) + +if TYPE_CHECKING: + from gram_core.services.gacha_log_rank.models import GachaLogQueryTypeEnum + + +class GachaLogRankCache(BaseService.Component): + def __init__(self, redis: RedisDB): + self.client = redis.client + self.qname = "gacha_log_ranks:" + + def get_key(self, rank_type: "GachaLogQueryTypeEnum") -> str: + return f"{self.qname}:{rank_type.value}" + + async def remove_all(self, rank_type: "GachaLogQueryTypeEnum") -> bool: + key = self.get_key(rank_type) + return await self.client.delete(key) + + async def add(self, rank_type: "GachaLogQueryTypeEnum", player_id: int, score: int) -> bool: + key = self.get_key(rank_type) + return await self.client.zadd(key, {player_id: score}) + + async def get_ranks(self, rank_type: "GachaLogQueryTypeEnum", limit: int, desc: bool): + """获取排行榜,默认由高到低排序""" + key = self.get_key(rank_type) + return await self.client.zrange(key, 0, limit - 1, withscores=True, desc=desc) + + async def get_rank_by_player_id(self, rank_type: "GachaLogQueryTypeEnum", player_id: int, desc: bool): + """获取玩家在排行榜中的排名,默认由高到低排序""" + key = self.get_key(rank_type) + func = self.client.zrevrank if desc else self.client.zrank + return await func(key, player_id) diff --git a/services/gacha_log_rank/models.py b/services/gacha_log_rank/models.py new file mode 100644 index 0000000..132f016 --- /dev/null +++ b/services/gacha_log_rank/models.py @@ -0,0 +1,46 @@ +import enum +from datetime import datetime +from typing import Optional, Dict, Any + +from sqlalchemy import func, BigInteger, JSON +from sqlmodel import Column, DateTime, Enum, Field, SQLModel, Integer + +__all__ = ("GachaLogRank", "GachaLogTypeEnum", "GachaLogQueryTypeEnum") + + +class GachaLogTypeEnum(int, enum.Enum): + CHARACTER = 0 # 角色 + WEAPON = 1 # 武器 + DEFAULT = 2 # 常驻 + DEFAULT_WEAPON = 3 # 常驻武器 + HUN = 4 # 集录 + PET = 5 # 邦布 + + +class GachaLogQueryTypeEnum(str, enum.Enum): + TOTAL = "score_1" + FIVE_STAR_AVG = "score_2" + UP_STAR_AVG = "score_3" + NO_WARP = "score_4" + + +class GachaLogRank(SQLModel, table=True): + __table_args__ = dict(mysql_charset="utf8mb4", mysql_collate="utf8mb4_general_ci") + id: Optional[int] = Field(default=None, sa_column=Column(Integer(), autoincrement=True)) + player_id: int = Field(sa_column=Column(BigInteger(), primary_key=True)) + type: GachaLogTypeEnum = Field(sa_column=Column(Enum(GachaLogTypeEnum), primary_key=True)) + score_1: int = Field(sa_column=Column(BigInteger(), default=0)) + """总抽数""" + score_2: int = Field(sa_column=Column(BigInteger(), default=0)) + """五星平均""" + score_3: int = Field(sa_column=Column(BigInteger(), default=0)) + """up 平均""" + score_4: int = Field(sa_column=Column(BigInteger(), default=0)) + """小保底不歪概率""" + score_5: int = Field(sa_column=Column(BigInteger(), default=0)) + """保留字段""" + data: Optional[Dict[str, Any]] = Field(sa_column=Column(JSON)) + time_created: Optional[datetime] = Field( + sa_column=Column(DateTime, server_default=func.now()) # pylint: disable=E1102 + ) + time_updated: Optional[datetime] = Field(sa_column=Column(DateTime, onupdate=func.now())) # pylint: disable=E1102 diff --git a/services/gacha_log_rank/repositories.py b/services/gacha_log_rank/repositories.py new file mode 100644 index 0000000..0014e56 --- /dev/null +++ b/services/gacha_log_rank/repositories.py @@ -0,0 +1,56 @@ +from typing import List, Optional + +from sqlmodel import col, select +from sqlmodel.ext.asyncio.session import AsyncSession + +from gram_core.base_service import BaseService +from gram_core.dependence.database import Database +from gram_core.services.gacha_log_rank.models import GachaLogRank, GachaLogQueryTypeEnum + +__all__ = ("GachaLogRankRepository",) + + +class GachaLogRankRepository(BaseService.Component): + def __init__(self, database: Database): + self.engine = database.engine + + async def add(self, rank: GachaLogRank): + async with AsyncSession(self.engine) as session: + session.add(rank) + await session.commit() + + async def update(self, rank: GachaLogRank) -> GachaLogRank: + async with AsyncSession(self.engine) as session: + session.add(rank) + await session.commit() + await session.refresh(rank) + return rank + + async def remove(self, rank: GachaLogRank): + async with AsyncSession(self.engine) as session: + await session.delete(rank) + await session.commit() + + async def get_all(self) -> List[GachaLogRank]: + async with AsyncSession(self.engine) as session: + statement = select(GachaLogRank) + results = await session.exec(statement) + return results.all() + + async def get_all_by_player_ids(self, rank_type: GachaLogQueryTypeEnum, ids: List[int]) -> List[GachaLogRank]: + async with AsyncSession(self.engine) as session: + statement = ( + select(GachaLogRank).where(col(GachaLogRank.player_id).in_(ids)).where(GachaLogRank.type == rank_type) + ) + results = await session.exec(statement) + return results.all() + + async def get_by_player_id( + self, player_id: int, rank_type: Optional[GachaLogQueryTypeEnum] + ) -> Optional[GachaLogRank]: + async with AsyncSession(self.engine) as session: + statement = select(GachaLogRank).where(GachaLogRank.player_id == player_id) + if rank_type: + statement = statement.where(GachaLogRank.type == rank_type) + results = await session.exec(statement) + return results.first() diff --git a/services/gacha_log_rank/services.py b/services/gacha_log_rank/services.py new file mode 100644 index 0000000..05ebab9 --- /dev/null +++ b/services/gacha_log_rank/services.py @@ -0,0 +1,56 @@ +from typing import List, Optional + +from gram_core.base_service import BaseService +from gram_core.services.gacha_log_rank.cache import GachaLogRankCache +from gram_core.services.gacha_log_rank.models import GachaLogRank, GachaLogQueryTypeEnum +from gram_core.services.gacha_log_rank.repositories import GachaLogRankRepository + +__all__ = ("GachaLogRankService",) + + +class GachaLogRankService(BaseService): + def __init__( + self, + gacha_log_rank_repository: GachaLogRankRepository, + gacha_log_rank_cache: GachaLogRankCache, + ): + self._repository = gacha_log_rank_repository + self._cache = gacha_log_rank_cache + + async def del_all_cache_by_type(self, rank_type: "GachaLogQueryTypeEnum") -> bool: + return await self._cache.remove_all(rank_type) + + async def add_cache(self, rank_type: "GachaLogQueryTypeEnum", player_id: int, score: int) -> bool: + return await self._cache.add(rank_type, player_id, score) + + async def get_ranks_cache(self, rank_type: "GachaLogQueryTypeEnum", limit: int = 20, desc: bool = True): + """获取排行榜,默认由高到低排序""" + return await self._cache.get_ranks(rank_type, limit, desc) + + async def get_rank_by_player_id_cache(self, rank_type: "GachaLogQueryTypeEnum", player_id: int, desc: bool = True): + """获取玩家在排行榜中的排名,默认由高到低排序""" + return await self._cache.get_rank_by_player_id(rank_type, player_id, desc) + + async def update_cache(self, rank: GachaLogRank): + for type_str in GachaLogQueryTypeEnum: + type_str: "GachaLogQueryTypeEnum" + score = getattr(rank, type_str.value) + await self.add_cache(type_str, rank.player_id, score) + + async def add(self, rank: GachaLogRank): + req = await self._repository.add(rank) + await self.update_cache(rank) + return req + + async def update(self, rank: GachaLogRank) -> GachaLogRank: + req = await self._repository.update(rank) + await self.update_cache(rank) + return req + + async def get_ranks_by_ids(self, rank_type: "GachaLogQueryTypeEnum", ranks_ids: List[int]) -> List[GachaLogRank]: + return await self._repository.get_all_by_player_ids(rank_type, ranks_ids) + + async def get_rank_by_user_id( + self, user_id: int, rank_type: Optional["GachaLogQueryTypeEnum"] + ) -> Optional[GachaLogRank]: + return await self._repository.get_by_player_id(user_id, rank_type)