From a0808d29fa95d259aefda99d94b547a4ed328e71 Mon Sep 17 00:00:00 2001 From: DIKER0K Date: Sun, 7 Dec 2025 17:26:43 +0500 Subject: [PATCH] add img to bonus and endpoint toogle activation bonus --- app/api/bonuses.py | 9 +++++++++ app/models/bonus.py | 3 +++ app/services/bonus.py | 47 +++++++++++++++++++++++++++++++++++++++++-- app/services/coins.py | 22 ++++++++++++++++++++ 4 files changed, 79 insertions(+), 2 deletions(-) diff --git a/app/api/bonuses.py b/app/api/bonuses.py index dbd909f..f4bf502 100644 --- a/app/api/bonuses.py +++ b/app/api/bonuses.py @@ -45,3 +45,12 @@ async def upgrade_user_bonus(username: str = Body(...), bonus_id: str = Body(... from app.services.bonus import BonusService return await BonusService().upgrade_bonus(username, bonus_id) +@router.post("/toggle-activation") +async def toggle_bonus_activation(username: str = Body(...), bonus_id: str = Body(...)): + """ + Переключить активность бонуса пользователя. + Передаём username и bonus_id, is_active переключается на противоположное значение. + """ + from app.services.bonus import BonusService + return await BonusService().toggle_bonus_activation(username, bonus_id) + diff --git a/app/models/bonus.py b/app/models/bonus.py index e44a9f0..cfa9cad 100644 --- a/app/models/bonus.py +++ b/app/models/bonus.py @@ -12,6 +12,7 @@ class CreateBonusType(BaseModel): upgrade_price: int duration: int # в секундах max_level: int = 0 + image_url: Optional[str] = None class PurchaseBonus(BaseModel): username: str @@ -33,6 +34,7 @@ class BonusType(BaseModel): upgrade_price: int # Цена улучшения за уровень duration: int # Длительность в секундах (0 для бесконечных) max_level: int = 0 # 0 = без ограничения уровней + image_url: Optional[str] = None class UserTypeBonus(BaseModel): id: str @@ -47,6 +49,7 @@ class UserTypeBonus(BaseModel): expires_at: Optional[datetime] = None is_active: bool = True is_permanent: bool + image_url: Optional[str] = None class UserBonus(BaseModel): id: str diff --git a/app/services/bonus.py b/app/services/bonus.py index efd5715..ff58340 100644 --- a/app/services/bonus.py +++ b/app/services/bonus.py @@ -25,7 +25,8 @@ class BonusService: "price": bonus_data.price, "upgrade_price": bonus_data.upgrade_price, "duration": bonus_data.duration, - "max_level": bonus_data.max_level + "max_level": bonus_data.max_level, + "image_url": bonus_data.image_url, } # Проверка на дубликат имени @@ -112,7 +113,8 @@ class BonusService: "level": level, "purchased_at": bonus["purchased_at"].isoformat(), "can_upgrade": bonus_type["max_level"] == 0 or level < bonus_type["max_level"], - "upgrade_price": bonus_type["upgrade_price"] + "upgrade_price": bonus_type["upgrade_price"], + "image_url": bonus_type.get("image_url"), } # Для временных бонусов добавляем срок @@ -125,6 +127,47 @@ class BonusService: result.append(bonus_data) return {"bonuses": result} + + async def toggle_bonus_activation(self, username: str, bonus_id: str): + """Переключить активность бонуса у пользователя""" + from app.db.database import users_collection + + # Находим пользователя + user = await users_collection.find_one({"username": username}) + if not user: + raise HTTPException(status_code=404, detail="Пользователь не найден") + + # Находим бонус пользователя + user_bonus = await user_bonuses_collection.find_one({ + "id": bonus_id, + "user_id": str(user["_id"]), + }) + + if not user_bonus: + raise HTTPException(status_code=404, detail="Бонус не найден или не принадлежит вам") + + # Проверяем, не истек ли бонус при попытке включить + if user_bonus.get("expires_at") and user_bonus["expires_at"] < datetime.utcnow(): + # На всякий случай зафиксируем в БД, что он не активен + await user_bonuses_collection.update_one( + {"id": bonus_id}, + {"$set": {"is_active": False}} + ) + raise HTTPException(status_code=400, detail="Срок действия бонуса истёк и он не может быть активирован") + + new_status = not user_bonus.get("is_active", False) + + await user_bonuses_collection.update_one( + {"id": bonus_id}, + {"$set": {"is_active": new_status}} + ) + + return { + "status": "success", + "message": "Активность бонуса переключена", + "bonus_id": bonus_id, + "is_active": new_status, + } async def purchase_bonus(self, username: str, bonus_type_id: str): """Покупка базового бонуса пользователем""" diff --git a/app/services/coins.py b/app/services/coins.py index 70e22f6..4a6ee57 100644 --- a/app/services/coins.py +++ b/app/services/coins.py @@ -46,6 +46,28 @@ class CoinsService: # Первое обновление (ограничиваем для безопасности) minutes_to_reward = min(online_time // 60, 5) + # НА ВСЯКИЙ ЕСЛИ ПОПЫ ВСЕ-РАВНО НЕПРАВИЛЬНО НАЧИСЛЯЮТСЯ + + # if last_update: + # last_timestamp = last_update["timestamp"] + # seconds_since_update = int((now - last_timestamp).total_seconds()) + + # # Берём минимум: сколько прошло по часам и сколько игрок реально онлайн + # seconds_for_reward = min(seconds_since_update, online_time) + + # # Начисляем только за полные минуты + # minutes_to_reward = seconds_for_reward // 60 + + # if minutes_to_reward < 1: + # return + + # minutes_to_reward = min(minutes_to_reward, MAX_MINUTES_PER_UPDATE) + # else: + # # Первое обновление — считаем только от online_time, с лимитом для безопасности + # minutes_to_reward = min(online_time // 60, 5) + # if minutes_to_reward < 1: + # return + if minutes_to_reward > 0: # Обновляем монеты и время new_coins = current_coins + minutes_to_reward