from fastapi import HTTPException, UploadFile from app.db.database import users_collection from app.core.config import FILES_URL from datetime import datetime import uuid from pathlib import Path import os import shutil from app.models.cape import CapeStore, CapeStoreUpdate # Создаем коллекцию для плащей в БД from app.db.database import db store_capes_collection = db.store_capes class StoreCapeService: async def add_cape(self, name: str, description: str, price: int, cape_file: UploadFile): """Добавление нового плаща в магазин""" # Проверка типа файла if not cape_file.content_type.startswith('image/'): raise HTTPException(status_code=400, detail="Файл должен быть изображением") # Определяем расширение ext = None if cape_file.content_type == "image/png": ext = "png" elif cape_file.content_type == "image/gif": ext = "gif" else: raise HTTPException(status_code=400, detail="Поддерживаются только PNG и GIF плащи") # Проверка размера файла (максимум 2MB) max_size = 2 * 1024 * 1024 # 2MB contents = await cape_file.read() if len(contents) > max_size: raise HTTPException(status_code=400, detail="Файл слишком большой (максимум 2MB)") # Создаем папку для плащей магазина, если ее нет cape_dir = Path("/app/static/capes_store") cape_dir.mkdir(parents=True, exist_ok=True) # Генерируем ID и имя файла cape_id = str(uuid.uuid4()) cape_filename = f"store_cape_{cape_id}.{ext}" cape_path = cape_dir / cape_filename # Сохраняем файл with open(cape_path, "wb") as f: f.write(contents) # Создаем запись в БД cape_data = { "id": cape_id, "name": name, "description": description, "price": price, "file_name": cape_filename, "created_at": datetime.utcnow() } await store_capes_collection.insert_one(cape_data) return {"id": cape_id, "status": "success"} async def get_all_capes(self): """Получение всех плащей из магазина""" capes = await store_capes_collection.find().to_list(1000) result = [] for cape in capes: result.append({ "id": cape["id"], "name": cape["name"], "description": cape["description"], "price": cape["price"], "image_url": f"{FILES_URL}/capes_store/{cape['file_name']}" }) return result async def get_cape_by_id(self, cape_id: str): """Получение плаща по ID""" cape = await store_capes_collection.find_one({"id": cape_id}) if not cape: raise HTTPException(status_code=404, detail="Плащ не найден") return { "id": cape["id"], "name": cape["name"], "description": cape["description"], "price": cape["price"], "image_url": f"{FILES_URL}/capes_store/{cape['file_name']}" } async def update_cape(self, cape_id: str, update_data: CapeStoreUpdate): """Обновление информации о плаще""" cape = await store_capes_collection.find_one({"id": cape_id}) if not cape: raise HTTPException(status_code=404, detail="Плащ не найден") # Готовим данные для обновления update = {} if update_data.name: update["name"] = update_data.name if update_data.description: update["description"] = update_data.description if update_data.price is not None: update["price"] = update_data.price if update: result = await store_capes_collection.update_one( {"id": cape_id}, {"$set": update} ) if result.modified_count == 0: raise HTTPException(status_code=500, detail="Ошибка при обновлении") return {"status": "success"} async def delete_cape(self, cape_id: str): """Удаление плаща из магазина""" cape = await store_capes_collection.find_one({"id": cape_id}) if not cape: raise HTTPException(status_code=404, detail="Плащ не найден") # Удаляем файл cape_path = Path(f"/app/static/capes_store/{cape['file_name']}") if cape_path.exists(): try: cape_path.unlink() except Exception as e: print(f"Ошибка при удалении файла: {e}") # Удаляем из БД плащей магазина result = await store_capes_collection.delete_one({"id": cape_id}) if result.deleted_count == 0: raise HTTPException(status_code=500, detail="Ошибка при удалении из БД") # Удаляем из БД купленных плащей purchases_collection = db.purchases purchases = await purchases_collection.find_one({"cape_id": cape_id}) if purchases: await purchases_collection.delete_one({"cape_id": cape_id}) # Удаляем плащ из массива purchased_capes всех пользователей users_collection = db.users await users_collection.update_many( {"purchased_capes.cape_id": cape_id}, {"$pull": {"purchased_capes": {"cape_id": cape_id}}} ) return {"status": "success"} async def purchase_cape(self, username: str, cape_id: str): """Покупка плаща пользователем""" # Находим пользователя user = await users_collection.find_one({"username": username}) if not user: raise HTTPException(status_code=404, detail="Пользователь не найден") # Находим плащ cape = await store_capes_collection.find_one({"id": cape_id}) if not cape: raise HTTPException(status_code=404, detail="Плащ не найден") # Проверяем достаточно ли монет user_coins = user.get("coins", 0) if user_coins < cape["price"]: raise HTTPException(status_code=400, detail=f"Недостаточно монет. Требуется: {cape['price']}, имеется: {user_coins}") # Копируем плащ из хранилища магазина в персональную папку пользователя cape_store_path = Path(f"/app/static/capes_store/{cape['file_name']}") # Создаем папку для плащей пользователя cape_dir = Path("/app/static/capes") cape_dir.mkdir(parents=True, exist_ok=True) # Генерируем имя файла для персонального плаща filename_parts = cape['file_name'].split('.') ext = filename_parts[-1] cape_filename = f"{username}_{int(datetime.now().timestamp())}.{ext}" cape_path = cape_dir / cape_filename # Копируем файл try: shutil.copy(cape_store_path, cape_path) except Exception as e: raise HTTPException(status_code=500, detail=f"Ошибка при копировании файла: {e}") # Обновляем данные пользователя # 1. Списываем монеты # 2. Устанавливаем новый плащ # 3. Добавляем плащ в список приобретенных result = await users_collection.update_one( {"username": username}, {"$set": { "coins": user_coins - cape["price"], "cloak_url": f"{FILES_URL}/capes/{cape_filename}" }, "$push": { "purchased_capes": { "cape_id": cape_id, "cape_name": cape["name"], "cape_description": cape["description"], "file_name": cape_filename, "purchased_at": datetime.utcnow() } }} ) if result.modified_count == 0: # Если обновление не удалось, удаляем файл плаща if os.path.exists(cape_path): os.remove(cape_path) raise HTTPException(status_code=500, detail="Ошибка при обновлении данных пользователя") # Логируем покупку в БД purchase_data = { "username": username, "user_id": user["_id"], "cape_id": cape_id, "cape_name": cape["name"], "price": cape["price"], "purchase_date": datetime.utcnow() } from app.db.database import db await db.purchases.insert_one(purchase_data) return { "status": "success", "message": f"Плащ '{cape['name']}' успешно приобретен", "remaining_coins": user_coins - cape["price"] } async def get_user_purchased_capes(self, username: str): """Получение всех плащей, приобретенных пользователем""" user = await users_collection.find_one({"username": username}) if not user: raise HTTPException(status_code=404, detail="Пользователь не найден") purchased_capes = user.get("purchased_capes", []) result = [] for cape in purchased_capes: result.append({ "cape_id": cape.get("cape_id"), "cape_name": cape.get("cape_name"), "cape_description": cape.get("cape_description"), "image_url": f"{FILES_URL}/capes/{cape.get('file_name')}", "purchased_at": cape.get("purchased_at"), "is_active": user.get("cloak_url") == f"{FILES_URL}/capes/{cape.get('file_name')}" }) return result async def activate_purchased_cape(self, username: str, cape_id: str): """Активация приобретенного плаща""" user = await users_collection.find_one({"username": username}) if not user: raise HTTPException(status_code=404, detail="Пользователь не найден") # Проверяем, что плащ был приобретен purchased_capes = user.get("purchased_capes", []) selected_cape = None for cape in purchased_capes: if cape.get("cape_id") == cape_id: selected_cape = cape break if not selected_cape: raise HTTPException(status_code=404, detail="Плащ не найден среди приобретенных") # Устанавливаем выбранный плащ await users_collection.update_one( {"username": username}, {"$set": {"cloak_url": f"{FILES_URL}/capes/{selected_cape.get('file_name')}"}} ) return { "status": "success", "message": f"Плащ '{selected_cape.get('cape_name')}' активирован" } async def deactivate_purchased_cape(self, username: str, cape_id: str): """Деактивация приобретенного плаща""" user = await users_collection.find_one({"username": username}) if not user: raise HTTPException(status_code=404, detail="Пользователь не найден") # Проверяем, что плащ был приобретен purchased_capes = user.get("purchased_capes", []) selected_cape = None for cape in purchased_capes: if cape.get("cape_id") == cape_id: selected_cape = cape break if not selected_cape: raise HTTPException(status_code=404, detail="Плащ не найден среди приобретенных") # Устанавливаем выбранный плащ await users_collection.update_one( {"username": username}, {"$set": {"cloak_url": None}} ) return { "status": "success", "message": f"Плащ '{selected_cape.get('cape_name')}' деактивирован" }