http://jira.mfd.msk:8088/browse/BKI-3213 Optimized flags calculating
This commit is contained in:
parent
fdbec299d6
commit
8e90a7624c
25 changed files with 1522 additions and 298 deletions
|
|
@ -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
|
||||
|
|
|
|||
144
pom.xml
144
pom.xml
|
|
@ -27,6 +27,10 @@
|
|||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-aop</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mybatis.spring.boot</groupId>
|
||||
<artifactId>mybatis-spring-boot-starter</artifactId>
|
||||
|
|
@ -122,8 +126,88 @@
|
|||
<version>3.0.4</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-junit-jupiter</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>docker-push-dev</id>
|
||||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>io.fabric8</groupId>
|
||||
<artifactId>docker-maven-plugin</artifactId>
|
||||
<version>0.45.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>Build and deploy docker container</id>
|
||||
<phase>install</phase>
|
||||
<goals>
|
||||
<!--<goal>build</goal>-->
|
||||
<goal>push</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<images>
|
||||
<image>
|
||||
<name>${project.artifactId}:${project.version}</name>
|
||||
<registry>${registry.url}</registry>
|
||||
<build>
|
||||
<dockerFile>${project.basedir}/Dockerfile</dockerFile>
|
||||
<assembly>
|
||||
<inline>
|
||||
<files>
|
||||
<file>
|
||||
<source>${project.build.directory}/${project.build.finalName}.jar</source>
|
||||
<outputDirectory>.</outputDirectory>
|
||||
</file>
|
||||
<file>
|
||||
<source>${project.basedir}/apm-agent/elastic-apm-agent-1.45.0.jar</source>
|
||||
<outputDirectory>.</outputDirectory>
|
||||
</file>
|
||||
<file>
|
||||
<source>${project.basedir}/start-credit-tracker.sh</source>
|
||||
<outputDirectory>.</outputDirectory>
|
||||
</file>
|
||||
<file>
|
||||
<source>${project.basedir}/credit-tracker.properties</source>
|
||||
<outputDirectory>.</outputDirectory>
|
||||
</file>
|
||||
<file>
|
||||
<source>${project.basedir}/monitoring-request.xsd</source>
|
||||
<outputDirectory>.</outputDirectory>
|
||||
</file>
|
||||
<file>
|
||||
<source>${project.basedir}/signal-package.xsd</source>
|
||||
<outputDirectory>.</outputDirectory>
|
||||
</file>
|
||||
</files>
|
||||
</inline>
|
||||
</assembly>
|
||||
<tags>
|
||||
<tag>${project.version}</tag>
|
||||
</tags>
|
||||
</build>
|
||||
</image>
|
||||
</images>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
<build>
|
||||
<finalName>credit-tracker</finalName>
|
||||
<plugins>
|
||||
|
|
@ -181,65 +265,7 @@
|
|||
</includeOnlyProperties>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>io.fabric8</groupId>
|
||||
<artifactId>docker-maven-plugin</artifactId>
|
||||
<version>0.45.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>Build and deploy docker container</id>
|
||||
<phase>install</phase>
|
||||
<goals>
|
||||
<goal>build</goal>
|
||||
<goal>push</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<images>
|
||||
<image>
|
||||
<name>${project.artifactId}:${project.version}</name>
|
||||
<registry>${registry.url}</registry>
|
||||
<build>
|
||||
<dockerFile>${project.basedir}/Dockerfile</dockerFile>
|
||||
<assembly>
|
||||
<inline>
|
||||
<files>
|
||||
<file>
|
||||
<source>${project.build.directory}/${project.build.finalName}.jar</source>
|
||||
<outputDirectory>.</outputDirectory>
|
||||
</file>
|
||||
<file>
|
||||
<source>${project.basedir}/apm-agent/elastic-apm-agent-1.45.0.jar</source>
|
||||
<outputDirectory>.</outputDirectory>
|
||||
</file>
|
||||
<file>
|
||||
<source>${project.basedir}/start-credit-tracker.sh</source>
|
||||
<outputDirectory>.</outputDirectory>
|
||||
</file>
|
||||
<file>
|
||||
<source>${project.basedir}/credit-tracker.properties</source>
|
||||
<outputDirectory>.</outputDirectory>
|
||||
</file>
|
||||
<file>
|
||||
<source>${project.basedir}/monitoring-request.xsd</source>
|
||||
<outputDirectory>.</outputDirectory>
|
||||
</file>
|
||||
<file>
|
||||
<source>${project.basedir}/signal-package.xsd</source>
|
||||
<outputDirectory>.</outputDirectory>
|
||||
</file>
|
||||
</files>
|
||||
</inline>
|
||||
</assembly>
|
||||
<tags>
|
||||
<tag>${project.version}</tag>
|
||||
</tags>
|
||||
</build>
|
||||
</image>
|
||||
</images>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
|
|
|||
|
|
@ -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 > 1000) {
|
||||
log.warn("Slow mapper call {} took {} ms", method, tookMs);
|
||||
} else {
|
||||
log.debug("Mapper call {} took {} ms", method, tookMs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package ru.nbch.credit_tracker.config.rest;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
|
|
@ -12,10 +13,12 @@ import ru.nbch.credit_tracker.entities.user_response.errors.*;
|
|||
import ru.nbch.credit_tracker.enums.SignalError;
|
||||
import ru.nbch.credit_tracker.exceptions.SignalClientException;
|
||||
import ru.nbch.credit_tracker.exceptions.SignalException;
|
||||
import ru.nbch.credit_tracker.exceptions.SignalServerException;
|
||||
import ru.nbch.credit_tracker.utils.error.SimpleMessageResolver;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
@Slf4j
|
||||
@RestControllerAdvice
|
||||
public class GlobalExceptionHandler {
|
||||
|
||||
|
|
@ -43,14 +46,20 @@ public class GlobalExceptionHandler {
|
|||
public ResponseEntity<CommonError> handleClientException(SignalException ex) {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(new MediaType("application", "xml", Charset.forName(encoding)));
|
||||
|
||||
String errorText = messageResolver.resolve(ex.getErrorMessage());
|
||||
|
||||
CommonError commonError = getCommonError(ex);
|
||||
commonError.setStatus("error");
|
||||
commonError.setErrorCode(ex.getErrorMessage().getSubject().getCode());
|
||||
commonError.setErrorText(messageResolver.resolve(ex.getErrorMessage()));
|
||||
commonError.setErrorText(errorText);
|
||||
|
||||
if (ex instanceof SignalClientException) {
|
||||
log.warn("Response error: {}", errorText);
|
||||
return new ResponseEntity<>(commonError, headers, HttpStatus.BAD_REQUEST);
|
||||
} else {
|
||||
Exception cause = ((SignalServerException) ex).getCause();
|
||||
log.error(cause.getMessage(), cause);
|
||||
return new ResponseEntity<>(commonError, headers, HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<String> 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<String, String> reqHeaders = headersMap(req);
|
||||
Map<String, String> 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<String, String> headersMap(HttpServletRequest req) {
|
||||
return Collections.list(req.getHeaderNames()).stream()
|
||||
.collect(Collectors.toMap(h -> h, req::getHeader, (a, b) -> a, LinkedHashMap::new));
|
||||
}
|
||||
|
||||
private static Map<String, String> headersMap(HttpServletResponse resp) {
|
||||
return resp.getHeaderNames().stream()
|
||||
.collect(Collectors.toMap(h -> h, resp::getHeader, (a, b) -> a, LinkedHashMap::new));
|
||||
}
|
||||
|
||||
private static void mask(Map<String, String> headers) {
|
||||
headers.replaceAll((k, v) -> SENSITIVE_HEADERS.contains(k.toLowerCase()) ? "***" : v);
|
||||
}
|
||||
}
|
||||
|
|
@ -3,13 +3,13 @@ package ru.nbch.credit_tracker.enums;
|
|||
public enum SignalError implements IEnumKeyCode {
|
||||
ERROR_001("001", "Ошибка распаковки архива"),
|
||||
ERROR_002("002", "Отсутствует обязательный элемент: {}"),
|
||||
ERROR_003("003", "Элемент ClientId не соответствует формату XXXXXXXXX"),
|
||||
ERROR_003("003", "Элемент ClientId не соответствует формату XXXXXXXXX. Id: {}"),
|
||||
ERROR_005("005", "Неверный логин или пароль"),
|
||||
ERROR_006("006", "Доступ к услуге закрыт, обратитесь к курирующему менеджеру"),
|
||||
ERROR_007("007", "В системе уже существует пакет с таким именем"),
|
||||
ERROR_008("008", "PackageName не соответствует допустимому формату"),
|
||||
ERROR_009("009", "Превышен допустимый размер пакета"),
|
||||
ERROR_010("010", "Идентификатор субъекта (Id) не соответствует допустимому формату"),
|
||||
ERROR_010("010", "Идентификатор субъекта (Id) не соответствует допустимому формату: {}"),
|
||||
ERROR_011("011", "Получен невалидный XML"),
|
||||
ERROR_012("012", "Пакет {} не найден"),
|
||||
ERROR_013("013", "На стороне сервиса произошла ошибка, попробуйте повторить запрос позже или сообщите в техническую поддержку"),
|
||||
|
|
|
|||
|
|
@ -1,12 +1,17 @@
|
|||
package ru.nbch.credit_tracker.exceptions;
|
||||
|
||||
import lombok.Getter;
|
||||
import ru.nbch.credit_tracker.enums.EnumKeyMessage;
|
||||
import ru.nbch.credit_tracker.enums.XmlErrorFormat;
|
||||
|
||||
@Getter
|
||||
public class SignalServerException extends SignalException {
|
||||
|
||||
public SignalServerException(EnumKeyMessage errorMessage, XmlErrorFormat errorFormat) {
|
||||
private final Exception cause;
|
||||
|
||||
public SignalServerException(EnumKeyMessage errorMessage, XmlErrorFormat errorFormat, Exception cause) {
|
||||
super(errorMessage, errorFormat);
|
||||
this.cause = cause;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -109,21 +109,17 @@ public class SignalFacade {
|
|||
zipInputStream.getNextEntry();
|
||||
|
||||
filePath = writeTmpFile(zipInputStream);
|
||||
if (filePath == null) {
|
||||
throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringError);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (e instanceof SignalClientException) {
|
||||
throw (SignalClientException) e;
|
||||
}
|
||||
log.error(e.getMessage(), e);
|
||||
throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringError);
|
||||
throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringError, e);
|
||||
} finally {
|
||||
if (zipInputStream != null) {
|
||||
try {
|
||||
zipInputStream.close();
|
||||
} catch (IOException e) {
|
||||
throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringError);
|
||||
throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringError, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -140,8 +136,7 @@ public class SignalFacade {
|
|||
if (e instanceof SignalClientException) {
|
||||
throw (SignalClientException) e;
|
||||
}
|
||||
log.error(e.getMessage(), e);
|
||||
throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringError);
|
||||
throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringError, e);
|
||||
}
|
||||
|
||||
try {
|
||||
|
|
@ -165,8 +160,7 @@ public class SignalFacade {
|
|||
if (e instanceof SignalClientException) {
|
||||
throw (SignalClientException) e;
|
||||
}
|
||||
log.error(e.getMessage(), e);
|
||||
throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringError);
|
||||
throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringError, e);
|
||||
}
|
||||
|
||||
// в отдельном потоке запускаем регистрацию субъектов, поиск фидов и расчет флагов просрочек
|
||||
|
|
@ -200,7 +194,7 @@ public class SignalFacade {
|
|||
return monitoringRequest;
|
||||
}
|
||||
|
||||
private String writeTmpFile(ZipInputStream zipInputStream) {
|
||||
private String writeTmpFile(ZipInputStream zipInputStream) throws IOException {
|
||||
String folderPath = config.getLoadDir() + File.separator + "tmp_folder" + File.separator;
|
||||
String filePath = folderPath + UUID.randomUUID() + "monitoring_request.xml";
|
||||
CTUtil.createFolder(folderPath);
|
||||
|
|
@ -214,9 +208,6 @@ public class SignalFacade {
|
|||
}
|
||||
|
||||
return filePath;
|
||||
} catch (Exception e) {
|
||||
log.error("Ошибка при записи файла запроса постановки на мониторинг на диск", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -281,8 +272,7 @@ public class SignalFacade {
|
|||
if (e instanceof SignalClientException) {
|
||||
throw (SignalClientException) e;
|
||||
}
|
||||
log.error(e.getMessage(), e);
|
||||
throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringActivePackagesError);
|
||||
throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringActivePackagesError, e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -318,8 +308,7 @@ public class SignalFacade {
|
|||
if (e instanceof SignalClientException) {
|
||||
throw (SignalClientException) e;
|
||||
}
|
||||
log.error(e.getMessage(), e);
|
||||
throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringActivePackagesError);
|
||||
throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringActivePackagesError, e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -344,8 +333,7 @@ public class SignalFacade {
|
|||
if (e instanceof SignalClientException) {
|
||||
throw (SignalClientException) e;
|
||||
}
|
||||
log.error(e.getMessage(), e);
|
||||
throw serverExcp(SignalError.ERROR_017, XmlErrorFormat.MonitoringPackageDeleteError);
|
||||
throw serverExcp(SignalError.ERROR_017, XmlErrorFormat.MonitoringPackageDeleteError, e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -391,8 +379,7 @@ public class SignalFacade {
|
|||
if (e instanceof SignalClientException) {
|
||||
throw (SignalClientException) e;
|
||||
}
|
||||
log.error(e.getMessage(), e);
|
||||
throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringReportListError);
|
||||
throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringReportListError, e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -418,8 +405,7 @@ public class SignalFacade {
|
|||
if (e instanceof SignalClientException) {
|
||||
throw (SignalClientException) e;
|
||||
}
|
||||
log.error(e.getMessage(), e);
|
||||
throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringDownloadError);
|
||||
throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringDownloadError, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,10 +10,8 @@ import java.util.List;
|
|||
@Mapper
|
||||
public interface FlagsMapper {
|
||||
|
||||
List<FlagsService.FlagFidData> findFlagsDebt5000(@Param("fids") List<Integer> fids);
|
||||
List<FlagsService.RuPastDuarearData> findRuPastDuarearData(@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.RuPaymtData> findRuPaymtData(@Param("fids") List<Integer> fids, @Param("reportedDate") LocalDate reportedDate);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,8 +16,9 @@ import ru.nbch.credit_tracker.entities.external_response.monitoring_report_error
|
|||
import ru.nbch.credit_tracker.entities.user_request.monitoring_request.MonitoringRequest;
|
||||
import ru.nbch.credit_tracker.enums.Status;
|
||||
import ru.nbch.credit_tracker.service.indic.FlagsService;
|
||||
import ru.nbch.credit_tracker.service.indic.FlagsService2;
|
||||
import ru.nbch.credit_tracker.service.indic.PersonService;
|
||||
import ru.nbch.credit_tracker.service.indic.PhoneService;
|
||||
import ru.nbch.credit_tracker.service.indic.PhoneService2;
|
||||
import ru.nbch.credit_tracker.service.signal.HitsService;
|
||||
import ru.nbch.credit_tracker.service.signal.PhoneFidMapService;
|
||||
import ru.nbch.credit_tracker.service.signal.UserPackageService;
|
||||
|
|
@ -43,8 +44,8 @@ public class SignalService {
|
|||
private final HitsService hitsService;
|
||||
private final UserPackageService userPackageService;
|
||||
private final PhoneFidMapService phoneFidMapService;
|
||||
private final PhoneService phoneService;
|
||||
private final FlagsService flagsService;
|
||||
private final PhoneService2 phoneService;
|
||||
private final FlagsService2 flagsService;
|
||||
private final PersonService personService;
|
||||
private final StaxXmlProcessor staxXmlProcessor;
|
||||
|
||||
|
|
@ -54,8 +55,8 @@ public class SignalService {
|
|||
HitsService hitsService,
|
||||
UserPackageService userPackageService,
|
||||
PhoneFidMapService phoneFidMapService,
|
||||
PhoneService phoneService,
|
||||
FlagsService flagsService,
|
||||
PhoneService2 phoneService,
|
||||
FlagsService2 flagsService,
|
||||
PersonService personService,
|
||||
StaxXmlProcessor staxXmlProcessor) {
|
||||
this.config = config;
|
||||
|
|
@ -110,6 +111,7 @@ public class SignalService {
|
|||
boolean noFidsFound = true;
|
||||
int offset = 0;
|
||||
List<PhoneFidMap> batch;
|
||||
Map<Integer, PhoneFidMap> phonesByFidToProcessing = new HashMap<>();
|
||||
do {
|
||||
batch = phoneFidMapService.findFullPhoneFidMap(packageId, offset, batchSize);
|
||||
|
||||
|
|
@ -125,15 +127,30 @@ public class SignalService {
|
|||
}
|
||||
|
||||
if (!filteredItems.isEmpty()) {
|
||||
boolean fidsFound = updateFlags(filteredItems, packageId);
|
||||
if (noFidsFound) {
|
||||
noFidsFound = !fidsFound;
|
||||
Map<Integer, PhoneFidMap> fidsMapByBatch = phoneService.defineFidByPhone(filteredItems);
|
||||
log.debug("Actual fids size by phone's batch: {}", fidsMapByBatch.size());
|
||||
|
||||
if (fidsMapByBatch.size() + phonesByFidToProcessing.size() <= batchSize) {
|
||||
phonesByFidToProcessing.putAll(fidsMapByBatch);
|
||||
} else {
|
||||
boolean fidsFound = updateFlags(phonesByFidToProcessing, packageId);
|
||||
if (noFidsFound) {
|
||||
noFidsFound = !fidsFound;
|
||||
}
|
||||
phonesByFidToProcessing = new HashMap<>(fidsMapByBatch);
|
||||
}
|
||||
}
|
||||
|
||||
offset += batchSize;
|
||||
} while (!batch.isEmpty());
|
||||
|
||||
if (!phonesByFidToProcessing.isEmpty()) {
|
||||
boolean fidsFound = updateFlags(phonesByFidToProcessing, packageId);
|
||||
if (noFidsFound) {
|
||||
noFidsFound = !fidsFound;
|
||||
}
|
||||
}
|
||||
|
||||
return new UpdateResult(updated, noFidsFound);
|
||||
}
|
||||
|
||||
|
|
@ -141,50 +158,37 @@ public class SignalService {
|
|||
* Обрабатывает пачку PhoneFidMap: обновляет фиды и флаги просрочек
|
||||
* @return fidsFound true - если хотя бы по одному номеру был найден фид, false - в противном случае
|
||||
*/
|
||||
public boolean updateFlags(List<PhoneFidMap> subjects, Integer packageId) {
|
||||
log.info("Start calculating fids and flags for subjects batch. PackageId: {}", packageId);
|
||||
Map<String, Integer> fidsMap = phoneService.findFidsPhoneData(subjects);
|
||||
LocalDateTime calculatedAt = LocalDateTime.now();
|
||||
boolean fidsFound = fidsMap.values().stream().anyMatch(Objects::nonNull);
|
||||
Map<Integer, FlagsService.FlagsData> flagsDataMap = flagsService.findFlags(fidsMap.values().stream().filter(Objects::nonNull).toList());
|
||||
// TODO Добавить отдельный метод. Если не нашелся фид, надо обновить в базе fid = null и флаги = null calculated_at=now()
|
||||
public boolean updateFlags(Map<Integer, PhoneFidMap> fidsMap, Integer packageId) {
|
||||
log.info("Start calculating flags for subjects batch. PackageId: {}", packageId);
|
||||
Map<Integer, FlagsService.FlagsData> flagsDataMap = flagsService.findFlags(fidsMap.keySet().stream().toList());
|
||||
List<PhoneFidMap> subjectToUpdate = new ArrayList<>();
|
||||
for (Map.Entry<Integer, FlagsService.FlagsData> entry : flagsDataMap.entrySet()) {
|
||||
Integer fid = entry.getKey();
|
||||
FlagsService.FlagsData flagsData = entry.getValue();
|
||||
PhoneFidMap phoneFidMap = fidsMap.get(fid);
|
||||
|
||||
subjects.forEach(subject -> {
|
||||
Integer fid = fidsMap.get("9" + subject.getPhone());
|
||||
if (fid != null) {
|
||||
FlagsService.FlagsData flagsData = flagsDataMap.get(fid);
|
||||
subject = PhoneFidMap.builder()
|
||||
.id(subject.getId())
|
||||
.packageId(packageId)
|
||||
.subjectId(subject.getSubjectId())
|
||||
.phone(subject.getPhone())
|
||||
.fid(fid)
|
||||
.flagDebt5000(flagsData.getFlagDebt5000())
|
||||
.flag90days(flagsData.getFlag90days())
|
||||
.calculatedAt(calculatedAt)
|
||||
.build();
|
||||
} else {
|
||||
subject = PhoneFidMap.builder()
|
||||
.id(subject.getId())
|
||||
.packageId(packageId)
|
||||
.subjectId(subject.getSubjectId())
|
||||
.phone(subject.getPhone())
|
||||
.calculatedAt(calculatedAt)
|
||||
.build();
|
||||
}
|
||||
subjectToUpdate.add(subject);
|
||||
});
|
||||
phoneFidMap.setFid(fid);
|
||||
phoneFidMap.setFlagDebt5000(flagsData.getFlagDebt5000());
|
||||
phoneFidMap.setFlag90days(flagsData.getFlag90days());
|
||||
phoneFidMap.setCalculatedAt(LocalDateTime.now());
|
||||
|
||||
int updateBatchSize = batchSize / 9; // 9 параметров на одну запись для обновления. Максимальное количество параметров ~65000
|
||||
for (int j = 0; j < subjectToUpdate.size(); j += updateBatchSize) {
|
||||
int endIndex = Math.min(j + updateBatchSize, subjectToUpdate.size());
|
||||
phoneFidMapService.updatePhoneFidMaps(subjectToUpdate.subList(j, endIndex));
|
||||
log.trace("Updated fids and flags for subject's batch from: {} to: {}. Total count: {}. PackageId: {}", j, endIndex, subjectToUpdate.size(), packageId);
|
||||
subjectToUpdate.add(phoneFidMap);
|
||||
}
|
||||
|
||||
log.info("Finish calculating fids and flags for subjects. PackageId: {}. Batch size: {}", packageId, subjects.size());
|
||||
log.debug("Start updating subject's batch");
|
||||
if (!subjectToUpdate.isEmpty()) {
|
||||
int updateBatchSize = 7000; // 9 параметров на одну запись для обновления. Максимальное количество параметров ~65000
|
||||
for (int j = 0; j < subjectToUpdate.size(); j += updateBatchSize) {
|
||||
int endIndex = Math.min(j + updateBatchSize, subjectToUpdate.size());
|
||||
phoneFidMapService.updatePhoneFidMaps(subjectToUpdate.subList(j, endIndex));
|
||||
log.trace("Updated fids and flags for subject's batch from: {} to: {}. Total count: {}. PackageId: {}", j, endIndex, subjectToUpdate.size(), packageId);
|
||||
}
|
||||
}
|
||||
log.debug("Complete updating subject's batch");
|
||||
|
||||
return fidsFound;
|
||||
log.info("Finish calculating flags for subjects. PackageId: {}. Batch size: {}", packageId, fidsMap.size());
|
||||
return !fidsMap.isEmpty();
|
||||
}
|
||||
|
||||
public void rejectPackage(MonitoringRequest request) {
|
||||
|
|
|
|||
|
|
@ -2,19 +2,22 @@ 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;
|
||||
import org.springframework.stereotype.Service;
|
||||
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.stream.Collectors;
|
||||
import java.util.*;
|
||||
|
||||
@Service
|
||||
public class FlagsService {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(FlagsService.class);
|
||||
private final FlagsMapper mapper;
|
||||
|
||||
public FlagsService(FlagsMapper mapper) {
|
||||
|
|
@ -25,11 +28,19 @@ public class FlagsService {
|
|||
if (fids.isEmpty()) {
|
||||
return new HashMap<>();
|
||||
}
|
||||
Map<Integer, FlagsData> result = new HashMap<>();
|
||||
Map<Integer, Integer> flags5000 = calcFlag5000(fids);
|
||||
Map<Integer, Integer> flags90DaysByPastDueArrear = calcFlag90DaysByPastDueArrear(fids);
|
||||
Map<Integer, Integer> flags90DaysByRuPaymt = calcFlag90DaysByRuPaymt(fids);
|
||||
List<Integer> distinctFids = fids.stream()
|
||||
.distinct()
|
||||
.sorted(Comparator.naturalOrder())
|
||||
.toList();
|
||||
|
||||
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);
|
||||
findFlagsRuPaymt(distinctFids, flags90DaysByRuPaymt);
|
||||
|
||||
Map<Integer, FlagsData> result = new HashMap<>();
|
||||
fids.forEach(fid -> {
|
||||
Integer flagDebt5000 = flags5000.get(fid);
|
||||
if (flagDebt5000 == null) {
|
||||
|
|
@ -37,7 +48,7 @@ public class FlagsService {
|
|||
}
|
||||
|
||||
Integer flag90days = flags90DaysByPastDueArrear.get(fid);
|
||||
if (flag90days == null) {
|
||||
if (flag90days == null || flag90days == 0) {
|
||||
flag90days = flags90DaysByRuPaymt.get(fid);
|
||||
if (flag90days == null) {
|
||||
flag90days = 0;
|
||||
|
|
@ -50,51 +61,118 @@ public class FlagsService {
|
|||
}
|
||||
|
||||
/**
|
||||
* Выбирает данные из RU_PASTDUEARREAR и ищет по ним флаги просрочек
|
||||
* По FID из RU_PASTDUEARREAR выбираются все уникальные ACC_SERIAL_NUM с максимальной датой REPORTED_DT.
|
||||
* Если по хотя бы одной такой записи AMT_PAST_DUE > 5000, то флаг устанавливается в 1.
|
||||
* Если по хотя бы одной такой записи 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 Map<Integer, Integer> calcFlag5000(List<Integer> fids) {
|
||||
return mapper.findFlagsDebt5000(fids).stream()
|
||||
.collect(Collectors.toMap(FlagFidData::getFid, FlagFidData::getFlag,
|
||||
(existing, replacement) -> {
|
||||
if (existing == 1) {
|
||||
return existing;
|
||||
} else {
|
||||
return replacement;
|
||||
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");
|
||||
List<RuPastDuarearData> ruPastDuarearDataList = mapper.findRuPastDuarearData(fids);
|
||||
|
||||
ruPastDuarearDataList.sort(Comparator.comparing(FlagsService.RuPastDuarearData::getFid) // сортируем сначала по фиду
|
||||
.thenComparing(FlagsService.RuPastDuarearData::getReportedDate, Comparator.reverseOrder())); // Затем сортируем по REPORTED_DT по убыванию
|
||||
|
||||
LocalDate yearAgo = LocalDate.now().minusYears(1);
|
||||
|
||||
for (Integer fid : fids) {
|
||||
Iterator<RuPastDuarearData> 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);
|
||||
}
|
||||
}));
|
||||
prevAccSerialNum = data.getAccSerialNum();
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// если мы так и не нашли флаги для этих фидов - выставляем их в 0
|
||||
flag5000Map.putIfAbsent(fid, 0);
|
||||
flag90Days.putIfAbsent(fid, 0);
|
||||
}
|
||||
log.debug("Complete calculating flag 5000 and 90 days by RU_PASTDUEARREAR");
|
||||
}
|
||||
|
||||
/**
|
||||
* Флаг = 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) {
|
||||
private void findFlagsRuPaymt(List<Integer> fids, Map<Integer, Integer> flag90Days) {
|
||||
log.debug("Start calculating flag 90 days by RU_PAPAYMT");
|
||||
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<RuPaymtData> ruPaymtDataList = mapper.findRuPaymtData(fids, yearAgo);
|
||||
ruPaymtDataList.sort(Comparator.comparing(RuPaymtData::getFid, Comparator.naturalOrder()));
|
||||
|
||||
private Map<Integer, Integer> calcFlag90DaysByRuPaymt(List<Integer> fids) {
|
||||
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;
|
||||
for (Integer fid : fids) {
|
||||
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 {
|
||||
return replacement;
|
||||
flag90Days.put(fid, 0);
|
||||
iterator.remove();
|
||||
}
|
||||
}));
|
||||
} else {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
flag90Days.putIfAbsent(fid, 0);
|
||||
}
|
||||
log.debug("Complete calculating flag 90 days by RU_PAPAYMT");
|
||||
}
|
||||
|
||||
@Getter
|
||||
|
|
@ -107,8 +185,23 @@ public class FlagsService {
|
|||
|
||||
@Getter
|
||||
@Setter
|
||||
public static class FlagFidData {
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public static class RuPastDuarearData {
|
||||
private Integer fid;
|
||||
private Integer flag;
|
||||
private BigInteger accSerialNum;
|
||||
private LocalDate reportedDate;
|
||||
private BigDecimal amtPastDue;
|
||||
private BigInteger daysPastDue;
|
||||
}
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public static class RuPaymtData {
|
||||
private Integer fid;
|
||||
private BigInteger daysPastDue;
|
||||
private LocalDate reportedDate;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<Integer, FlagsService.FlagsData> findFlags(List<Integer> fids) {
|
||||
if (fids.isEmpty()) {
|
||||
return new HashMap<>();
|
||||
}
|
||||
List<Integer> distinctFids = fids.stream()
|
||||
.distinct()
|
||||
.sorted(Comparator.naturalOrder())
|
||||
.toList();
|
||||
|
||||
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);
|
||||
findFlagsRuPaymt(distinctFids, flags90DaysByRuPaymt);
|
||||
|
||||
Map<Integer, FlagsService.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 FlagsService.FlagsData(flagDebt5000, flag90days));
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Выбирает данные из RU_PASTDUEARREAR и ищет по ним флаги просрочек
|
||||
* По FID из RU_PASTDUEARREAR выбираются все уникальные ACC_SERIAL_NUM с максимальной датой REPORTED_DT.
|
||||
* Если по хотя бы одной такой записи AMT_PAST_DUE > 5000, то flag500 устанавливается в 1.
|
||||
* Если ни по одному счёту условие не выполняется или счетов вообще не найдено, флаг = 0.
|
||||
* <p>
|
||||
* 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<Integer> fids, Map<Integer, Integer> flag5000Map, Map<Integer, Integer> flag90Days) {
|
||||
log.debug("Start calculating flag 5000 and 90 days by RU_PASTDUEARREAR");
|
||||
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");
|
||||
}
|
||||
|
||||
/**
|
||||
* Выбирает данные из RU_PAYMT и ищет по ним флаги просрочек 90Days
|
||||
* <p>
|
||||
* flag90Days = 1, если выполняется хотя бы одно из условий:
|
||||
* В INDIC.RU_PAYMT у данного FID есть запись с DAYS_PAST_DUE ≥ 90 и REPORTED_DT >= текущая дата – 12 месяцев.
|
||||
* Если ни по одной записи условия не выполняются или записей вообще не найдено, флаг = 0.
|
||||
*
|
||||
* @param fids - отсортирован по возрастанию фидов
|
||||
* @param flag90Days - рещультирующая мапа флагов просрочек 90_days
|
||||
*/
|
||||
private void findFlagsRuPaymt(List<Integer> fids, Map<Integer, Integer> flag90Days) {
|
||||
log.debug("Start calculating flag 90 days by RU_PAPAYMT");
|
||||
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");
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,18 +1,18 @@
|
|||
package ru.nbch.credit_tracker.service.indic;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
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.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.*;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class PhoneService {
|
||||
|
||||
|
|
@ -24,32 +24,45 @@ public class PhoneService {
|
|||
|
||||
/**
|
||||
* Ищет фиды по номерам телефона
|
||||
* @return Map: key - номер телефона; value - fid
|
||||
* @return Map: key - fid; value - {@link PhoneFidMap}
|
||||
*/
|
||||
public Map<String, Integer> findFidsPhoneData(List<PhoneFidMap> subjects) {
|
||||
List<String> phones = subjects.stream().map(subject -> "9" + subject.getPhone()).toList();
|
||||
return convertFidsDataToMap(mapper.findFids(phones));
|
||||
}
|
||||
public Map<Integer, PhoneFidMap> defineFidByPhone(List<PhoneFidMap> subjects) {
|
||||
Map<Integer, PhoneFidMap> phoneFidMap = new HashMap<>();
|
||||
List<String> phones = subjects.stream().
|
||||
map(subject -> "9" + subject.getPhone())
|
||||
.toList();
|
||||
|
||||
private Map<String, Integer> convertFidsDataToMap(List<FidsData> fidsData) {
|
||||
if (fidsData == null || fidsData.isEmpty()) {
|
||||
return new HashMap<>();
|
||||
subjects.sort(
|
||||
Comparator.comparing(
|
||||
PhoneFidMap::getPhone,
|
||||
Comparator.naturalOrder()
|
||||
)
|
||||
);
|
||||
|
||||
//отсортированный список по NUMBER, FID и FILE_SINCE_DT
|
||||
List<FidsData> phonesByFid = mapper.findFids(phones);
|
||||
log.debug("Start merging fids");
|
||||
for (PhoneFidMap phoneFid : subjects) {
|
||||
Iterator<FidsData> iterator = phonesByFid.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
FidsData fidsData = iterator.next();
|
||||
if (fidsData.getPhone().equals(9 + phoneFid.getPhone())) {
|
||||
//fixme доступ по ссылке, страшно, придумать что-то еще
|
||||
phoneFidMap.put(fidsData.getFid(), phoneFid);
|
||||
iterator.remove();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return fidsData.stream()
|
||||
.collect(Collectors.groupingBy(
|
||||
FidsData::getPhone,
|
||||
Collectors.collectingAndThen(
|
||||
Collectors.maxBy(
|
||||
Comparator.comparing(FidsData::getFileSinceDate)
|
||||
.thenComparing(FidsData::getFid)
|
||||
),
|
||||
data -> data.map(FidsData::getFid).orElse(null)
|
||||
)
|
||||
));
|
||||
log.debug("End merging fids");
|
||||
|
||||
return phoneFidMap;
|
||||
}
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public static class FidsData {
|
||||
private Integer fid;
|
||||
private String phone;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,96 @@
|
|||
package ru.nbch.credit_tracker.service.indic;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
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 ru.nbch.credit_tracker.entities.dto.PhoneFidMap;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.*;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class PhoneService2 {
|
||||
|
||||
private static final String SELCT_FIDS = """
|
||||
SELECT
|
||||
pn.number,
|
||||
pn.fid,
|
||||
pn.file_since_dt
|
||||
FROM PHONE_NORM pn
|
||||
JOIN (SELECT DISTINCT fid FROM NAME) nf
|
||||
ON nf.fid = pn.fid
|
||||
WHERE number in (:phones)
|
||||
ORDER BY
|
||||
pn.number ASC,
|
||||
pn.file_since_dt DESC,
|
||||
pn.fid DESC
|
||||
WITH UR
|
||||
""";
|
||||
|
||||
private final NamedParameterJdbcTemplate jdbcTemplate;
|
||||
|
||||
public PhoneService2(@Qualifier("jdbcTemplateIndic") NamedParameterJdbcTemplate jdbcTemplate) {
|
||||
this.jdbcTemplate = jdbcTemplate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ищет фиды по номерам телефона
|
||||
*
|
||||
* @return Map: key - fid; value - {@link PhoneFidMap}
|
||||
*/
|
||||
public Map<Integer, PhoneFidMap> defineFidByPhone(List<PhoneFidMap> subjects) {
|
||||
Map<Integer, PhoneFidMap> phoneFidMap = new HashMap<>();
|
||||
|
||||
List<String> phones = subjects.stream().
|
||||
map(subject -> "9" + subject.getPhone())
|
||||
.toList();
|
||||
|
||||
subjects.sort(
|
||||
Comparator.comparing(
|
||||
PhoneFidMap::getPhone,
|
||||
Comparator.naturalOrder()
|
||||
)
|
||||
);
|
||||
|
||||
MapSqlParameterSource params = new MapSqlParameterSource();
|
||||
params.addValue("phones", phones);
|
||||
|
||||
List<FidsData> phonesByFid = jdbcTemplate.query(SELCT_FIDS, params, (rs, rowNum) ->
|
||||
new FidsData(rs.getInt("fid"), rs.getString("number"), rs.getObject("file_since_dt", LocalDate.class))
|
||||
);
|
||||
|
||||
log.debug("Start merging fids");
|
||||
for (PhoneFidMap phoneFid : subjects) {
|
||||
Iterator<FidsData> iterator = phonesByFid.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
FidsData fidsData = iterator.next();
|
||||
if (fidsData.getPhone().equals(9 + phoneFid.getPhone())) {
|
||||
//fixme доступ по ссылке, страшно, придумать что-то еще
|
||||
phoneFidMap.put(fidsData.getFid(), phoneFid);
|
||||
iterator.remove();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
log.debug("End merging fids");
|
||||
|
||||
return phoneFidMap;
|
||||
}
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public static class FidsData {
|
||||
private Integer fid;
|
||||
private String phone;
|
||||
private LocalDate fileSinceDate;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
package ru.nbch.credit_tracker.utils;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import ru.nbch.credit_tracker.entities.user_request.monitoring_request.MonitoringRequest;
|
||||
import ru.nbch.credit_tracker.entities.user_request.monitoring_request.Subject;
|
||||
import ru.nbch.credit_tracker.enums.SignalError;
|
||||
|
|
@ -10,25 +11,30 @@ import java.util.regex.Pattern;
|
|||
|
||||
import static ru.nbch.credit_tracker.utils.error.ExceptionUtils.clientExcp;
|
||||
|
||||
@Slf4j
|
||||
public class ValidationUtil {
|
||||
|
||||
private static String phoneRegex = "^\\d{9}$";
|
||||
private static String packageNameRegex = "^[A-Z0-9_]{1,22}$";
|
||||
private static String npIdRegex = "^\\S{1,36}$";
|
||||
private static final String phoneRegex = "^\\d{9}$";
|
||||
private static final String packageNameRegex = "^[A-Z0-9_]{1,22}$";
|
||||
private static final String npIdRegex = "^\\S{1,36}$";
|
||||
|
||||
public static void validateMonitoringRequest(MonitoringRequest request) throws SignalClientException {
|
||||
if (request == null) {
|
||||
throw clientExcp(SignalError.ERROR_011, XmlErrorFormat.MonitoringError);
|
||||
}
|
||||
|
||||
if (request.getUserId() == null) {
|
||||
throw clientExcp(SignalError.ERROR_002, XmlErrorFormat.MonitoringError, "UserId");
|
||||
}
|
||||
|
||||
if (request.getPackageName() == null) {
|
||||
throw clientExcp(SignalError.ERROR_002, XmlErrorFormat.MonitoringError, "PackageName");
|
||||
}
|
||||
|
||||
if (!validatePackageName(request.getPackageName())) {
|
||||
throw clientExcp(SignalError.ERROR_008, XmlErrorFormat.MonitoringError);
|
||||
}
|
||||
|
||||
if (request.getUserId() == null) {
|
||||
throw clientExcp(SignalError.ERROR_002, XmlErrorFormat.MonitoringError, "UserId");
|
||||
}
|
||||
|
||||
if (request.getPassword() == null) {
|
||||
throw clientExcp(SignalError.ERROR_002, XmlErrorFormat.MonitoringError, "Password");
|
||||
}
|
||||
|
|
@ -41,16 +47,12 @@ public class ValidationUtil {
|
|||
throw clientExcp(SignalError.ERROR_002, XmlErrorFormat.MonitoringError, "Subject");
|
||||
}
|
||||
|
||||
if (!validatePackageName(request.getPackageName())) {
|
||||
throw clientExcp(SignalError.ERROR_008, XmlErrorFormat.MonitoringError);
|
||||
}
|
||||
|
||||
for (Subject subject : request.getSubjects().getSubject()) {
|
||||
if (!validatePhone(subject.getClientId())) {
|
||||
throw clientExcp(SignalError.ERROR_003, XmlErrorFormat.MonitoringError);
|
||||
}
|
||||
if (!validateNPId(subject.getId())) {
|
||||
throw clientExcp(SignalError.ERROR_010, XmlErrorFormat.MonitoringError);
|
||||
throw clientExcp(SignalError.ERROR_010, XmlErrorFormat.MonitoringError, subject.getId());
|
||||
}
|
||||
if (!validatePhone(subject.getClientId())) {
|
||||
throw clientExcp(SignalError.ERROR_003, XmlErrorFormat.MonitoringError, subject.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ public class ExceptionUtils {
|
|||
return new SignalClientException(new EnumKeyMessage(enumKeyCode, args), xmlErrorFormat);
|
||||
}
|
||||
|
||||
public static SignalServerException serverExcp(IEnumKeyCode enumKeyCode, XmlErrorFormat xmlErrorFormat, Object... args) {
|
||||
return new SignalServerException(new EnumKeyMessage(enumKeyCode, args), xmlErrorFormat);
|
||||
public static SignalServerException serverExcp(IEnumKeyCode enumKeyCode, XmlErrorFormat xmlErrorFormat, Exception cause, Object... args) {
|
||||
return new SignalServerException(new EnumKeyMessage(enumKeyCode, args), xmlErrorFormat, cause);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
||||
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
||||
<charset>utf-8</charset>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
|
@ -63,12 +63,12 @@
|
|||
</appender>
|
||||
|
||||
<!--SQL запросы и результаты их выполнения-->
|
||||
<logger name="ru.nbch.credit_tracker.mapper" level="INFO" additivity="false">
|
||||
<logger name="ru.nbch.credit_tracker.mapper" level="DEBUG" additivity="false">
|
||||
<appender-ref ref="FILE"/>
|
||||
<appender-ref ref="FILE-JSON"/>
|
||||
</logger>
|
||||
|
||||
<logger name="ru.nbch.credit_tracker.service" level="INFO" additivity="false">
|
||||
<logger name="ru.nbch.credit_tracker.service" level="TRACE" additivity="false">
|
||||
<appender-ref ref="FILE"/>
|
||||
<appender-ref ref="FILE-JSON"/>
|
||||
<appender-ref ref="CONSOLE"/>
|
||||
|
|
|
|||
|
|
@ -5,76 +5,39 @@
|
|||
|
||||
<mapper namespace="ru.nbch.credit_tracker.mapper.indic.FlagsMapper">
|
||||
|
||||
<resultMap id="FlagsFidDataResultMap" type="ru.nbch.credit_tracker.service.indic.FlagsService$FlagFidData">
|
||||
<resultMap id="RuPastDuarearResultMap" type="ru.nbch.credit_tracker.service.indic.FlagsService$RuPastDuarearData">
|
||||
<result property="fid" column="fid" />
|
||||
<result property="flag" column="flag" />
|
||||
<result property="accSerialNum" column="ACC_SERIAL_NUM" />
|
||||
<result property="reportedDate" column="REPORTED_DT" />
|
||||
<result property="amtPastDue" column="AMT_PAST_DUE" />
|
||||
<result property="daysPastDue" column="DAYS_PAST_DUE" />
|
||||
</resultMap>
|
||||
|
||||
<select id="findFlagsDebt5000" resultMap="FlagsFidDataResultMap">
|
||||
SELECT rp.FID as fid,
|
||||
CASE
|
||||
WHEN EXISTS (SELECT 1
|
||||
FROM RU_PASTDUEARREAR rp2
|
||||
WHERE rp2.FID = rp.FID
|
||||
AND rp2.ACC_SERIAL_NUM = rp.ACC_SERIAL_NUM
|
||||
AND rp2.REPORTED_DT = rp.MAX_REP_DATE
|
||||
AND rp2.AMT_PAST_DUE > 5000) THEN 1
|
||||
ELSE 0
|
||||
END as flag
|
||||
FROM (SELECT FID,
|
||||
ACC_SERIAL_NUM,
|
||||
MAX(REPORTED_DT) as MAX_REP_DATE
|
||||
FROM RU_PASTDUEARREAR
|
||||
WHERE FID IN
|
||||
<foreach item="fid" collection="fids" open="(" separator="," close=")">
|
||||
#{fid}
|
||||
</foreach>
|
||||
GROUP BY FID, ACC_SERIAL_NUM) rp
|
||||
GROUP BY rp.FID, rp.ACC_SERIAL_NUM, rp.MAX_REP_DATE
|
||||
WITH UR
|
||||
<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
|
||||
WHERE FID IN
|
||||
<foreach item="fid" collection="fids" open="(" separator="," close=")">
|
||||
#{fid}
|
||||
</foreach>
|
||||
with UR
|
||||
</select>
|
||||
|
||||
<select id="findFlag90ByPastDueArrear" resultMap="FlagsFidDataResultMap">
|
||||
SELECT rp.FID as fid,
|
||||
CASE
|
||||
WHEN EXISTS (SELECT 1
|
||||
FROM RU_PASTDUEARREAR rp2
|
||||
WHERE rp2.FID = rp.FID
|
||||
AND rp2.REPORTED_DT >= #{reportedDate}
|
||||
AND rp2.DAYS_PAST_DUE > 90) THEN 1
|
||||
ELSE 0
|
||||
END as flag
|
||||
FROM (SELECT FID,
|
||||
DAYS_PAST_DUE
|
||||
FROM RU_PASTDUEARREAR
|
||||
WHERE FID IN
|
||||
<foreach item="fid" collection="fids" open="(" separator="," close=")">
|
||||
#{fid}
|
||||
</foreach>
|
||||
GROUP BY FID, DAYS_PAST_DUE) rp
|
||||
GROUP BY rp.FID, rp.DAYS_PAST_DUE
|
||||
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>
|
||||
|
||||
<select id="findFlag90ByRuPaymt" resultMap="FlagsFidDataResultMap">
|
||||
SELECT rp.FID as fid,
|
||||
CASE
|
||||
WHEN EXISTS (SELECT 1
|
||||
FROM RU_PAYMT rp2
|
||||
WHERE rp2.FID = rp.FID
|
||||
AND rp2.REPORTED_DT >= #{reportedDate}
|
||||
AND rp2.DAYS_PAST_DUE > 90) THEN 1
|
||||
ELSE 0
|
||||
END as flag
|
||||
FROM (SELECT FID,
|
||||
DAYS_PAST_DUE
|
||||
FROM RU_PAYMT
|
||||
WHERE FID IN
|
||||
<foreach item="fid" collection="fids" open="(" separator="," close=")">
|
||||
#{fid}
|
||||
</foreach>
|
||||
GROUP BY FID, DAYS_PAST_DUE) rp
|
||||
GROUP BY rp.FID, rp.DAYS_PAST_DUE
|
||||
WITH UR
|
||||
</select>
|
||||
</mapper>
|
||||
|
|
@ -12,14 +12,21 @@
|
|||
</resultMap>
|
||||
|
||||
<select id="findFids" resultMap="fidsDataResultMap">
|
||||
SELECT pn.NUMBER AS number, pn.FID AS fid, pn.FILE_SINCE_DT AS fileSinceDate
|
||||
FROM PHONE_NORM pn
|
||||
INNER JOIN NAME n ON n.FID = pn.FID
|
||||
SELECT
|
||||
pn.number,
|
||||
pn.fid,
|
||||
pn.file_since_dt
|
||||
FROM indic.PHONE_NORM pn
|
||||
JOIN (SELECT DISTINCT fid FROM indic.NAME) nf
|
||||
ON nf.fid = pn.fid
|
||||
WHERE number in
|
||||
<foreach item="phone" collection="phones" open="(" separator="," close=")">
|
||||
#{phone}
|
||||
</foreach>
|
||||
ORDER BY fileSinceDate DESC, fid DESC
|
||||
ORDER BY
|
||||
pn.number ASC,
|
||||
pn.file_since_dt DESC,
|
||||
pn.fid DESC
|
||||
WITH UR
|
||||
</select>
|
||||
</mapper>
|
||||
|
|
@ -0,0 +1,280 @@
|
|||
package ru.nbch.credit_tracker.service;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.jdbc.DataSourceBuilder;
|
||||
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
|
||||
import ru.nbch.credit_tracker.service.indic.FlagsService;
|
||||
import ru.nbch.credit_tracker.service.indic.FlagsService2;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class FlagsService2Test {
|
||||
|
||||
private static final LocalDate INITIAL_DATE = LocalDate.parse("2025-09-25");
|
||||
|
||||
private static final String RU_PASTDUEAREAR_CREATE = """
|
||||
create table RU_PASTDUEARREAR
|
||||
(
|
||||
FID INTEGER not null,
|
||||
SERIAL_NUM BIGINT not null primary key,
|
||||
ACC_SERIAL_NUM BIGINT not null,
|
||||
REPORTED_DT DATE not null,
|
||||
AMT_PAST_DUE DECIMAL(17, 2),
|
||||
DAYS_PAST_DUE BIGINT
|
||||
);
|
||||
""";
|
||||
|
||||
private static final String RU_PASTDUEAREAR_DROP = """
|
||||
DROP TABLE IF EXISTS RU_PASTDUEARREAR;
|
||||
""";
|
||||
|
||||
private static final String RU_PAYMT_DROP = """
|
||||
DROP TABLE IF EXISTS RU_PAYMT;
|
||||
""";
|
||||
|
||||
private static final String RU_PAYMT_CREATE = """
|
||||
create table RU_PAYMT
|
||||
(
|
||||
FID INTEGER not null,
|
||||
SERIAL_NUM BIGINT not null primary key,
|
||||
DAYS_PAST_DUE INTEGER,
|
||||
REPORTED_DT DATE not null
|
||||
);
|
||||
""";
|
||||
|
||||
private static final String FILL_RU_PASTDUEARREAR = String.format("""
|
||||
INSERT INTO RU_PASTDUEARREAR VALUES
|
||||
(1, 1, 1, '%s', 36000.00, 159), -- просрочка 5000 и 90_days по этой записи
|
||||
(2, 2, 2, '%s', 3200.00, null), -- не макс reported_date для того же acc_serial_num
|
||||
(2, 3, 2, '%s', 6700.00, null), -- не макс reported_date для того же acc_serial_num
|
||||
(2, 4, 2, '%s', 5700.00, null), -- просрочка 5000 по этой записи
|
||||
(3, 5, 3, '%s', 300000000000.00, null),
|
||||
(2, 6, 2, '%s', 0.00, null), -- не макс reported_date для того же acc_serial_num
|
||||
(4, 7, 4, '%s', 4000.00, null), -- нет просрочек
|
||||
(5, 8, 5, '%s', 2000.31, 182), -- не макс reported_date для того же acc_serial_num
|
||||
(5, 9, 5, '%s', 2000.31, 212), -- не макс reported_date для того же acc_serial_num
|
||||
(5, 10, 5, '%s', 2000.31, null), -- не макс reported_date для того же acc_serial_num
|
||||
(5, 11, 5, '%s', 2000.31, 273), -- не макс reported_date для того же acc_serial_num
|
||||
(5, 12, 5, '%s', 2000.31, null), -- не макс reported_date для того же acc_serial_num
|
||||
(5, 13, 5, '%s', 7000.31, 307), -- не макс reported_date для того же acc_serial_num
|
||||
(5, 14, 5, '%s', 2000.31, 338), -- не макс reported_date для того же acc_serial_num, просрочка 90_days будет взята у этой записи
|
||||
(5, 15, 5, '%s', 7000.31, 368), -- Просрочка 5000 будет взята у этой записи. Уникальный acc_serial_num с макс reported_dt
|
||||
(5, 16, 5, '%s', 2000.31, 399), -- не макс reported_date для того же acc_serial_num
|
||||
(5, 17, 5, '%s', 5000.31, 5), -- не макс reported_date для того же acc_serial_num
|
||||
(6, 18, 6, '%s', 3000, null), -- Нет просрочки 5000, нет просрочки 90_days (90_days будет в RU_PAYMT)
|
||||
(7, 19, 7, '%s', 5500, 90), -- не макс reported_date для того же acc_serial_num. 90_days будет взята отсюда
|
||||
(7, 20, 7, '%s', 3000, null) -- Нет просрочки 5000. Уникальный acc_serial_num с макс reported_dt
|
||||
""",
|
||||
calcLocalDateFromInitialDate("2025-05-31"),
|
||||
calcLocalDateFromInitialDate("2021-12-29"),
|
||||
calcLocalDateFromInitialDate("2022-01-28"),
|
||||
calcLocalDateFromInitialDate("2025-04-06"),
|
||||
calcLocalDateFromInitialDate("2025-01-14"),
|
||||
calcLocalDateFromInitialDate("2021-11-29"),
|
||||
calcLocalDateFromInitialDate("2025-12-06"),
|
||||
calcLocalDateFromInitialDate("2021-04-01"),
|
||||
calcLocalDateFromInitialDate("2021-05-01"),
|
||||
calcLocalDateFromInitialDate("2021-05-31"),
|
||||
calcLocalDateFromInitialDate("2021-07-01"),
|
||||
calcLocalDateFromInitialDate("2021-07-31"),
|
||||
calcLocalDateFromInitialDate("2021-08-04"),
|
||||
calcLocalDateFromInitialDate("2025-09-08"),
|
||||
calcLocalDateFromInitialDate("2025-09-09"),
|
||||
calcLocalDateFromInitialDate("2021-11-04"),
|
||||
calcLocalDateFromInitialDate("2022-01-04"),
|
||||
calcLocalDateFromInitialDate("2025-09-09"),
|
||||
calcLocalDateFromInitialDate("2025-09-09"),
|
||||
calcLocalDateFromInitialDate("2025-09-10"));
|
||||
|
||||
private static final String FILL_RU_PAYMT = String.format("""
|
||||
INSERT INTO RU_PAYMT VALUES
|
||||
(1, 1, null, '%s'),
|
||||
(2, 2, 30, '%s'),
|
||||
(3, 3, null, '%s'),
|
||||
(3, 4, 100, '%s'),
|
||||
(4, 5, 10, '%s'),
|
||||
(5, 6, 10, '%s'),
|
||||
(6, 7, 90, '%s'),
|
||||
(6, 8, 80, '%s'),
|
||||
(7, 9, null, '%s'),
|
||||
(8, 10, null, '%s'),
|
||||
(8, 11, 83, '%s'),
|
||||
(8, 12, 91, '%s'),
|
||||
(9, 13, 100, '%s')
|
||||
""",
|
||||
calcLocalDateFromInitialDate("2024-11-09"),
|
||||
calcLocalDateFromInitialDate("2024-11-09"),
|
||||
calcLocalDateFromInitialDate("2024-11-10"),
|
||||
calcLocalDateFromInitialDate("2024-11-15"),
|
||||
calcLocalDateFromInitialDate("2024-11-15"),
|
||||
calcLocalDateFromInitialDate("2023-11-15"),
|
||||
calcLocalDateFromInitialDate("2024-11-09"),
|
||||
calcLocalDateFromInitialDate("2024-10-09"),
|
||||
calcLocalDateFromInitialDate("2024-10-09"),
|
||||
calcLocalDateFromInitialDate("2024-10-09"),
|
||||
calcLocalDateFromInitialDate("2024-10-09"),
|
||||
calcLocalDateFromInitialDate("2024-10-09"),
|
||||
calcLocalDateFromInitialDate("2024-10-09"));
|
||||
|
||||
|
||||
private DataSource dataSource;
|
||||
private NamedParameterJdbcTemplate jdbcTemplate;
|
||||
private FlagsService2 flagsService;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
dataSource = DataSourceBuilder.create()
|
||||
.url("jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;MODE=DB2;DATABASE_TO_LOWER=TRUE")
|
||||
.driverClassName("org.h2.Driver")
|
||||
.username("sa")
|
||||
.password("")
|
||||
.build();
|
||||
|
||||
jdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
|
||||
|
||||
jdbcTemplate.getJdbcTemplate().execute(RU_PASTDUEAREAR_DROP);
|
||||
jdbcTemplate.getJdbcTemplate().execute(RU_PAYMT_DROP);
|
||||
|
||||
jdbcTemplate.getJdbcTemplate().execute(RU_PASTDUEAREAR_CREATE);
|
||||
jdbcTemplate.getJdbcTemplate().execute(RU_PAYMT_CREATE);
|
||||
|
||||
flagsService = new FlagsService2(jdbcTemplate);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testfindFlags() {
|
||||
jdbcTemplate.getJdbcTemplate().execute(FILL_RU_PASTDUEARREAR);
|
||||
jdbcTemplate.getJdbcTemplate().execute(FILL_RU_PAYMT);
|
||||
|
||||
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());
|
||||
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() {
|
||||
jdbcTemplate.getJdbcTemplate().execute(FILL_RU_PASTDUEARREAR);
|
||||
|
||||
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());
|
||||
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(0, resultMap.get(3).getFlag90days());
|
||||
Assertions.assertEquals(0, resultMap.get(4).getFlag90days());
|
||||
Assertions.assertEquals(1, resultMap.get(5).getFlag90days());
|
||||
Assertions.assertEquals(0, resultMap.get(6).getFlag90days());
|
||||
Assertions.assertEquals(1, resultMap.get(7).getFlag90days());
|
||||
Assertions.assertEquals(0, resultMap.get(8).getFlag90days());
|
||||
Assertions.assertEquals(0, resultMap.get(9).getFlag90days());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testfindFlagsWithoutRuPastduearreae() {
|
||||
jdbcTemplate.getJdbcTemplate().execute(FILL_RU_PAYMT);
|
||||
|
||||
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 static LocalDate calcLocalDateFromInitialDate(String date) {
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
long daysPassed = ChronoUnit.DAYS.between(INITIAL_DATE, now);
|
||||
return LocalDate.parse(date).plusDays(daysPassed);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,218 @@
|
|||
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.Mockito;
|
||||
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.time.LocalDateTime;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class FlagsServiceTest {
|
||||
|
||||
private static final LocalDate INITIAL_DATE = LocalDate.parse("2025-09-25");
|
||||
|
||||
@Mock
|
||||
FlagsMapper flagsMapper;
|
||||
|
||||
@Test
|
||||
public void testfindFlags() {
|
||||
FlagsService flagsService = new FlagsService(flagsMapper);
|
||||
|
||||
when(flagsMapper.findRuPastDuarearData(Mockito.anyList())).thenReturn(ruPastDuarearDataList);
|
||||
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(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());
|
||||
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(0, resultMap.get(3).getFlag90days());
|
||||
Assertions.assertEquals(0, resultMap.get(4).getFlag90days());
|
||||
Assertions.assertEquals(1, resultMap.get(5).getFlag90days());
|
||||
Assertions.assertEquals(0, resultMap.get(6).getFlag90days());
|
||||
Assertions.assertEquals(1, resultMap.get(7).getFlag90days());
|
||||
Assertions.assertEquals(0, resultMap.get(8).getFlag90days());
|
||||
Assertions.assertEquals(0, resultMap.get(9).getFlag90days());
|
||||
}
|
||||
|
||||
|
||||
@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 по этой записи
|
||||
|
||||
new FlagsService.RuPastDuarearData(2, BigInteger.valueOf(2), calcLocalDateFromInitialDate("2021-12-29"), BigDecimal.valueOf(3200.00), null), // не макс reported_date для того же acc_serial_num
|
||||
new FlagsService.RuPastDuarearData(2, BigInteger.valueOf(2), calcLocalDateFromInitialDate("2022-01-28"), BigDecimal.valueOf(6700.00), null), // не макс reported_date для того же acc_serial_num
|
||||
new FlagsService.RuPastDuarearData(2, BigInteger.valueOf(2), calcLocalDateFromInitialDate("2025-04-06"), BigDecimal.valueOf(5700.00), null), // просрочка 5000 по этой записи
|
||||
|
||||
new FlagsService.RuPastDuarearData(3, BigInteger.valueOf(3), calcLocalDateFromInitialDate("2025-01-14"), BigDecimal.valueOf(300000000000.00), null),
|
||||
|
||||
new FlagsService.RuPastDuarearData(2, BigInteger.valueOf(2), calcLocalDateFromInitialDate("2021-11-29"), BigDecimal.valueOf(0.00), null), // не макс reported_date для того же acc_serial_num
|
||||
|
||||
new FlagsService.RuPastDuarearData(4, BigInteger.valueOf(4), calcLocalDateFromInitialDate("2025-12-06"), BigDecimal.valueOf(4000.00), null), // нет просрочек
|
||||
|
||||
new FlagsService.RuPastDuarearData(5, BigInteger.valueOf(5), calcLocalDateFromInitialDate("2021-04-01"), BigDecimal.valueOf(2000.31), BigInteger.valueOf(182)), // не макс reported_date для того же acc_serial_num
|
||||
new FlagsService.RuPastDuarearData(5, BigInteger.valueOf(5), calcLocalDateFromInitialDate("2021-05-01"), BigDecimal.valueOf(2000.31), BigInteger.valueOf(212)), // не макс reported_date для того же acc_serial_num
|
||||
new FlagsService.RuPastDuarearData(5, BigInteger.valueOf(5), calcLocalDateFromInitialDate("2021-05-31"), BigDecimal.valueOf(2000.31), null), // не макс reported_date для того же acc_serial_num
|
||||
new FlagsService.RuPastDuarearData(5, BigInteger.valueOf(5), calcLocalDateFromInitialDate("2021-07-01"), BigDecimal.valueOf(2000.31), BigInteger.valueOf(273)), // не макс reported_date для того же acc_serial_num
|
||||
new FlagsService.RuPastDuarearData(5, BigInteger.valueOf(5), calcLocalDateFromInitialDate("2021-07-31"), BigDecimal.valueOf(2000.31), null), // не макс reported_date для того же acc_serial_num
|
||||
new FlagsService.RuPastDuarearData(5, BigInteger.valueOf(5), calcLocalDateFromInitialDate("2021-08-04"), BigDecimal.valueOf(7000.31), BigInteger.valueOf(307)), // не макс reported_date для того же acc_serial_num
|
||||
new FlagsService.RuPastDuarearData(5, BigInteger.valueOf(5), calcLocalDateFromInitialDate("2025-09-08"), BigDecimal.valueOf(2000.31), BigInteger.valueOf(338)), // не макс reported_date для того же acc_serial_num, просрочка 90_days будет взята у этой записи
|
||||
new FlagsService.RuPastDuarearData(5, BigInteger.valueOf(51), calcLocalDateFromInitialDate("2025-09-09"), BigDecimal.valueOf(7000.31), BigInteger.valueOf(368)), // Просрочка 5000 будет взята у этой записи. Уникальный acc_serial_num с макс reported_dt
|
||||
new FlagsService.RuPastDuarearData(5, BigInteger.valueOf(5), calcLocalDateFromInitialDate("2021-11-04"), BigDecimal.valueOf(2000.31), BigInteger.valueOf(399)), // не макс reported_date для того же acc_serial_num
|
||||
new FlagsService.RuPastDuarearData(5, BigInteger.valueOf(5), calcLocalDateFromInitialDate("2022-01-04"), BigDecimal.valueOf(5000.31), BigInteger.valueOf(5)), // не макс reported_date для того же acc_serial_num
|
||||
|
||||
new FlagsService.RuPastDuarearData(6, BigInteger.valueOf(6), calcLocalDateFromInitialDate("2025-09-09"), BigDecimal.valueOf(3000), null), // Нет просрочки 5000, нет просрочки 90_days (90_days будет в RU_PAYMT)
|
||||
|
||||
new FlagsService.RuPastDuarearData(7, BigInteger.valueOf(7), calcLocalDateFromInitialDate("2025-09-09"), BigDecimal.valueOf(5500), BigInteger.valueOf(90)), // не макс reported_date для того же acc_serial_num. 90_days будет взята отсюда
|
||||
new FlagsService.RuPastDuarearData(7, BigInteger.valueOf(7), calcLocalDateFromInitialDate("2025-09-10"), BigDecimal.valueOf(3000), null) // Нет просрочки 5000. Уникальный acc_serial_num с макс reported_dt
|
||||
|
||||
)
|
||||
);
|
||||
|
||||
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);
|
||||
return LocalDate.parse(date).plusDays(daysPassed);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
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 org.springframework.jdbc.core.RowMapper;
|
||||
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
|
||||
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
|
||||
import ru.nbch.credit_tracker.entities.dto.PhoneFidMap;
|
||||
import ru.nbch.credit_tracker.service.indic.PhoneService2;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class PhoneService2Test {
|
||||
|
||||
@Mock
|
||||
NamedParameterJdbcTemplate jdbcTemplate;
|
||||
|
||||
@Test
|
||||
public void defineFidByPhoneTest() {
|
||||
PhoneService2 phoneService = new PhoneService2(jdbcTemplate);
|
||||
|
||||
fidsDataWithEqualSinceDt.sort(byDbOrder);
|
||||
when(jdbcTemplate.query(anyString(), any(MapSqlParameterSource.class), any(RowMapper.class))).thenReturn(fidsDataWithEqualSinceDt);
|
||||
|
||||
List<PhoneFidMap> phoneFidMap = new ArrayList<>();
|
||||
phoneFidMap.add(PhoneFidMap.builder().phone("1111").build());
|
||||
phoneFidMap.add(PhoneFidMap.builder().phone("1112").build());
|
||||
phoneFidMap.add(PhoneFidMap.builder().phone("1113").build());
|
||||
phoneFidMap.add(PhoneFidMap.builder().phone("1114").build());
|
||||
|
||||
Map<Integer, PhoneFidMap> 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<PhoneService2.FidsData> fidsDataWithEqualSinceDt = new ArrayList<>(
|
||||
List.of(
|
||||
new PhoneService2.FidsData(123, "91111", LocalDate.of(2025, 9, 10)),
|
||||
new PhoneService2.FidsData(124, "91111", LocalDate.of(2025, 9, 10)),
|
||||
new PhoneService2.FidsData(125, "91112", LocalDate.of(2024, 10, 10)),
|
||||
new PhoneService2.FidsData(126, "91114", LocalDate.of(2024, 10, 11))
|
||||
)
|
||||
);
|
||||
|
||||
Comparator<PhoneService2.FidsData> byDbOrder =
|
||||
Comparator.comparing(PhoneService2.FidsData::getPhone, Comparator.nullsFirst(Comparator.naturalOrder())) // pn.number ASC
|
||||
.thenComparing(PhoneService2.FidsData::getFileSinceDate, Comparator.nullsLast(Comparator.reverseOrder())) // file_since_dt DESC
|
||||
.thenComparing(PhoneService2.FidsData::getFid, Comparator.reverseOrder());
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
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.Mockito;
|
||||
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;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@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> phoneFidMap = new ArrayList<>();
|
||||
phoneFidMap.add(PhoneFidMap.builder().phone("1111").build());
|
||||
phoneFidMap.add(PhoneFidMap.builder().phone("1112").build());
|
||||
phoneFidMap.add(PhoneFidMap.builder().phone("1113").build());
|
||||
phoneFidMap.add(PhoneFidMap.builder().phone("1114").build());
|
||||
|
||||
Map<Integer, PhoneFidMap> 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<PhoneService.FidsData> fidsDataWithEqualSinceDt = new ArrayList<>(
|
||||
List.of(
|
||||
new PhoneService.FidsData(123, "91111", LocalDate.of(2025, 9, 10)),
|
||||
new PhoneService.FidsData(124, "91111", LocalDate.of(2025, 9, 10)),
|
||||
new PhoneService.FidsData(125, "91112", LocalDate.of(2024, 10, 10)),
|
||||
new PhoneService.FidsData(126, "91114", LocalDate.of(2024, 10, 11))
|
||||
)
|
||||
);
|
||||
|
||||
Comparator<PhoneService.FidsData> 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());
|
||||
}
|
||||
|
|
@ -69,12 +69,12 @@ public class ValidationUtilTest {
|
|||
Arguments.of(
|
||||
"/monitoring_request/invalid_phone_request.zip",
|
||||
SignalError.ERROR_003,
|
||||
Collections.emptyList().toArray(new Object[0])
|
||||
new Object[]{"abc123"}
|
||||
),
|
||||
Arguments.of(
|
||||
"/monitoring_request/invalid_id_request.zip",
|
||||
SignalError.ERROR_010,
|
||||
Collections.emptyList().toArray(new Object[0])
|
||||
new Object[]{"#####"}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue