Files
popa_minecraft_launcher_api/app/services/case.py
2025-12-16 00:15:29 +05:00

210 lines
7.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import uuid
import random
from datetime import datetime
from fastapi import HTTPException
from app.db.database import db, users_collection
from app.services.coins import CoinsService
from app.models.case import CaseCreate, CaseUpdate
cases_collection = db.cases
case_openings_collection = db.case_openings
game_servers_collection = db.game_servers
marketplace_operations = db.marketplace_operations
player_inventory_collection = db.player_inventory
class CaseService:
def __init__(self):
self.coins_service = CoinsService()
async def create_case(self, case_data: CaseCreate):
case_id = str(uuid.uuid4())
# Генерим id для предметов, если не заданы
items = []
for item in case_data.items:
item_dict = item.dict()
if not item_dict.get("id"):
item_dict["id"] = str(uuid.uuid4())
items.append(item_dict)
doc = {
"id": case_id,
"name": case_data.name,
"description": case_data.description,
"price": case_data.price,
"server_ips": case_data.server_ips or ["*"],
"image_url": case_data.image_url, # 🔹 сохраняем картинку
"items": items,
"created_at": datetime.utcnow()
}
await cases_collection.insert_one(doc)
return {"status": "success", "id": case_id}
async def list_cases(self):
cases = await cases_collection.find().to_list(1000)
return [
{
"id": c["id"],
"name": c["name"],
"description": c.get("description"),
"price": c["price"],
"server_ips": c.get("server_ips", ["*"]),
"image_url": c.get("image_url"), # 🔹 отдаем картинку
"items_count": len(c.get("items", []))
}
for c in cases
]
async def get_case(self, case_id: str):
# проекция {"_id": 0} говорит Mongo не возвращать поле _id
case = await cases_collection.find_one({"id": case_id}, {"_id": 0})
if not case:
raise HTTPException(status_code=404, detail="Кейс не найден")
return case
async def update_case(self, case_id: str, data: CaseUpdate):
case = await cases_collection.find_one({"id": case_id})
if not case:
raise HTTPException(status_code=404, detail="Кейс не найден")
update = {}
if data.name is not None:
update["name"] = data.name
if data.description is not None:
update["description"] = data.description
if data.price is not None:
update["price"] = data.price
if data.server_ips is not None:
update["server_ips"] = data.server_ips
if data.image_url is not None:
update["image_url"] = data.image_url # 🔹 обновляем картинку
if data.items is not None:
items = []
for item in data.items:
item_dict = item.dict()
if not item_dict.get("id"):
item_dict["id"] = str(uuid.uuid4())
items.append(item_dict)
update["items"] = items
if update:
await cases_collection.update_one({"id": case_id}, {"$set": update})
return {"status": "success"}
async def delete_case(self, case_id: str):
result = await cases_collection.delete_one({"id": case_id})
if result.deleted_count == 0:
raise HTTPException(status_code=404, detail="Кейс не найден")
return {"status": "success"}
async def open_case(self, username: str, case_id: str, server_ip: str):
# 1. Пользователь
user = await users_collection.find_one({"username": username})
if not user:
raise HTTPException(status_code=404, detail="Пользователь не найден")
# 2. Кейс
case = await cases_collection.find_one({"id": case_id})
if not case:
raise HTTPException(status_code=404, detail="Кейс не найден")
items = case.get("items", [])
if not items:
raise HTTPException(status_code=400, detail="В кейсе нет предметов")
allowed = case.get("server_ips") or ["*"]
if "*" not in allowed and server_ip not in allowed:
raise HTTPException(status_code=403, detail="Этот кейс не может быть открыт на этом сервере")
# 3. Сервер
server = await game_servers_collection.find_one({"ip": server_ip})
if not server:
raise HTTPException(status_code=404, detail="Сервер не найден")
# 4. Проверяем баланс
user_balance = await self.coins_service.get_balance(username)
price = case["price"]
if user_balance < price:
raise HTTPException(
status_code=400,
detail=f"Недостаточно монет. Требуется: {price}, имеется: {user_balance}"
)
# 5. Выбираем предмет по весам (шансам)
weights = [max(0, i.get("weight", 1)) for i in items]
total_weight = sum(weights)
if total_weight <= 0:
raise HTTPException(status_code=500, detail="Неверно настроены шансы предметов (вес ≤ 0)")
rnd = random.uniform(0, total_weight)
current = 0
chosen_item = None
for item, w in zip(items, weights):
current += w
if rnd <= current:
chosen_item = item
break
if not chosen_item:
# на всякий случай, но по логике не должно случиться
chosen_item = items[-1]
# 6. Списываем монеты
await self.coins_service.decrease_balance(username, price)
# 7. Создаём операцию для сервера — как в Marketplace, только другой type :contentReference[oaicite:3]{index=3}
operation_id = str(uuid.uuid4())
item_data = {
"material": chosen_item["material"],
"amount": chosen_item.get("amount", 1),
"meta": (chosen_item.get("meta") or {})
}
inventory_item = {
"id": str(uuid.uuid4()),
"username": username,
"server_ip": server_ip,
"item_data": item_data,
"source": {
"type": "case",
"case_id": case_id,
"case_name": case.get("name"),
},
"status": "stored",
"created_at": datetime.utcnow(),
"delivered_at": None,
"withdraw_operation_id": None,
}
await player_inventory_collection.insert_one(inventory_item)
# 8. Лог открытия кейса
opening_log = {
"id": str(uuid.uuid4()),
"username": username,
"user_id": user.get("_id"),
"case_id": case_id,
"case_name": case["name"],
"server_ip": server_ip,
"reward_item": chosen_item,
"price": price,
"opened_at": datetime.utcnow()
}
await case_openings_collection.insert_one(opening_log)
# 9. Можно вернуть новый баланс
new_balance = await self.coins_service.get_balance(username)
return {
"status": "success",
"message": f"Кейс '{case['name']}' открыт",
"reward": chosen_item,
"inventory_item_id": inventory_item["id"],
"balance": new_balance
}