Компонент для выполнения переводов с расчётом комиссии и отправкой уведомления

Основные ошибки: double для денег, неоднозначные границы комиссии, два Fee при сумме выше 5000 и отсутствие комиссии ровно при 1000. Внешние вызовы внутри транзакции создают дополнительные риски.

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

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

Основные ошибки: double для денег, неоднозначные границы комиссии, два Fee при сумме выше 5000 и отсутствие комиссии ровно при 1000. Внешние вызовы внутри транзакции создают дополнительные риски.

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

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

Условие

Компонент реализует процесс перевода средств между пользователями. В нём реализована логика вычисления комиссии в зависимости от суммы перевода и интеграция с внешним сервисом отправки уведомлений.

Код‑ревью:

@Service
public class PaymentService {
    @Autowired
    private PaymentRepository paymentRepository;
    @Autowired
    private FeeRepository feeRepository;
    @Autowired
    private UserRepository userRepository;

    private NotificationRestClient notificationRestClient = new NotificationRestClient();
    private CbrRestClient cbrRestClient = new CbrRestClient();

    @Transactional
    public void processPayment(double amount, Currency currency, Long recipientId) {
        double amountInRub = amount * cbrRestClient.doRequest().getRates().get(currency.getCode());
        Long userId = (Long) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        User user = userRepository.findUserById(userId).get();

        Payment payment = new Payment(amountInRub, user, recipientId);
        paymentRepository.save(payment);

        if (amountInRub < 1000) {
            Fee fee = new Fee(amountInRub * 0.015, user);
            feeRepository.save(fee);
        }
        if (amountInRub > 1000) {
            Fee fee = new Fee(amountInRub * 0.01, user);
            feeRepository.save(fee);
        }
        if (amountInRub > 5000) {
            Fee fee = new Fee(amountInRub * 0.005, user);
            feeRepository.save(fee);
        }

        try {
            notificationRestClient.notify(payment);
        } catch (Throwable t) {
            // do nothing
        }
    }
}

@Entity
class Fee {
    private Double value;

    @ManyToOne
    private User user;

    public Fee() {}
    public Fee(Double value, User user) {
        this.value = value;
        this.user = user;
    }

    public void setValue(Double value) {
        this.value = value;
    }

    public void setUser(User user) {
        this.user = user;
    }
}

Ответ

Основные ошибки: double для денег, неоднозначные границы комиссии, два Fee при сумме выше 5000 и отсутствие комиссии ровно при 1000. Внешние вызовы внутри транзакции создают дополнительные риски.

Нужны BigDecimal/целые единицы, согласованное округление и взаимоисключающие диапазоны. Проверьте сумму, валюту, курс, получателя, права и idempotency key; cast principal и Optional.get небезопасны без контракта. Клиенты внедряют зависимостями, сетевые ожидания ограничивают. Уведомление перед commit может сообщить об откатившемся платеже — используйте outbox. Не глотайте Throwable. У Fee в показанном коде нет @Id. Сохранение Payment само по себе ещё не реализует корректное списание и зачисление остатков.

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

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

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

Подробнее