feat: full woriking markertplace

This commit is contained in:
2025-07-19 04:39:51 +05:00
parent 6b8f116608
commit 39cd14f1d7
2 changed files with 111 additions and 123 deletions

View File

@ -42,4 +42,22 @@ async def buy_item(
):
"""Купить предмет"""
from app.services.marketplace import MarketplaceService
return await MarketplaceService().buy_item(request.username, item_id)
return await MarketplaceService().buy_item(request.username, item_id)
@router.get("/operations")
async def get_marketplace_operations(server_ip: str):
"""Получить список операций для выполнения на сервере"""
from app.services.marketplace import MarketplaceService
return await MarketplaceService().get_pending_operations(server_ip)
@router.post("/operations/confirm")
async def confirm_marketplace_operation(data: dict):
"""Подтвердить выполнение операции"""
from app.services.marketplace import MarketplaceService
return await MarketplaceService().confirm_operation(data["operation_id"], data.get("status"), data.get("error"))
@router.post("/items/details")
async def submit_item_details(data: dict):
"""Получить подробные данные о предмете"""
from app.services.marketplace import MarketplaceService
return await MarketplaceService().update_item_details(data["operation_id"], data["item_data"])

View File

@ -8,12 +8,19 @@ 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
@ -74,96 +81,86 @@ class MarketplaceService:
async def add_item(self, username: str, slot_index: int, amount: int, price: int, server_ip: str):
"""Выставить предмет на продажу"""
# 1. Получаем инвентарь игрока
cmd_service = CommandService()
inventory_result = await cmd_service.get_player_inventory(username, server_ip, timeout=15)
# Создаем операцию продажи
operation_id = str(uuid.uuid4())
if inventory_result["status"] != "success":
raise HTTPException(status_code=400,
detail=f"Не удалось получить инвентарь игрока. Игрок должен быть онлайн.")
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()
}
# 2. Находим предмет в указанном слоте
item_data = None
for item in inventory_result.get("inventory", []):
if item.get("slot") == slot_index:
item_data = item
break
if not item_data or item_data.get("material") == "AIR" or item_data.get("amount") < amount:
raise HTTPException(status_code=400,
detail=f"В указанном слоте нет предмета или недостаточное количество")
await marketplace_operations.insert_one(operation)
# 3. Создаем запись о предмете на торговой площадке
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": amount,
"price": price,
"seller_name": username,
"server_ip": server_ip,
"display_name": item_data.get("display_name"),
"lore": item_data.get("lore"),
"enchants": item_data.get("enchants"),
"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)
# 4. Удаляем предмет из инвентаря игрока
# Определяем тип слота и его номер для команды replaceitem
slot_type = "inventory"
slot_num = slot_index
# Преобразуем слот инвентаря в соответствующий формат для команды
if 0 <= slot_index <= 8:
# Панель быстрого доступа (хотбар)
slot_type = "hotbar"
slot_num = slot_index
elif 9 <= slot_index <= 35:
# Основной инвентарь
slot_type = "inventory"
slot_num = slot_index - 9
elif slot_index == 36:
# Ботинки
slot_type = "armor.feet"
slot_num = 0
elif slot_index == 37:
# Поножи
slot_type = "armor.legs"
slot_num = 0
elif slot_index == 38:
# Нагрудник
slot_type = "armor.chest"
slot_num = 0
elif slot_index == 39:
# Шлем
slot_type = "armor.head"
slot_num = 0
elif slot_index == 40:
# Вторая рука
slot_type = "weapon.offhand"
slot_num = 0
# Выполняем команду
# Для Minecraft 1.16.5+
command = f"item replace entity {username} {slot_type}.{slot_num} with air"
# Для более старых версий (1.13-1.16)
# command = f"replaceitem entity {username} {slot_type}.{slot_num} air"
from app.models.server.command import ServerCommand
cmd = ServerCommand(
command=command,
server_ip=server_ip,
require_online_player=True,
target_message=f"Вы выставили на продажу {amount} шт. предмета {item_data.get('display_name', item_data.get('material'))} за {price} монет"
# Обновляем операцию
await marketplace_operations.update_one(
{"id": operation_id},
{"$set": {"item_id": item_id, "status": "completed"}}
)
await cmd_service.add_command(cmd)
return {"status": "success", "item_id": item_id}
return {"status": "success"}
async def buy_item(self, buyer_username: str, item_id: str):
"""Купить предмет с торговой площадки"""
# 1. Находим предмет
@ -183,13 +180,22 @@ class MarketplaceService:
raise HTTPException(status_code=400,
detail=f"Недостаточно монет. Требуется: {item['price']}, имеется: {buyer_balance}")
# 4. Проверяем, что покупатель онлайн на сервере
cmd_service = CommandService()
try:
await cmd_service.get_player_inventory(buyer_username, item["server_ip"], timeout=5)
except:
raise HTTPException(status_code=400,
detail=f"Вы должны быть онлайн на сервере для совершения покупки")
# 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"])
@ -197,47 +203,11 @@ class MarketplaceService:
# 6. Начисляем деньги продавцу
await coins_service.increase_balance(item["seller_name"], item["price"])
# 7. Добавляем предмет в инвентарь покупателя
material = item["material"]
amount = item["amount"]
# Создаем команду с учетом всех свойств предмета
command_base = f"give {buyer_username} {material} {amount}"
# Если у предмета есть мета-данные, добавляем их через NBT
nbt_tags = []
if item.get("display_name"):
nbt_tags.append(f'display:{{Name:\'[{{"text":"{item["display_name"]}","italic":false}}]\'}}')
if item.get("lore"):
lore_json = ','.join([f'[{{"text":"{line}","italic":false}}]' for line in item["lore"]])
nbt_tags.append(f'display:{{Lore:[{lore_json}]}}')
if item.get("enchants"):
enchant_tags = []
for ench_id, level in item["enchants"].items():
enchant_tags.append(f'{{id:"{ench_id}",lvl:{level}s}}')
nbt_tags.append(f'Enchantments:[{",".join(enchant_tags)}]')
if nbt_tags:
command_base += " " + "{" + ",".join(nbt_tags) + "}"
from app.models.server.command import ServerCommand
cmd = ServerCommand(
command=command_base,
server_ip=item["server_ip"],
require_online_player=True,
target_message=f"Вы купили {amount} шт. предмета {item.get('display_name', material)} за {item['price']} монет"
)
await cmd_service.add_command(cmd)
# 8. Удаляем предмет с торговой площадки
# 7. Удаляем предмет с торговой площадки
await marketplace_collection.delete_one({"id": item_id})
return {
"status": "success",
"message": f"Вы купили {amount} шт. предмета {item.get('display_name', material)}",
"remaining_balance": buyer_balance - item["price"]
"status": "pending",
"operation_id": operation_id,
"message": "Покупка в обработке. Предмет будет добавлен в ваш инвентарь."
}