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 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_collection = db.marketplace_items
# Добавьте новую коллекцию для операций
marketplace_operations = db.marketplace_operations
# Добавьте эту функцию # Добавьте эту функцию
def _serialize_mongodb_doc(doc): def _serialize_mongodb_doc(doc):
"""Преобразует MongoDB документ для JSON сериализации""" """Преобразует MongoDB документ для JSON сериализации"""
if doc is None: if doc is None:
return None return None
# Добавить проверку на список
if isinstance(doc, list):
return [_serialize_mongodb_doc(item) for item in doc]
result = {} result = {}
for key, value in doc.items(): for key, value in doc.items():
# Обработка ObjectId # Обработка ObjectId
@ -74,96 +81,86 @@ class MarketplaceService:
async def add_item(self, username: str, slot_index: int, amount: int, price: int, server_ip: str): async def add_item(self, username: str, slot_index: int, amount: int, price: int, server_ip: str):
"""Выставить предмет на продажу""" """Выставить предмет на продажу"""
# 1. Получаем инвентарь игрока # Создаем операцию продажи
cmd_service = CommandService() operation_id = str(uuid.uuid4())
inventory_result = await cmd_service.get_player_inventory(username, server_ip, timeout=15)
if inventory_result["status"] != "success": operation = {
raise HTTPException(status_code=400, "id": operation_id,
detail=f"Не удалось получить инвентарь игрока. Игрок должен быть онлайн.") "type": "sell",
"player_name": username,
"slot_index": slot_index,
"amount": amount,
"price": price,
"server_ip": server_ip,
"status": "pending",
"created_at": datetime.utcnow()
}
# 2. Находим предмет в указанном слоте await marketplace_operations.insert_one(operation)
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"В указанном слоте нет предмета или недостаточное количество")
# 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()) item_id = str(uuid.uuid4())
marketplace_item = { marketplace_item = {
"id": item_id, "id": item_id,
"material": item_data.get("material"), "material": item_data.get("material"),
"amount": amount, "amount": item_data.get("amount"),
"price": price, "price": operation.get("price"),
"seller_name": username, "seller_name": operation.get("player_name"),
"server_ip": server_ip, "server_ip": operation.get("server_ip"),
"display_name": item_data.get("display_name"), "display_name": item_data.get("meta", {}).get("display_name"),
"lore": item_data.get("lore"), "lore": item_data.get("meta", {}).get("lore"),
"enchants": item_data.get("enchants"), "enchants": item_data.get("meta", {}).get("enchants"),
"durability": item_data.get("meta", {}).get("durability"),
"item_data": item_data, "item_data": item_data,
"created_at": datetime.utcnow() "created_at": datetime.utcnow()
} }
await marketplace_collection.insert_one(marketplace_item) await marketplace_collection.insert_one(marketplace_item)
# 4. Удаляем предмет из инвентаря игрока # Обновляем операцию
# Определяем тип слота и его номер для команды replaceitem await marketplace_operations.update_one(
slot_type = "inventory" {"id": operation_id},
slot_num = slot_index {"$set": {"item_id": item_id, "status": "completed"}}
# Преобразуем слот инвентаря в соответствующий формат для команды
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 cmd_service.add_command(cmd) return {"status": "success"}
return {"status": "success", "item_id": item_id}
async def buy_item(self, buyer_username: str, item_id: str): async def buy_item(self, buyer_username: str, item_id: str):
"""Купить предмет с торговой площадки""" """Купить предмет с торговой площадки"""
# 1. Находим предмет # 1. Находим предмет
@ -183,13 +180,22 @@ class MarketplaceService:
raise HTTPException(status_code=400, raise HTTPException(status_code=400,
detail=f"Недостаточно монет. Требуется: {item['price']}, имеется: {buyer_balance}") detail=f"Недостаточно монет. Требуется: {item['price']}, имеется: {buyer_balance}")
# 4. Проверяем, что покупатель онлайн на сервере # 4. Создаем операцию покупки
cmd_service = CommandService() operation_id = str(uuid.uuid4())
try:
await cmd_service.get_player_inventory(buyer_username, item["server_ip"], timeout=5) operation = {
except: "id": operation_id,
raise HTTPException(status_code=400, "type": "buy",
detail=f"Вы должны быть онлайн на сервере для совершения покупки") "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. Списываем деньги с покупателя # 5. Списываем деньги с покупателя
await coins_service.decrease_balance(buyer_username, item["price"]) await coins_service.decrease_balance(buyer_username, item["price"])
@ -197,47 +203,11 @@ class MarketplaceService:
# 6. Начисляем деньги продавцу # 6. Начисляем деньги продавцу
await coins_service.increase_balance(item["seller_name"], item["price"]) await coins_service.increase_balance(item["seller_name"], item["price"])
# 7. Добавляем предмет в инвентарь покупателя # 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. Удаляем предмет с торговой площадки
await marketplace_collection.delete_one({"id": item_id}) await marketplace_collection.delete_one({"id": item_id})
return { return {
"status": "success", "status": "pending",
"message": f"Вы купили {amount} шт. предмета {item.get('display_name', material)}", "operation_id": operation_id,
"remaining_balance": buyer_balance - item["price"] "message": "Покупка в обработке. Предмет будет добавлен в ваш инвентарь."
} }