Changed logic for loading flag90days from Ru_Paymt; Added tests

This commit is contained in:
Mikhail Trofimov 2025-09-25 18:02:05 +03:00
parent d1064b6b9f
commit 45ca5f4440
5 changed files with 177 additions and 189 deletions

View file

@ -3,39 +3,12 @@ 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.indic.FlagsService;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@SpringBootApplication
public class CreditTrackerApplication {
public static void main(String[] args) {
ConfigurableApplicationContext ctx = SpringApplication.run(CreditTrackerApplication.class, args);
List<Integer> fids = new ArrayList<>();
fids.add(11085548);
fids.add(16442833);
fids.add(109128766);
fids.add(115150769);
fids.add(16442929);
fids.add(114429070);
fids.add(108696892);
FlagsService flagService = ctx.getBean(FlagsService.class);
Map<Integer, FlagsService.FlagsData> oldMap = flagService.findFlags(fids);
Map<Integer, FlagsService.FlagsData> newMap = flagService.findFlags2(fids);
System.out.println(oldMap);
System.out.println(newMap);
for (Integer fid : fids) {
FlagsService.FlagsData oldValue = oldMap.get(fid);
FlagsService.FlagsData newValue = newMap.get(fid);
System.out.println("FID: " + fid);
System.out.println("OLD: " + oldValue.getFlagDebt5000() +"|" +oldValue.getFlag90days());
System.out.println("NEW: " + newValue.getFlagDebt5000() +"|" +newValue.getFlag90days());
System.out.println("_________________________________");
}
System.exit(0);
// UpdateProcessingPackagesService updateProcessingPackagesService = ctx.getBean(UpdateProcessingPackagesService.class);
// MonitoringService monitoringService = ctx.getBean(MonitoringService.class);
// UpdatingFlagsService updatingFlagsService = ctx.getBean(UpdatingFlagsService.class);

View file

@ -10,12 +10,8 @@ import java.util.List;
@Mapper
public interface FlagsMapper {
List<FlagsService.FlagFidData> findFlagsDebt5000(@Param("fids") List<Integer> fids);
List<FlagsService.FlagFidData> findFlag90ByPastDueArrear(@Param("fids") List<Integer> fids, @Param("reportedDate") LocalDate reportedDate);
List<FlagsService.FlagFidData> findFlag90ByRuPaymt(@Param("fids") List<Integer> fids, @Param("reportedDate") LocalDate reportedDate);
List<FlagsService.RuPastDuarearData> findRuPastDuarearData(@Param("fids") List<Integer> fids);
List<FlagsService.RuPaymtData> findRuPaymtData(@Param("fids") List<Integer> fids, @Param("reportedDate") LocalDate reportedDate);
}

View file

@ -25,36 +25,6 @@ public class FlagsService {
}
public Map<Integer, FlagsData> findFlags(List<Integer> fids) {
if (fids.isEmpty()) {
return new HashMap<>();
}
List<Integer> distinctFids = fids.stream().distinct().toList();
log.debug("Fids count: {}", distinctFids.size());
Map<Integer, Integer> flags5000 = calcFlag5000(distinctFids);
Map<Integer, Integer> flags90DaysByPastDueArrear = calcFlag90DaysByPastDueArrear(distinctFids);
Map<Integer, Integer> flags90DaysByRuPaymt = calcFlag90DaysByRuPaymt(distinctFids);
Map<Integer, FlagsData> 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 FlagsData(flagDebt5000, flag90days));
});
return result;
}
public Map<Integer, FlagsData> findFlags2(List<Integer> fids) {
if (fids.isEmpty()) {
return new HashMap<>();
}
@ -66,8 +36,9 @@ public class FlagsService {
log.debug("Fids count: {}", distinctFids.size());
Map<Integer, Integer> flags5000 = new HashMap<>();
Map<Integer, Integer> flags90DaysByPastDueArrear = new HashMap<>();
Map<Integer, Integer> flags90DaysByRuPaymt = new HashMap<>();
findFlagsRuPastDueArrear(distinctFids, flags5000, flags90DaysByPastDueArrear);
Map<Integer, Integer> flags90DaysByRuPaymt = calcFlag90DaysByRuPaymt(distinctFids);
findFlagsRuPaymt(distinctFids, flags90DaysByRuPaymt);
Map<Integer, FlagsData> result = new HashMap<>();
fids.forEach(fid -> {
@ -90,10 +61,17 @@ public class FlagsService {
}
/**
* Выбирает дату из RU_PASTDUEARREAR и ищет по ним флаги просрочек
* @param fids - отсортирован по возрастанию фидов
* Выбирает данные из 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
* @param flag90Days - рещультирующая мапа флагов просрочек 90_days
*/
private void findFlagsRuPastDueArrear(List<Integer> fids, Map<Integer, Integer> flag5000Map, Map<Integer, Integer> flag90Days) {
log.debug("Start calculating flag 5000 and 90 days by RU_PASTDUEARREAR");
@ -150,50 +128,49 @@ public class FlagsService {
}
/**
* По FID из RU_PASTDUEARREAR выбираются все уникальные ACC_SERIAL_NUM с максимальной датой REPORTED_DT.
* Если по хотя бы одной такой записи AMT_PAST_DUE > 5000, то флаг устанавливается в 1.
* Если ни по одному счёту условие не выполняется или счетов вообще не найдено, флаг = 0.
*/
private Map<Integer, Integer> calcFlag5000(List<Integer> fids) {
log.debug("Start calculating flag 5000");
List<FlagFidData> rows = mapper.findFlagsDebt5000(fids);
Map<Integer, Integer> res = makeFidFlagMapByRs(fids, rows);
log.debug("Complete calculating flag 5000");
return res;
}
/**
* Флаг = 1, если выполняется хотя бы одно из условий:
* В INDIC.RU_PASTDUEARREAR или RU_PAYMT у данного FID есть запись с DAYS_PAST_DUE 90 и REPORTED_DT >= текущая дата 12 месяцев.
* Выбирает данные из RU_PAYMT и ищет по ним флаги просрочек 90Days
*
* flag90Days = 1, если выполняется хотя бы одно из условий:
* В INDIC.RU_PAYMT у данного FID есть запись с DAYS_PAST_DUE 90 и REPORTED_DT >= текущая дата 12 месяцев.
* Если ни по одной записи условия не выполняются или записей вообще не найдено, флаг = 0.
* @param fids - отсортирован по возрастанию фидов
* @param flag90Days - рещультирующая мапа флагов просрочек 90_days
*/
private Map<Integer, Integer> calcFlag90DaysByPastDueArrear(List<Integer> fids) {
log.debug("Start calculating flag 90 by PastDueArrear");
private void findFlagsRuPaymt(List<Integer> fids, Map<Integer, Integer> flag90Days) {
LocalDate yearAgo = LocalDate.now().minusYears(1);
List<FlagFidData> rows = mapper.findFlag90ByPastDueArrear(fids, yearAgo);
Map<Integer, Integer> res = makeFidFlagMapByRs(fids, rows);
log.debug("Complete calculating flag 90 by PastDueArrear");
return res;
}
List<RuPaymtData> ruPaymtDataList = mapper.findRuPaymtData(fids, yearAgo);
ruPaymtDataList.sort(Comparator.comparing(RuPaymtData::getFid, Comparator.naturalOrder()));
private Map<Integer, Integer> calcFlag90DaysByRuPaymt(List<Integer> fids) {
log.debug("Start calculating flag 90 by ruPaymt");
LocalDate yearAgo = LocalDate.now().minusYears(1);
List<FlagFidData> rows = mapper.findFlag90ByRuPaymt(fids, yearAgo);
Map<Integer, Integer> res = makeFidFlagMapByRs(fids, rows);
log.debug("Complete calculating flag 90 by ruPaymt");
return res;
}
private Map<Integer, Integer> makeFidFlagMapByRs(List<Integer> fids, List<FlagFidData> rows) {
Map<Integer, Integer> result = new HashMap<>();
for (Integer fid : fids) {
result.put(fid, 0);
Iterator<RuPaymtData> iterator = ruPaymtDataList.iterator();
while (iterator.hasNext()) {
RuPaymtData data = iterator.next();
if (fid.equals(data.getFid())) {
Integer flag90 = flag90Days.get(fid);
if (flag90 != null && flag90 == 1) {
iterator.remove();
break;
}
if (flag90 == null || flag90 == 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();
}
}
}
}
flag90Days.putIfAbsent(fid, 0);
}
for (FlagFidData row : rows) {
result.put(row.getFid(), row.getFlag() != null ? row.getFlag() : 0);
}
return result;
}
@Getter
@ -204,13 +181,6 @@ public class FlagsService {
private Integer flag90days;
}
@Getter
@Setter
public static class FlagFidData {
private Integer fid;
private Integer flag;
}
@Getter
@Setter
@AllArgsConstructor
@ -225,7 +195,9 @@ public class FlagsService {
@Getter
@Setter
public static class Flag90DaysData {
@AllArgsConstructor
@NoArgsConstructor
public static class RuPaymtData {
private Integer fid;
private BigInteger daysPastDue;
private LocalDate reportedDate;

View file

@ -5,11 +5,6 @@
<mapper namespace="ru.nbch.credit_tracker.mapper.indic.FlagsMapper">
<resultMap id="FlagsFidDataResultMap" type="ru.nbch.credit_tracker.service.indic.FlagsService$FlagFidData">
<result property="fid" column="fid" />
<result property="flag" column="flag" />
</resultMap>
<resultMap id="RuPastDuarearResultMap" type="ru.nbch.credit_tracker.service.indic.FlagsService$RuPastDuarearData">
<result property="fid" column="fid" />
<result property="accSerialNum" column="ACC_SERIAL_NUM" />
@ -18,6 +13,12 @@
<result property="daysPastDue" column="DAYS_PAST_DUE" />
</resultMap>
<resultMap id="RuPaymtResultMap" type="ru.nbch.credit_tracker.service.indic.FlagsService$RuPaymtData">
<result property="fid" column="fid" />
<result property="daysPastDue" column="DAYS_PAST_DUE" />
<result property="reportedDate" column="REPORTED_DT" />
</resultMap>
<select id="findRuPastDuarearData" resultMap="RuPastDuarearResultMap">
SELECT FID, ACC_SERIAL_NUM, REPORTED_DT, AMT_PAST_DUE, DAYS_PAST_DUE
FROM RU_PASTDUEARREAR
@ -28,73 +29,15 @@
with UR
</select>
<select id="findFlagsDebt5000" resultMap="FlagsFidDataResultMap">
WITH latest AS (
SELECT
fid,
acc_serial_num,
amt_past_due,
ROW_NUMBER() OVER (PARTITION BY fid, acc_serial_num ORDER BY reported_dt DESC) AS rn
FROM RU_PASTDUEARREAR
WHERE fid IN
<foreach item="fid" collection="fids" open="(" separator="," close=")">
#{fid}
</foreach>
)
SELECT
fid,
CASE WHEN SUM(CASE WHEN amt_past_due > 5000 THEN 1 ELSE 0 END) > 0
THEN 1 ELSE 0
END AS flag
FROM latest WHERE rn = 1
GROUP BY fid
WITH UR
</select>
<select id="findFlag90ByPastDueArrear" resultMap="FlagsFidDataResultMap">
WITH latest AS (
SELECT
r.fid,
r.days_past_due,
ROW_NUMBER() OVER (PARTITION BY r.fid ORDER BY r.reported_dt DESC) AS rn
FROM RU_PASTDUEARREAR r
WHERE r.fid IN
<foreach item="fid" collection="fids" open="(" separator="," close=")">
#{fid}
</foreach>
AND r.reported_dt >= #{reportedDate}
)
SELECT
fid,
CASE WHEN MAX(CASE WHEN days_past_due > 90 THEN 1 ELSE 0 END) = 1
THEN 1 ELSE 0
END AS flag
FROM latest WHERE rn = 1
GROUP BY fid
WITH UR
</select>
<select id="findFlag90ByRuPaymt" resultMap="FlagsFidDataResultMap">
WITH latest AS (
SELECT
r.fid,
r.days_past_due,
ROW_NUMBER() OVER (PARTITION BY r.fid ORDER BY r.reported_dt DESC) AS rn
FROM RU_PAYMT r
WHERE r.fid IN
<foreach item="fid" collection="fids" open="(" separator="," close=")">
#{fid}
</foreach>
AND r.reported_dt >= #{reportedDate}
)
SELECT
fid,
CASE WHEN MAX(CASE WHEN days_past_due > 90 THEN 1 ELSE 0 END) = 1
THEN 1 ELSE 0
END AS flag
FROM latest WHERE rn = 1
GROUP BY fid
WITH UR
<select id="findRuPaymtData" resultMap="RuPaymtResultMap">
SELECT FID, DAYS_PAST_DUE, REPORTED_DT
FROM RU_PAYMT
WHERE FID IN
<foreach item="fid" collection="fids" open="(" separator="," close=")">
#{fid}
</foreach>
AND REPORTED_DT >= #{reportedDate}
with UR
</select>
</mapper>

View file

@ -29,12 +29,11 @@ public class FlagsServiceTest {
FlagsMapper flagsMapper;
@Test
public void testfindFlagsWithoutRuPaymt() {
public void testfindFlags() {
FlagsService flagsService = new FlagsService(flagsMapper);
when(flagsMapper.findRuPastDuarearData(Mockito.anyList())).thenReturn(ruPastDuarearDataList);
when(flagsMapper.findFlag90ByRuPaymt(Mockito.anyList(), Mockito.any())).thenReturn(new ArrayList<>());
when(flagsMapper.findRuPaymtData(Mockito.anyList(), Mockito.any())).thenReturn(ruPaymtDataList);
List<Integer> fids = new ArrayList<>(List.of(
1, // есть просрочка 5000, есть просрочка 90_days в RU_PASTDUEARREAR
@ -48,7 +47,50 @@ public class FlagsServiceTest {
9) // нету в RU_PASTDUEARREAR, нету в RU_PAYMT
);
Map<Integer, FlagsService.FlagsData> resultMap = flagsService.findFlags2(fids);
Map<Integer, FlagsService.FlagsData> resultMap = flagsService.findFlags(fids);
Assertions.assertEquals(9, resultMap.size());
Assertions.assertEquals(1, resultMap.get(1).getFlagDebt5000());
Assertions.assertEquals(1, resultMap.get(2).getFlagDebt5000());
Assertions.assertEquals(1, resultMap.get(3).getFlagDebt5000());
Assertions.assertEquals(0, resultMap.get(4).getFlagDebt5000());
Assertions.assertEquals(1, resultMap.get(5).getFlagDebt5000());
Assertions.assertEquals(0, resultMap.get(6).getFlagDebt5000());
Assertions.assertEquals(0, resultMap.get(7).getFlagDebt5000());
Assertions.assertEquals(0, resultMap.get(8).getFlagDebt5000());
Assertions.assertEquals(0, resultMap.get(9).getFlagDebt5000());
Assertions.assertEquals(1, resultMap.get(1).getFlag90days());
Assertions.assertEquals(0, resultMap.get(2).getFlag90days());
Assertions.assertEquals(1, resultMap.get(3).getFlag90days());
Assertions.assertEquals(0, resultMap.get(4).getFlag90days());
Assertions.assertEquals(1, resultMap.get(5).getFlag90days());
Assertions.assertEquals(1, resultMap.get(6).getFlag90days());
Assertions.assertEquals(1, resultMap.get(7).getFlag90days());
Assertions.assertEquals(1, resultMap.get(8).getFlag90days());
Assertions.assertEquals(1, resultMap.get(9).getFlag90days());
}
@Test
public void testfindFlagsWithoutRuPaymt() {
FlagsService flagsService = new FlagsService(flagsMapper);
when(flagsMapper.findRuPastDuarearData(Mockito.anyList())).thenReturn(ruPastDuarearDataList);
when(flagsMapper.findRuPaymtData(Mockito.anyList(), Mockito.any())).thenReturn(new ArrayList<>());
List<Integer> 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
);
Map<Integer, FlagsService.FlagsData> resultMap = flagsService.findFlags(fids);
Assertions.assertEquals(9, resultMap.size());
Assertions.assertEquals(1, resultMap.get(1).getFlagDebt5000());
@ -73,6 +115,50 @@ public class FlagsServiceTest {
}
@Test
public void testfindFlagsWithoutRuPastduearreae() {
FlagsService flagsService = new FlagsService(flagsMapper);
when(flagsMapper.findRuPastDuarearData(Mockito.anyList())).thenReturn(new ArrayList<>());
when(flagsMapper.findRuPaymtData(Mockito.anyList(), Mockito.any())).thenReturn(ruPaymtDataList);
List<Integer> 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
);
Map<Integer, FlagsService.FlagsData> resultMap = flagsService.findFlags(fids);
Assertions.assertEquals(9, resultMap.size());
Assertions.assertEquals(0, resultMap.get(1).getFlagDebt5000());
Assertions.assertEquals(0, resultMap.get(2).getFlagDebt5000());
Assertions.assertEquals(0, resultMap.get(3).getFlagDebt5000());
Assertions.assertEquals(0, resultMap.get(4).getFlagDebt5000());
Assertions.assertEquals(0, resultMap.get(5).getFlagDebt5000());
Assertions.assertEquals(0, resultMap.get(6).getFlagDebt5000());
Assertions.assertEquals(0, resultMap.get(7).getFlagDebt5000());
Assertions.assertEquals(0, resultMap.get(8).getFlagDebt5000());
Assertions.assertEquals(0, resultMap.get(9).getFlagDebt5000());
Assertions.assertEquals(0, resultMap.get(1).getFlag90days());
Assertions.assertEquals(0, resultMap.get(2).getFlag90days());
Assertions.assertEquals(1, resultMap.get(3).getFlag90days());
Assertions.assertEquals(0, resultMap.get(4).getFlag90days());
Assertions.assertEquals(0, resultMap.get(5).getFlag90days());
Assertions.assertEquals(1, resultMap.get(6).getFlag90days());
Assertions.assertEquals(0, resultMap.get(7).getFlag90days());
Assertions.assertEquals(1, resultMap.get(8).getFlag90days());
Assertions.assertEquals(1, resultMap.get(9).getFlag90days());
}
private final List<FlagsService.RuPastDuarearData> ruPastDuarearDataList = new ArrayList<>(
List.of(
new FlagsService.RuPastDuarearData(1, BigInteger.valueOf(1), calcLocalDateFromInitialDate("2025-05-31"), BigDecimal.valueOf(36000.00), BigInteger.valueOf(159)), // просрочка 5000 и 90_days по этой записи
@ -106,6 +192,24 @@ public class FlagsServiceTest {
)
);
private final List<FlagsService.RuPaymtData> ruPaymtDataList = new ArrayList<>(
List.of(
new FlagsService.RuPaymtData(1, null, calcLocalDateFromInitialDate("2024-11-09")),
new FlagsService.RuPaymtData(2, BigInteger.valueOf(30), calcLocalDateFromInitialDate("2024-11-09")),
new FlagsService.RuPaymtData(3, null, calcLocalDateFromInitialDate("2024-11-10")),
new FlagsService.RuPaymtData(3, BigInteger.valueOf(100), calcLocalDateFromInitialDate("2024-11-15")),
new FlagsService.RuPaymtData(4, BigInteger.valueOf(10), calcLocalDateFromInitialDate("2024-11-15")),
new FlagsService.RuPaymtData(5, BigInteger.valueOf(10), calcLocalDateFromInitialDate("2023-11-15")),
new FlagsService.RuPaymtData(6, BigInteger.valueOf(90), calcLocalDateFromInitialDate("2024-11-09")),
new FlagsService.RuPaymtData(6, BigInteger.valueOf(80), calcLocalDateFromInitialDate("2024-10-09")),
new FlagsService.RuPaymtData(7, null, calcLocalDateFromInitialDate("2024-10-09")),
new FlagsService.RuPaymtData(8, null, calcLocalDateFromInitialDate("2024-10-09")),
new FlagsService.RuPaymtData(8, BigInteger.valueOf(83), calcLocalDateFromInitialDate("2024-10-09")),
new FlagsService.RuPaymtData(8, BigInteger.valueOf(91), calcLocalDateFromInitialDate("2024-10-09")),
new FlagsService.RuPaymtData(9, BigInteger.valueOf(100), calcLocalDateFromInitialDate("2024-10-09"))
)
);
private LocalDate calcLocalDateFromInitialDate(String date) {
LocalDateTime now = LocalDateTime.now();
long daysPassed = ChronoUnit.DAYS.between(INITIAL_DATE, now);