Войти Регистрация
Код-ревью

32. Анкета на кредит наличными в Сбере

Сложная

Java · 55 строк

Клиент заполняет анкету на кредит до 5 млн ₽ под 19,9 % годовых. Контроллер проверяет возраст 21–70 лет и что аннуитетный платёж не больше половины дохода, отсекает повторную анкету за 30 дней и зовёт скоринг.

CreditForm содержит ФИО, серию и номер паспорта, дату рождения, доход, сумму и срок; toString() сгенерирован Lombok. Зарплатные клиенты поле дохода не заполняют — его подтягивают из зарплатного проекта. Скоринг отвечает 2–8 секунд, пул Hikari — 20 соединений. Отметьте строки с ошибками.

В работе: Онлайн-заявка на кредит в СберБанк Онлайн: пик — вечер пятницы и дни зарплаты.

Как проверяем Нажмите на строку с ошибкой и коротко напишите, что не так. Баг засчитан, если отмечена хотя бы одна его строка. Зачёт — найдено не меньше 60 % багов с учётом серьёзности (критичный весит 3, обычный 2, мелкий 1), а лишних отметок не больше, чем багов в коде. Пустые строки, скобки и комментарии лишними не считаем. Ответ один на подход: после него показываем разбор.
SpringJDBCперсональные данныеаннуитетпул соединений

Источник: наша задача на данных Сбера: кредитная анкета и скоринг. Условие пересказано своими словами.

  1. @RestController
  2. @RequestMapping("/api/credit")
  3. public class CreditApplicationController {
  4. private static final Logger log = LoggerFactory.getLogger(CreditApplicationController.class);
  5. private static final BigDecimal MAX_AMOUNT = new BigDecimal("5000000");
  6. private static final double ANNUAL_RATE = 0.199;
  7. private final DataSource dataSource;
  8. private final ScoringClient scoring;
  9. public CreditApplicationController(DataSource dataSource, ScoringClient scoring) {
  10. this.dataSource = dataSource;
  11. this.scoring = scoring;
  12. }
  13. @PostMapping("/apply")
  14. public ResponseEntity<String> apply(@RequestBody CreditForm form) throws SQLException {
  15. log.info("Новая анкета: {}", form);
  16. int age = LocalDate.now().getYear() - form.getBirthDate().getYear();
  17. if (age < 21 || age > 70) {
  18. return ResponseEntity.badRequest().body("Возраст вне диапазона 21–70 лет");
  19. }
  20. if (form.getAmount().compareTo(MAX_AMOUNT) > 0) {
  21. return ResponseEntity.badRequest().body("Сумма больше 5 млн ₽");
  22. }
  23. BigDecimal payment = monthlyPayment(form.getAmount(), form.getTermMonths());
  24. if (payment.compareTo(form.getIncome().multiply(new BigDecimal("0.5"))) > 0) {
  25. return ResponseEntity.ok("Отказ: платёж больше половины дохода");
  26. }
  27. Connection conn = dataSource.getConnection();
  28. Statement st = conn.createStatement();
  29. ResultSet rs = st.executeQuery("SELECT count(*) FROM applications WHERE passport = '"
  30. + form.getPassport() + "' AND created_at > now() - interval '30 days'");
  31. rs.next();
  32. if (rs.getInt(1) > 0) {
  33. return ResponseEntity.ok("Анкета уже на рассмотрении");
  34. }
  35. ScoreResult score = scoring.check(form);
  36. PreparedStatement ins = conn.prepareStatement(
  37. "INSERT INTO applications(passport, amount, term, score) VALUES (?, ?, ?, ?)");
  38. ins.setString(1, form.getPassport());
  39. ins.setBigDecimal(2, form.getAmount());
  40. ins.setInt(3, form.getTermMonths());
  41. ins.setInt(4, score.getValue());
  42. ins.executeUpdate();
  43. return ResponseEntity.ok(score.getValue() >= 600 ? "Предварительно одобрено" : "Отказ");
  44. }
  45. private BigDecimal monthlyPayment(BigDecimal amount, int months) {
  46. double r = ANNUAL_RATE;
  47. double k = r * Math.pow(1 + r, months) / (Math.pow(1 + r, months) - 1);
  48. return new BigDecimal(amount.doubleValue() * k);
  49. }
  50. }