279 lines
11 KiB
Python
279 lines
11 KiB
Python
import uuid
|
||
from datetime import datetime
|
||
from fastapi import HTTPException
|
||
from app.db.database import db
|
||
from app.services.coins import CoinsService
|
||
from app.services.server.command import CommandService
|
||
|
||
# Коллекция для хранения товаров на торговой площадке
|
||
marketplace_collection = db.marketplace_items
|
||
|
||
# Добавьте новую коллекцию для операций
|
||
marketplace_operations = 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 MarketplaceService:
|
||
async def list_items(self, server_ip: str = None, page: int = 1, limit: int = 20):
|
||
"""Получить список предметов на торговой площадке"""
|
||
query = {}
|
||
if server_ip:
|
||
query["server_ip"] = server_ip
|
||
|
||
total = await marketplace_collection.count_documents(query)
|
||
|
||
items_cursor = marketplace_collection.find(query) \
|
||
.sort("created_at", -1) \
|
||
.skip((page - 1) * limit) \
|
||
.limit(limit)
|
||
|
||
items = await items_cursor.to_list(limit)
|
||
|
||
# Преобразуем каждый документ
|
||
serialized_items = [_serialize_mongodb_doc(item) for item in items]
|
||
|
||
return {
|
||
"items": serialized_items,
|
||
"total": total,
|
||
"page": page,
|
||
"pages": (total + limit - 1) // limit
|
||
}
|
||
|
||
async def get_item(self, item_id: str):
|
||
"""Получить информацию о конкретном предмете"""
|
||
item = await marketplace_collection.find_one({"id": item_id})
|
||
if not item:
|
||
raise HTTPException(status_code=404, detail="Предмет не найден")
|
||
return _serialize_mongodb_doc(item)
|
||
|
||
async def add_item(self, username: str, slot_index: int, amount: int, price: int, server_ip: str):
|
||
"""Выставить предмет на продажу"""
|
||
# Создаем операцию продажи
|
||
operation_id = str(uuid.uuid4())
|
||
|
||
operation = {
|
||
"id": operation_id,
|
||
"type": "sell",
|
||
"player_name": username,
|
||
"slot_index": slot_index,
|
||
"amount": amount,
|
||
"price": price,
|
||
"server_ip": server_ip,
|
||
"status": "pending",
|
||
"created_at": datetime.utcnow()
|
||
}
|
||
|
||
await marketplace_operations.insert_one(operation)
|
||
|
||
return {"status": "pending", "operation_id": operation_id}
|
||
|
||
async def get_pending_operations(self, server_ip: str):
|
||
"""Получить список операций для выполнения на сервере"""
|
||
operations = await marketplace_operations.find({
|
||
"server_ip": server_ip,
|
||
"status": "pending"
|
||
}).to_list(100)
|
||
|
||
return {
|
||
"operations": _serialize_mongodb_doc(operations)
|
||
}
|
||
|
||
async def confirm_operation(self, operation_id: str, status: str = "success", error: str = None):
|
||
"""Подтвердить выполнение операции"""
|
||
update = {
|
||
"status": status
|
||
}
|
||
|
||
if error:
|
||
update["error"] = error
|
||
|
||
result = await marketplace_operations.update_one(
|
||
{"id": operation_id},
|
||
{"$set": update}
|
||
)
|
||
|
||
return {"status": "success"}
|
||
|
||
async def update_item_details(self, operation_id: str, item_data: dict):
|
||
"""Обновить детальную информацию о предмете"""
|
||
operation = await marketplace_operations.find_one({"id": operation_id})
|
||
|
||
if not operation:
|
||
return {"status": "error", "message": "Операция не найдена"}
|
||
|
||
# Создаем запись о предмете на торговой площадке
|
||
item_id = str(uuid.uuid4())
|
||
marketplace_item = {
|
||
"id": item_id,
|
||
"material": item_data.get("material"),
|
||
"amount": item_data.get("amount"),
|
||
"price": operation.get("price"),
|
||
"seller_name": operation.get("player_name"),
|
||
"server_ip": operation.get("server_ip"),
|
||
"display_name": item_data.get("meta", {}).get("display_name"),
|
||
"lore": item_data.get("meta", {}).get("lore"),
|
||
"enchants": item_data.get("meta", {}).get("enchants"),
|
||
"durability": item_data.get("meta", {}).get("durability"),
|
||
"item_data": item_data,
|
||
"created_at": datetime.utcnow()
|
||
}
|
||
|
||
await marketplace_collection.insert_one(marketplace_item)
|
||
|
||
# Обновляем операцию
|
||
await marketplace_operations.update_one(
|
||
{"id": operation_id},
|
||
{"$set": {"item_id": item_id, "status": "completed"}}
|
||
)
|
||
|
||
return {"status": "success"}
|
||
|
||
async def buy_item(self, buyer_username: str, item_id: str):
|
||
"""Купить предмет с торговой площадки"""
|
||
# 1. Находим предмет
|
||
item = await marketplace_collection.find_one({"id": item_id})
|
||
if not item:
|
||
raise HTTPException(status_code=404, detail="Предмет не найден")
|
||
|
||
# 2. Проверяем, что покупатель не является продавцом
|
||
if item["seller_name"] == buyer_username:
|
||
raise HTTPException(status_code=400, detail="Вы не можете купить свой же предмет")
|
||
|
||
# 3. Проверяем баланс покупателя
|
||
coins_service = CoinsService()
|
||
buyer_balance = await coins_service.get_balance(buyer_username)
|
||
|
||
if buyer_balance < item["price"]:
|
||
raise HTTPException(status_code=400,
|
||
detail=f"Недостаточно монет. Требуется: {item['price']}, имеется: {buyer_balance}")
|
||
|
||
# 4. Создаем операцию покупки
|
||
operation_id = str(uuid.uuid4())
|
||
|
||
operation = {
|
||
"id": operation_id,
|
||
"type": "buy",
|
||
"player_name": buyer_username,
|
||
"item_id": item_id,
|
||
"item_data": item["item_data"],
|
||
"price": item["price"],
|
||
"server_ip": item["server_ip"],
|
||
"status": "pending",
|
||
"created_at": datetime.utcnow()
|
||
}
|
||
|
||
await marketplace_operations.insert_one(operation)
|
||
|
||
# 5. Списываем деньги с покупателя
|
||
await coins_service.decrease_balance(buyer_username, item["price"])
|
||
|
||
# 6. Начисляем деньги продавцу
|
||
await coins_service.increase_balance(item["seller_name"], item["price"])
|
||
|
||
# 7. Удаляем предмет с торговой площадки
|
||
await marketplace_collection.delete_one({"id": item_id})
|
||
|
||
return {
|
||
"status": "pending",
|
||
"operation_id": operation_id,
|
||
"message": "Покупка в обработке. Предмет будет добавлен в ваш инвентарь."
|
||
}
|
||
|
||
async def cancel_item_sale(self, username: str, item_id: str):
|
||
"""Снять предмет с продажи"""
|
||
# Находим предмет
|
||
item = await marketplace_collection.find_one({"id": item_id})
|
||
if not item:
|
||
raise HTTPException(status_code=404, detail="Предмет не найден")
|
||
|
||
# Проверяем, что пользователь является владельцем предмета
|
||
if item["seller_name"] != username:
|
||
raise HTTPException(status_code=403, detail="Вы не можете снять с продажи чужой предмет")
|
||
|
||
# Создаем операцию возврата предмета
|
||
operation_id = str(uuid.uuid4())
|
||
|
||
operation = {
|
||
"id": operation_id,
|
||
"type": "cancel_sale",
|
||
"player_name": username,
|
||
"item_id": item_id,
|
||
"item_data": item["item_data"],
|
||
"server_ip": item["server_ip"],
|
||
"status": "pending",
|
||
"created_at": datetime.utcnow()
|
||
}
|
||
|
||
await marketplace_operations.insert_one(operation)
|
||
|
||
# Удаляем предмет с торговой площадки
|
||
await marketplace_collection.delete_one({"id": item_id})
|
||
|
||
return {
|
||
"status": "pending",
|
||
"operation_id": operation_id,
|
||
"message": "Предмет снят с продажи и будет возвращен в ваш инвентарь"
|
||
}
|
||
|
||
async def update_item_price(self, username: str, item_id: str, new_price: int):
|
||
"""Обновить цену предмета на торговой площадке"""
|
||
# Находим предмет
|
||
item = await marketplace_collection.find_one({"id": item_id})
|
||
if not item:
|
||
raise HTTPException(status_code=404, detail="Предмет не найден")
|
||
|
||
# Проверяем, что пользователь является владельцем предмета
|
||
if item["seller_name"] != username:
|
||
raise HTTPException(status_code=403, detail="Вы не можете изменить цену чужого предмета")
|
||
|
||
# Валидация новой цены
|
||
if new_price <= 0:
|
||
raise HTTPException(status_code=400, detail="Цена должна быть положительным числом")
|
||
|
||
# Обновляем цену предмета
|
||
result = await marketplace_collection.update_one(
|
||
{"id": item_id},
|
||
{"$set": {"price": new_price}}
|
||
)
|
||
|
||
if result.modified_count == 0:
|
||
raise HTTPException(status_code=500, detail="Не удалось обновить цену предмета")
|
||
|
||
return {
|
||
"status": "success",
|
||
"message": f"Цена предмета обновлена на {new_price} монет"
|
||
}
|