From c245790eb5c5e945bb8733fb109391962dd7266f Mon Sep 17 00:00:00 2001 From: Mikhail Trofimov Date: Thu, 25 Sep 2025 13:07:39 +0300 Subject: [PATCH] Added test variant of counting flags 500 and 90_days from RU_PASTDUEARREAR --- credit-tracker.properties | 6 +- .../CreditTrackerApplication.java | 28 ++- .../mapper/indic/FlagsMapper.java | 6 +- .../service/indic/FlagsService.java | 113 +++++++++--- .../mybatis/mapper/indic/FlagsMapper.xml | 164 ++---------------- .../service/FlagsServiceTest.java | 152 ++++++++++++++++ 6 files changed, 281 insertions(+), 188 deletions(-) create mode 100644 src/test/java/ru/nbch/credit_tracker/service/FlagsServiceTest.java diff --git a/credit-tracker.properties b/credit-tracker.properties index a337a0e..c9fdfd6 100644 --- a/credit-tracker.properties +++ b/credit-tracker.properties @@ -8,9 +8,9 @@ server.tomcat.accesslog.pattern=%h %l %u %t "%r" %s %b # datasources # indic ct.indic-data-source-settings.driver=com.ibm.db2.jcc.DB2Driver -ct.indic-data-source-settings.url=${DB_URL_INDIC:jdbc:db2://10.230.227.101:2668/cprosd22:currentSchema=INDIC;} -ct.indic-data-source-settings.username=${USERNAME_INDIC:tester} -ct.indic-data-source-settings.password=${PASSWORD_INDIC:tester} +ct.indic-data-source-settings.url=${DB_URL_INDIC:jdbc:db2://10.230.227.100:2668/cprosd22:currentSchema=INDIC;} +ct.indic-data-source-settings.username=${USERNAME_INDIC:ros9appl} +ct.indic-data-source-settings.password=${PASSWORD_INDIC:R0s9Appl} # signal ct.signal-data-source-settings.driver=org.postgresql.Driver diff --git a/src/main/java/ru/nbch/credit_tracker/CreditTrackerApplication.java b/src/main/java/ru/nbch/credit_tracker/CreditTrackerApplication.java index d3a78f1..b30948b 100644 --- a/src/main/java/ru/nbch/credit_tracker/CreditTrackerApplication.java +++ b/src/main/java/ru/nbch/credit_tracker/CreditTrackerApplication.java @@ -3,21 +3,31 @@ package ru.nbch.credit_tracker; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext; -import ru.nbch.credit_tracker.service.MonitoringService; -import ru.nbch.credit_tracker.service.UpdateProcessingPackagesService; -import ru.nbch.credit_tracker.service.UpdatingFlagsService; +import ru.nbch.credit_tracker.service.indic.FlagsService; + +import java.util.ArrayList; +import java.util.List; @SpringBootApplication public class CreditTrackerApplication { public static void main(String[] args) { ConfigurableApplicationContext ctx = SpringApplication.run(CreditTrackerApplication.class, args); - UpdateProcessingPackagesService updateProcessingPackagesService = ctx.getBean(UpdateProcessingPackagesService.class); - MonitoringService monitoringService = ctx.getBean(MonitoringService.class); - UpdatingFlagsService updatingFlagsService = ctx.getBean(UpdatingFlagsService.class); - updateProcessingPackagesService.run(); - monitoringService.run(); - updatingFlagsService.run(); + List fids = new ArrayList<>(); + fids.add(11085548); + fids.add(16442833); + fids.add(109128766); + fids.add(115150769); + fids.add(16442929); + fids.add(114429070); + fids.add(108696892); + ctx.getBean(FlagsService.class).test(fids); +// UpdateProcessingPackagesService updateProcessingPackagesService = ctx.getBean(UpdateProcessingPackagesService.class); +// MonitoringService monitoringService = ctx.getBean(MonitoringService.class); +// UpdatingFlagsService updatingFlagsService = ctx.getBean(UpdatingFlagsService.class); +// updateProcessingPackagesService.run(); +// monitoringService.run(); +// updatingFlagsService.run(); } } diff --git a/src/main/java/ru/nbch/credit_tracker/mapper/indic/FlagsMapper.java b/src/main/java/ru/nbch/credit_tracker/mapper/indic/FlagsMapper.java index 3ab6e2b..1a162a7 100644 --- a/src/main/java/ru/nbch/credit_tracker/mapper/indic/FlagsMapper.java +++ b/src/main/java/ru/nbch/credit_tracker/mapper/indic/FlagsMapper.java @@ -16,11 +16,7 @@ public interface FlagsMapper { List findFlag90ByRuPaymt(@Param("fids") List fids, @Param("reportedDate") LocalDate reportedDate); - - List findFlagsDebt5000NewWithOrderBy(@Param("fids") List fids); - - - List findFlag90ByPastDueArrearNewWithOrderBy(@Param("fids") List fids); + List findRuPastDuarearData(@Param("fids") List fids); List findFlag90ByRuPaymtNewWithOrderBy(@Param("fids") List fids); diff --git a/src/main/java/ru/nbch/credit_tracker/service/indic/FlagsService.java b/src/main/java/ru/nbch/credit_tracker/service/indic/FlagsService.java index 783ec5c..bb5d848 100644 --- a/src/main/java/ru/nbch/credit_tracker/service/indic/FlagsService.java +++ b/src/main/java/ru/nbch/credit_tracker/service/indic/FlagsService.java @@ -2,6 +2,7 @@ package ru.nbch.credit_tracker.service.indic; import lombok.AllArgsConstructor; import lombok.Getter; +import lombok.NoArgsConstructor; import lombok.Setter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -11,9 +12,7 @@ import ru.nbch.credit_tracker.mapper.indic.FlagsMapper; import java.math.BigDecimal; import java.math.BigInteger; import java.time.LocalDate; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; @Service public class FlagsService { @@ -25,6 +24,88 @@ public class FlagsService { this.mapper = mapper; } + public void test(List fids) { + Map oldFlags5000 = calcFlag5000(fids); + Map oldFlags90 = calcFlag90DaysByPastDueArrear(fids); + + fids.sort(Comparator.naturalOrder()); + + Map flag5000Map = new HashMap<>(); + Map flag90Days = new HashMap<>(); + findFlags5000(fids, flag5000Map, flag90Days); + + for (Map.Entry flag : flag5000Map.entrySet()) { + if(!flag.getValue().equals(oldFlags5000.get(flag.getKey()))) { + String error = "New: "+flag.getKey() + " = " + flag.getValue() + "\n" +"Old: " + flag.getKey() + " = " + oldFlags5000.get(flag.getKey()); + throw new AssertionError(error); + } + } + + for (Map.Entry flag : flag90Days.entrySet()) { + if(!flag.getValue().equals(oldFlags90.get(flag.getKey()))) { + String error = "New: "+flag.getKey() + " = " + flag.getValue() + "\n" +"Old: " + flag.getKey() + " = " + oldFlags90.get(flag.getKey()); + throw new AssertionError(error); + } + } + + } + + private void findFlags5000(List fids, Map flag5000Map, Map flag90Days) { + List ruPastDuarearDataList = mapper.findRuPastDuarearData(fids); + ruPastDuarearDataList.sort(Comparator.comparing(FlagsService.RuPastDuarearData::getFid) // сортируем сначала по фиду + .thenComparing(FlagsService.RuPastDuarearData::getReportedDate, Comparator.reverseOrder())); // Затем сортируем по REPORTED_DT по убыванию + + LocalDate yearAgo = LocalDate.parse("2024-09-09"); + + for (Integer fid : fids) { + Iterator iterator = ruPastDuarearDataList.iterator(); + BigInteger prevAccSerialNum = null; + while (iterator.hasNext()) { + FlagsService.RuPastDuarearData data = iterator.next(); + + if (fid.equals(data.getFid())) { + if (flag5000Map.get(fid) == null) { // Первая запись в списке с макс REPORTED_DT. Нас интересует только она для флага 5000 + if (prevAccSerialNum == null || !prevAccSerialNum.equals(data.getAccSerialNum())) { + if (data.getAmtPastDue() != null && data.getAmtPastDue().compareTo(BigDecimal.valueOf(5000)) >= 0) { + flag5000Map.put(fid, 1); + } else { + flag5000Map.put(fid, 0); + } + prevAccSerialNum = data.getAccSerialNum(); + } + } + + if (flag90Days.get(fid) != null && flag90Days.get(fid) == 1) { + iterator.remove(); + break; + } + + if (flag90Days.get(fid) == null || flag90Days.get(fid) == 0) { + if (data.getDaysPastDue() != null) { + boolean has90daysFlag = data.getDaysPastDue().compareTo(BigInteger.valueOf(90)) >= 0 && !data.getReportedDate().isBefore(yearAgo); + if (has90daysFlag) { + flag90Days.put(fid, 1); + iterator.remove(); + break; + } else { + flag90Days.put(fid, 0); + iterator.remove(); + } + } else { + iterator.remove(); + } + } + } + } + if (flag5000Map.get(fid) == null) { + flag5000Map.put(fid, 0); + } + if (flag90Days.get(fid) == null) { + flag90Days.put(fid, 0); + } + } + } + public Map findFlags(List fids) { if (fids.isEmpty()) { return new HashMap<>(); @@ -32,11 +113,8 @@ public class FlagsService { List distinctFids = fids.stream().distinct().toList(); log.debug("Fids count: {}", distinctFids.size()); Map flags5000 = calcFlag5000(distinctFids); - calcFlag500Test(distinctFids); Map flags90DaysByPastDueArrear = calcFlag90DaysByPastDueArrear(distinctFids); - calcFlag90DaysByPastDueArrearTest(distinctFids); Map flags90DaysByRuPaymt = calcFlag90DaysByRuPaymt(distinctFids); - calcFlag90DaysByRuPaymtTest(distinctFids); Map result = new HashMap<>(); fids.forEach(fid -> { @@ -71,12 +149,6 @@ public class FlagsService { return res; } - private void calcFlag500Test(List fids) { - log.debug("Start findFlagsDebt5000NewWithOrderBy"); - mapper.findFlagsDebt5000NewWithOrderBy(fids); - log.debug("Complete findFlagsDebt5000NewWithOrderBy"); - } - /** * Флаг = 1, если выполняется хотя бы одно из условий: * В INDIC.RU_PASTDUEARREAR или RU_PAYMT у данного FID есть запись с DAYS_PAST_DUE ≥ 90 и REPORTED_DT >= текущая дата – 12 месяцев. @@ -91,12 +163,6 @@ public class FlagsService { return res; } - private void calcFlag90DaysByPastDueArrearTest(List fids) { - log.debug("Start findFlag90ByPastDueArrearNewWithOrderBy"); - mapper.findFlag90ByPastDueArrearNewWithOrderBy(fids); - log.debug("Complete findFlag90ByPastDueArrearNewWithOrderBy"); - } - private Map calcFlag90DaysByRuPaymt(List fids) { log.debug("Start calculating flag 90 by ruPaymt"); LocalDate yearAgo = LocalDate.now().minusYears(1); @@ -106,12 +172,6 @@ public class FlagsService { return res; } - private void calcFlag90DaysByRuPaymtTest(List fids) { - log.debug("Start findFlag90ByRuPaymtNewWithOrderBy"); - mapper.findFlag90ByRuPaymtNewWithOrderBy(fids); - log.debug("Complete findFlag90ByRuPaymtNewWithOrderBy"); - } - private Map makeFidFlagMapByRs(List fids, List rows) { Map result = new HashMap<>(); for (Integer fid : fids) { @@ -140,11 +200,14 @@ public class FlagsService { @Getter @Setter - public static class Debt5000Data { + @AllArgsConstructor + @NoArgsConstructor + public static class RuPastDuarearData { private Integer fid; private BigInteger accSerialNum; private LocalDate reportedDate; private BigDecimal amtPastDue; + private BigInteger daysPastDue; } @Getter diff --git a/src/main/resources/mybatis/mapper/indic/FlagsMapper.xml b/src/main/resources/mybatis/mapper/indic/FlagsMapper.xml index 4ad119e..ab3bb98 100644 --- a/src/main/resources/mybatis/mapper/indic/FlagsMapper.xml +++ b/src/main/resources/mybatis/mapper/indic/FlagsMapper.xml @@ -10,6 +10,24 @@ + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/test/java/ru/nbch/credit_tracker/service/FlagsServiceTest.java b/src/test/java/ru/nbch/credit_tracker/service/FlagsServiceTest.java new file mode 100644 index 0000000..41fb346 --- /dev/null +++ b/src/test/java/ru/nbch/credit_tracker/service/FlagsServiceTest.java @@ -0,0 +1,152 @@ +package ru.nbch.credit_tracker.service; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import ru.nbch.credit_tracker.mapper.indic.FlagsMapper; +import ru.nbch.credit_tracker.service.indic.FlagsService; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.time.LocalDate; +import java.util.*; + +@ExtendWith(MockitoExtension.class) +public class FlagsServiceTest { + + @Mock + FlagsMapper flagsMapper; + + @Test + public void test() { + List fids = new ArrayList<>(List.of( + 1, // есть просрочка 5000, есть просрочка 90_days в RU_PASTDUEARREAR + 2, // есть просрочка 5000, нет просрочки 90_days в RU_PASTDUEARREAR. Нет просрочки 90_days в RU_PAYMT + 3, // есть просрочка 5000, нет просрочки 90_days в RU_PASTDUEARREAR. Есть просрочка 90_days в RU_PAYMT + 4, // нет никаких просрочек + 7, // нет просрочки 5000, есть просрочка 90_days в RU_PASTDUEARREAR + 5, // есть просрочка 5000, есть просрочка 90_days в RU_PASTDUEARREAR + 6, // нет просрочки 5000, нет просрочки 90_days в RU_PASTDUEARREAR. Есть просрочка 90_days в RU_PAYMT + 8, // нету в RU_PASTDUEARREAR, есть в RU_PAYMT + 9) // нету в RU_PASTDUEARREAR, нету в RU_PAYMT + ); + + fids.sort(Comparator.naturalOrder()); + + Map flag5000Map = new HashMap<>(); + Map flag90Days = new HashMap<>(); + findFlags5000(fids, flag5000Map, flag90Days); + + Assertions.assertEquals(9, flag5000Map.size()); + Assertions.assertEquals(true, flag5000Map.get(1)); + Assertions.assertEquals(true, flag5000Map.get(2)); + Assertions.assertEquals(true, flag5000Map.get(3)); + Assertions.assertEquals(false, flag5000Map.get(4)); + Assertions.assertEquals(true, flag5000Map.get(5)); + Assertions.assertEquals(false, flag5000Map.get(6)); + Assertions.assertEquals(false, flag5000Map.get(7)); + Assertions.assertEquals(false, flag5000Map.get(8)); + Assertions.assertEquals(false, flag5000Map.get(9)); + + Assertions.assertEquals(9, flag90Days.size()); + Assertions.assertEquals(true, flag90Days.get(1)); + Assertions.assertEquals(false, flag90Days.get(2)); + Assertions.assertEquals(false, flag90Days.get(3)); + Assertions.assertEquals(false, flag90Days.get(4)); + Assertions.assertEquals(true, flag90Days.get(5)); + Assertions.assertEquals(false, flag90Days.get(6)); + Assertions.assertEquals(true, flag90Days.get(7)); + Assertions.assertEquals(false, flag90Days.get(8)); + Assertions.assertEquals(false, flag90Days.get(9)); + } + + private void findFlags5000(List fids, Map flag5000Map, Map flag90Days) { + ruPastDuarearDataList.sort(Comparator.comparing(FlagsService.RuPastDuarearData::getFid) // сортируем сначала по фиду + .thenComparing(FlagsService.RuPastDuarearData::getReportedDate, Comparator.reverseOrder())); // Затем сортируем по REPORTED_DT по убыванию + + LocalDate yearAgo = LocalDate.parse("2024-09-09"); + + for (Integer fid : fids) { + Iterator iterator = ruPastDuarearDataList.iterator(); + BigInteger prevAccSerialNum = null; + while (iterator.hasNext()) { + FlagsService.RuPastDuarearData data = iterator.next(); + + if (fid.equals(data.getFid())) { + if (flag5000Map.get(fid) == null) { // Первая запись в списке с макс REPORTED_DT. Нас интересует только она для флага 5000 + if (prevAccSerialNum == null || !prevAccSerialNum.equals(data.getAccSerialNum())) { + if (data.getAmtPastDue() != null && data.getAmtPastDue().compareTo(BigDecimal.valueOf(5000)) >= 0) { + flag5000Map.put(fid, true); + } else { + flag5000Map.put(fid, false); + } + prevAccSerialNum = data.getAccSerialNum(); + } + } + + if (flag90Days.get(fid) != null && flag90Days.get(fid).equals(Boolean.TRUE)) { + iterator.remove(); + break; + } + + if (flag90Days.get(fid) == null || flag90Days.get(fid).equals(Boolean.FALSE)) { + if (data.getDaysPastDue() != null) { + boolean has90daysFlag = data.getDaysPastDue().compareTo(BigInteger.valueOf(90)) >= 0 && !data.getReportedDate().isBefore(yearAgo); + if (has90daysFlag) { + flag90Days.put(fid, true); + iterator.remove(); + break; + } else { + flag90Days.put(fid, false); + iterator.remove(); + } + } else { + iterator.remove(); + } + } + } + } + if (flag5000Map.get(fid) == null) { + flag5000Map.put(fid, false); + } + if (flag90Days.get(fid) == null) { + flag90Days.put(fid, false); + } + } + } + + private final List ruPastDuarearDataList = new ArrayList<>( + List.of( + new FlagsService.RuPastDuarearData(1, BigInteger.valueOf(1), LocalDate.parse("2025-12-31"), BigDecimal.valueOf(36000.00), BigInteger.valueOf(159)), // просрочка 5000 и 90_days по этой записи + + new FlagsService.RuPastDuarearData(2, BigInteger.valueOf(2), LocalDate.parse("2021-12-29"), BigDecimal.valueOf(3200.00), null), // не макс reported_date для того же acc_serial_num + new FlagsService.RuPastDuarearData(2, BigInteger.valueOf(2), LocalDate.parse("2022-01-28"), BigDecimal.valueOf(6700.00), null), // не макс reported_date для того же acc_serial_num + new FlagsService.RuPastDuarearData(2, BigInteger.valueOf(2), LocalDate.parse("2025-04-06"), BigDecimal.valueOf(5700.00), null), // просрочка 5000 по этой записи + + new FlagsService.RuPastDuarearData(3, BigInteger.valueOf(3), LocalDate.parse("2025-01-14"), BigDecimal.valueOf(300000000000.00), null), + + new FlagsService.RuPastDuarearData(2, BigInteger.valueOf(2), LocalDate.parse("2021-11-29"), BigDecimal.valueOf(0.00), null), // не макс reported_date для того же acc_serial_num + + new FlagsService.RuPastDuarearData(4, BigInteger.valueOf(4), LocalDate.parse("2025-12-06"), BigDecimal.valueOf(4000.00), null), // нет просрочек + + new FlagsService.RuPastDuarearData(5, BigInteger.valueOf(5), LocalDate.parse("2021-04-01"), BigDecimal.valueOf(2000.31), BigInteger.valueOf(182)), // не макс reported_date для того же acc_serial_num + new FlagsService.RuPastDuarearData(5, BigInteger.valueOf(5), LocalDate.parse("2021-05-01"), BigDecimal.valueOf(2000.31), BigInteger.valueOf(212)), // не макс reported_date для того же acc_serial_num + new FlagsService.RuPastDuarearData(5, BigInteger.valueOf(5), LocalDate.parse("2021-05-31"), BigDecimal.valueOf(2000.31), null), // не макс reported_date для того же acc_serial_num + new FlagsService.RuPastDuarearData(5, BigInteger.valueOf(5), LocalDate.parse("2021-07-01"), BigDecimal.valueOf(2000.31), BigInteger.valueOf(273)), // не макс reported_date для того же acc_serial_num + new FlagsService.RuPastDuarearData(5, BigInteger.valueOf(5), LocalDate.parse("2021-07-31"), BigDecimal.valueOf(2000.31), null), // не макс reported_date для того же acc_serial_num + new FlagsService.RuPastDuarearData(5, BigInteger.valueOf(5), LocalDate.parse("2021-08-04"), BigDecimal.valueOf(7000.31), BigInteger.valueOf(307)), // не макс reported_date для того же acc_serial_num + new FlagsService.RuPastDuarearData(5, BigInteger.valueOf(5), LocalDate.parse("2025-09-08"), BigDecimal.valueOf(2000.31), BigInteger.valueOf(338)), // не макс reported_date для того же acc_serial_num, просрочка 90_days будет взята у этой записи + new FlagsService.RuPastDuarearData(5, BigInteger.valueOf(51), LocalDate.parse("2025-09-09"), BigDecimal.valueOf(7000.31), BigInteger.valueOf(368)), // Просрочка 5000 будет взята у этой записи. Уникальный acc_serial_num с макс reported_dt + new FlagsService.RuPastDuarearData(5, BigInteger.valueOf(5), LocalDate.parse("2021-11-04"), BigDecimal.valueOf(2000.31), BigInteger.valueOf(399)), // не макс reported_date для того же acc_serial_num + new FlagsService.RuPastDuarearData(5, BigInteger.valueOf(5), LocalDate.parse("2022-01-04"), BigDecimal.valueOf(5000.31), BigInteger.valueOf(5)), // не макс reported_date для того же acc_serial_num + + new FlagsService.RuPastDuarearData(6, BigInteger.valueOf(6), LocalDate.parse("2025-09-09"), BigDecimal.valueOf(3000), null), // Нет просрочки 5000, нет просрочки 90_days (90_days будет в RU_PAYMT) + + new FlagsService.RuPastDuarearData(7, BigInteger.valueOf(7), LocalDate.parse("2025-09-09"), BigDecimal.valueOf(5500), BigInteger.valueOf(90)), // не макс reported_date для того же acc_serial_num. 90_days будет взята отсюда + new FlagsService.RuPastDuarearData(7, BigInteger.valueOf(7), LocalDate.parse("2025-09-10"), BigDecimal.valueOf(3000), null) // Нет просрочки 5000. Уникальный acc_serial_num с макс reported_dt + + ) + ); +}