public class MoneyTransferService {

В представленном коде есть несколько важных моментов и одна критическая ошибка:

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

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

В представленном коде есть несколько важных моментов и одна критическая ошибка:

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

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

Условие

public class MoneyTransferService { /** * @param fromAccountId уникальный идентификатор пользователя который переводит средства * @param toAccountId уникальный идентификатор пользователя кому переводят средства * @param amount сумма перевода. Положительное число */ @Transactional public void transferMoney(Long fromAccountId, Long toAccountId, BigDecimal amount) { if (amount.compareTo(BigDecimal.ZERO) <= 0) { throw new RuntimeException("Amount must be +"); } if (fromAccountId.equals(toAccountId)) { throw new RuntimeException("Account can't be the same"); }

    Long firstId = Math.min(fromAccountId, toAccountId);
    Long secondId = Math.max(fromAccountId, toAccountId);
    Account first = accountRepository.findByIdForUpdate(firstId)
        .orElseThrow(() -> new RuntimeException("Account not found"));
    Account second = accountRepository.findByIdForUpdate(secondId)
        .orElseThrow(() -> new RuntimeException("Account not found"));
    Account from = fromAccountId.equals(firstId) ? first : second;
    Account to = fromAccountId.equals(firstId) ? second : first;
    if (from.getBalance().compareTo(amount) < 0) {
        throw new RuntimeException("Balance is less 0");
    }
    from.setBalance(from.getBalance().subtract(amount));
    to.setBalance(from.getBalance().add(amount));
    accountRepository.save(to);
    accountRepository.save(from);
}

}

@Entity @Data public class Account { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private BigDecimal balance; }

Ответ

В представленном коде есть несколько важных моментов и одна критическая ошибка:

  1. Проверка amount > 0 и что fromAccountId != toAccountId — правильные валидации.
  2. Для предотвращения дедлоков при конкурентных переводах используется упорядоченное блокирование счетов через findByIdForUpdate — хорошая практика.
  3. Ошибка в логике обновления балансов:
from.setBalance(from.getBalance().subtract(amount));
to.setBalance(from.getBalance().add(amount));

Здесь после вычитания amount из from.getBalance(), при вычислении нового баланса для to используется уже уменьшенный баланс from, а нужно прибавлять amount к текущему балансу to.

Правильно:

to.setBalance(to.getBalance().add(amount));
  1. Сохранение счетов через accountRepository.save() — корректно.

Итог: исправьте обновление баланса получателя, иначе деньги будут списываться, но не зачисляться.


Пример исправленного участка:

from.setBalance(from.getBalance().subtract(amount));
to.setBalance(to.getBalance().add(amount));

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

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

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

Подробнее