add: store cape

This commit is contained in:
2025-07-18 03:39:21 +05:00
parent ff65e4a333
commit d52d4dbf75
5 changed files with 350 additions and 2 deletions

270
app/services/store_cape.py Normal file
View File

@ -0,0 +1,270 @@
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="Ошибка при удалении из БД")
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"],
"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"),
"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')}' активирован"
}