diff --git a/src/main/java/ru/nbch/credit_tracker/config/datasource/IndicDataSourceConfig.java b/src/main/java/ru/nbch/credit_tracker/config/datasource/IndicDataSourceConfig.java index e83429b..b802514 100644 --- a/src/main/java/ru/nbch/credit_tracker/config/datasource/IndicDataSourceConfig.java +++ b/src/main/java/ru/nbch/credit_tracker/config/datasource/IndicDataSourceConfig.java @@ -9,6 +9,7 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.Resource; +import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import ru.nbch.credit_tracker.config.settings.CreditTrackerSettings; import javax.sql.DataSource; @@ -47,5 +48,10 @@ public class IndicDataSourceConfig extends DatabaseConfig { sqlSessionFactoryBean.setTypeHandlersPackage("ru.nbch.credit_tracker.config.datasource.handlers"); return sqlSessionFactoryBean.getObject(); } + + @Bean(name = "jdbcTemplateIndic") + public NamedParameterJdbcTemplate getNamedParameterJdbcTemplate(@Qualifier("indicDataSource") DataSource dataSource) { + return new NamedParameterJdbcTemplate(dataSource); + } } diff --git a/src/main/java/ru/nbch/credit_tracker/service/indic/FlagsService2.java b/src/main/java/ru/nbch/credit_tracker/service/indic/FlagsService2.java new file mode 100644 index 0000000..720f8e3 --- /dev/null +++ b/src/main/java/ru/nbch/credit_tracker/service/indic/FlagsService2.java @@ -0,0 +1,256 @@ +package ru.nbch.credit_tracker.service.indic; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; +import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.time.LocalDate; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Slf4j +@Service +public class FlagsService2 { + + private static final String SELECT_RU_PASDUEARREAR_DATA = + "SELECT FID, ACC_SERIAL_NUM, REPORTED_DT, AMT_PAST_DUE, DAYS_PAST_DUE " + + "FROM RU_PASTDUEARREAR " + + "WHERE FID IN (:fids) " + + "ORDER BY FID, REPORTED_DT DESC " + + "WITH UR"; + + private static final String SELECT_RU_PAYMT_DATA = + "SELECT FID, DAYS_PAST_DUE, REPORTED_DT " + + "FROM RU_PAYMT " + + "WHERE FID IN (:fids) " + + "AND REPORTED_DT >= :reportedDate " + + "WITH UR"; + + private final NamedParameterJdbcTemplate jdbcTemplate; + + public FlagsService2(@Qualifier("jdbcTemplateIndic") NamedParameterJdbcTemplate jdbcTemplate) { + this.jdbcTemplate = jdbcTemplate; + } + + public Map findFlags(List fids) { + if (fids.isEmpty()) { + return new HashMap<>(); + } + List distinctFids = fids.stream() + .distinct() + .sorted(Comparator.naturalOrder()) + .toList(); + + log.debug("Fids count: {}", distinctFids.size()); + Map flags5000 = new HashMap<>(); + Map flags90DaysByPastDueArrear = new HashMap<>(); + Map flags90DaysByRuPaymt = new HashMap<>(); + findFlagsRuPastDueArrear(distinctFids, flags5000, flags90DaysByPastDueArrear); + findFlagsRuPaymt(distinctFids, flags90DaysByRuPaymt); + + Map result = new HashMap<>(); + fids.forEach(fid -> { + Integer flagDebt5000 = flags5000.get(fid); + if (flagDebt5000 == null) { + flagDebt5000 = 0; + } + + Integer flag90days = flags90DaysByPastDueArrear.get(fid); + if (flag90days == null || flag90days == 0) { + flag90days = flags90DaysByRuPaymt.get(fid); + if (flag90days == null) { + flag90days = 0; + } + } + result.put(fid, new FlagsService.FlagsData(flagDebt5000, flag90days)); + }); + + return result; + } + + /** + * Выбирает данные из RU_PASTDUEARREAR и ищет по ним флаги просрочек + * По FID из RU_PASTDUEARREAR выбираются все уникальные ACC_SERIAL_NUM с максимальной датой REPORTED_DT. + * Если по хотя бы одной такой записи AMT_PAST_DUE > 5000, то flag500 устанавливается в 1. + * Если ни по одному счёту условие не выполняется или счетов вообще не найдено, флаг = 0. + *

+ * flag90Days = 1, если выполняется хотя бы одно из условий: + * В INDIC.RU_PASTDUEARREAR у данного FID есть запись с DAYS_PAST_DUE ≥ 90 и REPORTED_DT >= текущая дата – 12 месяцев. + * Если ни по одной записи условия не выполняются или записей вообще не найдено, флаг = 0. + * + * @param fids - отсортирован по возрастанию фидов + * @param flag5000Map - рещультирующая мапа флагов просрочек debt5000 + * @param flag90Days - рещультирующая мапа флагов просрочек 90_days + */ + private void findFlagsRuPastDueArrear(List fids, Map flag5000Map, Map flag90Days) { + log.debug("Start calculating flag 5000 and 90 days by RU_PASTDUEARREAR 2"); + MapSqlParameterSource params = new MapSqlParameterSource(); + params.addValue("fids", fids); + + jdbcTemplate.query(SELECT_RU_PASDUEARREAR_DATA, params, (ResultSet rs) -> { + Integer currentFid = null; + BigInteger prevAccSerialNum = null; + boolean processed5000ForCurrentId = false; + boolean processed90DaysForCurrentId = false; + LocalDate yearAgo = LocalDate.now().minusYears(1); + + while (rs.next()) { + Integer fid = rs.getInt("FID"); + + // Переход к новому fid. Сброс состояния + if (!fid.equals(currentFid)) { + currentFid = fid; + prevAccSerialNum = null; + processed5000ForCurrentId = false; + processed90DaysForCurrentId = false; + + // Инициализируем значения по умолчанию + flag5000Map.putIfAbsent(fid, 0); + flag90Days.putIfAbsent(fid, 0); + } + + // Если для текущего FID уже обработали оба флага, пропускаем остальные записи + if (processed5000ForCurrentId && processed90DaysForCurrentId) { + continue; + } + + // Обработка флага 5000 + if (!processed5000ForCurrentId && flag5000Map.get(fid) == 0) { + BigInteger currentAccSerialNum = getBigInteger(rs, "ACC_SERIAL_NUM"); + BigDecimal amtPastDue = getBigDecimal(rs, "AMT_PAST_DUE"); + + if (prevAccSerialNum == null || !prevAccSerialNum.equals(currentAccSerialNum)) { + if (amtPastDue != null && amtPastDue.compareTo(BigDecimal.valueOf(5000)) >= 0) { + flag5000Map.put(fid, 1); + processed5000ForCurrentId = true; + } + prevAccSerialNum = currentAccSerialNum; + } + } + + // Обработка флага 90 дней + if (!processed90DaysForCurrentId) { + Integer currentFlag90 = flag90Days.get(fid); + + if (currentFlag90 != null && currentFlag90 == 1) { + processed90DaysForCurrentId = true; + continue; + } + + if (currentFlag90 == null || currentFlag90 == 0) { + BigInteger days = getBigInteger(rs, "DAYS_PAST_DUE"); + LocalDate reportedDate = rs.getObject("REPORTED_DT", LocalDate.class); + + if (days != null) { + boolean has90daysFlag = days.compareTo(BigInteger.valueOf(90)) >= 0 + && reportedDate != null && !reportedDate.isBefore(yearAgo); + + if (has90daysFlag) { + flag90Days.put(fid, 1); + processed90DaysForCurrentId = true; + } else { + flag90Days.put(fid, 0); + } + } else { + flag90Days.put(fid, 0); + } + } + } + } + return null; + }); + log.debug("Complete calculating flag 5000 and 90 days by RU_PASTDUEARREAR 2"); + } + + /** + * Выбирает данные из RU_PAYMT и ищет по ним флаги просрочек 90Days + *

+ * flag90Days = 1, если выполняется хотя бы одно из условий: + * В INDIC.RU_PAYMT у данного FID есть запись с DAYS_PAST_DUE ≥ 90 и REPORTED_DT >= текущая дата – 12 месяцев. + * Если ни по одной записи условия не выполняются или записей вообще не найдено, флаг = 0. + * + * @param fids - отсортирован по возрастанию фидов + * @param flag90Days - рещультирующая мапа флагов просрочек 90_days + */ + private void findFlagsRuPaymt(List fids, Map flag90Days) { + log.debug("Start calculating flag 90 days by RU_PAPAYMT 2"); + LocalDate yearAgo = LocalDate.now().minusYears(1); + MapSqlParameterSource params = new MapSqlParameterSource(); + params.addValue("fids", fids); + params.addValue("reportedDate", yearAgo); + jdbcTemplate.query(SELECT_RU_PAYMT_DATA, params, rs -> { + Integer currentFid = null; + boolean processed90DaysForCurrentId = false; + + while (rs.next()) { + Integer fid = rs.getInt("FID"); + + // Переход к новому fid. Сброс состояния + if (!fid.equals(currentFid)) { + currentFid = fid; + processed90DaysForCurrentId = false; + + // Инициализируем значения по умолчанию + flag90Days.putIfAbsent(fid, 0); + } + + // Обработка флага 90 дней + if (!processed90DaysForCurrentId) { + Integer currentFlag90 = flag90Days.get(fid); + + if (currentFlag90 != null && currentFlag90 == 1) { + processed90DaysForCurrentId = true; + continue; + } + + if (currentFlag90 == null || currentFlag90 == 0) { + BigInteger days = getBigInteger(rs, "DAYS_PAST_DUE"); + LocalDate date = rs.getObject("REPORTED_DT", LocalDate.class); + + if (days != null) { + boolean has90daysFlag = days.compareTo(BigInteger.valueOf(90)) >= 0 + && date != null && !date.isBefore(yearAgo); + + if (has90daysFlag) { + flag90Days.put(fid, 1); + processed90DaysForCurrentId = true; + } else { + flag90Days.put(fid, 0); + } + } else { + flag90Days.put(fid, 0); + } + } + } + } + return null; + }); + log.debug("Complete calculating flag 90 days by RU_PAPAYMT 2"); + } + + + private BigInteger getBigInteger(ResultSet rs, String columnName) { + try { + String value = rs.getString(columnName); + return value != null ? new BigInteger(value) : null; + } catch (SQLException e) { + return null; + } + } + + private BigDecimal getBigDecimal(ResultSet rs, String columnName) { + try { + return rs.getBigDecimal(columnName); + } catch (SQLException e) { + return null; + } + } +}