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..68fe156 --- /dev/null +++ b/services/gacha_log_rank/cache.py @@ -0,0 +1,48 @@ +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 GachaLogTypeEnum, 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: "GachaLogTypeEnum", query_type: "GachaLogQueryTypeEnum") -> str: + return f"{self.qname}:{rank_type.value}:{query_type.value}" + + async def remove_all(self, rank_type: "GachaLogTypeEnum", query_type: "GachaLogQueryTypeEnum") -> bool: + key = self.get_key(rank_type, query_type) + return await self.client.delete(key) + + async def add( + self, rank_type: "GachaLogTypeEnum", query_type: "GachaLogQueryTypeEnum", player_id: int, score: int + ) -> bool: + key = self.get_key(rank_type, query_type) + return await self.client.zadd(key, {player_id: score}) + + async def get_ranks( + self, rank_type: "GachaLogTypeEnum", query_type: "GachaLogQueryTypeEnum", limit: int, desc: bool + ): + """获取排行榜,默认由高到低排序""" + key = self.get_key(rank_type, query_type) + return await self.client.zrange(key, 0, limit - 1, withscores=True, desc=desc) + + async def get_ranks_length(self, rank_type: "GachaLogTypeEnum", query_type: "GachaLogQueryTypeEnum"): + """获取排行榜长度""" + key = self.get_key(rank_type, query_type) + return await self.client.zcard(key) + + async def get_rank_by_player_id( + self, rank_type: "GachaLogTypeEnum", query_type: "GachaLogQueryTypeEnum", player_id: int, desc: bool + ): + """获取玩家在排行榜中的排名,默认由高到低排序""" + key = self.get_key(rank_type, query_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..e81347b --- /dev/null +++ b/services/gacha_log_rank/models.py @@ -0,0 +1,56 @@ +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): + __tablename__ = "gacha_log_rank" + __table_args__ = dict(mysql_charset="utf8mb4", mysql_collate="utf8mb4_general_ci") + id: Optional[int] = Field(default=None, sa_column=Column(Integer(), primary_key=True, 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 + + def update_by_new(self, new_ins: "GachaLogRank"): + self.score_1 = new_ins.score_1 + self.score_2 = new_ins.score_2 + self.score_3 = new_ins.score_3 + self.score_4 = new_ins.score_4 + self.score_5 = new_ins.score_5 + self.data = new_ins.data + self.time_updated = datetime.now() diff --git a/services/gacha_log_rank/repositories.py b/services/gacha_log_rank/repositories.py new file mode 100644 index 0000000..46c9545 --- /dev/null +++ b/services/gacha_log_rank/repositories.py @@ -0,0 +1,54 @@ +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, GachaLogTypeEnum, 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: "GachaLogTypeEnum", 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[GachaLogTypeEnum]) -> List[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.all() diff --git a/services/gacha_log_rank/services.py b/services/gacha_log_rank/services.py new file mode 100644 index 0000000..8ab2e9a --- /dev/null +++ b/services/gacha_log_rank/services.py @@ -0,0 +1,67 @@ +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, GachaLogTypeEnum, 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: "GachaLogTypeEnum", query_type: "GachaLogQueryTypeEnum") -> bool: + return await self._cache.remove_all(rank_type, query_type) + + async def add_cache( + self, rank_type: "GachaLogTypeEnum", query_type: "GachaLogQueryTypeEnum", player_id: int, score: int + ) -> bool: + return await self._cache.add(rank_type, query_type, player_id, score) + + async def get_ranks_cache( + self, rank_type: "GachaLogTypeEnum", query_type: "GachaLogQueryTypeEnum", limit: int = 20, desc: bool = True + ): + """获取排行榜,默认由高到低排序""" + return await self._cache.get_ranks(rank_type, query_type, limit, desc) + + async def get_ranks_length_cache(self, rank_type: "GachaLogTypeEnum", query_type: "GachaLogQueryTypeEnum"): + """获取排行榜长度""" + return await self._cache.get_ranks_length(rank_type, query_type) + + async def get_rank_by_player_id_cache( + self, rank_type: "GachaLogTypeEnum", query_type: "GachaLogQueryTypeEnum", player_id: int, desc: bool = True + ): + """获取玩家在排行榜中的排名,默认由高到低排序""" + return await self._cache.get_rank_by_player_id(rank_type, query_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) + if score: + await self.add_cache(rank.type, type_str, rank.player_id, score) + + async def add(self, rank: GachaLogRank): + await self.update_cache(rank) + req = await self._repository.add(rank) + return req + + async def update(self, rank: GachaLogRank) -> GachaLogRank: + await self.update_cache(rank) + req = await self._repository.update(rank) + return req + + async def get_ranks_by_ids(self, rank_type: "GachaLogTypeEnum", 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["GachaLogTypeEnum"] = None + ) -> List[GachaLogRank]: + return await self._repository.get_by_player_id(user_id, rank_type)