class PayoutService:

Сохраните результат по idempotency_key и защищайте проверку вместе с созданием одной критической секцией. В production идемпотентность должна опираться на постоянное хранилище.

Короткий ответ

Что ответить на собеседовании

Сохраните результат по idempotency_key и защищайте проверку вместе с созданием одной критической секцией. В production идемпотентность должна опираться на постоянное хранилище.

Подробный разбор

Ответ с пояснениями

Условие

class PayoutService: def init(self): # ... pass

def create_payout(self, idempotency_key: str, terminal_id: int, amount: float) -> dict:
    """
    Должен вернуть словарь вида:
    {"payout_id": <int>, "terminal_id": <int>, "amount": <float>, "status": "success"}

    Гарантии:
    - при первом вызове с новым idempotency_key — создаётся новая выплата
    - при повторном вызове с уже использованным idempotency_key — возвращается
      результат первого вызова, новая выплата НЕ создаётся
    - amount должен быть > 0, иначе — бросить ValueError
    """
    raise NotImplementedError

Ответ

Сохраните результат по idempotency_key и защищайте проверку вместе с созданием одной критической секцией. В production идемпотентность должна опираться на постоянное хранилище.

Ниже учебная реализация в памяти одного экземпляра. Копия ответа не позволяет вызывающему испортить сохранённый результат. Повтор с тем же ключом возвращает первый результат, как требует условие.

import math
from threading import Lock

class PayoutService:
    def __init__(self):
        self._results = {}
        self._lock = Lock()
        self._next_id = 1

    def create_payout(self, idempotency_key: str,
                      terminal_id: int, amount: float) -> dict:
        if not math.isfinite(amount) or amount <= 0:
            raise ValueError("amount must be finite and positive")
        with self._lock:
            if idempotency_key not in self._results:
                self._results[idempotency_key] = {
                    "payout_id": self._next_id,
                    "terminal_id": terminal_id,
                    "amount": amount,
                    "status": "success",
                }
                self._next_id += 1
            return self._results[idempotency_key].copy()

Здесь нет реального перевода денег. Для настоящих выплат нужны уникальный ключ в БД, атомарное сохранение состояния и идемпотентный вызов провайдера; float заменяют денежным представлением без двоичного округления.

ИИ-помощник для собеседований

Хочешь уверенно проходить собеседования?

Попробуй ИИ-помощник для собеседований: слышит вас и собеседника, анализирует экран, подсказывает ответы в реальном времени, работает без VPN и не попадает в захват экрана.

Подробнее