feat: full woriking markertplace
This commit is contained in:
@ -43,3 +43,21 @@ async def buy_item(
|
||||
"""Купить предмет"""
|
||||
from app.services.marketplace import MarketplaceService
|
||||
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"])
|
||||
|
@ -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,95 +81,85 @@ 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
|
||||
await marketplace_operations.insert_one(operation)
|
||||
|
||||
if not item_data or item_data.get("material") == "AIR" or item_data.get("amount") < amount:
|
||||
raise HTTPException(status_code=400,
|
||||
detail=f"В указанном слоте нет предмета или недостаточное количество")
|
||||
return {"status": "pending", "operation_id": operation_id}
|
||||
|
||||
# 3. Создаем запись о предмете на торговой площадке
|
||||
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):
|
||||
"""Купить предмет с торговой площадки"""
|
||||
@ -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": "Покупка в обработке. Предмет будет добавлен в ваш инвентарь."
|
||||
}
|
||||
|
Reference in New Issue
Block a user