test player_inventory

This commit is contained in:
2025-12-16 00:15:29 +05:00
parent 80a9fbe148
commit bb74dbbba7
8 changed files with 204 additions and 43 deletions

View File

@ -2,7 +2,6 @@ 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
@ -10,7 +9,8 @@ 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 # уже есть в Marketplace.py :contentReference[oaicite:1]{index=1}
marketplace_operations = db.marketplace_operations
player_inventory_collection = db.player_inventory
class CaseService:
@ -33,7 +33,7 @@ class CaseService:
"name": case_data.name,
"description": case_data.description,
"price": case_data.price,
"server_ids": case_data.server_ids or ["*"],
"server_ips": case_data.server_ips or ["*"],
"image_url": case_data.image_url, # 🔹 сохраняем картинку
"items": items,
"created_at": datetime.utcnow()
@ -50,7 +50,7 @@ class CaseService:
"name": c["name"],
"description": c.get("description"),
"price": c["price"],
"server_ids": c.get("server_ids", ["*"]),
"server_ips": c.get("server_ips", ["*"]),
"image_url": c.get("image_url"), # 🔹 отдаем картинку
"items_count": len(c.get("items", []))
}
@ -76,8 +76,8 @@ class CaseService:
update["description"] = data.description
if data.price is not None:
update["price"] = data.price
if data.server_ids is not None:
update["server_ids"] = data.server_ids
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:
@ -100,7 +100,7 @@ class CaseService:
raise HTTPException(status_code=404, detail="Кейс не найден")
return {"status": "success"}
async def open_case(self, username: str, case_id: str, server_id: str):
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:
@ -114,17 +114,16 @@ class CaseService:
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({"id": server_id})
server = await game_servers_collection.find_one({"ip": server_ip})
if not server:
raise HTTPException(status_code=404, detail="Сервер не найден")
# Проверяем, доступен ли кейс на этом сервере (логика как у пакостей) :contentReference[oaicite:2]{index=2}
server_ids = case.get("server_ids", ["*"])
if server_ids and "*" not in server_ids and server_id not in server_ids:
raise HTTPException(status_code=400, detail="Кейс недоступен на выбранном сервере")
# 4. Проверяем баланс
user_balance = await self.coins_service.get_balance(username)
price = case["price"]
@ -166,20 +165,23 @@ class CaseService:
"meta": (chosen_item.get("meta") or {})
}
operation = {
"id": operation_id,
"type": "case_reward",
"player_name": username,
inventory_item = {
"id": str(uuid.uuid4()),
"username": username,
"server_ip": server_ip,
"item_data": item_data,
"price": 0, # тут можно не использовать, т.к. уже оплачен сам кейс
"server_ip": server.get("ip"),
"status": "pending",
"source": {
"type": "case",
"case_id": case_id,
"case_name": case.get("name"),
},
"status": "stored",
"created_at": datetime.utcnow(),
"case_id": case_id,
"case_name": case["name"],
"delivered_at": None,
"withdraw_operation_id": None,
}
await marketplace_operations.insert_one(operation)
await player_inventory_collection.insert_one(inventory_item)
# 8. Лог открытия кейса
opening_log = {
@ -188,8 +190,7 @@ class CaseService:
"user_id": user.get("_id"),
"case_id": case_id,
"case_name": case["name"],
"server_id": server_id,
"server_ip": server.get("ip"),
"server_ip": server_ip,
"reward_item": chosen_item,
"price": price,
"opened_at": datetime.utcnow()
@ -203,6 +204,6 @@ class CaseService:
"status": "success",
"message": f"Кейс '{case['name']}' открыт",
"reward": chosen_item,
"operation_id": operation_id,
"inventory_item_id": inventory_item["id"],
"balance": new_balance
}

95
app/services/inventory.py Normal file
View File

@ -0,0 +1,95 @@
from datetime import datetime
from uuid import uuid4
from fastapi import HTTPException
from app.db.database import db
player_inventory_collection = db.player_inventory
marketplace_operations_collection = db.marketplace_operations
def _serialize_mongodb_doc(doc):
"""Преобразует MongoDB документ для JSON сериализации"""
if doc is None:
return None
# Добавить проверку на список
if isinstance(doc, list):
return [_serialize_mongodb_doc(item) for item in doc]
result = {}
for key, value in doc.items():
# Обработка ObjectId
if key == "_id":
result["_id"] = str(value)
continue
# Обработка ISODate
if isinstance(value, datetime):
result[key] = value.isoformat()
# Обработка вложенных словарей
elif isinstance(value, dict):
if "$date" in value:
# Это ISODate
result[key] = datetime.fromisoformat(value["$date"].replace("Z", "+00:00")).isoformat()
else:
result[key] = _serialize_mongodb_doc(value)
# Обработка списков
elif isinstance(value, list):
result[key] = [_serialize_mongodb_doc(item) if isinstance(item, dict) else item for item in value]
else:
result[key] = value
return result
class InventoryService:
async def list_items(self, username: str, server_ip: str, page: int = 1, limit: int = 20):
q = {"username": username, "server_ip": server_ip, "status": {"$in": ["stored", "withdrawing"]}}
skip = max(page - 1, 0) * limit
items = await player_inventory_collection.find(q) \
.sort("created_at", -1) \
.skip(skip) \
.limit(limit) \
.to_list(length=limit)
serialized_items = _serialize_mongodb_doc(items)
total = await player_inventory_collection.count_documents(q)
return {"items": serialized_items, "page": page, "limit": limit, "total": total}
async def withdraw_item(self, username: str, item_id: str, server_ip: str):
item = await player_inventory_collection.find_one({"id": item_id})
if not item:
raise HTTPException(status_code=404, detail="Item not found")
if item["username"] != username:
raise HTTPException(status_code=403, detail="Not your item")
if item["server_ip"] != server_ip:
raise HTTPException(status_code=400, detail="Wrong server_ip for this item")
if item.get("status") != "stored":
raise HTTPException(status_code=400, detail="Item is not available for withdraw")
# создаём операцию выдачи НА СЕРВЕР (тип оставляем case_reward)
op_id = str(uuid4())
operation = {
"id": op_id,
"type": "case_reward",
"player_name": username,
"item_data": item["item_data"],
"server_ip": server_ip,
"status": "pending",
"created_at": datetime.utcnow(),
# важно: связка с инвентарём, чтобы confirm мог отметить delivered
"inventory_item_id": item_id,
"source": item.get("source"),
}
await marketplace_operations_collection.insert_one(operation)
# помечаем предмет как withdrawing
await player_inventory_collection.update_one(
{"id": item_id},
{"$set": {"status": "withdrawing", "withdraw_operation_id": op_id}}
)
return {"ok": True, "operation_id": op_id, "item_id": item_id}

View File

@ -112,19 +112,28 @@ class MarketplaceService:
}
async def confirm_operation(self, operation_id: str, status: str = "success", error: str = None):
"""Подтвердить выполнение операции"""
update = {
"status": status
}
update = {"status": status}
if error:
update["error"] = error
result = await marketplace_operations.update_one(
{"id": operation_id},
{"$set": update}
)
await marketplace_operations.update_one({"id": operation_id}, {"$set": update})
# ✅ ДОБАВИТЬ ЭТО:
operation = await marketplace_operations.find_one({"id": operation_id})
if operation and operation.get("type") == "case_reward" and operation.get("inventory_item_id"):
inv_id = operation["inventory_item_id"]
if status in ("success", "completed", "done"):
await db.player_inventory.update_one(
{"id": inv_id},
{"$set": {"status": "delivered", "delivered_at": datetime.utcnow()}}
)
elif status in ("failed", "error", "cancelled"):
await db.player_inventory.update_one(
{"id": inv_id},
{"$set": {"status": "stored", "withdraw_operation_id": None}}
)
return {"status": "success"}
async def update_item_details(self, operation_id: str, item_data: dict):