315 lines
13 KiB
Python
315 lines
13 KiB
Python
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')}' деактивирован"
|
||
}
|