Files
DIKER0K 8c4db146c9
All checks were successful
Build and Deploy / deploy (push) Successful in 22s
fix: tabulation in marketplace update price and cancel item sale
2025-07-21 22:28:51 +05:00

279 lines
11 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import uuid
from datetime import datetime
from fastapi import HTTPException
from app.db.database import db
from app.services.coins import CoinsService
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
if key == "_id":
result["_id"] = str(value)
continue
# Обработка ISODate
if isinstance(value, datetime):
result[key] = value.isoformat()
# Обработка вложенных словарей
elif isinstance(value, dict):
if "$date" in value:
# Это ISODate
result[key] = datetime.fromisoformat(value["$date"].replace("Z", "+00:00")).isoformat()
else:
result[key] = _serialize_mongodb_doc(value)
# Обработка списков
elif isinstance(value, list):
result[key] = [_serialize_mongodb_doc(item) if isinstance(item, dict) else item for item in value]
else:
result[key] = value
return result
class MarketplaceService:
async def list_items(self, server_ip: str = None, page: int = 1, limit: int = 20):
"""Получить список предметов на торговой площадке"""
query = {}
if server_ip:
query["server_ip"] = server_ip
total = await marketplace_collection.count_documents(query)
items_cursor = marketplace_collection.find(query) \
.sort("created_at", -1) \
.skip((page - 1) * limit) \
.limit(limit)
items = await items_cursor.to_list(limit)
# Преобразуем каждый документ
serialized_items = [_serialize_mongodb_doc(item) for item in items]
return {
"items": serialized_items,
"total": total,
"page": page,
"pages": (total + limit - 1) // limit
}
async def get_item(self, item_id: str):
"""Получить информацию о конкретном предмете"""
item = await marketplace_collection.find_one({"id": item_id})
if not item:
raise HTTPException(status_code=404, detail="Предмет не найден")
return _serialize_mongodb_doc(item)
async def add_item(self, username: str, slot_index: int, amount: int, price: int, server_ip: str):
"""Выставить предмет на продажу"""
# Создаем операцию продажи
operation_id = str(uuid.uuid4())
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()
}
await marketplace_operations.insert_one(operation)
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": 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)
# Обновляем операцию
await marketplace_operations.update_one(
{"id": operation_id},
{"$set": {"item_id": item_id, "status": "completed"}}
)
return {"status": "success"}
async def buy_item(self, buyer_username: str, item_id: str):
"""Купить предмет с торговой площадки"""
# 1. Находим предмет
item = await marketplace_collection.find_one({"id": item_id})
if not item:
raise HTTPException(status_code=404, detail="Предмет не найден")
# 2. Проверяем, что покупатель не является продавцом
if item["seller_name"] == buyer_username:
raise HTTPException(status_code=400, detail="Вы не можете купить свой же предмет")
# 3. Проверяем баланс покупателя
coins_service = CoinsService()
buyer_balance = await coins_service.get_balance(buyer_username)
if buyer_balance < item["price"]:
raise HTTPException(status_code=400,
detail=f"Недостаточно монет. Требуется: {item['price']}, имеется: {buyer_balance}")
# 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"])
# 6. Начисляем деньги продавцу
await coins_service.increase_balance(item["seller_name"], item["price"])
# 7. Удаляем предмет с торговой площадки
await marketplace_collection.delete_one({"id": item_id})
return {
"status": "pending",
"operation_id": operation_id,
"message": "Покупка в обработке. Предмет будет добавлен в ваш инвентарь."
}
async def cancel_item_sale(self, username: str, item_id: str):
"""Снять предмет с продажи"""
# Находим предмет
item = await marketplace_collection.find_one({"id": item_id})
if not item:
raise HTTPException(status_code=404, detail="Предмет не найден")
# Проверяем, что пользователь является владельцем предмета
if item["seller_name"] != username:
raise HTTPException(status_code=403, detail="Вы не можете снять с продажи чужой предмет")
# Создаем операцию возврата предмета
operation_id = str(uuid.uuid4())
operation = {
"id": operation_id,
"type": "cancel_sale",
"player_name": username,
"item_id": item_id,
"item_data": item["item_data"],
"server_ip": item["server_ip"],
"status": "pending",
"created_at": datetime.utcnow()
}
await marketplace_operations.insert_one(operation)
# Удаляем предмет с торговой площадки
await marketplace_collection.delete_one({"id": item_id})
return {
"status": "pending",
"operation_id": operation_id,
"message": "Предмет снят с продажи и будет возвращен в ваш инвентарь"
}
async def update_item_price(self, username: str, item_id: str, new_price: int):
"""Обновить цену предмета на торговой площадке"""
# Находим предмет
item = await marketplace_collection.find_one({"id": item_id})
if not item:
raise HTTPException(status_code=404, detail="Предмет не найден")
# Проверяем, что пользователь является владельцем предмета
if item["seller_name"] != username:
raise HTTPException(status_code=403, detail="Вы не можете изменить цену чужого предмета")
# Валидация новой цены
if new_price <= 0:
raise HTTPException(status_code=400, detail="Цена должна быть положительным числом")
# Обновляем цену предмета
result = await marketplace_collection.update_one(
{"id": item_id},
{"$set": {"price": new_price}}
)
if result.modified_count == 0:
raise HTTPException(status_code=500, detail="Не удалось обновить цену предмета")
return {
"status": "success",
"message": f"Цена предмета обновлена на {new_price} монет"
}