diff --git a/pom.xml b/pom.xml
index f876eb6..ffd17aa 100644
--- a/pom.xml
+++ b/pom.xml
@@ -27,6 +27,10 @@
org.springframework.boot
spring-boot-starter-web
+
+ org.springframework.boot
+ spring-boot-starter-aop
+
org.mybatis.spring.boot
mybatis-spring-boot-starter
@@ -122,8 +126,83 @@
3.0.4
test
+
+ org.mockito
+ mockito-junit-jupiter
+ test
+
+
+
+ docker-push-dev
+
+
+
+
+ io.fabric8
+ docker-maven-plugin
+ 0.45.0
+
+
+ Build and deploy docker container
+ install
+
+
+ push
+
+
+
+
+
+
+ ${project.artifactId}:${project.version}
+ ${registry.url}
+
+ ${project.basedir}/Dockerfile
+
+
+
+
+ ${project.build.directory}/${project.build.finalName}.jar
+ .
+
+
+ ${project.basedir}/apm-agent/elastic-apm-agent-1.45.0.jar
+ .
+
+
+ ${project.basedir}/start-credit-tracker.sh
+ .
+
+
+ ${project.basedir}/credit-tracker.properties
+ .
+
+
+ ${project.basedir}/monitoring-request.xsd
+ .
+
+
+ ${project.basedir}/signal-package.xsd
+ .
+
+
+
+
+
+ ${project.version}
+
+
+
+
+
+
+
+
+
+
+
credit-tracker
@@ -181,65 +260,7 @@
-
- io.fabric8
- docker-maven-plugin
- 0.45.0
-
-
- Build and deploy docker container
- install
-
- build
- push
-
-
-
-
-
-
- ${project.artifactId}:${project.version}
- ${registry.url}
-
- ${project.basedir}/Dockerfile
-
-
-
-
- ${project.build.directory}/${project.build.finalName}.jar
- .
-
-
- ${project.basedir}/apm-agent/elastic-apm-agent-1.45.0.jar
- .
-
-
- ${project.basedir}/start-credit-tracker.sh
- .
-
-
- ${project.basedir}/credit-tracker.properties
- .
-
-
- ${project.basedir}/monitoring-request.xsd
- .
-
-
- ${project.basedir}/signal-package.xsd
- .
-
-
-
-
-
- ${project.version}
-
-
-
-
-
-
+
diff --git a/src/main/java/ru/nbch/credit_tracker/component/MapperTimingAspect.java b/src/main/java/ru/nbch/credit_tracker/component/MapperTimingAspect.java
new file mode 100644
index 0000000..7802484
--- /dev/null
+++ b/src/main/java/ru/nbch/credit_tracker/component/MapperTimingAspect.java
@@ -0,0 +1,28 @@
+package ru.nbch.credit_tracker.component;
+
+import lombok.extern.slf4j.Slf4j;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.springframework.stereotype.Component;
+
+@Aspect
+@Slf4j
+@Component
+public class MapperTimingAspect {
+ @Around("within(@org.apache.ibatis.annotations.Mapper *)")
+ public Object timeMapper(ProceedingJoinPoint pjp) throws Throwable {
+ long start = System.nanoTime();
+ try {
+ return pjp.proceed();
+ } finally {
+ long tookMs = (System.nanoTime() - start) / 1_000_000;
+ String method = pjp.getSignature().toShortString();
+ if (tookMs > 200) {
+ log.warn("Slow mapper call {} took {} ms", method, tookMs);
+ } else {
+ log.debug("Mapper call {} took {} ms", method, tookMs);
+ }
+ }
+ }
+}
diff --git a/src/main/java/ru/nbch/credit_tracker/config/rest/filters/HttpLoggingFilter.java b/src/main/java/ru/nbch/credit_tracker/config/rest/filters/HttpLoggingFilter.java
new file mode 100644
index 0000000..bd8bd3c
--- /dev/null
+++ b/src/main/java/ru/nbch/credit_tracker/config/rest/filters/HttpLoggingFilter.java
@@ -0,0 +1,111 @@
+package ru.nbch.credit_tracker.config.rest.filters;
+
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.UUID;
+import java.util.stream.Collectors;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
+import org.springframework.web.filter.OncePerRequestFilter;
+import org.springframework.web.util.ContentCachingRequestWrapper;
+import org.springframework.web.util.ContentCachingResponseWrapper;
+
+@Component
+@Slf4j
+@Order(0)
+public class HttpLoggingFilter extends OncePerRequestFilter {
+
+ private static final Set SENSITIVE_HEADERS = Set.of("authorization", "cookie", "set-cookie");
+ private static final int MAX_BODY = 10 * 1024;
+
+ @Override
+ protected boolean shouldNotFilter(HttpServletRequest request) {
+ String path = request.getRequestURI();
+ return path.startsWith("/actuator/health") || path.startsWith("/swagger")
+ || path.startsWith("/v3/api-docs") || path.startsWith("/static/");
+ }
+
+ @Override
+ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
+ throws ServletException, IOException {
+
+ long startNs = System.nanoTime();
+
+ ContentCachingRequestWrapper req = wrapRequest(request);
+ ContentCachingResponseWrapper resp = new ContentCachingResponseWrapper(response);
+
+ String correlationId = Optional.ofNullable(request.getHeader("X-Correlation-Id"))
+ .filter(StringUtils::hasText)
+ .orElse(UUID.randomUUID().toString());
+ resp.setHeader("X-Correlation-Id", correlationId);
+
+ try {
+ chain.doFilter(req, resp);
+ } finally {
+ long tookMs = (System.nanoTime() - startNs) / 1_000_000;
+
+ String requestBody = readBody(req.getContentAsByteArray(), req.getCharacterEncoding());
+ String responseBody = readBody(resp.getContentAsByteArray(), resp.getCharacterEncoding());
+
+ Map reqHeaders = headersMap(req);
+ Map respHeaders = headersMap(resp);
+
+ mask(reqHeaders);
+ mask(respHeaders);
+
+ log.info(
+ "HTTP {} {}{} | status={} | took={}ms | cid={} | reqHeaders={} | reqBody={} | respHeaders={} | respBody={}",
+ req.getMethod(),
+ req.getRequestURI(),
+ req.getQueryString() == null ? "" : "?" + req.getQueryString(),
+ resp.getStatus(),
+ tookMs,
+ correlationId,
+ reqHeaders,
+ requestBody,
+ respHeaders,
+ responseBody
+ );
+
+ resp.copyBodyToResponse();
+ }
+ }
+
+ private static ContentCachingRequestWrapper wrapRequest(HttpServletRequest request) {
+ return (request instanceof ContentCachingRequestWrapper c) ? c : new ContentCachingRequestWrapper(request, MAX_BODY);
+ }
+
+ private static String readBody(byte[] buf, String encoding) {
+ if (buf == null || buf.length == 0) return "";
+ var bytes = buf.length > MAX_BODY ? Arrays.copyOf(buf, MAX_BODY) : buf;
+ String body = new String(bytes, encoding != null ? java.nio.charset.Charset.forName(encoding) : StandardCharsets.UTF_8);
+ if (buf.length > MAX_BODY) body += "…[truncated]";
+ return body;
+ }
+
+ private static Map headersMap(HttpServletRequest req) {
+ return Collections.list(req.getHeaderNames()).stream()
+ .collect(Collectors.toMap(h -> h, req::getHeader, (a, b) -> a, LinkedHashMap::new));
+ }
+
+ private static Map headersMap(HttpServletResponse resp) {
+ return resp.getHeaderNames().stream()
+ .collect(Collectors.toMap(h -> h, resp::getHeader, (a, b) -> a, LinkedHashMap::new));
+ }
+
+ private static void mask(Map headers) {
+ headers.replaceAll((k, v) -> SENSITIVE_HEADERS.contains(k.toLowerCase()) ? "***" : v);
+ }
+}
diff --git a/src/main/java/ru/nbch/credit_tracker/mapper/signal/PhoneFidMapMapper.java b/src/main/java/ru/nbch/credit_tracker/mapper/signal/PhoneFidMapMapper.java
index 0e42995..80f216f 100644
--- a/src/main/java/ru/nbch/credit_tracker/mapper/signal/PhoneFidMapMapper.java
+++ b/src/main/java/ru/nbch/credit_tracker/mapper/signal/PhoneFidMapMapper.java
@@ -15,6 +15,7 @@ public interface PhoneFidMapMapper {
List findFullPhoneFidMap(@Param("packageId") Integer packageId, @Param("offset") int offset, @Param("limit") int limit);
void updatePhoneFidMaps(@Param("subjects") List subjects);
+ void updatePhoneFid(@Param("subject") PhoneFidMap subject);
void updatePhoneFidMapsErrors(@Param("subjects") List subjects);
diff --git a/src/main/java/ru/nbch/credit_tracker/service/MonitoringService.java b/src/main/java/ru/nbch/credit_tracker/service/MonitoringService.java
index 1f30550..1a8e25f 100644
--- a/src/main/java/ru/nbch/credit_tracker/service/MonitoringService.java
+++ b/src/main/java/ru/nbch/credit_tracker/service/MonitoringService.java
@@ -1,8 +1,22 @@
package ru.nbch.credit_tracker.service;
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.UUID;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
-import org.springframework.scheduling.concurrent.CustomizableThreadFactory;
import org.springframework.stereotype.Service;
import ru.nbch.credit_tracker.config.settings.CreditTrackerSettings;
import ru.nbch.credit_tracker.entities.dto.Hit;
@@ -22,21 +36,6 @@ import ru.nbch.credit_tracker.service.xml.stax.MonitoringSignalResponseStaxXmlWr
import ru.nbch.credit_tracker.service.xml.stax.StaxXmlProcessor;
import ru.nbch.credit_tracker.utils.CTUtil;
-import java.io.*;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.time.LocalDateTime;
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.List;
-import java.util.UUID;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipOutputStream;
-
/**
* Сервис периодического опроса официального сервиса сигналов на наличие новых отчетов по пакетам находящимся в статусе ON_MONITORING
*/
@@ -60,9 +59,9 @@ public class MonitoringService {
}
public void run() {
- ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(new CustomizableThreadFactory("monitoring-"));
- long period = config.getMonitoringInterval() != null ? config.getMonitoringInterval() : 15;
- scheduler.scheduleAtFixedRate(this::monitor, 0, period, TimeUnit.MINUTES);
+// ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(new CustomizableThreadFactory("monitoring-"));
+// long period = config.getMonitoringInterval() != null ? config.getMonitoringInterval() : 15;
+// scheduler.scheduleAtFixedRate(this::monitor, 0, period, TimeUnit.MINUTES);
}
private void monitor() {
diff --git a/src/main/java/ru/nbch/credit_tracker/service/SignalService.java b/src/main/java/ru/nbch/credit_tracker/service/SignalService.java
index 138a075..eb7db82 100644
--- a/src/main/java/ru/nbch/credit_tracker/service/SignalService.java
+++ b/src/main/java/ru/nbch/credit_tracker/service/SignalService.java
@@ -15,6 +15,7 @@ import ru.nbch.credit_tracker.entities.external_request.signals.Signals;
import ru.nbch.credit_tracker.entities.external_response.monitoring_report_error.MonitoringReportErrors;
import ru.nbch.credit_tracker.entities.user_request.monitoring_request.MonitoringRequest;
import ru.nbch.credit_tracker.enums.Status;
+import ru.nbch.credit_tracker.mapper.signal.PhoneFidMapMapper;
import ru.nbch.credit_tracker.service.indic.FlagsService;
import ru.nbch.credit_tracker.service.indic.PersonService;
import ru.nbch.credit_tracker.service.indic.PhoneService;
@@ -43,6 +44,7 @@ public class SignalService {
private final HitsService hitsService;
private final UserPackageService userPackageService;
private final PhoneFidMapService phoneFidMapService;
+ private final PhoneFidMapMapper phoneFidMapMapper;
private final PhoneService phoneService;
private final FlagsService flagsService;
private final PersonService personService;
@@ -54,6 +56,7 @@ public class SignalService {
HitsService hitsService,
UserPackageService userPackageService,
PhoneFidMapService phoneFidMapService,
+ PhoneFidMapMapper mapper,
PhoneService phoneService,
FlagsService flagsService,
PersonService personService,
@@ -62,6 +65,7 @@ public class SignalService {
this.hitsService = hitsService;
this.userPackageService = userPackageService;
this.phoneFidMapService = phoneFidMapService;
+ this.phoneFidMapMapper = mapper;
this.phoneService = phoneService;
this.flagsService = flagsService;
this.personService = personService;
@@ -125,7 +129,7 @@ public class SignalService {
}
if (!filteredItems.isEmpty()) {
- boolean fidsFound = updateFlags(filteredItems, packageId);
+ boolean fidsFound = updateFlags2(filteredItems, packageId);
if (noFidsFound) {
noFidsFound = !fidsFound;
}
@@ -187,6 +191,26 @@ public class SignalService {
return fidsFound;
}
+ public boolean updateFlags2(List subjects, Integer packageId) {
+ log.info("Start calculating fids and flags for subjects batch. PackageId: {}", packageId);
+ Map fidsMap = phoneService.defineFidByPhone(subjects);
+ Map flagsDataMap = flagsService.findFlags(fidsMap.keySet().stream().toList());
+ for (Map.Entry entry : flagsDataMap.entrySet()) {
+ Integer fid = entry.getKey();
+ FlagsService.FlagsData flagsData = entry.getValue();
+ PhoneFidMap phoneFidMap = fidsMap.get(fid);
+
+ phoneFidMap.setFid(fid);
+ phoneFidMap.setFlagDebt5000(flagsData.getFlagDebt5000());
+ phoneFidMap.setFlag90days(flagsData.getFlag90days());
+ phoneFidMap.setCalculatedAt(LocalDateTime.now());
+
+ phoneFidMapMapper.updatePhoneFid(phoneFidMap);
+ }
+ log.info("Finish calculating fids and flags for subjects. PackageId: {}. Batch size: {}", packageId, subjects.size());
+ return !fidsMap.isEmpty();
+ }
+
public void rejectPackage(MonitoringRequest request) {
UserPackage userPackage = UserPackage.builder()
.userId(request.getUserId())
diff --git a/src/main/java/ru/nbch/credit_tracker/service/UpdatingFlagsService.java b/src/main/java/ru/nbch/credit_tracker/service/UpdatingFlagsService.java
index 19549fb..6b0a373 100644
--- a/src/main/java/ru/nbch/credit_tracker/service/UpdatingFlagsService.java
+++ b/src/main/java/ru/nbch/credit_tracker/service/UpdatingFlagsService.java
@@ -1,7 +1,10 @@
package ru.nbch.credit_tracker.service;
+import java.time.LocalDateTime;
+import java.time.temporal.ChronoUnit;
+import java.util.List;
+import java.util.function.Predicate;
import lombok.extern.slf4j.Slf4j;
-import org.springframework.scheduling.concurrent.CustomizableThreadFactory;
import org.springframework.stereotype.Service;
import ru.nbch.credit_tracker.config.settings.CreditTrackerSettings;
import ru.nbch.credit_tracker.entities.dto.PhoneFidMap;
@@ -9,14 +12,6 @@ import ru.nbch.credit_tracker.entities.dto.UserPackage;
import ru.nbch.credit_tracker.enums.Status;
import ru.nbch.credit_tracker.service.signal.SignalRestService;
-import java.time.LocalDateTime;
-import java.time.temporal.ChronoUnit;
-import java.util.List;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.TimeUnit;
-import java.util.function.Predicate;
-
/**
* Сервис периодического обновления фидов и флагов просрочек по пакетам находящимся в статусе ON_MONITORING
* По обновленным пакетам формируется новый запрос постановки на мониторинг в официальный сервис сигналов
@@ -43,8 +38,8 @@ public class UpdatingFlagsService {
}
public void run() {
- ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(new CustomizableThreadFactory("updating-flags-"));
- scheduler.scheduleAtFixedRate(this::update, 0, updateFlagsInterval, TimeUnit.DAYS);
+// ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(new CustomizableThreadFactory("updating-flags-"));
+// scheduler.scheduleAtFixedRate(this::update, 0, updateFlagsInterval, TimeUnit.DAYS);
}
private void update() {
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 f2bafa2..5a11f68 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
@@ -1,20 +1,21 @@
package ru.nbch.credit_tracker.service.indic;
-import lombok.AllArgsConstructor;
-import lombok.Getter;
-import lombok.Setter;
-import org.springframework.stereotype.Service;
-import ru.nbch.credit_tracker.mapper.indic.FlagsMapper;
-
import java.time.LocalDate;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.stream.Collectors;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.Setter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+import ru.nbch.credit_tracker.mapper.indic.FlagsMapper;
@Service
public class FlagsService {
+ private static final Logger log = LoggerFactory.getLogger(FlagsService.class);
private final FlagsMapper mapper;
public FlagsService(FlagsMapper mapper) {
@@ -25,11 +26,13 @@ public class FlagsService {
if (fids.isEmpty()) {
return new HashMap<>();
}
- Map result = new HashMap<>();
- Map flags5000 = calcFlag5000(fids);
- Map flags90DaysByPastDueArrear = calcFlag90DaysByPastDueArrear(fids);
- Map flags90DaysByRuPaymt = calcFlag90DaysByRuPaymt(fids);
+ List distinctFids = fids.stream().distinct().toList();
+ log.debug("Fids count: {}", distinctFids.size());
+ Map flags5000 = calcFlag5000(distinctFids);
+ Map flags90DaysByPastDueArrear = calcFlag90DaysByPastDueArrear(distinctFids);
+ Map flags90DaysByRuPaymt = calcFlag90DaysByRuPaymt(distinctFids);
+ Map result = new HashMap<>();
fids.forEach(fid -> {
Integer flagDebt5000 = flags5000.get(fid);
if (flagDebt5000 == null) {
@@ -55,15 +58,11 @@ public class FlagsService {
* Если ни по одному счёту условие не выполняется или счетов вообще не найдено, флаг = 0.
*/
private Map calcFlag5000(List fids) {
- return mapper.findFlagsDebt5000(fids).stream()
- .collect(Collectors.toMap(FlagFidData::getFid, FlagFidData::getFlag,
- (existing, replacement) -> {
- if (existing == 1) {
- return existing;
- } else {
- return replacement;
- }
- }));
+ log.debug("Start calculating flag 5000");
+ List rows = mapper.findFlagsDebt5000(fids);
+ Map res = makeFidFlagMapByRs(fids, rows);
+ log.debug("Complete calculating flag 5000");
+ return res;
}
/**
@@ -72,31 +71,33 @@ public class FlagsService {
* Если ни по одной записи условия не выполняются или записей вообще не найдено, флаг = 0.
*/
private Map calcFlag90DaysByPastDueArrear(List fids) {
+ log.debug("Start calculating flag 90 by PastDueArrear");
LocalDate yearAgo = LocalDate.now().minusYears(1);
- return mapper.findFlag90ByPastDueArrear(fids, yearAgo).stream()
- .collect(Collectors.toMap(FlagFidData::getFid, FlagFidData::getFlag,
- (existing, replacement) -> {
- if (existing == 1) {
- return existing;
- } else {
- return replacement;
- }
- }));
+ List rows = mapper.findFlag90ByPastDueArrear(fids, yearAgo);
+ Map res = makeFidFlagMapByRs(fids, rows);
+ log.debug("Complete calculating flag 90 by PastDueArrear");
+ return res;
}
private Map calcFlag90DaysByRuPaymt(List fids) {
+ log.debug("Start calculating flag 90 by ruPaymt");
LocalDate yearAgo = LocalDate.now().minusYears(1);
- return mapper.findFlag90ByRuPaymt(fids, yearAgo).stream()
- .collect(Collectors.toMap(FlagFidData::getFid, FlagFidData::getFlag,
- (existing, replacement) -> {
- if (existing == 1) {
- return existing;
- } else {
- return replacement;
- }
- }));
+ List rows = mapper.findFlag90ByRuPaymt(fids, yearAgo);
+ Map res = makeFidFlagMapByRs(fids, rows);
+ log.debug("Complete calculating flag 90 by ruPaymt");
+ return res;
}
+ private Map makeFidFlagMapByRs(List fids, List rows) {
+ Map result = new HashMap<>();
+ for (Integer fid : fids) {
+ result.put(fid, 0);
+ }
+ for (FlagFidData row : rows) {
+ result.put(row.getFid(), row.getFlag() != null ? row.getFlag() : 0);
+ }
+ return result;
+ }
@Getter
@Setter
@AllArgsConstructor
diff --git a/src/main/java/ru/nbch/credit_tracker/service/indic/PhoneService.java b/src/main/java/ru/nbch/credit_tracker/service/indic/PhoneService.java
index d063be1..5b8e1a6 100644
--- a/src/main/java/ru/nbch/credit_tracker/service/indic/PhoneService.java
+++ b/src/main/java/ru/nbch/credit_tracker/service/indic/PhoneService.java
@@ -1,17 +1,19 @@
package ru.nbch.credit_tracker.service.indic;
-import lombok.Getter;
-import lombok.Setter;
-import org.springframework.stereotype.Service;
-import ru.nbch.credit_tracker.entities.dto.PhoneFidMap;
-import ru.nbch.credit_tracker.mapper.indic.PhoneMapper;
-
import java.time.LocalDate;
import java.util.Comparator;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import org.springframework.stereotype.Service;
+import ru.nbch.credit_tracker.entities.dto.PhoneFidMap;
+import ru.nbch.credit_tracker.mapper.indic.PhoneMapper;
@Service
public class PhoneService {
@@ -31,6 +33,37 @@ public class PhoneService {
return convertFidsDataToMap(mapper.findFids(phones));
}
+ public Map defineFidByPhone(List subjects) {
+ Map phoneFidMap = new HashMap<>();
+ List phones = subjects.stream().
+ map(subject -> "9" + subject.getPhone())
+ .toList();
+
+ subjects.sort(
+ Comparator.comparing(
+ PhoneFidMap::getPhone,
+ Comparator.naturalOrder()
+ )
+ );
+
+ //отсортированный список по NUMBER, FID и FILE_SINCE_DT
+ List phonesByFid = mapper.findFids(phones);
+ Iterator iterator = phonesByFid.iterator();
+ for (PhoneFidMap phoneFid : subjects) {
+ while (iterator.hasNext()) {
+ FidsData fidsData = iterator.next();
+ if (fidsData.getPhone().equals(phoneFid.getPhone())) {
+ //fixme доступ по ссылке, страшно, придумать что-то еще
+ phoneFidMap.put(fidsData.getFid(), phoneFid);
+ iterator.remove();
+ break;
+ }
+ iterator.remove();
+ }
+ }
+ return phoneFidMap;
+ }
+
private Map convertFidsDataToMap(List fidsData) {
if (fidsData == null || fidsData.isEmpty()) {
return new HashMap<>();
@@ -50,6 +83,8 @@ public class PhoneService {
@Getter
@Setter
+ @AllArgsConstructor
+ @NoArgsConstructor
public static class FidsData {
private Integer fid;
private String phone;
diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml
index 0825d76..88ce91c 100644
--- a/src/main/resources/logback.xml
+++ b/src/main/resources/logback.xml
@@ -8,7 +8,7 @@
- %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n
+ %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
utf-8
diff --git a/src/main/resources/mybatis/mapper/indic/FlagsMapper.xml b/src/main/resources/mybatis/mapper/indic/FlagsMapper.xml
index 24572db..ccdde14 100644
--- a/src/main/resources/mybatis/mapper/indic/FlagsMapper.xml
+++ b/src/main/resources/mybatis/mapper/indic/FlagsMapper.xml
@@ -11,70 +11,70 @@
\ No newline at end of file
diff --git a/src/main/resources/mybatis/mapper/indic/PhoneMapper.xml b/src/main/resources/mybatis/mapper/indic/PhoneMapper.xml
index bd754a9..856a06d 100644
--- a/src/main/resources/mybatis/mapper/indic/PhoneMapper.xml
+++ b/src/main/resources/mybatis/mapper/indic/PhoneMapper.xml
@@ -12,14 +12,21 @@
\ No newline at end of file
diff --git a/src/main/resources/mybatis/mapper/signal/PhoneFidMapMapper.xml b/src/main/resources/mybatis/mapper/signal/PhoneFidMapMapper.xml
index 9d668d9..3ff64c4 100644
--- a/src/main/resources/mybatis/mapper/signal/PhoneFidMapMapper.xml
+++ b/src/main/resources/mybatis/mapper/signal/PhoneFidMapMapper.xml
@@ -42,6 +42,17 @@
+
+ UPDATE signals_phone_fid_map
+ SET
+ fid = #{subject.fid},
+ flag_debt5000 = #{subject.flagDebt5000},
+ flag_90_12 = #{subject.flag90days},
+ calculated_at = #{subject.calculatedAt},
+ error_text = #{subject.errorText}
+ WHERE id = #{subject.id}
+
+
UPDATE signals_phone_fid_map
diff --git a/src/test/java/ru/nbch/credit_tracker/service/PhoneServiceTest.java b/src/test/java/ru/nbch/credit_tracker/service/PhoneServiceTest.java
new file mode 100644
index 0000000..cba98b0
--- /dev/null
+++ b/src/test/java/ru/nbch/credit_tracker/service/PhoneServiceTest.java
@@ -0,0 +1,57 @@
+package ru.nbch.credit_tracker.service;
+
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+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.Mockito;
+import static org.mockito.Mockito.when;
+import org.mockito.junit.jupiter.MockitoExtension;
+import ru.nbch.credit_tracker.entities.dto.PhoneFidMap;
+import ru.nbch.credit_tracker.mapper.indic.PhoneMapper;
+import ru.nbch.credit_tracker.service.indic.PhoneService;
+
+@ExtendWith(MockitoExtension.class)
+public class PhoneServiceTest {
+
+ @Mock
+ PhoneMapper phoneMapper;
+
+ @Test
+ public void defineFidByPhoneTest() {
+ PhoneService phoneService = new PhoneService(phoneMapper);
+
+ fidsDataWithEqualSinceDt.sort(byDbOrder);
+ when(phoneMapper.findFids(Mockito.anyList())).thenReturn(fidsDataWithEqualSinceDt);
+
+ List phoneFidMap = new ArrayList<>();
+ phoneFidMap.add(PhoneFidMap.builder().phone("1111").build());
+ phoneFidMap.add(PhoneFidMap.builder().phone("1112").build());
+ phoneFidMap.add(PhoneFidMap.builder().phone("1114").build());
+
+ Map res = phoneService.defineFidByPhone(phoneFidMap);
+ Assertions.assertEquals(3, res.size());
+ Assertions.assertEquals("1111", res.get(124).getPhone());
+ Assertions.assertEquals("1112", res.get(125).getPhone());
+ Assertions.assertEquals("1114", res.get(126).getPhone());
+ }
+
+ private final List fidsDataWithEqualSinceDt = new ArrayList<>(
+ List.of(
+ new PhoneService.FidsData(123, "1111", LocalDate.of(2025, 9, 10)),
+ new PhoneService.FidsData(124, "1111", LocalDate.of(2025, 9, 10)),
+ new PhoneService.FidsData(125, "1112", LocalDate.of(2024, 10, 10)),
+ new PhoneService.FidsData(126, "1114", LocalDate.of(2024, 10, 11))
+ )
+ );
+
+ Comparator byDbOrder =
+ Comparator.comparing(PhoneService.FidsData::getPhone, Comparator.nullsFirst(Comparator.naturalOrder())) // pn.number ASC
+ .thenComparing(PhoneService.FidsData::getFileSinceDate, Comparator.nullsLast(Comparator.reverseOrder())) // file_since_dt DESC
+ .thenComparing(PhoneService.FidsData::getFid, Comparator.reverseOrder());
+}