From 39cd14f1d78d6ba7e65ca54d73117cff6b9dd150 Mon Sep 17 00:00:00 2001 From: DIKER0K Date: Sat, 19 Jul 2025 04:39:51 +0500 Subject: [PATCH] feat: full woriking markertplace --- app/api/marketplace.py | 20 +++- app/services/marketplace.py | 214 ++++++++++++++++-------------------- 2 files changed, 111 insertions(+), 123 deletions(-) diff --git a/app/api/marketplace.py b/app/api/marketplace.py index 2476d63..9967d8f 100644 --- a/app/api/marketplace.py +++ b/app/api/marketplace.py @@ -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"]) diff --git a/app/services/marketplace.py b/app/services/marketplace.py index 05433cb..3c11fc6 100644 --- a/app/services/marketplace.py +++ b/app/services/marketplace.py @@ -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": "Покупка в обработке. Предмет будет добавлен в ваш инвентарь." }