feat: full woriking markertplace
This commit is contained in:
@ -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"])
|
||||||
|
@ -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": "Покупка в обработке. Предмет будет добавлен в ваш инвентарь."
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user