Compare commits
55 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4cfdd75dd9 | ||
|
|
28ec19775e | ||
|
|
3ecb5282cb | ||
|
|
8ce1948b07 | ||
|
|
945694e50d | ||
|
|
395613ae01 | ||
|
|
53a2ab77a9 | ||
|
|
aaf91195b1 | ||
|
|
bf693c3fbc | ||
|
|
b487fc832f | ||
|
|
a67cc4056a | ||
|
|
6da5991483 | ||
|
|
0f04ab3e42 | ||
|
|
4e6ab1a862 | ||
|
|
7176a49814 | ||
|
|
881c31becd | ||
|
|
83a452fed6 | ||
|
|
00662f9685 | ||
|
|
f10540e80a | ||
|
|
525de91285 | ||
|
|
786004e3c8 | ||
|
|
74084f93c8 | ||
|
|
9813fe4053 | ||
|
|
d24cd03656 | ||
|
|
8e53f24aeb | ||
|
|
9ce437a438 | ||
|
|
9cb5a16490 | ||
|
|
f2df1c0183 | ||
|
|
5d3507e12b | ||
|
|
999ac42f48 | ||
|
|
e7d5ff82f4 | ||
|
|
bb7606125a | ||
|
|
76489609c7 | ||
|
|
386ea4c18e | ||
|
|
438956f8b5 | ||
|
|
1680abbec9 | ||
|
|
7fe066c42d | ||
|
|
bc34f3f89c | ||
|
|
e0d96d9246 | ||
|
|
fc830f8eae | ||
|
|
c013b616c7 | ||
|
|
b272123802 | ||
|
|
e5945a1ef2 | ||
|
|
7fc7bacbc8 | ||
|
|
194828d0b7 | ||
|
|
f4b49c06ed | ||
|
|
fcca92e6a5 | ||
|
|
c31d515c84 | ||
|
|
f95b547a81 | ||
|
|
daceff96af | ||
|
|
978b45eda8 | ||
|
|
769fabf79a | ||
|
|
ae7260d790 | ||
|
|
ae18c5d568 | ||
|
|
8e90a7624c |
98 changed files with 4893 additions and 848 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}
|
||||
ct.indic-data-source-settings.maximum-pool-size=${MAXIMUM_POOL_SIZE_INDIC:10}
|
||||
ct.indic-data-source-settings.minimum-idle=${MINIMUM_IDLE_INDIC:10}
|
||||
ct.indic-data-source-settings.connection-timeout=${CONNECTION_TIMEOUT_INDIC:10000}
|
||||
|
|
@ -60,13 +60,18 @@ ct.signals-root-uri=${SIGNALS_ROOT_URI:http://10.230.227.102:11094/signalRUalfa/
|
|||
ct.signals-token=${SIGNALS_TOKEN:gttcHxwcGojoAvZK}
|
||||
ct.signals-user=${SIGNALS_USER:0001ZZ000001}
|
||||
|
||||
ct.load-dir=/opt/app/reports
|
||||
#ct.load-dir=D:\\folder_name\\signals
|
||||
#ct.load-dir=/opt/app/reports
|
||||
ct.load-dir=D:\\folder_name\\signals
|
||||
|
||||
# min
|
||||
ct.monitoring-interval=${MONITORING_INTERVAL:15}
|
||||
# days
|
||||
ct.update-flags-cron-expression=${UPDATE_FLAGS_CRON_EXPRESSION:0 0 22 * * *}
|
||||
ct.update-flags-interval=${UPDATE_FLAGS_INTERVAL:1}
|
||||
ct.days-to-update-flags=${DAYS_TO_UPDATE_FLAGS:30}
|
||||
|
||||
ct.encoding=windows-1251
|
||||
ct.encoding=windows-1251
|
||||
|
||||
#Batch size and global pipeline timeout
|
||||
ct.batchSize=1000
|
||||
#in minute
|
||||
ct.timeout=30
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
#commons
|
||||
APP_VERSION=1.24
|
||||
APP_VERSION=1.20
|
||||
REGISTRY_URL=rh8-vm3.nbki.msk:5000
|
||||
JMX_PORT=
|
||||
JMX_HOSTNAME=
|
||||
|
|
@ -71,7 +71,7 @@ SIGNALS_USER=0001ZZ000001
|
|||
# min
|
||||
MONITORING_INTERVAL=15
|
||||
# days
|
||||
UPDATE_FLAGS_CRON_EXPRESSION=0 22 * * *
|
||||
UPDATE_FLAGS_INTERVAL=1
|
||||
DAYS_TO_UPDATE_FLAGS=30
|
||||
|
||||
|
||||
|
|
|
|||
8
pom.xml
8
pom.xml
|
|
@ -10,12 +10,12 @@
|
|||
</parent>
|
||||
<groupId>ru.nbch</groupId>
|
||||
<artifactId>credit-tracker</artifactId>
|
||||
<version>1.24</version>
|
||||
<version>2.0</version>
|
||||
<name>credit_tracker</name>
|
||||
<description>Credit Tracker Application</description>
|
||||
|
||||
<properties>
|
||||
<java.version>17</java.version>
|
||||
<java.version>21</java.version>
|
||||
<git.info.prefix>git_info</git.info.prefix>
|
||||
<git.info.filename>git.properties</git.info.filename>
|
||||
<registry.url>10.230.238.56:5000</registry.url>
|
||||
|
|
@ -41,6 +41,10 @@
|
|||
<artifactId>postgresql</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||
<artifactId>jackson-datatype-jsr310</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.ibm.db2</groupId>
|
||||
<artifactId>jcc</artifactId>
|
||||
|
|
|
|||
|
|
@ -3,21 +3,18 @@ package ru.nbch.credit_tracker;
|
|||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import ru.nbch.credit_tracker.service.MonitoringService;
|
||||
import ru.nbch.credit_tracker.service.UpdateProcessingPackagesService;
|
||||
import ru.nbch.credit_tracker.service.UpdatingFlagsService;
|
||||
|
||||
@SpringBootApplication
|
||||
public class CreditTrackerApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
ConfigurableApplicationContext ctx = SpringApplication.run(CreditTrackerApplication.class, args);
|
||||
UpdateProcessingPackagesService updateProcessingPackagesService = ctx.getBean(UpdateProcessingPackagesService.class);
|
||||
MonitoringService monitoringService = ctx.getBean(MonitoringService.class);
|
||||
UpdatingFlagsService updatingFlagsService = ctx.getBean(UpdatingFlagsService.class);
|
||||
monitoringService.run();
|
||||
updatingFlagsService.run();
|
||||
updateProcessingPackagesService.run();
|
||||
// UpdateProcessingPackagesService updateProcessingPackagesService = ctx.getBean(UpdateProcessingPackagesService.class);
|
||||
// MonitoringService monitoringService = ctx.getBean(MonitoringService.class);
|
||||
// UpdatingFlagsService updatingFlagsService = ctx.getBean(UpdatingFlagsService.class);
|
||||
// updateProcessingPackagesService.run();
|
||||
// monitoringService.run();
|
||||
// updatingFlagsService.run();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,122 @@
|
|||
package ru.nbch.credit_tracker.builder;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import ru.nbch.credit_tracker.entities.external_request.signals.Person;
|
||||
import ru.nbch.credit_tracker.model.IdData;
|
||||
import ru.nbch.credit_tracker.model.NameData;
|
||||
import ru.nbch.credit_tracker.model.SubjectProfile;
|
||||
import ru.nbch.credit_tracker.utils.CTUtil;
|
||||
|
||||
@Slf4j
|
||||
public class PersonFactoryBuilderV2 {
|
||||
private final static DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("dd.MM.yyyy");
|
||||
|
||||
private final static String DEFAULT_DATE = "01.01.2000";
|
||||
private final static String IP = "0";
|
||||
private final static String OKSM = "999";
|
||||
private final static String CONSENT_PURPOSE = "16";
|
||||
private final static String REPORT_USER = "АО «НБКИ»";
|
||||
private final static String LIABILITY = "1";
|
||||
private final static String CONSENT_PERIOD = "3";
|
||||
private final static String REPORT_USER_REG_NUM = "1057746710713";
|
||||
private final static String REPORT_USER_TAX_ID = "7703548386";
|
||||
private final static String CONSENT_HASH = "hash";
|
||||
private final static String CONSENT_DIV_CODE = "000-000";
|
||||
private final static String INQ_PURPOSE = "16";
|
||||
private final static String ODT = "Иной документ";
|
||||
private final static String CONSENT_ISSUE_AUTHORITY = "Кем выдан";
|
||||
|
||||
|
||||
private SubjectProfile personData;
|
||||
private Long uid;
|
||||
|
||||
public static PersonFactoryBuilderV2 builder() {
|
||||
return new PersonFactoryBuilderV2();
|
||||
}
|
||||
|
||||
public PersonFactoryBuilderV2 personData(SubjectProfile personData) {
|
||||
this.personData = personData;
|
||||
//todo откуда брать этот id
|
||||
// this.uid = personData.getIdentifier();
|
||||
return this;
|
||||
}
|
||||
|
||||
public Person build() {
|
||||
if (personData == null) {
|
||||
return null;
|
||||
}
|
||||
NameData nameData = personData.getNameData();
|
||||
IdData passportData = personData.getIdData();
|
||||
Person person = new Person();
|
||||
person.setS(nameData.getLastName());
|
||||
person.setN(nameData.getFirstName());
|
||||
person.setM(StringUtils.isNotBlank(nameData.getMiddleName()) ? nameData.getMiddleName() : null);
|
||||
person.setD(DATE_FORMAT.format(nameData.getBirthDate()));
|
||||
|
||||
person.setPn(getPn(passportData.getSerNum(), passportData.getIdNum()));
|
||||
person.setPd(DATE_FORMAT.format(LocalDate.now())); // Do not use passport iss_date. Signal service could reject the package
|
||||
person.setDt(getDt(passportData.getIdType(), passportData.getIdType2()));
|
||||
if (person.getDt() != null && person.getDt().equals("999")) {
|
||||
person.setOdt(ODT);
|
||||
person.setConsentOdt(ODT);
|
||||
}
|
||||
person.setIp(IP);
|
||||
person.setUid(uid);
|
||||
person.setOksm(OKSM);
|
||||
person.setConsentDate(DATE_FORMAT.format(LocalDate.now()));
|
||||
person.setConsentPurpose(CONSENT_PURPOSE);
|
||||
person.setReportUser(REPORT_USER);
|
||||
person.setLiability(LIABILITY);
|
||||
person.setConsentPeriod(CONSENT_PERIOD);
|
||||
person.setAgrDate(DATE_FORMAT.format(LocalDate.now()));
|
||||
person.setReportUserRegNum(REPORT_USER_REG_NUM);
|
||||
person.setReportUserTaxID(REPORT_USER_TAX_ID);
|
||||
person.setConsentHash(CONSENT_HASH);
|
||||
|
||||
person.setCs(person.getS());
|
||||
person.setConsentN(person.getN());
|
||||
person.setConsentM(person.getM());
|
||||
person.setConsentBd(person.getD());
|
||||
person.setConsentPn(person.getPn());
|
||||
person.setConsentPd(person.getPd());
|
||||
person.setConsentDivCode(CONSENT_DIV_CODE);
|
||||
person.setConsentDt(person.getDt());
|
||||
person.setConsentIssueAuthority(CONSENT_ISSUE_AUTHORITY);
|
||||
person.setInqPurpose(INQ_PURPOSE);
|
||||
return person;
|
||||
}
|
||||
|
||||
private String getPn(String serNum, String idNum) {
|
||||
String normSerNum = CTUtil.normalize(serNum);
|
||||
String normIdNum = CTUtil.normalize(idNum);
|
||||
return StringUtils.isNotBlank(normSerNum) ? normSerNum + normIdNum : normIdNum;
|
||||
}
|
||||
|
||||
private String getDt(String idType, String idType2) {
|
||||
if (StringUtils.isNotBlank(idType2)) {
|
||||
return idType2;
|
||||
} else {
|
||||
return switch (idType) {
|
||||
case "21" -> "21";
|
||||
case "22" -> "22.1";
|
||||
case "09" -> "22.2";
|
||||
case "26" -> "23";
|
||||
case "04" -> "24";
|
||||
case "07" -> "25";
|
||||
case "14", "03" -> "27";
|
||||
case "10" -> "31";
|
||||
case "15" -> "32";
|
||||
case "13" -> "37";
|
||||
case "06", "27" -> "28";
|
||||
case "01", "02" -> "999";
|
||||
default -> {
|
||||
log.warn("ID_TYPE2 is blank and no matching found for ID_TYPE {}", idType);
|
||||
yield "";
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
package ru.nbch.credit_tracker.component;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import ru.nbch.credit_tracker.config.settings.CreditTrackerSettings;
|
||||
import ru.nbch.credit_tracker.entities.user_request.monitoring_request.MonitoringRequest;
|
||||
import ru.nbch.credit_tracker.enums.SignalError;
|
||||
import ru.nbch.credit_tracker.enums.XmlErrorFormat;
|
||||
import ru.nbch.credit_tracker.exceptions.SignalException;
|
||||
import ru.nbch.credit_tracker.model.MonitoringValidationError;
|
||||
import ru.nbch.credit_tracker.model.ValidationResult;
|
||||
import ru.nbch.credit_tracker.service.xml.stax.StaxXmlProcessor;
|
||||
import ru.nbch.credit_tracker.utils.ValidationService;
|
||||
import ru.nbch.credit_tracker.utils.ZipUtils;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Optional;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
import static ru.nbch.credit_tracker.utils.error.ExceptionUtils.clientExcp;
|
||||
import static ru.nbch.credit_tracker.utils.error.ExceptionUtils.serverExcp;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
public class MonitoringRequestValidator {
|
||||
private final CreditTrackerSettings config;
|
||||
private final ValidationService validationService;
|
||||
private final StaxXmlProcessor staxXmlProcessor;
|
||||
|
||||
public MonitoringRequestValidator(CreditTrackerSettings config,
|
||||
ValidationService validationService,
|
||||
StaxXmlProcessor staxXmlProcessor) {
|
||||
this.config = config;
|
||||
this.validationService = validationService;
|
||||
this.staxXmlProcessor = staxXmlProcessor;
|
||||
}
|
||||
|
||||
public void validateZipContent(MultipartFile file) {
|
||||
log.trace("Validating zip content");
|
||||
if (file.isEmpty() || !ZipUtils.isZipFile(file)) {
|
||||
throw clientExcp(SignalError.ERROR_001, XmlErrorFormat.MonitoringError);
|
||||
}
|
||||
|
||||
// Валидация Архива на количетсво файлов, размер и расширение
|
||||
try (ZipInputStream zipInputStream = new ZipInputStream(file.getInputStream())) {
|
||||
Optional<ZipEntry> xmlFileEntry = ZipUtils.extractValidEntry(zipInputStream);
|
||||
if (xmlFileEntry.isEmpty()) {
|
||||
throw clientExcp(SignalError.ERROR_001, XmlErrorFormat.MonitoringError);
|
||||
}
|
||||
long maxXmlFileSize = config.getMaxXmlFileSize() * 1024 * 1024;
|
||||
if (xmlFileEntry.get().getSize() > maxXmlFileSize) {
|
||||
throw clientExcp(SignalError.ERROR_009, XmlErrorFormat.MonitoringError);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringError, e);
|
||||
}
|
||||
log.trace("Validated zip content ok!");
|
||||
}
|
||||
|
||||
public ValidationResult readAndValidateMonitoringRequest(String filePath) {
|
||||
log.debug("Validating monitoring request...");
|
||||
ValidationResult validationResult = new ValidationResult();
|
||||
try (BufferedInputStream in = new BufferedInputStream(Files.newInputStream(Path.of(filePath)), 8 * 1024 * 1024)) {
|
||||
staxXmlProcessor.read(in, MonitoringRequest.class,
|
||||
request -> {
|
||||
try {
|
||||
validationService.validateMonitoringRequest(request);
|
||||
if (request.getPackageName() != null && validationResult.getPackageName() == null) {
|
||||
validationResult.setPackageName(request.getPackageName());
|
||||
}
|
||||
if (request.getUserId() != null && validationResult.getUserId() == null) {
|
||||
validationResult.setUserId(request.getUserId());
|
||||
}
|
||||
if (request.getPassword() != null && validationResult.getPassword() == null) {
|
||||
validationResult.setPassword(request.getPassword());
|
||||
}
|
||||
request.getSubjects().getSubject().clear();
|
||||
} catch (SignalException e) {
|
||||
validationResult.addError(new MonitoringValidationError(-1, "", e));
|
||||
}
|
||||
},
|
||||
request -> request.getSubjects().getSubject().isEmpty(),
|
||||
40000, "/MonitoringRequest/Subjects/Subject");
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Error while parsing and validating monitoring request", e);
|
||||
}
|
||||
log.debug("Validating monitoring request {}!", validationResult.getValidationErrors().isEmpty() ? "ok" : "fail");
|
||||
return validationResult;
|
||||
}
|
||||
}
|
||||
|
|
@ -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 RepositoryTimingAspect {
|
||||
@Around("within(@org.springframework.stereotype.Repository *)")
|
||||
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 jdbc template call {} took {} ms", method, tookMs);
|
||||
} else {
|
||||
log.debug("Jdbc template call {} took {} ms", method, tookMs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,8 @@ import com.zaxxer.hikari.HikariDataSource;
|
|||
import org.springframework.boot.jdbc.DataSourceBuilder;
|
||||
import ru.nbch.credit_tracker.config.settings.DataSourceSettings;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
public class DatabaseConfig {
|
||||
|
||||
protected HikariDataSource createDataSource(DataSourceSettings settings) {
|
||||
|
|
|
|||
|
|
@ -10,6 +10,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 ru.nbch.credit_tracker.config.settings.DataSourceSettings;
|
||||
|
||||
|
|
@ -60,5 +61,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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,53 @@
|
|||
package ru.nbch.credit_tracker.config.datasource.handlers;
|
||||
|
||||
import java.sql.CallableStatement;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
import java.util.UUID;
|
||||
import org.apache.ibatis.type.JdbcType;
|
||||
import org.apache.ibatis.type.MappedTypes;
|
||||
import org.apache.ibatis.type.TypeHandler;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@MappedTypes({UUID.class})
|
||||
public class UUIDTypeHandler implements TypeHandler<UUID> {
|
||||
private static final Logger log = LoggerFactory.getLogger(UUIDTypeHandler.class);
|
||||
|
||||
@Override
|
||||
public void setParameter(PreparedStatement ps, int i, UUID parameter, JdbcType jdbcType) throws SQLException {
|
||||
if (parameter == null) {
|
||||
ps.setObject(i, null, Types.OTHER);
|
||||
} else {
|
||||
ps.setObject(i, parameter.toString(), Types.OTHER);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getResult(ResultSet rs, String columnName) throws SQLException {
|
||||
return toUUID(rs.getString(columnName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getResult(ResultSet rs, int columnIndex) throws SQLException {
|
||||
return toUUID(rs.getString(columnIndex));
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getResult(CallableStatement cs, int columnIndex) throws SQLException {
|
||||
return toUUID(cs.getString(columnIndex));
|
||||
}
|
||||
|
||||
private static UUID toUUID(String val) {
|
||||
if (val != null && !val.isEmpty() && !val.isBlank()) {
|
||||
try {
|
||||
return UUID.fromString(val);
|
||||
} catch (IllegalArgumentException e) {
|
||||
log.warn("Bad UUID found: {}", val);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package ru.nbch.credit_tracker.config.mdc;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import ru.nbch.credit_tracker.utils.LogUtil;
|
||||
|
||||
// Annotation to only clear the MDC key at method exit
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface ClearMdc {
|
||||
String key() default LogUtil.mdcKey;
|
||||
}
|
||||
|
|
@ -1,18 +1,27 @@
|
|||
package ru.nbch.credit_tracker.config.mdc;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.slf4j.MDC;
|
||||
import ru.nbch.credit_tracker.entities.dto.UserPackage;
|
||||
import ru.nbch.credit_tracker.entities.app.UserPackage;
|
||||
import ru.nbch.credit_tracker.entities.user_request.monitoring_request.MonitoringRequest;
|
||||
import ru.nbch.credit_tracker.utils.LogUtil;
|
||||
|
||||
@Slf4j
|
||||
@Aspect
|
||||
public class MdcAspect {
|
||||
|
||||
// For methods that only require clearing the MDC at the end
|
||||
@Around("@annotation(clearMdc)")
|
||||
public Object clearMdcAdvice(ProceedingJoinPoint pjp, ClearMdc clearMdc) throws Throwable {
|
||||
try {
|
||||
return pjp.proceed();
|
||||
} finally {
|
||||
LogUtil.removeMdcLogKey(clearMdc.key());
|
||||
}
|
||||
}
|
||||
|
||||
// For methods that set an MDC value (from a method argument) and then clear it at the end
|
||||
@Around("@annotation(setAndClearMdc)")
|
||||
public Object setAndClearMdcAdvice(ProceedingJoinPoint pjp, SetAndClearMdc setAndClearMdc) throws Throwable {
|
||||
|
|
@ -20,30 +29,18 @@ public class MdcAspect {
|
|||
int index = setAndClearMdc.argIndex();
|
||||
Object obj = (args != null && args.length > index) ? args[index] : null;
|
||||
if (obj instanceof MonitoringRequest request) {
|
||||
if (request.getPackageName() != null) {
|
||||
putMdcValue(setAndClearMdc.key(), request.getPackageName());
|
||||
}
|
||||
LogUtil.addMdcLogKey(setAndClearMdc.key(), request.getPackageName());
|
||||
} else if (obj instanceof UserPackage userPackage) {
|
||||
if (userPackage.getPackageName() != null) {
|
||||
putMdcValue(setAndClearMdc.key(), userPackage.getPackageName());
|
||||
}
|
||||
LogUtil.addMdcLogKey(setAndClearMdc.key(), userPackage.getPackageName());
|
||||
} else if (obj instanceof String packageName) {
|
||||
putMdcValue(setAndClearMdc.key(), packageName);
|
||||
LogUtil.addMdcLogKey(setAndClearMdc.key(), packageName);
|
||||
} else {
|
||||
log.debug("no packageName found for for {}", pjp.toShortString());
|
||||
}
|
||||
try {
|
||||
return pjp.proceed();
|
||||
} finally {
|
||||
MDC.remove(setAndClearMdc.key());
|
||||
}
|
||||
}
|
||||
|
||||
private void putMdcValue(String key, String value) {
|
||||
try {
|
||||
MDC.put(key, "[" + value + "] ");
|
||||
} catch (Throwable e) {
|
||||
log.error("{}", ExceptionUtils.getStackTrace(e));
|
||||
LogUtil.removeMdcLogKey(setAndClearMdc.key());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,12 +4,13 @@ import java.lang.annotation.ElementType;
|
|||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import ru.nbch.credit_tracker.utils.LogUtil;
|
||||
|
||||
// Annotation to set an MDC key based on a method argument and clear it after method execution
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface SetAndClearMdc {
|
||||
String key() default "ru.nbch.credit_tracker.log.mdc.key";
|
||||
String key() default LogUtil.mdcKey;
|
||||
// The index of the method argument to use for the MDC value (defaults to the first argument)
|
||||
int argIndex() default 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package ru.nbch.credit_tracker.config.rest;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
|
@ -9,15 +10,19 @@ import org.springframework.web.bind.annotation.ExceptionHandler;
|
|||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
import org.springframework.web.multipart.MaxUploadSizeExceededException;
|
||||
import ru.nbch.credit_tracker.config.settings.CreditTrackerSettings;
|
||||
import ru.nbch.credit_tracker.entities.user_response.errors.*;
|
||||
import ru.nbch.credit_tracker.entities.user_response.errors.CommonError;
|
||||
import ru.nbch.credit_tracker.entities.user_response.errors.MonitoringActivePackagesError;
|
||||
import ru.nbch.credit_tracker.entities.user_response.errors.MonitoringAllPackagesError;
|
||||
import ru.nbch.credit_tracker.entities.user_response.errors.MonitoringDownloadError;
|
||||
import ru.nbch.credit_tracker.entities.user_response.errors.MonitoringError;
|
||||
import ru.nbch.credit_tracker.entities.user_response.errors.MonitoringPackageDeleteError;
|
||||
import ru.nbch.credit_tracker.entities.user_response.errors.MonitoringReportListError;
|
||||
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 {
|
||||
|
|
@ -42,34 +47,33 @@ public class GlobalExceptionHandler {
|
|||
return new ResponseEntity<>(error, headers, HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
@ExceptionHandler(value = SignalException.class, produces = MediaType.APPLICATION_XML_VALUE)
|
||||
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(errorText);
|
||||
|
||||
if (ex instanceof SignalClientException) {
|
||||
String packageName = ((SignalClientException) ex).getPackageName();
|
||||
if(packageName != null ) {
|
||||
log.warn("Response error: {}. PackageName: {}", errorText, packageName);
|
||||
} else {
|
||||
log.warn("Response error: {}", errorText);
|
||||
}
|
||||
return new ResponseEntity<>(commonError, headers, HttpStatus.BAD_REQUEST);
|
||||
@ExceptionHandler(value = SignalClientException.class, produces = MediaType.APPLICATION_XML_VALUE)
|
||||
public ResponseEntity<CommonError> handleClientException(SignalClientException ex) {
|
||||
CommonError err = getCommonError(ex);
|
||||
String packageName = ex.getPackageName();
|
||||
if(packageName != null ) {
|
||||
log.warn("Response error: {}. PackageName: {}", err.getErrorText(), packageName);
|
||||
} else {
|
||||
Exception cause = ((SignalServerException) ex).getCause();
|
||||
log.error(cause.getMessage(), cause);
|
||||
return new ResponseEntity<>(commonError, headers, HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
log.warn("Response error: {}", err.getErrorText());
|
||||
}
|
||||
return new ResponseEntity<>(err, xmlHeaders(), HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
private static CommonError getCommonError(SignalException ex) {
|
||||
@ExceptionHandler(value = SignalServerException.class, produces = MediaType.APPLICATION_XML_VALUE)
|
||||
public ResponseEntity<CommonError> handleServerException(SignalServerException ex) {
|
||||
CommonError commonError = getCommonError(ex);
|
||||
Exception cause = ex.getCause();
|
||||
log.error(cause.getMessage(), cause);
|
||||
return new ResponseEntity<>(commonError, xmlHeaders(), HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
private HttpHeaders xmlHeaders() {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(new MediaType("application", "xml", Charset.forName(encoding)));
|
||||
return headers;
|
||||
}
|
||||
|
||||
private CommonError getCommonError(SignalException ex) {
|
||||
CommonError commonError;
|
||||
switch (ex.getErrorFormat()) {
|
||||
case MonitoringError -> commonError = new MonitoringError();
|
||||
|
|
@ -80,6 +84,10 @@ public class GlobalExceptionHandler {
|
|||
case MonitoringPackageDeleteError -> commonError = new MonitoringPackageDeleteError();
|
||||
default -> throw new IllegalStateException("Unsupported error format: " + ex.getErrorFormat());
|
||||
}
|
||||
commonError.setStatus("error");
|
||||
commonError.setErrorCode(ex.getErrorMessage().getSubject().getCode());
|
||||
String errorText = messageResolver.resolve(ex.getErrorMessage());
|
||||
commonError.setErrorText(errorText);
|
||||
return commonError;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,8 +29,11 @@ public class CreditTrackerSettings {
|
|||
private Long maxXmlFileSize;
|
||||
private String loadDir;
|
||||
private Long monitoringInterval;
|
||||
private String updateFlagsCronExpression;
|
||||
private Long updateFlagsInterval;
|
||||
private Long daysToUpdateFlags;
|
||||
|
||||
private String encoding;
|
||||
|
||||
private Integer batchSize;
|
||||
private Integer timeout;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
package ru.nbch.credit_tracker.config.task;
|
||||
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Supplier;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import ru.nbch.credit_tracker.config.settings.CreditTrackerSettings;
|
||||
import ru.nbch.credit_tracker.mapper.signal.PhoneFidMapMapper;
|
||||
import ru.nbch.credit_tracker.model.impl.PartFilePayload;
|
||||
import ru.nbch.credit_tracker.model.impl.SignalXmlFilePayload;
|
||||
import ru.nbch.credit_tracker.model.impl.SubjectPayload;
|
||||
import ru.nbch.credit_tracker.service.indic.FlagsService2;
|
||||
import ru.nbch.credit_tracker.service.task.impl.FlagsEnricher;
|
||||
import ru.nbch.credit_tracker.service.task.impl.KickPipeLineImpl;
|
||||
import ru.nbch.credit_tracker.service.task.impl.XmlMergeCoordinator;
|
||||
import ru.nbch.credit_tracker.service.task.setup.IPipeLineStarter;
|
||||
import ru.nbch.credit_tracker.service.task.setup.IPipeLineTerminator;
|
||||
import ru.nbch.credit_tracker.service.xml.stax.StaxXmlProcessor;
|
||||
|
||||
@Configuration
|
||||
public class PipelineStarterConfig {
|
||||
@Bean
|
||||
public BiFunction<Long, String, IPipeLineStarter<SubjectPayload>> pipelineStarterFactory(StaxXmlProcessor xml, CreditTrackerSettings creditTrackerSettings) {
|
||||
return (packageId, packageName) -> new KickPipeLineImpl(xml, packageId, packageName, creditTrackerSettings);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Supplier<IPipeLineTerminator<PartFilePayload, SignalXmlFilePayload>> pipelineTerminatorFactory(CreditTrackerSettings creditTrackerSettings) {
|
||||
return () -> new XmlMergeCoordinator(creditTrackerSettings);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Supplier<FlagsEnricher> flagEnricherFactory(FlagsService2 flagsService, PhoneFidMapMapper mapper) {
|
||||
return () -> new FlagsEnricher(flagsService, mapper);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package ru.nbch.credit_tracker.config.task;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import ru.nbch.credit_tracker.model.impl.PartFilePayload;
|
||||
import ru.nbch.credit_tracker.model.impl.SubjectPayload;
|
||||
|
||||
@Configuration
|
||||
public class StageConfig {
|
||||
|
||||
@Bean("saveSubjectQueueFactory")
|
||||
public StageQueueFactory<SubjectPayload> saveSubjectQueueFactory() {
|
||||
return new StageQueueFactory<>(5);
|
||||
}
|
||||
|
||||
@Bean("idEnricherQueueFactory")
|
||||
public StageQueueFactory<SubjectPayload> idEnricherQueueFactory() {
|
||||
return new StageQueueFactory<>();
|
||||
}
|
||||
|
||||
@Bean("partFileQueueFactory")
|
||||
public StageQueueFactory<PartFilePayload> partFileQueueFactory() {
|
||||
return new StageQueueFactory<>(10000);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
package ru.nbch.credit_tracker.config.task;
|
||||
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import ru.nbch.credit_tracker.model.StagePayload;
|
||||
|
||||
public class StageQueueFactory<T extends StagePayload> implements FactoryBean<BlockingQueue<T>> {
|
||||
|
||||
private final int capacity;
|
||||
public StageQueueFactory(int capacity) {
|
||||
this.capacity = capacity;
|
||||
}
|
||||
|
||||
public StageQueueFactory() {
|
||||
this(10);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockingQueue<T> getObject() throws Exception {
|
||||
return new LinkedBlockingQueue<>(capacity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return BlockingQueue.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSingleton() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -3,8 +3,14 @@ package ru.nbch.credit_tracker.controller;
|
|||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import ru.nbch.credit_tracker.config.mdc.ClearMdc;
|
||||
import ru.nbch.credit_tracker.entities.user_request.active_packages.MonitoringActivePackagesRequest;
|
||||
import ru.nbch.credit_tracker.entities.user_request.all_packages.MonitoringAllPackagesRequest;
|
||||
import ru.nbch.credit_tracker.entities.user_response.active_packages.MonitoringActivePackagesResponse;
|
||||
|
|
@ -12,47 +18,70 @@ import ru.nbch.credit_tracker.entities.user_response.all_packages.MonitoringAllP
|
|||
import ru.nbch.credit_tracker.entities.user_response.success.MonitoringPackageDeleteResponse;
|
||||
import ru.nbch.credit_tracker.entities.user_response.success.MonitoringReportListResponse;
|
||||
import ru.nbch.credit_tracker.entities.user_response.success.MonitoringSetupResponse;
|
||||
import ru.nbch.credit_tracker.facade.SignalFacade;
|
||||
import ru.nbch.credit_tracker.enums.SignalError;
|
||||
import ru.nbch.credit_tracker.enums.XmlErrorFormat;
|
||||
import ru.nbch.credit_tracker.exceptions.SignalException;
|
||||
import ru.nbch.credit_tracker.facade.SignalFacadeV3;
|
||||
import static ru.nbch.credit_tracker.utils.error.ExceptionUtils.serverExcp;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/signal")
|
||||
public class SignalController {
|
||||
public class SignalControllerV3 {
|
||||
|
||||
private final SignalFacade signalFacade;
|
||||
private final SignalFacadeV3 signalFacadeV3;
|
||||
|
||||
public SignalController(SignalFacade signalFacade) {
|
||||
this.signalFacade = signalFacade;
|
||||
public SignalControllerV3(SignalFacadeV3 signalFacadeV3) {
|
||||
this.signalFacadeV3 = signalFacadeV3;
|
||||
}
|
||||
|
||||
@PostMapping(value = "/set", produces = MediaType.APPLICATION_XML_VALUE)
|
||||
@ClearMdc
|
||||
public ResponseEntity<MonitoringSetupResponse> setupMonitoring(@RequestParam("file") MultipartFile file) {
|
||||
return signalFacade.setupMonitoring(file);
|
||||
signalFacadeV3.setupMonitoringRequest(file);
|
||||
return ResponseEntity.ok().body(new MonitoringSetupResponse());
|
||||
}
|
||||
|
||||
@PostMapping(value = "/active_packages", consumes = MediaType.APPLICATION_XML_VALUE, produces = MediaType.APPLICATION_XML_VALUE)
|
||||
public ResponseEntity<MonitoringActivePackagesResponse> findActivePackages(@RequestBody MonitoringActivePackagesRequest activePackagesRequest) {
|
||||
return signalFacade.findActivePackages(activePackagesRequest);
|
||||
try {
|
||||
return signalFacadeV3.findActivePackages(activePackagesRequest);
|
||||
} catch (Exception e) {
|
||||
throwOrWrap(SignalError.ERROR_013, XmlErrorFormat.MonitoringActivePackagesError, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@PostMapping(value = "/all_packages", consumes = MediaType.APPLICATION_XML_VALUE, produces = MediaType.APPLICATION_XML_VALUE)
|
||||
public ResponseEntity<MonitoringAllPackagesResponse> findAllPackages(@RequestBody MonitoringAllPackagesRequest allPackagesRequest) {
|
||||
return signalFacade.findAllPackages(allPackagesRequest);
|
||||
try {
|
||||
return signalFacadeV3.findAllPackages(allPackagesRequest);
|
||||
} catch (Exception e) {
|
||||
throwOrWrap(SignalError.ERROR_013, XmlErrorFormat.MonitoringAllPackagesError, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@PostMapping(value = "/delete", params = {"username", "packageName"}, produces = MediaType.APPLICATION_XML_VALUE)
|
||||
public ResponseEntity<MonitoringPackageDeleteResponse> deletePackage(@RequestParam("username") String username, @RequestParam("packageName") String packageName) {
|
||||
return signalFacade.deletePackage(username, packageName);
|
||||
return signalFacadeV3.deletePackage(username, packageName);
|
||||
}
|
||||
|
||||
@GetMapping(value = "/report_list", params = {"username", "packageName"}, produces = MediaType.APPLICATION_XML_VALUE)
|
||||
public ResponseEntity<MonitoringReportListResponse> getReportList(@RequestParam("username") String username, @RequestParam("packageName") String packageName) {
|
||||
return signalFacade.getReportList(username, packageName);
|
||||
return signalFacadeV3.getReportList(username, packageName);
|
||||
}
|
||||
|
||||
@GetMapping(value = "/download", params = "file", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
|
||||
public ResponseEntity<Resource> downloadFile(@RequestParam("file") String file) {
|
||||
return signalFacade.downloadFile(file);
|
||||
return signalFacadeV3.downloadFile(file);
|
||||
}
|
||||
|
||||
private static void throwOrWrap(SignalError error, XmlErrorFormat specific, Exception ex) {
|
||||
if (ex instanceof SignalException) {
|
||||
throw (SignalException) ex;
|
||||
}
|
||||
throw serverExcp(error, specific, ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package ru.nbch.credit_tracker.entities.dto;
|
||||
package ru.nbch.credit_tracker.entities.app;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
package ru.nbch.credit_tracker.entities.app;
|
||||
|
||||
import java.util.UUID;
|
||||
import lombok.*;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Builder
|
||||
public class PhoneFidMap {
|
||||
private Long id;
|
||||
private Long packageId;
|
||||
private UUID recUuid;
|
||||
private String subjectId;
|
||||
private String phone;
|
||||
private Integer fid;
|
||||
private Integer flagDebt5000;
|
||||
private Integer flag90days;
|
||||
private LocalDateTime calculatedAt;
|
||||
private String errorText;
|
||||
|
||||
// NAME data
|
||||
private String firstName;
|
||||
private String lastName;
|
||||
private String middleName;
|
||||
private LocalDate birthDate;
|
||||
private LocalDate fileSinceDate;
|
||||
//ID data
|
||||
private String serNum;
|
||||
private String idNum;
|
||||
private LocalDate issDate;
|
||||
private String idType;
|
||||
private String idType2;
|
||||
private LocalDate IdfileSinceDate;
|
||||
|
||||
boolean isFullFilled = false;
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package ru.nbch.credit_tracker.entities.dto;
|
||||
package ru.nbch.credit_tracker.entities.app;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
package ru.nbch.credit_tracker.entities.dto;
|
||||
|
||||
import lombok.*;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Builder
|
||||
public class PhoneFidMap {
|
||||
private Long id;
|
||||
private Long packageId;
|
||||
private String subjectId;
|
||||
private String phone;
|
||||
private Integer fid;
|
||||
private Integer flagDebt5000;
|
||||
private Integer flag90days;
|
||||
private LocalDateTime calculatedAt;
|
||||
private String errorText;
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package ru.nbch.credit_tracker.entities.indic;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Builder
|
||||
public class Name {
|
||||
private Integer fid;
|
||||
private String number;
|
||||
private LocalDate fileSinceDate;
|
||||
private String first;
|
||||
private String last;
|
||||
private String middle;
|
||||
private LocalDate birthDate;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package ru.nbch.credit_tracker.entities.indic;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class PaymentsFlag {
|
||||
private Integer fid;
|
||||
private Integer flag;
|
||||
}
|
||||
|
|
@ -4,4 +4,5 @@ public interface CommonError {
|
|||
void setStatus(String status);
|
||||
void setErrorCode(String errorCode);
|
||||
void setErrorText(String errorText);
|
||||
String getErrorText();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,436 +0,0 @@
|
|||
package ru.nbch.credit_tracker.facade;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.slf4j.MDC;
|
||||
import org.springframework.aop.framework.AopContext;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.annotation.EnableAspectJAutoProxy;
|
||||
import org.springframework.core.io.FileUrlResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import ru.nbch.credit_tracker.config.mdc.SetAndClearMdc;
|
||||
import ru.nbch.credit_tracker.config.settings.CreditTrackerSettings;
|
||||
import ru.nbch.credit_tracker.entities.dto.UserPackage;
|
||||
import ru.nbch.credit_tracker.entities.user_request.active_packages.MonitoringActivePackagesRequest;
|
||||
import ru.nbch.credit_tracker.entities.user_request.all_packages.MonitoringAllPackagesRequest;
|
||||
import ru.nbch.credit_tracker.entities.user_request.monitoring_request.MonitoringRequest;
|
||||
import ru.nbch.credit_tracker.entities.user_response.active_packages.MonitoringActivePackagesResponse;
|
||||
import ru.nbch.credit_tracker.entities.user_response.active_packages.Package;
|
||||
import ru.nbch.credit_tracker.entities.user_response.active_packages.Packages;
|
||||
import ru.nbch.credit_tracker.entities.user_response.all_packages.MonitoringAllPackagesResponse;
|
||||
import ru.nbch.credit_tracker.entities.user_response.all_packages.PackageAll;
|
||||
import ru.nbch.credit_tracker.entities.user_response.all_packages.PackagesAll;
|
||||
import ru.nbch.credit_tracker.entities.user_response.success.MonitoringPackageDeleteResponse;
|
||||
import ru.nbch.credit_tracker.entities.user_response.success.MonitoringReportListResponse;
|
||||
import ru.nbch.credit_tracker.entities.user_response.success.MonitoringSetupResponse;
|
||||
import ru.nbch.credit_tracker.enums.SignalError;
|
||||
import ru.nbch.credit_tracker.enums.Status;
|
||||
import ru.nbch.credit_tracker.enums.XmlErrorFormat;
|
||||
import ru.nbch.credit_tracker.exceptions.SignalClientException;
|
||||
import ru.nbch.credit_tracker.service.MonitoringErrorProcesingService;
|
||||
import ru.nbch.credit_tracker.service.SignalService;
|
||||
import ru.nbch.credit_tracker.service.scoring.AuthorizationService;
|
||||
import ru.nbch.credit_tracker.service.signal.SignalRestService;
|
||||
import ru.nbch.credit_tracker.service.xml.stax.StaxXmlProcessor;
|
||||
import ru.nbch.credit_tracker.utils.CTUtil;
|
||||
import ru.nbch.credit_tracker.utils.ValidationService;
|
||||
import ru.nbch.credit_tracker.utils.ZipUtils;
|
||||
|
||||
import javax.xml.stream.XMLStreamException;
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
import static ru.nbch.credit_tracker.utils.error.ExceptionUtils.clientExcp;
|
||||
import static ru.nbch.credit_tracker.utils.error.ExceptionUtils.serverExcp;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@EnableAspectJAutoProxy(exposeProxy = true)
|
||||
public class SignalFacade {
|
||||
|
||||
private SignalFacade self;
|
||||
|
||||
private final CreditTrackerSettings config;
|
||||
private final AuthorizationService authorizationService;
|
||||
private final SignalService signalService;
|
||||
private final SignalRestService signalRestService;
|
||||
private final MonitoringErrorProcesingService monitoringErrorProcesingService;
|
||||
private final ValidationService validationService;
|
||||
private final StaxXmlProcessor staxXmlProcessor;
|
||||
|
||||
private final ExecutorService executor;
|
||||
private final long maxXmlFileSize;
|
||||
|
||||
|
||||
public SignalFacade(CreditTrackerSettings config,
|
||||
AuthorizationService authorizationService,
|
||||
SignalService signalService,
|
||||
SignalRestService signalRestService,
|
||||
MonitoringErrorProcesingService monitoringErrorProcesingService,
|
||||
ValidationService validationService,
|
||||
StaxXmlProcessor staxXmlProcessor,
|
||||
@Qualifier("executorService") ExecutorService executor) {
|
||||
this.config = config;
|
||||
this.authorizationService = authorizationService;
|
||||
this.signalService = signalService;
|
||||
this.signalRestService = signalRestService;
|
||||
this.monitoringErrorProcesingService = monitoringErrorProcesingService;
|
||||
this.validationService = validationService;
|
||||
this.staxXmlProcessor = staxXmlProcessor;
|
||||
this.executor = executor;
|
||||
this.maxXmlFileSize = config.getMaxXmlFileSize() * 1024 * 1024;
|
||||
}
|
||||
|
||||
public ResponseEntity<MonitoringSetupResponse> setupMonitoring(MultipartFile file) {
|
||||
// первичные валидации
|
||||
if (file.isEmpty() || !isZipFile(file)) {
|
||||
throw clientExcp(SignalError.ERROR_001, XmlErrorFormat.MonitoringError);
|
||||
}
|
||||
|
||||
// Валидация Архива на количетсво файлов, размер и расширение
|
||||
MonitoringRequest monitoringRequest;
|
||||
Long packageId;
|
||||
ZipInputStream zipInputStream = null;
|
||||
String filePath;
|
||||
try {
|
||||
zipInputStream = new ZipInputStream(file.getInputStream());
|
||||
Optional<ZipEntry> xmlFileEntry = ZipUtils.extractValidEntry(zipInputStream);
|
||||
if (xmlFileEntry.isEmpty()) {
|
||||
throw clientExcp(SignalError.ERROR_001, XmlErrorFormat.MonitoringError);
|
||||
}
|
||||
if (xmlFileEntry.get().getSize() > maxXmlFileSize) {
|
||||
throw clientExcp(SignalError.ERROR_009, XmlErrorFormat.MonitoringError);
|
||||
}
|
||||
|
||||
zipInputStream.close();
|
||||
zipInputStream = new ZipInputStream(file.getInputStream());
|
||||
zipInputStream.getNextEntry();
|
||||
|
||||
filePath = writeTmpFile(zipInputStream);
|
||||
} catch (Exception e) {
|
||||
if (e instanceof SignalClientException) {
|
||||
throw (SignalClientException) e;
|
||||
}
|
||||
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, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// валидация xml
|
||||
try {
|
||||
monitoringRequest = readAndValidateMonitoringRequest(filePath);
|
||||
} catch (Exception e) {
|
||||
CTUtil.removeFile(filePath);
|
||||
if (e.getCause() instanceof XMLStreamException) {
|
||||
throw clientExcp(SignalError.ERROR_011, XmlErrorFormat.MonitoringError);
|
||||
}
|
||||
if (e instanceof SignalClientException) {
|
||||
throw (SignalClientException) e;
|
||||
}
|
||||
throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringError, e);
|
||||
}
|
||||
|
||||
try {
|
||||
// проверка авторизации
|
||||
AuthorizationService.AuthResult authResult = authorizationService.findAuthInfo(monitoringRequest.getUserId(), monitoringRequest.getPassword());
|
||||
if (!authResult.isSuccess()) {
|
||||
signalService.rejectPackage(monitoringRequest);
|
||||
throw clientExcp(authResult.getError(), monitoringRequest.getPackageName(), XmlErrorFormat.MonitoringError);
|
||||
}
|
||||
|
||||
// проверка на уникальность пакета
|
||||
if (signalService.isPackageNotUnique(monitoringRequest)) {
|
||||
signalService.rejectPackage(monitoringRequest);
|
||||
throw clientExcp(SignalError.ERROR_007, monitoringRequest.getPackageName(), XmlErrorFormat.MonitoringError);
|
||||
}
|
||||
|
||||
packageId = signalService.registerPackage(monitoringRequest);
|
||||
log.info("Package registered. PackageId {}. Status: {}", packageId, Status.PROCESSING.name());
|
||||
} catch (Exception e) {
|
||||
CTUtil.removeFile(filePath);
|
||||
if (e instanceof SignalClientException) {
|
||||
throw (SignalClientException) e;
|
||||
}
|
||||
throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringError, e);
|
||||
}
|
||||
|
||||
// в отдельном потоке запускаем регистрацию субъектов, поиск фидов и расчет флагов просрочек
|
||||
getCurrentProxy().registerFidsAndDebts(packageId, monitoringRequest.getPackageName(), filePath);
|
||||
|
||||
return ResponseEntity.ok().body(new MonitoringSetupResponse());
|
||||
}
|
||||
|
||||
private MonitoringRequest readAndValidateMonitoringRequest(String filePath) {
|
||||
MonitoringRequest monitoringRequest;
|
||||
try (BufferedInputStream in = new BufferedInputStream(Files.newInputStream(Path.of(filePath)), 8 * 1024 * 1024)) {
|
||||
monitoringRequest = staxXmlProcessor.read(in, MonitoringRequest.class,
|
||||
request -> {
|
||||
try {
|
||||
validationService.validateMonitoringRequest(request);
|
||||
} catch (SignalClientException e) {
|
||||
if (request != null) {
|
||||
signalService.rejectPackage(request);
|
||||
}
|
||||
request = null;
|
||||
throw e;
|
||||
}
|
||||
request.getSubjects().getSubject().clear();
|
||||
},
|
||||
request -> request.getSubjects().getSubject().isEmpty()
|
||||
,
|
||||
40000, "/MonitoringRequest/Subjects/Subject");
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Error while parsing and validating monitoring request", e);
|
||||
}
|
||||
return monitoringRequest;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
try (OutputStream out = Files.newOutputStream(Path.of(filePath))) {
|
||||
|
||||
byte[] buffer = new byte[8 * 1024 * 1024];
|
||||
int bytesRead;
|
||||
while ((bytesRead = zipInputStream.read(buffer)) > 0) {
|
||||
out.write(buffer, 0, bytesRead);
|
||||
}
|
||||
|
||||
return filePath;
|
||||
}
|
||||
}
|
||||
|
||||
@SetAndClearMdc(argIndex = 1)
|
||||
public void registerFidsAndDebts(Long packageId, String packageName, String filePath) {
|
||||
Map<String, String> context = MDC.getCopyOfContextMap();
|
||||
executor.execute(() -> {
|
||||
if (context != null) {
|
||||
MDC.setContextMap(context);
|
||||
}
|
||||
try {
|
||||
signalService.registerSubjects(filePath, packageId);
|
||||
CTUtil.removeFile(filePath);
|
||||
SignalService.UpdateResult updateResult = signalService.updateSubjects(packageId, null);
|
||||
if (updateResult.isNoFidsFound()) {
|
||||
log.info("No fids found for PackageId: {} PackageName: {}. Package status ON_MONITORING. No registration in signal service.", packageId, packageName);
|
||||
signalService.updatePackageStatus(packageId, Status.ON_MONITORING);
|
||||
return;
|
||||
}
|
||||
if (signalRestService.setupMonitoring(packageId)) {
|
||||
LocalDateTime monitoringAt = LocalDateTime.now();
|
||||
log.info("Package was successfully registered in signal service. PackageId: {} PackageName: {}, MonitoringAt: {}", packageId, packageName, monitoringAt);
|
||||
signalService.updatePackageOnMonitoringSuccess(packageId, monitoringAt);
|
||||
monitoringErrorProcesingService.findErrors(packageId);
|
||||
} else {
|
||||
log.info("Package registration was rejected by signal service. PackageId: {} PackageName: {}", packageId, packageName);
|
||||
signalService.updatePackageStatus(packageId, Status.ERROR);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
CTUtil.removeFile(filePath);
|
||||
signalService.updatePackageStatus(packageId, Status.ERROR);
|
||||
log.error("Ошибка при поиске фидов или расчете флагов просрочек. Package_id {}", packageId, e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private boolean isZipFile(MultipartFile file) {
|
||||
return file.getContentType() != null &&
|
||||
(file.getContentType().equals("application/zip") || file.getContentType().equals("application/x-zip-compressed")) ||
|
||||
file.getOriginalFilename() != null && file.getOriginalFilename().toLowerCase().endsWith(".zip");
|
||||
}
|
||||
|
||||
public ResponseEntity<MonitoringActivePackagesResponse> findActivePackages(MonitoringActivePackagesRequest activePackagesRequest) {
|
||||
try {
|
||||
AuthorizationService.AuthResult authResult = authorizationService.findAuthInfo(activePackagesRequest.getUserId(), activePackagesRequest.getPassword());
|
||||
if (!authResult.isSuccess()) {
|
||||
throw clientExcp(authResult.getError(), XmlErrorFormat.MonitoringActivePackagesError);
|
||||
}
|
||||
List<UserPackage> activePackages = signalService.findActivePackages(activePackagesRequest.getUserId());
|
||||
|
||||
if (!activePackages.isEmpty()) {
|
||||
MonitoringActivePackagesResponse response = new MonitoringActivePackagesResponse();
|
||||
response.setStatus("OK");
|
||||
response.setMembercode(activePackagesRequest.getUserId().substring(0, 6));
|
||||
Packages packages = new Packages();
|
||||
activePackages.forEach(data -> {
|
||||
Package pack = new Package();
|
||||
pack.setPackageName(data.getPackageName());
|
||||
pack.setCreatedAt(data.getCreatedAt() != null ? data.getCreatedAt().toString() : null);
|
||||
packages.getPackages().add(pack);
|
||||
});
|
||||
response.setPackages(packages);
|
||||
return ResponseEntity.ok(response);
|
||||
} else {
|
||||
throw clientExcp(SignalError.ERROR_015, XmlErrorFormat.MonitoringActivePackagesError);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (e instanceof SignalClientException) {
|
||||
throw (SignalClientException) e;
|
||||
}
|
||||
throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringActivePackagesError, e);
|
||||
}
|
||||
}
|
||||
|
||||
public ResponseEntity<MonitoringAllPackagesResponse> findAllPackages(MonitoringAllPackagesRequest allPackagesRequest) {
|
||||
try {
|
||||
AuthorizationService.AuthResult authResult = authorizationService.findAuthInfo(allPackagesRequest.getUserId(), allPackagesRequest.getPassword());
|
||||
if (!authResult.isSuccess()) {
|
||||
throw clientExcp(authResult.getError(), XmlErrorFormat.MonitoringAllPackagesError);
|
||||
}
|
||||
List<UserPackage> activePackages = signalService.findAllPackages(allPackagesRequest.getUserId());
|
||||
|
||||
if (!activePackages.isEmpty()) {
|
||||
MonitoringAllPackagesResponse response = new MonitoringAllPackagesResponse();
|
||||
response.setStatus("OK");
|
||||
response.setMembercode(allPackagesRequest.getUserId().substring(0, 6));
|
||||
PackagesAll packages = new PackagesAll();
|
||||
activePackages.forEach(data -> {
|
||||
PackageAll pack = new PackageAll();
|
||||
pack.setPackageName(data.getPackageName());
|
||||
pack.setCreatedAt(data.getCreatedAt() != null ? data.getCreatedAt().toString() : null);
|
||||
pack.setStatus(data.getStatus());
|
||||
if (data.getStatus().equals(Status.DELETED.name())) {
|
||||
pack.setDeactivatedAt(data.getDeactivatedAt() != null ? data.getDeactivatedAt().toString() : null);
|
||||
}
|
||||
packages.getPackages().add(pack);
|
||||
});
|
||||
response.setPackages(packages);
|
||||
return ResponseEntity.ok(response);
|
||||
} else {
|
||||
throw clientExcp(SignalError.ERROR_019, XmlErrorFormat.MonitoringAllPackagesError);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (e instanceof SignalClientException) {
|
||||
throw (SignalClientException) e;
|
||||
}
|
||||
throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringAllPackagesError, e);
|
||||
}
|
||||
}
|
||||
|
||||
public ResponseEntity<MonitoringPackageDeleteResponse> deletePackage(String username, String packageName) {
|
||||
try {
|
||||
if (!signalService.isPackageExists(username, packageName)) {
|
||||
throw clientExcp(SignalError.ERROR_016, XmlErrorFormat.MonitoringPackageDeleteError, packageName);
|
||||
}
|
||||
|
||||
LocalDateTime deactivatedAt = LocalDateTime.now();
|
||||
signalService.deletePackage(username, packageName, deactivatedAt);
|
||||
|
||||
MonitoringPackageDeleteResponse response = new MonitoringPackageDeleteResponse();
|
||||
response.setStatus("Deleted");
|
||||
response.setPackageName(packageName);
|
||||
response.setDeactivatedAt(deactivatedAt.toString());
|
||||
|
||||
signalRestService.deletePackage(username, packageName);
|
||||
log.info("Package {} deleted", packageName);
|
||||
return ResponseEntity.ok(response);
|
||||
} catch (Exception e) {
|
||||
if (e instanceof SignalClientException) {
|
||||
throw (SignalClientException) e;
|
||||
}
|
||||
throw serverExcp(SignalError.ERROR_017, XmlErrorFormat.MonitoringPackageDeleteError, e);
|
||||
}
|
||||
}
|
||||
|
||||
public ResponseEntity<MonitoringReportListResponse> getReportList(String username, String packageName) {
|
||||
try {
|
||||
if (signalService.isPackagesByStatusExist(username, packageName, Status.PROCESSING)) {
|
||||
throw clientExcp(SignalError.ERROR_018, XmlErrorFormat.MonitoringReportListError, packageName);
|
||||
}
|
||||
if (signalService.isPackagesByStatusExist(username, packageName, Status.REJECTED)) {
|
||||
throw clientExcp(SignalError.ERROR_020, XmlErrorFormat.MonitoringReportListError, packageName);
|
||||
}
|
||||
if (signalService.isPackagesByStatusExist(username, packageName, Status.ERROR)) {
|
||||
throw clientExcp(SignalError.ERROR_021, XmlErrorFormat.MonitoringReportListError, packageName);
|
||||
}
|
||||
if (signalService.isPackagesByStatusExist(username, packageName, Status.DELETED)) {
|
||||
throw clientExcp(SignalError.ERROR_022, XmlErrorFormat.MonitoringReportListError, packageName);
|
||||
}
|
||||
if (!signalService.isPackagesByStatusExist(username, packageName, Status.ON_MONITORING)) {
|
||||
throw clientExcp(SignalError.ERROR_012, XmlErrorFormat.MonitoringReportListError, packageName);
|
||||
}
|
||||
|
||||
String memberCode = username.substring(0, 6);
|
||||
String filePrefix = (memberCode + "_" + packageName).toUpperCase();
|
||||
String dirPath = config.getLoadDir() + File.separator + memberCode;
|
||||
File folder = new File(dirPath);
|
||||
|
||||
MonitoringReportListResponse.Reports reports = new MonitoringReportListResponse.Reports();
|
||||
if (folder.exists() && folder.isDirectory()) {
|
||||
File[] files = folder.listFiles();
|
||||
if (files != null) {
|
||||
for (File file : files) {
|
||||
if (file.isFile() && file.getName().startsWith(filePrefix)) {
|
||||
reports.getReports().add(file.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
MonitoringReportListResponse response = new MonitoringReportListResponse();
|
||||
response.setPackageName(packageName);
|
||||
response.setReports(reports);
|
||||
return ResponseEntity.ok(response);
|
||||
} catch (Exception e) {
|
||||
if (e instanceof SignalClientException) {
|
||||
throw (SignalClientException) e;
|
||||
}
|
||||
throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringReportListError, e);
|
||||
}
|
||||
}
|
||||
|
||||
public ResponseEntity<Resource> downloadFile(String fileName) {
|
||||
try {
|
||||
String memberCode = CTUtil.getMemberCodeFromFileName(fileName);
|
||||
if (memberCode == null) {
|
||||
throw clientExcp(SignalError.ERROR_014, XmlErrorFormat.MonitoringDownloadError, fileName);
|
||||
}
|
||||
String dirPath = config.getLoadDir() + File.separator + memberCode;
|
||||
File folder = new File(dirPath);
|
||||
|
||||
if (folder.exists() && folder.isDirectory()) {
|
||||
File file = new File(dirPath + File.separator + fileName);
|
||||
if (file.exists() && file.isFile()) {
|
||||
return ResponseEntity.ok().body(new FileUrlResource(file.getPath()));
|
||||
}
|
||||
} else {
|
||||
throw clientExcp(SignalError.ERROR_014, XmlErrorFormat.MonitoringDownloadError, fileName);
|
||||
}
|
||||
throw clientExcp(SignalError.ERROR_014, XmlErrorFormat.MonitoringDownloadError, fileName);
|
||||
} catch (Exception e) {
|
||||
if (e instanceof SignalClientException) {
|
||||
throw (SignalClientException) e;
|
||||
}
|
||||
throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringDownloadError, e);
|
||||
}
|
||||
}
|
||||
|
||||
private SignalFacade getCurrentProxy() {
|
||||
if (self == null) {
|
||||
self = (SignalFacade) AopContext.currentProxy();
|
||||
}
|
||||
return self;
|
||||
}
|
||||
}
|
||||
280
src/main/java/ru/nbch/credit_tracker/facade/SignalFacadeV3.java
Normal file
280
src/main/java/ru/nbch/credit_tracker/facade/SignalFacadeV3.java
Normal file
|
|
@ -0,0 +1,280 @@
|
|||
package ru.nbch.credit_tracker.facade;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import javax.xml.stream.XMLStreamException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.springframework.core.io.FileUrlResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import ru.nbch.credit_tracker.component.MonitoringRequestValidator;
|
||||
import ru.nbch.credit_tracker.config.settings.CreditTrackerSettings;
|
||||
import ru.nbch.credit_tracker.entities.app.UserPackage;
|
||||
import ru.nbch.credit_tracker.entities.user_request.active_packages.MonitoringActivePackagesRequest;
|
||||
import ru.nbch.credit_tracker.entities.user_request.all_packages.MonitoringAllPackagesRequest;
|
||||
import ru.nbch.credit_tracker.entities.user_response.active_packages.MonitoringActivePackagesResponse;
|
||||
import ru.nbch.credit_tracker.entities.user_response.active_packages.Package;
|
||||
import ru.nbch.credit_tracker.entities.user_response.active_packages.Packages;
|
||||
import ru.nbch.credit_tracker.entities.user_response.all_packages.MonitoringAllPackagesResponse;
|
||||
import ru.nbch.credit_tracker.entities.user_response.all_packages.PackageAll;
|
||||
import ru.nbch.credit_tracker.entities.user_response.all_packages.PackagesAll;
|
||||
import ru.nbch.credit_tracker.entities.user_response.success.MonitoringPackageDeleteResponse;
|
||||
import ru.nbch.credit_tracker.entities.user_response.success.MonitoringReportListResponse;
|
||||
import ru.nbch.credit_tracker.enums.SignalError;
|
||||
import ru.nbch.credit_tracker.enums.Status;
|
||||
import ru.nbch.credit_tracker.enums.XmlErrorFormat;
|
||||
import ru.nbch.credit_tracker.exceptions.SignalClientException;
|
||||
import ru.nbch.credit_tracker.model.MonitoringValidationError;
|
||||
import ru.nbch.credit_tracker.model.ValidationResult;
|
||||
import ru.nbch.credit_tracker.service.SignalServiceV2;
|
||||
import ru.nbch.credit_tracker.service.scoring.AuthorizationService;
|
||||
import ru.nbch.credit_tracker.service.signal.SignalRestService;
|
||||
import ru.nbch.credit_tracker.service.task.PipelinePackageManager;
|
||||
import ru.nbch.credit_tracker.utils.CTUtil;
|
||||
import ru.nbch.credit_tracker.utils.LogUtil;
|
||||
import ru.nbch.credit_tracker.utils.ZipUtils;
|
||||
import static ru.nbch.credit_tracker.utils.error.ExceptionUtils.clientExcp;
|
||||
import static ru.nbch.credit_tracker.utils.error.ExceptionUtils.serverExcp;
|
||||
|
||||
@Service
|
||||
@Slf4j
|
||||
public class SignalFacadeV3 {
|
||||
|
||||
private final MonitoringRequestValidator monitoringRequestValidator;
|
||||
private final CreditTrackerSettings config;
|
||||
private final SignalServiceV2 signalService;
|
||||
private final AuthorizationService authorizationService;
|
||||
private final PipelinePackageManager pipelinePackageManager;
|
||||
private final SignalRestService signalRestService;
|
||||
public SignalFacadeV3(MonitoringRequestValidator monitoringRequestValidator,
|
||||
CreditTrackerSettings config,
|
||||
SignalServiceV2 signalService,
|
||||
AuthorizationService authorizationService,
|
||||
PipelinePackageManager pipelinePackageManager,
|
||||
SignalRestService signalRestService) {
|
||||
this.monitoringRequestValidator = monitoringRequestValidator;
|
||||
this.config = config;
|
||||
this.signalService = signalService;
|
||||
this.authorizationService = authorizationService;
|
||||
this.pipelinePackageManager = pipelinePackageManager;
|
||||
this.signalRestService = signalRestService;
|
||||
}
|
||||
|
||||
public void setupMonitoringRequest(MultipartFile file) {
|
||||
log.debug("Start processing monitoring request");
|
||||
monitoringRequestValidator.validateZipContent(file);
|
||||
|
||||
log.trace("Saving monitoring request to tmp file...");
|
||||
String filePath;
|
||||
try {
|
||||
String folderPath = config.getLoadDir() + File.separator + "tmp_folder" + File.separator;
|
||||
filePath = ZipUtils.writeTmpFile(folderPath, file);
|
||||
} catch (IOException e) {
|
||||
throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringError, e);
|
||||
}
|
||||
|
||||
log.debug("File saved to {}", filePath);
|
||||
ValidationResult validationResult = null;
|
||||
try {
|
||||
validationResult = monitoringRequestValidator.readAndValidateMonitoringRequest(filePath);
|
||||
if (!validationResult.getValidationErrors().isEmpty()) {
|
||||
MonitoringValidationError firstError = validationResult.getValidationErrors().get(0);
|
||||
signalService.rejectPackage(validationResult.getUserId(), validationResult.getPackageName());
|
||||
FileUtils.delete(new File(filePath));
|
||||
throw firstError.getSignalException();
|
||||
}
|
||||
} catch (RuntimeException e) {
|
||||
if (e.getCause() instanceof XMLStreamException) {
|
||||
throw clientExcp(SignalError.ERROR_011, XmlErrorFormat.MonitoringError);
|
||||
}
|
||||
throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringError, e);
|
||||
} catch (IOException e) {
|
||||
log.warn("Error deleting tmp file", e);
|
||||
}
|
||||
|
||||
String packageName = validationResult.getPackageName();
|
||||
String userId = validationResult.getUserId();
|
||||
String password = validationResult.getPassword();
|
||||
try {
|
||||
// проверка авторизации
|
||||
AuthorizationService.AuthResult authResult = authorizationService.findAuthInfo(userId, password);
|
||||
if (!authResult.isSuccess()) {
|
||||
signalService.rejectPackage(userId, packageName);
|
||||
throw clientExcp(authResult.getError(), XmlErrorFormat.MonitoringError);
|
||||
}
|
||||
|
||||
// проверка на уникальность пакета
|
||||
// if (signalService.isPackageNotUnique(userId, packageName)) {
|
||||
// signalService.rejectPackage(userId, packageName);
|
||||
// throw clientExcp(SignalError.ERROR_007, XmlErrorFormat.MonitoringError);
|
||||
// }
|
||||
|
||||
Long packageId = signalService.registerPackage(userId, packageName);
|
||||
LogUtil.addMdcLogKey("%s %d".formatted(packageName, packageId));
|
||||
log.info("Package registered. PackageId {}. Status: {}", packageId, Status.PROCESSING.name());
|
||||
pipelinePackageManager.buildAndRun(packageId, filePath);
|
||||
} catch (Exception e) {
|
||||
CTUtil.removeFile(filePath);
|
||||
if (e instanceof SignalClientException) {
|
||||
throw (SignalClientException) e;
|
||||
}
|
||||
throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringError, e);
|
||||
}
|
||||
}
|
||||
|
||||
public ResponseEntity<MonitoringActivePackagesResponse> findActivePackages(MonitoringActivePackagesRequest activePackagesRequest) throws Exception {
|
||||
AuthorizationService.AuthResult authResult = authorizationService.findAuthInfo(activePackagesRequest.getUserId(), activePackagesRequest.getPassword());
|
||||
if (!authResult.isSuccess()) {
|
||||
throw clientExcp(authResult.getError(), XmlErrorFormat.MonitoringActivePackagesError);
|
||||
}
|
||||
|
||||
List<UserPackage> activePackages = signalService.findActivePackages(activePackagesRequest.getUserId());
|
||||
if (activePackages.isEmpty()) {
|
||||
throw clientExcp(SignalError.ERROR_015, XmlErrorFormat.MonitoringActivePackagesError);
|
||||
}
|
||||
|
||||
MonitoringActivePackagesResponse response = new MonitoringActivePackagesResponse();
|
||||
response.setStatus("OK");
|
||||
response.setMembercode(activePackagesRequest.getUserId().substring(0, 6));
|
||||
Packages packages = new Packages();
|
||||
activePackages.forEach(data -> {
|
||||
Package pack = new Package();
|
||||
pack.setPackageName(data.getPackageName());
|
||||
pack.setCreatedAt(data.getCreatedAt() != null ? data.getCreatedAt().toString() : null);
|
||||
packages.getPackages().add(pack);
|
||||
});
|
||||
response.setPackages(packages);
|
||||
return ResponseEntity.ok(response);
|
||||
}
|
||||
|
||||
public ResponseEntity<MonitoringAllPackagesResponse> findAllPackages(MonitoringAllPackagesRequest allPackagesRequest) throws Exception {
|
||||
AuthorizationService.AuthResult authResult = authorizationService.findAuthInfo(allPackagesRequest.getUserId(), allPackagesRequest.getPassword());
|
||||
if (!authResult.isSuccess()) {
|
||||
throw clientExcp(authResult.getError(), XmlErrorFormat.MonitoringAllPackagesError);
|
||||
}
|
||||
|
||||
List<UserPackage> activePackages = signalService.findAllPackages(allPackagesRequest.getUserId());
|
||||
if (activePackages.isEmpty()) {
|
||||
throw clientExcp(SignalError.ERROR_019, XmlErrorFormat.MonitoringAllPackagesError);
|
||||
}
|
||||
|
||||
MonitoringAllPackagesResponse response = new MonitoringAllPackagesResponse();
|
||||
response.setStatus("OK");
|
||||
response.setMembercode(allPackagesRequest.getUserId().substring(0, 6));
|
||||
PackagesAll packages = new PackagesAll();
|
||||
activePackages.forEach(data -> {
|
||||
PackageAll pack = new PackageAll();
|
||||
pack.setPackageName(data.getPackageName());
|
||||
pack.setCreatedAt(data.getCreatedAt() != null ? data.getCreatedAt().toString() : null);
|
||||
pack.setStatus(data.getStatus());
|
||||
if (data.getStatus().equals(Status.DELETED.name())) {
|
||||
pack.setDeactivatedAt(data.getDeactivatedAt() != null ? data.getDeactivatedAt().toString() : null);
|
||||
}
|
||||
packages.getPackages().add(pack);
|
||||
});
|
||||
response.setPackages(packages);
|
||||
return ResponseEntity.ok(response);
|
||||
}
|
||||
|
||||
public ResponseEntity<MonitoringPackageDeleteResponse> deletePackage(String username, String packageName) {
|
||||
try {
|
||||
if (!signalService.isPackageExists(username, packageName)) {
|
||||
throw clientExcp(SignalError.ERROR_016, XmlErrorFormat.MonitoringPackageDeleteError, packageName);
|
||||
}
|
||||
|
||||
LocalDateTime deactivatedAt = LocalDateTime.now();
|
||||
signalService.deletePackage(username, packageName, deactivatedAt);
|
||||
|
||||
MonitoringPackageDeleteResponse response = new MonitoringPackageDeleteResponse();
|
||||
response.setStatus("Deleted");
|
||||
response.setPackageName(packageName);
|
||||
response.setDeactivatedAt(deactivatedAt.toString());
|
||||
|
||||
signalRestService.deletePackage(username, packageName);
|
||||
log.info("Package {} deleted", packageName);
|
||||
return ResponseEntity.ok(response);
|
||||
} catch (Exception e) {
|
||||
if (e instanceof SignalClientException) {
|
||||
throw (SignalClientException) e;
|
||||
}
|
||||
throw serverExcp(SignalError.ERROR_017, XmlErrorFormat.MonitoringPackageDeleteError, e);
|
||||
}
|
||||
}
|
||||
|
||||
public ResponseEntity<MonitoringReportListResponse> getReportList(String username, String packageName) {
|
||||
try {
|
||||
if (signalService.isPackagesByStatusExist(username, packageName, Status.PROCESSING)) {
|
||||
throw clientExcp(SignalError.ERROR_018, XmlErrorFormat.MonitoringReportListError, packageName);
|
||||
}
|
||||
if (signalService.isPackagesByStatusExist(username, packageName, Status.REJECTED)) {
|
||||
throw clientExcp(SignalError.ERROR_020, XmlErrorFormat.MonitoringReportListError, packageName);
|
||||
}
|
||||
if (signalService.isPackagesByStatusExist(username, packageName, Status.ERROR)) {
|
||||
throw clientExcp(SignalError.ERROR_021, XmlErrorFormat.MonitoringReportListError, packageName);
|
||||
}
|
||||
if (signalService.isPackagesByStatusExist(username, packageName, Status.DELETED)) {
|
||||
throw clientExcp(SignalError.ERROR_022, XmlErrorFormat.MonitoringReportListError, packageName);
|
||||
}
|
||||
if (!signalService.isPackagesByStatusExist(username, packageName, Status.ON_MONITORING)) {
|
||||
throw clientExcp(SignalError.ERROR_012, XmlErrorFormat.MonitoringReportListError, packageName);
|
||||
}
|
||||
|
||||
String memberCode = username.substring(0, 6);
|
||||
String filePrefix = (memberCode + "_" + packageName).toUpperCase();
|
||||
String dirPath = config.getLoadDir() + File.separator + memberCode;
|
||||
File folder = new File(dirPath);
|
||||
|
||||
MonitoringReportListResponse.Reports reports = new MonitoringReportListResponse.Reports();
|
||||
if (folder.exists() && folder.isDirectory()) {
|
||||
File[] files = folder.listFiles();
|
||||
if (files != null) {
|
||||
for (File file : files) {
|
||||
if (file.isFile() && file.getName().startsWith(filePrefix)) {
|
||||
reports.getReports().add(file.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
MonitoringReportListResponse response = new MonitoringReportListResponse();
|
||||
response.setPackageName(packageName);
|
||||
response.setReports(reports);
|
||||
return ResponseEntity.ok(response);
|
||||
} catch (Exception e) {
|
||||
if (e instanceof SignalClientException) {
|
||||
throw (SignalClientException) e;
|
||||
}
|
||||
throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringReportListError, e);
|
||||
}
|
||||
}
|
||||
|
||||
public ResponseEntity<Resource> downloadFile(String fileName) {
|
||||
try {
|
||||
String memberCode = CTUtil.getMemberCodeFromFileName(fileName);
|
||||
if (memberCode == null) {
|
||||
throw clientExcp(SignalError.ERROR_014, XmlErrorFormat.MonitoringDownloadError, fileName);
|
||||
}
|
||||
String dirPath = config.getLoadDir() + File.separator + memberCode;
|
||||
File folder = new File(dirPath);
|
||||
|
||||
if (folder.exists() && folder.isDirectory()) {
|
||||
File file = new File(dirPath + File.separator + fileName);
|
||||
if (file.exists() && file.isFile()) {
|
||||
return ResponseEntity.ok().body(new FileUrlResource(file.getPath()));
|
||||
}
|
||||
} else {
|
||||
throw clientExcp(SignalError.ERROR_014, XmlErrorFormat.MonitoringDownloadError, fileName);
|
||||
}
|
||||
throw clientExcp(SignalError.ERROR_014, XmlErrorFormat.MonitoringDownloadError, fileName);
|
||||
} catch (Exception e) {
|
||||
if (e instanceof SignalClientException) {
|
||||
throw (SignalClientException) e;
|
||||
}
|
||||
throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringDownloadError, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,19 +1,22 @@
|
|||
package ru.nbch.credit_tracker.mapper.indic;
|
||||
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import ru.nbch.credit_tracker.service.indic.FlagsService;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import ru.nbch.credit_tracker.entities.indic.PaymentsFlag;
|
||||
import ru.nbch.credit_tracker.service.indic.FlagsService;
|
||||
|
||||
@Mapper
|
||||
public interface FlagsMapper {
|
||||
List<FlagsService.RuPastDuarearData> findRuPastDuarearData(@Param("fids") List<Integer> fids);
|
||||
|
||||
List<FlagsService.FlagFidData> findFlagsDebt5000(@Param("fids") List<Integer> fids);
|
||||
List<FlagsService.RuPaymtData> findRuPaymtData(@Param("fids") List<Integer> fids, @Param("reportedDate") LocalDate reportedDate);
|
||||
|
||||
List<FlagsService.FlagFidData> findFlag90ByPastDueArrear(@Param("fids") List<Integer> fids, @Param("reportedDate") LocalDate reportedDate);
|
||||
List<PaymentsFlag> findFlagsDebt5000(@Param("fids") List<Integer> fids);
|
||||
|
||||
List<FlagsService.FlagFidData> findFlag90ByRuPaymt(@Param("fids") List<Integer> fids, @Param("reportedDate") LocalDate reportedDate);
|
||||
List<PaymentsFlag> findFlag90ByPastDueArrear(@Param("fids") List<Integer> fids, @Param("reportedDate") LocalDate reportedDate);
|
||||
|
||||
List<PaymentsFlag> findFlag90ByRuPaymt(@Param("fids") List<Integer> fids, @Param("reportedDate") LocalDate reportedDate);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +0,0 @@
|
|||
package ru.nbch.credit_tracker.mapper.indic;
|
||||
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import ru.nbch.credit_tracker.service.indic.PhoneService;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface PhoneMapper {
|
||||
List<PhoneService.FidsData> findFids(@Param("phones") List<String> phones);
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@ package ru.nbch.credit_tracker.mapper.signal;
|
|||
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import ru.nbch.credit_tracker.entities.dto.Hit;
|
||||
import ru.nbch.credit_tracker.entities.app.Hit;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
|
|
|||
|
|
@ -2,24 +2,29 @@ package ru.nbch.credit_tracker.mapper.signal;
|
|||
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import ru.nbch.credit_tracker.entities.dto.PhoneFidMap;
|
||||
import ru.nbch.credit_tracker.entities.app.PhoneFidMap;
|
||||
import ru.nbch.credit_tracker.entities.user_request.monitoring_request.Subject;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface PhoneFidMapMapper {
|
||||
|
||||
void registerSubjects(@Param("subjects") List<Subject> subjects, @Param("packageId") Long packageId);
|
||||
void register(@Param("phoneFidMap") List<PhoneFidMap> phoneFidMap);
|
||||
void registerJson(@Param("json") String json);
|
||||
void registerWithId(@Param("phoneFidMap") List<PhoneFidMap> phoneFidMap);
|
||||
|
||||
List<PhoneFidMap> findFullPhoneFidMap(@Param("packageId") Long packageId, @Param("offset") int offset, @Param("limit") int limit);
|
||||
|
||||
void updatePhoneFidMaps(@Param("subjects") List<PhoneFidMap> subjects);
|
||||
void updatePhoneFidMaps(@Param("subjects") Collection<PhoneFidMap> subjects);
|
||||
void updateOne(@Param("subject") PhoneFidMap subject);
|
||||
void updateFid(@Param("subjects") Collection<PhoneFidMap> subjects);
|
||||
|
||||
void updatePhoneFidMapsErrors(@Param("subjects") List<PhoneFidMap> subjects);
|
||||
|
||||
PhoneFidMap findPhoneFidMapById(@Param("id") Long packageId);
|
||||
|
||||
boolean hasNonNullFids(@Param("packageId") Long packageId);
|
||||
boolean hasOldRow(@Param("packageId") Long packageId, @Param("daysCount") Integer daysCount);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ package ru.nbch.credit_tracker.mapper.signal;
|
|||
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import ru.nbch.credit_tracker.entities.dto.UserPackage;
|
||||
import ru.nbch.credit_tracker.entities.app.UserPackage;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
|
|
|||
16
src/main/java/ru/nbch/credit_tracker/model/DummyPayload.java
Normal file
16
src/main/java/ru/nbch/credit_tracker/model/DummyPayload.java
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
package ru.nbch.credit_tracker.model;
|
||||
|
||||
public class DummyPayload implements StagePayload {
|
||||
|
||||
private final boolean poison;
|
||||
public static DummyPayload POISON = new DummyPayload(true);
|
||||
|
||||
public DummyPayload(boolean poison) {
|
||||
this.poison = poison;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPoison() {
|
||||
return poison;
|
||||
}
|
||||
}
|
||||
37
src/main/java/ru/nbch/credit_tracker/model/FlagsPayload.java
Normal file
37
src/main/java/ru/nbch/credit_tracker/model/FlagsPayload.java
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
package ru.nbch.credit_tracker.model;
|
||||
|
||||
import ru.nbch.credit_tracker.entities.app.PhoneFidMap;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class FlagsPayload implements StagePayload {
|
||||
private final Long packageId;
|
||||
private final List<PhoneFidMap> phoneFidMaps;
|
||||
private final boolean poison;
|
||||
|
||||
public FlagsPayload(Long packageId, List<PhoneFidMap> fidsMapByBatch) {
|
||||
this.packageId = packageId;
|
||||
this.phoneFidMaps = fidsMapByBatch;
|
||||
this.poison = false;
|
||||
}
|
||||
|
||||
public FlagsPayload(boolean poison) {
|
||||
this.packageId = null;
|
||||
this.phoneFidMaps = null;
|
||||
this.poison = poison;
|
||||
}
|
||||
|
||||
public Long getPackageId() {
|
||||
return packageId;
|
||||
}
|
||||
|
||||
public List<PhoneFidMap> getPhoneFidMaps() {
|
||||
return phoneFidMaps;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPoison() {
|
||||
return poison;
|
||||
}
|
||||
|
||||
}
|
||||
21
src/main/java/ru/nbch/credit_tracker/model/IdData.java
Normal file
21
src/main/java/ru/nbch/credit_tracker/model/IdData.java
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
package ru.nbch.credit_tracker.model;
|
||||
|
||||
|
||||
import java.time.LocalDate;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Builder
|
||||
//todo подумать над неймингом класс
|
||||
public class IdData {
|
||||
private Integer fid;
|
||||
private String serNum;
|
||||
private String idNum;
|
||||
private LocalDate issDate;
|
||||
private String idType;
|
||||
private String idType2;
|
||||
private LocalDate fileSinceDate;
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
package ru.nbch.credit_tracker.model;
|
||||
|
||||
import ru.nbch.credit_tracker.exceptions.SignalException;
|
||||
|
||||
public class MonitoringValidationError {
|
||||
private final int line;
|
||||
private final String message;
|
||||
private final SignalException signalException;
|
||||
|
||||
|
||||
public MonitoringValidationError(int line,
|
||||
String message,
|
||||
SignalException signalException) {
|
||||
this.line = line;
|
||||
this.message = message;
|
||||
this.signalException = signalException;
|
||||
}
|
||||
|
||||
public int getLine() {
|
||||
return line;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public SignalException getSignalException() {
|
||||
return signalException;
|
||||
}
|
||||
}
|
||||
20
src/main/java/ru/nbch/credit_tracker/model/NameData.java
Normal file
20
src/main/java/ru/nbch/credit_tracker/model/NameData.java
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
package ru.nbch.credit_tracker.model;
|
||||
|
||||
|
||||
import java.time.LocalDate;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Builder
|
||||
//todo подумать над неймингом класс
|
||||
public class NameData {
|
||||
private Integer fid;
|
||||
private String firstName;
|
||||
private String lastName;
|
||||
private String middleName;
|
||||
private LocalDate birthDate;
|
||||
private LocalDate fileSinceDate;
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package ru.nbch.credit_tracker.model;
|
||||
|
||||
public class SendSignalsPayload implements StagePayload {
|
||||
private String signalXmlPath;
|
||||
public static final SendSignalsPayload POISON = new SendSignalsPayload(true);
|
||||
private boolean poison;
|
||||
|
||||
public SendSignalsPayload(boolean poison) {
|
||||
this.poison = poison;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPoison() {
|
||||
return poison;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
package ru.nbch.credit_tracker.model;
|
||||
|
||||
public interface StagePayload {
|
||||
boolean isPoison();
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package ru.nbch.credit_tracker.model;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Builder
|
||||
public class SubjectProfile {
|
||||
private String identifier;
|
||||
private String phone;
|
||||
private Integer fid;
|
||||
private NameData nameData;
|
||||
private IdData idData;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
package ru.nbch.credit_tracker.model;
|
||||
|
||||
public interface TerminatePayload {
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
package ru.nbch.credit_tracker.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ValidationResult {
|
||||
private String packageName;
|
||||
private String userId;
|
||||
private String password;
|
||||
private List<MonitoringValidationError> validationErrors = new ArrayList<>();
|
||||
|
||||
public void setPackageName(String packageName) {
|
||||
this.packageName = packageName;
|
||||
}
|
||||
|
||||
public void setUserId(String userId) {
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public void setValidationErrors(List<MonitoringValidationError> validationErrors) {
|
||||
this.validationErrors = validationErrors;
|
||||
}
|
||||
|
||||
public ValidationResult() {
|
||||
}
|
||||
|
||||
public String getPackageName() {
|
||||
return packageName;
|
||||
}
|
||||
|
||||
public String getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public List<MonitoringValidationError> getValidationErrors() {
|
||||
return validationErrors;
|
||||
}
|
||||
|
||||
public void addError(MonitoringValidationError error) {
|
||||
this.validationErrors.add(error);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
package ru.nbch.credit_tracker.model.impl;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import lombok.Getter;
|
||||
import ru.nbch.credit_tracker.model.StagePayload;
|
||||
|
||||
@Getter
|
||||
public class PartFilePayload implements StagePayload {
|
||||
private final Path partPath;
|
||||
private final Long packageId;
|
||||
private final boolean poison;
|
||||
public static final PartFilePayload POISON = new PartFilePayload(true);
|
||||
|
||||
public PartFilePayload(boolean poison) {
|
||||
this.poison = poison;
|
||||
this.partPath = null;
|
||||
this.packageId = null;
|
||||
}
|
||||
|
||||
public PartFilePayload(Path partPath, Long packageId) {
|
||||
this.partPath = partPath;
|
||||
this.packageId = packageId;
|
||||
this.poison = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPoison() {
|
||||
return poison;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package ru.nbch.credit_tracker.model.impl;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import ru.nbch.credit_tracker.model.TerminatePayload;
|
||||
|
||||
public class SignalXmlFilePayload implements TerminatePayload {
|
||||
private final Path signalXmlFilePath;
|
||||
|
||||
public SignalXmlFilePayload(Path signalXmlFilePath) {
|
||||
this.signalXmlFilePath = signalXmlFilePath;
|
||||
}
|
||||
|
||||
public Path getSignalXmlFilePath() {
|
||||
return signalXmlFilePath;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
package ru.nbch.credit_tracker.model.impl;
|
||||
|
||||
import java.util.List;
|
||||
import ru.nbch.credit_tracker.model.StagePayload;
|
||||
import ru.nbch.credit_tracker.model.SubjectProfile;
|
||||
|
||||
|
||||
public class SubjectPayload implements StagePayload {
|
||||
private final Long packageId;
|
||||
private final List<SubjectProfile> subjects;
|
||||
public static final SubjectPayload POISON = new SubjectPayload(true);
|
||||
private final boolean poison;
|
||||
|
||||
public SubjectPayload(boolean poison) {
|
||||
this.packageId = null;
|
||||
this.subjects = null;
|
||||
this.poison = poison;
|
||||
}
|
||||
|
||||
public SubjectPayload(Long packageId, List<SubjectProfile> subjects) {
|
||||
this.packageId = packageId;
|
||||
this.subjects = subjects;
|
||||
this.poison = false;
|
||||
}
|
||||
|
||||
public Long getPackageId() {
|
||||
return packageId;
|
||||
}
|
||||
|
||||
public List<SubjectProfile> getSubjects() {
|
||||
return subjects;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPoison() {
|
||||
return poison;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,150 @@
|
|||
package ru.nbch.credit_tracker.repository;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
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.Repository;
|
||||
import ru.nbch.credit_tracker.entities.indic.Name;
|
||||
|
||||
@Repository
|
||||
public class PhoneRepository {
|
||||
|
||||
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 static final String SELCT_FIDS_WITH_NAME = """
|
||||
SELECT
|
||||
pn.number,
|
||||
pn.fid,
|
||||
pn.file_since_dt,
|
||||
nf.first,
|
||||
nf.middle,
|
||||
nf.name_1,
|
||||
nf.birth_dt,
|
||||
nf.file_since_dt as file_since_dt_name
|
||||
FROM PHONE_NORM pn
|
||||
JOIN NAME nf
|
||||
ON nf.fid = pn.fid
|
||||
WHERE number in (:phones) WITH UR
|
||||
""";
|
||||
|
||||
private final NamedParameterJdbcTemplate jdbcTemplate;
|
||||
|
||||
public PhoneRepository(@Qualifier("jdbcTemplateIndic") NamedParameterJdbcTemplate jdbcTemplate) {
|
||||
this.jdbcTemplate = jdbcTemplate;
|
||||
}
|
||||
|
||||
|
||||
public List<FidsData> findFids(List<String> phones) {
|
||||
MapSqlParameterSource params = new MapSqlParameterSource();
|
||||
params.addValue("phones", phones);
|
||||
|
||||
return jdbcTemplate.query(SELCT_FIDS, params, (rs, rowNum) ->
|
||||
new PhoneRepository.FidsData(rs.getInt("fid"), rs.getString("number"), rs.getObject("file_since_dt", LocalDate.class))
|
||||
);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public List<FidsDataWithName> findFidsWithName(List<String> phones) {
|
||||
MapSqlParameterSource params = new MapSqlParameterSource();
|
||||
params.addValue("phones", phones);
|
||||
|
||||
return jdbcTemplate.query(SELCT_FIDS_WITH_NAME, params, (rs, rowNum) ->
|
||||
new PhoneRepository.FidsDataWithName(
|
||||
rs.getInt("FID"),
|
||||
rs.getString("NUMBER"),
|
||||
getLocalDate(rs, "FILE_SINCE_DT"),
|
||||
rs.getString("FIRST"),
|
||||
rs.getString("NAME_1"),
|
||||
rs.getString("MIDDLE"),
|
||||
getLocalDate(rs, "BIRTH_DT"),
|
||||
getLocalDate(rs, "FILE_SINCE_DT_NAME")
|
||||
));
|
||||
}
|
||||
|
||||
public List<Name> findNameByPhones(List<String> phones) {
|
||||
MapSqlParameterSource params = new MapSqlParameterSource();
|
||||
params.addValue("phones", phones);
|
||||
|
||||
return jdbcTemplate.query(SELCT_FIDS_WITH_NAME, params, (rs, rowNum) ->
|
||||
Name.builder()
|
||||
.fid(rs.getInt("FID"))
|
||||
.number(rs.getString("NUMBER"))
|
||||
.fileSinceDate(getLocalDate(rs, "FILE_SINCE_DT_NAME"))
|
||||
.first(rs.getString("FIRST"))
|
||||
.middle(rs.getString("MIDDLE"))
|
||||
.last(rs.getString("NAME_1"))
|
||||
.birthDate(getLocalDate(rs, "BIRTH_DT"))
|
||||
.build());
|
||||
}
|
||||
|
||||
private static LocalDate getLocalDate(ResultSet rs, String col) throws SQLException {
|
||||
java.sql.Date d = rs.getDate(col);
|
||||
return d != null ? d.toLocalDate() : null;
|
||||
}
|
||||
|
||||
public List<FidsDataLong> findFidsLong(List<String> phones) {
|
||||
MapSqlParameterSource params = new MapSqlParameterSource();
|
||||
params.addValue("phones", phones);
|
||||
|
||||
return jdbcTemplate.query(SELCT_FIDS, params, (rs, rowNum) ->
|
||||
new PhoneRepository.FidsDataLong(rs.getInt("fid"), Long.parseLong(rs.getString("number")), rs.getObject("file_since_dt", LocalDate.class))
|
||||
);
|
||||
}
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public static class FidsData {
|
||||
private Integer fid;
|
||||
private String phone;
|
||||
private LocalDate fileSinceDate;
|
||||
}
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public static class FidsDataWithName {
|
||||
private Integer fid;
|
||||
private String phone;
|
||||
private LocalDate fileSinceDate;
|
||||
private String firstName;
|
||||
private String lastName;
|
||||
private String middleName;
|
||||
private LocalDate birthDate;
|
||||
private LocalDate fileSinceDateName;
|
||||
}
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public static class FidsDataLong {
|
||||
private Integer fid;
|
||||
private Long phone;
|
||||
private LocalDate fileSinceDate;
|
||||
}
|
||||
}
|
||||
|
|
@ -6,7 +6,7 @@ import org.springframework.aop.framework.AopContext;
|
|||
import org.springframework.context.annotation.EnableAspectJAutoProxy;
|
||||
import org.springframework.stereotype.Service;
|
||||
import ru.nbch.credit_tracker.config.mdc.SetAndClearMdc;
|
||||
import ru.nbch.credit_tracker.entities.dto.UserPackage;
|
||||
import ru.nbch.credit_tracker.entities.app.UserPackage;
|
||||
import ru.nbch.credit_tracker.service.signal.SignalRestService;
|
||||
import ru.nbch.credit_tracker.utils.CTUtil;
|
||||
|
||||
|
|
|
|||
|
|
@ -6,9 +6,9 @@ import org.slf4j.MDC;
|
|||
import org.springframework.scheduling.concurrent.CustomizableThreadFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
import ru.nbch.credit_tracker.config.settings.CreditTrackerSettings;
|
||||
import ru.nbch.credit_tracker.entities.dto.Hit;
|
||||
import ru.nbch.credit_tracker.entities.dto.PhoneFidMap;
|
||||
import ru.nbch.credit_tracker.entities.dto.UserPackage;
|
||||
import ru.nbch.credit_tracker.entities.app.Hit;
|
||||
import ru.nbch.credit_tracker.entities.app.PhoneFidMap;
|
||||
import ru.nbch.credit_tracker.entities.app.UserPackage;
|
||||
import ru.nbch.credit_tracker.entities.external_response.online_download.Inquiry;
|
||||
import ru.nbch.credit_tracker.entities.external_response.online_download.Person;
|
||||
import ru.nbch.credit_tracker.entities.external_response.online_download.Persons;
|
||||
|
|
|
|||
|
|
@ -6,18 +6,16 @@ import lombok.extern.slf4j.Slf4j;
|
|||
import org.springframework.stereotype.Service;
|
||||
import ru.nbch.credit_tracker.builder.PersonFactoryBuilder;
|
||||
import ru.nbch.credit_tracker.config.settings.CreditTrackerSettings;
|
||||
import ru.nbch.credit_tracker.entities.dto.Hit;
|
||||
import ru.nbch.credit_tracker.entities.dto.PhoneFidMap;
|
||||
import ru.nbch.credit_tracker.entities.dto.UserPackage;
|
||||
import ru.nbch.credit_tracker.entities.app.Hit;
|
||||
import ru.nbch.credit_tracker.entities.app.PhoneFidMap;
|
||||
import ru.nbch.credit_tracker.entities.app.UserPackage;
|
||||
import ru.nbch.credit_tracker.entities.external_request.signals.Auth;
|
||||
import ru.nbch.credit_tracker.entities.external_request.signals.Person;
|
||||
import ru.nbch.credit_tracker.entities.external_request.signals.Signals;
|
||||
import ru.nbch.credit_tracker.entities.external_response.monitoring_report_error.MonitoringReportErrors;
|
||||
import ru.nbch.credit_tracker.entities.user_request.monitoring_request.MonitoringRequest;
|
||||
import ru.nbch.credit_tracker.enums.Status;
|
||||
import ru.nbch.credit_tracker.service.indic.FlagsService;
|
||||
import ru.nbch.credit_tracker.service.indic.PersonService;
|
||||
import ru.nbch.credit_tracker.service.indic.PhoneService;
|
||||
import ru.nbch.credit_tracker.service.indic.*;
|
||||
import ru.nbch.credit_tracker.service.signal.HitsService;
|
||||
import ru.nbch.credit_tracker.service.signal.PhoneFidMapService;
|
||||
import ru.nbch.credit_tracker.service.signal.UserPackageService;
|
||||
|
|
@ -37,12 +35,15 @@ import java.util.function.Predicate;
|
|||
|
||||
@Slf4j
|
||||
@Service
|
||||
@Deprecated
|
||||
//todo оставил для временного подсмотреть на старую логику
|
||||
public class SignalService {
|
||||
|
||||
private final CreditTrackerSettings config;
|
||||
private final HitsService hitsService;
|
||||
private final UserPackageService userPackageService;
|
||||
private final PhoneFidMapService phoneFidMapService;
|
||||
private final PhoneService2 phoneService2;
|
||||
private final PhoneService phoneService;
|
||||
private final FlagsService flagsService;
|
||||
private final PersonService personService;
|
||||
|
|
@ -54,6 +55,7 @@ public class SignalService {
|
|||
HitsService hitsService,
|
||||
UserPackageService userPackageService,
|
||||
PhoneFidMapService phoneFidMapService,
|
||||
PhoneService2 phoneService2,
|
||||
PhoneService phoneService,
|
||||
FlagsService flagsService,
|
||||
PersonService personService,
|
||||
|
|
@ -62,6 +64,7 @@ public class SignalService {
|
|||
this.hitsService = hitsService;
|
||||
this.userPackageService = userPackageService;
|
||||
this.phoneFidMapService = phoneFidMapService;
|
||||
this.phoneService2 = phoneService2;
|
||||
this.phoneService = phoneService;
|
||||
this.flagsService = flagsService;
|
||||
this.personService = personService;
|
||||
|
|
@ -72,6 +75,10 @@ public class SignalService {
|
|||
return userPackageService.isPackageNotUnique(request.getUserId(), request.getPackageName());
|
||||
}
|
||||
|
||||
public boolean isPackageNotUnique(String userId, String packageName) {
|
||||
return userPackageService.isPackageNotUnique(userId, packageName);
|
||||
}
|
||||
|
||||
public Long registerPackage(MonitoringRequest request) {
|
||||
UserPackage userPackage = UserPackage.builder()
|
||||
.userId(request.getUserId())
|
||||
|
|
@ -82,6 +89,16 @@ public class SignalService {
|
|||
return userPackageService.insertPackage(userPackage);
|
||||
}
|
||||
|
||||
public Long registerPackage(String userId, String packageName) {
|
||||
UserPackage userPackage = UserPackage.builder()
|
||||
.userId(userId)
|
||||
.packageName(packageName)
|
||||
.createdAt(LocalDateTime.now())
|
||||
.status(Status.PROCESSING.name())
|
||||
.build();
|
||||
return userPackageService.insertPackage(userPackage);
|
||||
}
|
||||
|
||||
public void registerSubjects(String filePath, Long packageId) {
|
||||
log.info("Start registering subjects. PackageId: {}", packageId);
|
||||
try (BufferedInputStream in = new BufferedInputStream(Files.newInputStream(Path.of(filePath)), 8 * 1024 * 1024)) {
|
||||
|
|
@ -102,6 +119,25 @@ public class SignalService {
|
|||
log.info("Subjects were registered. PackageId: {}", packageId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Выгружает {@link PhoneFidMap} батчами. Передает обогащенные фидами и данными из Name {@link PhoneFidMap}
|
||||
* на поиск флагов и формирование отчета в оф. сервис сигналов
|
||||
* @param packageId id пакета, по которому идет выборка {@link PhoneFidMap}
|
||||
*/
|
||||
public void processSubjects(Long packageId) {
|
||||
int offset = 0;
|
||||
List<PhoneFidMap> batch;
|
||||
do {
|
||||
batch = phoneFidMapService.findFullPhoneFidMap(packageId, offset, batchSize);
|
||||
if (!batch.isEmpty()) {
|
||||
Map<Integer, PhoneFidMap> fidsMapByBatch = phoneService.defineFidsAndNameByPhone(batch);
|
||||
// TODO process fidsMapByBatch here
|
||||
}
|
||||
offset += batchSize;
|
||||
} while (!batch.isEmpty());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Обрабатывает все PhoneFidMap по packageId, выгружая их пачками и фильтруя по переданному фильтру
|
||||
*/
|
||||
|
|
@ -110,6 +146,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 +162,32 @@ public class SignalService {
|
|||
}
|
||||
|
||||
if (!filteredItems.isEmpty()) {
|
||||
boolean fidsFound = updateFlags(filteredItems, packageId);
|
||||
if (noFidsFound) {
|
||||
noFidsFound = !fidsFound;
|
||||
Map<Integer, PhoneFidMap> fidsMapByBatch = phoneService.defineFidByPhone(filteredItems);
|
||||
phoneService.defineFidByPhone(filteredItems);
|
||||
log.debug("Actual fids size by phone's batch: {}", fidsMapByBatch.size());
|
||||
|
||||
if (fidsMapByBatch.size() + phonesByFidToProcessing.size() <= batchSize) {
|
||||
phonesByFidToProcessing.putAll(fidsMapByBatch);
|
||||
} else {
|
||||
//todo execute find name and id -> forming xml
|
||||
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 +195,37 @@ public class SignalService {
|
|||
* Обрабатывает пачку PhoneFidMap: обновляет фиды и флаги просрочек
|
||||
* @return fidsFound true - если хотя бы по одному номеру был найден фид, false - в противном случае
|
||||
*/
|
||||
public boolean updateFlags(List<PhoneFidMap> subjects, Long 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, Long 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) {
|
||||
|
|
@ -197,6 +238,16 @@ public class SignalService {
|
|||
userPackageService.insertPackage(userPackage);
|
||||
}
|
||||
|
||||
public void rejectPackage(String userId, String packageName) {
|
||||
UserPackage userPackage = UserPackage.builder()
|
||||
.userId(userId)
|
||||
.packageName(packageName)
|
||||
.createdAt(LocalDateTime.now())
|
||||
.status(Status.REJECTED.name())
|
||||
.build();
|
||||
userPackageService.insertPackage(userPackage);
|
||||
}
|
||||
|
||||
public void updatePackageStatus(Long packageId, Status status) {
|
||||
userPackageService.updatePackageStatus(packageId, status);
|
||||
}
|
||||
|
|
@ -226,7 +277,7 @@ public class SignalService {
|
|||
return filePath;
|
||||
}
|
||||
|
||||
private Signals buildSignals(Long packageId) {
|
||||
public Signals buildSignals(Long packageId) {
|
||||
UserPackage userPackage = userPackageService.findPackage(packageId);
|
||||
Signals signals = new Signals();
|
||||
signals.setOnline("2"); // Всегда=2 (только онлайн-сигналы)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,70 @@
|
|||
package ru.nbch.credit_tracker.service;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import ru.nbch.credit_tracker.entities.app.UserPackage;
|
||||
import ru.nbch.credit_tracker.entities.user_request.monitoring_request.MonitoringRequest;
|
||||
import ru.nbch.credit_tracker.enums.Status;
|
||||
import ru.nbch.credit_tracker.service.signal.UserPackageService;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class SignalServiceV2 {
|
||||
private final UserPackageService userPackageService;
|
||||
|
||||
public SignalServiceV2(UserPackageService userPackageService) {
|
||||
this.userPackageService = userPackageService;
|
||||
}
|
||||
|
||||
public boolean isPackageNotUnique(MonitoringRequest request) {
|
||||
return userPackageService.isPackageNotUnique(request.getUserId(), request.getPackageName());
|
||||
}
|
||||
|
||||
public boolean isPackageNotUnique(String userId, String packageName) {
|
||||
return userPackageService.isPackageNotUnique(userId, packageName);
|
||||
}
|
||||
|
||||
|
||||
public Long registerPackage(String userId, String packageName) {
|
||||
UserPackage userPackage = UserPackage.builder()
|
||||
.userId(userId)
|
||||
.packageName(packageName)
|
||||
.createdAt(LocalDateTime.now())
|
||||
.status(Status.PROCESSING.name())
|
||||
.build();
|
||||
return userPackageService.insertPackage(userPackage);
|
||||
}
|
||||
|
||||
public void rejectPackage(String userId, String packageName) {
|
||||
UserPackage userPackage = UserPackage.builder()
|
||||
.userId(userId)
|
||||
.packageName(packageName)
|
||||
.createdAt(LocalDateTime.now())
|
||||
.status(Status.REJECTED.name())
|
||||
.build();
|
||||
userPackageService.insertPackage(userPackage);
|
||||
}
|
||||
|
||||
|
||||
public List<UserPackage> findActivePackages(String userId) {
|
||||
return userPackageService.findUserPackages(userId, Status.ON_MONITORING);
|
||||
}
|
||||
|
||||
public List<UserPackage> findAllPackages(String userId) {
|
||||
return userPackageService.findAllPackagesByUserId(userId);
|
||||
}
|
||||
|
||||
public boolean isPackageExists(String username, String packageName) {
|
||||
return userPackageService.isPackageExists(username, packageName, Status.ON_MONITORING);
|
||||
}
|
||||
|
||||
public boolean isPackagesByStatusExist(String username, String packageName, Status status) {
|
||||
return userPackageService.hasActivePackages(username, packageName, status);
|
||||
}
|
||||
|
||||
public void deletePackage(String username, String packageName, LocalDateTime deactivatedAt) {
|
||||
userPackageService.deletePackage(username, packageName, Status.DELETED, Status.ON_MONITORING, deactivatedAt);
|
||||
}
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@ import org.springframework.aop.framework.AopContext;
|
|||
import org.springframework.context.annotation.EnableAspectJAutoProxy;
|
||||
import org.springframework.stereotype.Service;
|
||||
import ru.nbch.credit_tracker.config.mdc.SetAndClearMdc;
|
||||
import ru.nbch.credit_tracker.entities.dto.UserPackage;
|
||||
import ru.nbch.credit_tracker.entities.app.UserPackage;
|
||||
import ru.nbch.credit_tracker.enums.Status;
|
||||
import ru.nbch.credit_tracker.service.signal.SignalRestService;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,18 +1,22 @@
|
|||
package ru.nbch.credit_tracker.service;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.slf4j.MDC;
|
||||
import org.springframework.scheduling.concurrent.CustomizableThreadFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
import ru.nbch.credit_tracker.config.settings.CreditTrackerSettings;
|
||||
import ru.nbch.credit_tracker.entities.app.PhoneFidMap;
|
||||
import ru.nbch.credit_tracker.entities.app.UserPackage;
|
||||
import ru.nbch.credit_tracker.enums.Status;
|
||||
import ru.nbch.credit_tracker.service.signal.SignalRestService;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.List;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.slf4j.MDC;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
|
||||
import org.springframework.scheduling.support.CronTrigger;
|
||||
import org.springframework.stereotype.Service;
|
||||
import ru.nbch.credit_tracker.config.settings.CreditTrackerSettings;
|
||||
import ru.nbch.credit_tracker.entities.dto.UserPackage;
|
||||
import ru.nbch.credit_tracker.enums.Status;
|
||||
import ru.nbch.credit_tracker.mapper.signal.PhoneFidMapMapper;
|
||||
import ru.nbch.credit_tracker.service.signal.SignalRestService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* Сервис периодического обновления фидов и флагов просрочек по пакетам находящимся в статусе ON_MONITORING
|
||||
|
|
@ -26,40 +30,31 @@ public class UpdatingFlagsService {
|
|||
private final SignalRestService signalRestService;
|
||||
private final MonitoringErrorProcesingService monitoringErrorProcesingService;
|
||||
private final Long daysToUpdateFlags;
|
||||
private final String updateFlagsCronExpression;
|
||||
private final PhoneFidMapMapper phoneFidMapMapper;
|
||||
private final Long updateFlagsInterval;
|
||||
|
||||
public UpdatingFlagsService(SignalService signalService,
|
||||
SignalRestService signalRestService,
|
||||
MonitoringErrorProcesingService monitoringErrorProcesingService,
|
||||
CreditTrackerSettings config,
|
||||
PhoneFidMapMapper phoneFidMapMapper) {
|
||||
CreditTrackerSettings config) {
|
||||
this.signalService = signalService;
|
||||
this.signalRestService = signalRestService;
|
||||
this.monitoringErrorProcesingService = monitoringErrorProcesingService;
|
||||
this.daysToUpdateFlags = config.getDaysToUpdateFlags() != null ? config.getDaysToUpdateFlags() : 1;
|
||||
this.updateFlagsCronExpression = config.getUpdateFlagsCronExpression() != null ? config.getUpdateFlagsCronExpression() : "*/10 * * * * *";
|
||||
this.phoneFidMapMapper = phoneFidMapMapper;
|
||||
this.updateFlagsInterval = config.getUpdateFlagsInterval() != null ? config.getUpdateFlagsInterval() : 1;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
|
||||
scheduler.setPoolSize(1);
|
||||
scheduler.initialize();
|
||||
scheduler.schedule(this::update, new CronTrigger(updateFlagsCronExpression));
|
||||
ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(new CustomizableThreadFactory("updating-flags-"));
|
||||
scheduler.scheduleAtFixedRate(this::update, 0, updateFlagsInterval, TimeUnit.DAYS);
|
||||
}
|
||||
|
||||
public void update() {
|
||||
private void update() {
|
||||
log.info("Start updating flags");
|
||||
List<UserPackage> packages = signalService.findPackagesOnMonitoring();
|
||||
for (UserPackage userPackage : packages) {
|
||||
log.info("Updating flags for Package {}", userPackage.getPackageName());
|
||||
MDC.put("ru.nbch.credit_tracker.log.mdc.key", "[" + userPackage.getPackageName() +"]");
|
||||
log.info("Check on updating flags for Package {}", userPackage.getPackageName());
|
||||
boolean hasOldRow = phoneFidMapMapper.hasOldRow(userPackage.getId(), daysToUpdateFlags.intValue());
|
||||
log.info("There are {} old rows", hasOldRow ? "exist" : "doesn't exist");
|
||||
if (hasOldRow) {
|
||||
updateFlags(userPackage);
|
||||
}
|
||||
updateFlags(userPackage);
|
||||
MDC.remove("ru.nbch.credit_tracker.log.mdc.key");
|
||||
}
|
||||
log.info("Finish updating flags");
|
||||
|
|
@ -70,7 +65,8 @@ public class UpdatingFlagsService {
|
|||
String packageName = userPackage.getPackageName();
|
||||
try {
|
||||
signalService.updatePackageStatus(packageId, Status.PROCESSING);
|
||||
SignalService.UpdateResult updateResult = signalService.updateSubjects(packageId, null);
|
||||
Predicate<PhoneFidMap> filter = subject -> subject.getCalculatedAt() != null && hasDaysPassed(subject.getCalculatedAt());
|
||||
SignalService.UpdateResult updateResult = signalService.updateSubjects(packageId, filter);
|
||||
if (updateResult.isUpdated()) {
|
||||
if (signalRestService.setupMonitoring(packageId)) {
|
||||
LocalDateTime monitoringAt = userPackage.getMonitoringAt() != null ? userPackage.getMonitoringAt() : LocalDateTime.now();
|
||||
|
|
|
|||
|
|
@ -1,35 +1,70 @@
|
|||
package ru.nbch.credit_tracker.service.indic;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
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.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.*;
|
||||
import ru.nbch.credit_tracker.model.SubjectProfile;
|
||||
|
||||
@Service
|
||||
public class FlagsService {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(FlagsService.class);
|
||||
private final FlagsMapper mapper;
|
||||
|
||||
public FlagsService(FlagsMapper mapper) {
|
||||
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 FlagsService(FlagsMapper mapper,
|
||||
@Qualifier("jdbcTemplateIndic") NamedParameterJdbcTemplate jdbcTemplate) {
|
||||
this.mapper = mapper;
|
||||
this.jdbcTemplate = jdbcTemplate;
|
||||
}
|
||||
|
||||
public Map<Integer, FlagsData> findFlags(List<Integer> fids) {
|
||||
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 +72,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;
|
||||
|
|
@ -49,52 +84,352 @@ public class FlagsService {
|
|||
return result;
|
||||
}
|
||||
|
||||
// TODO optimize
|
||||
public void fillFlagsData(List<SubjectProfile> subjects) {
|
||||
List<Integer> fids = subjects.stream().map(SubjectProfile::getFid).filter(Objects::nonNull).toList();
|
||||
Map<Integer, FlagsService.FlagsData> fidsflagsDataMap = findFlags2(fids);
|
||||
|
||||
fidsflagsDataMap.forEach((fid, data) -> {
|
||||
subjects.stream().filter(s-> s.getFid() != null && s.getFid().equals(fid)).forEach(phoneFidMap -> {
|
||||
|
||||
// phoneFidMap.setFlagDebt5000(data.getFlagDebt5000());
|
||||
// phoneFidMap.setFlag90days(data.getFlag90days());
|
||||
// phoneFidMap.setCalculatedAt(LocalDateTime.now());
|
||||
});
|
||||
});
|
||||
|
||||
subjects.stream().filter(s-> s.getFid() == null).forEach(phoneFidMap -> {
|
||||
// phoneFidMap.setCalculatedAt(LocalDateTime.now());
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Выбирает данные из 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;
|
||||
List<RuPaymtData> ruPaymtDataList = mapper.findRuPaymtData(fids, yearAgo);
|
||||
ruPaymtDataList.sort(Comparator.comparing(RuPaymtData::getFid, Comparator.naturalOrder()));
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
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;
|
||||
public Map<Integer, FlagsService.FlagsData> findFlags2(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<>();
|
||||
findFlagsRuPastDueArrear2(distinctFids, flags5000, flags90DaysByPastDueArrear);
|
||||
findFlagsRuPaymt2(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 findFlagsRuPastDueArrear2(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 {
|
||||
return replacement;
|
||||
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 findFlagsRuPaymt2(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;
|
||||
}
|
||||
}
|
||||
|
||||
@Getter
|
||||
|
|
@ -107,8 +442,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,37 @@
|
|||
package ru.nbch.credit_tracker.service.indic;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import ru.nbch.credit_tracker.entities.indic.PaymentsFlag;
|
||||
import ru.nbch.credit_tracker.mapper.indic.FlagsMapper;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class FlagsService2 {
|
||||
private final FlagsMapper flagsMapper;
|
||||
|
||||
public FlagsService2(FlagsMapper flagsMapper) {
|
||||
this.flagsMapper = flagsMapper;
|
||||
}
|
||||
|
||||
public Map<Integer, Integer> findFlagsDebt5000(List<Integer> fids) {
|
||||
return flagsMapper.findFlagsDebt5000(fids).stream()
|
||||
.collect(Collectors.toMap(PaymentsFlag::getFid, PaymentsFlag::getFlag));
|
||||
}
|
||||
|
||||
public Map<Integer, Integer> findFlag90ByPastDueArrear(List<Integer> fids, LocalDate date) {
|
||||
return flagsMapper.findFlag90ByPastDueArrear(fids, date).stream()
|
||||
.collect(Collectors.toMap(PaymentsFlag::getFid, PaymentsFlag::getFlag));
|
||||
}
|
||||
|
||||
public Map<Integer, Integer> findFlag90ByRuPaymt(List<Integer> fids, LocalDate date) {
|
||||
return flagsMapper.findFlag90ByRuPaymt(fids, date).stream()
|
||||
.collect(Collectors.toMap(PaymentsFlag::getFid, PaymentsFlag::getFlag));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -1,19 +1,20 @@
|
|||
package ru.nbch.credit_tracker.service.indic;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import ru.nbch.credit_tracker.mapper.indic.PersonMapper;
|
||||
|
||||
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 lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import ru.nbch.credit_tracker.mapper.indic.PersonMapper;
|
||||
import ru.nbch.credit_tracker.model.IdData;
|
||||
import ru.nbch.credit_tracker.model.SubjectProfile;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
|
|
@ -51,22 +52,22 @@ public class PersonService {
|
|||
private Map<Integer, NameData> findNameData(List<Integer> fids) {
|
||||
List<NameData> persons = mapper.findNameData(fids);
|
||||
return persons.stream()
|
||||
.collect(Collectors.toMap(NameData::getFid, nameData -> nameData,
|
||||
(existing, replacement) -> {
|
||||
LocalDate existingDate = existing.getFileSinceDate();
|
||||
LocalDate replacementDate = replacement.getFileSinceDate();
|
||||
if (existingDate != null && replacementDate != null) {
|
||||
if (existingDate.isAfter(replacementDate)) {
|
||||
return existing;
|
||||
} else {
|
||||
return replacement;
|
||||
}
|
||||
} else if (existingDate != null) {
|
||||
return existing;
|
||||
} else {
|
||||
return replacement;
|
||||
}
|
||||
}));
|
||||
.collect(Collectors.toMap(NameData::getFid, nameData -> nameData,
|
||||
(existing, replacement) -> {
|
||||
LocalDate existingDate = existing.getFileSinceDate();
|
||||
LocalDate replacementDate = replacement.getFileSinceDate();
|
||||
if (existingDate != null && replacementDate != null) {
|
||||
if (existingDate.isAfter(replacementDate)) {
|
||||
return existing;
|
||||
} else {
|
||||
return replacement;
|
||||
}
|
||||
} else if (existingDate != null) {
|
||||
return existing;
|
||||
} else {
|
||||
return replacement;
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private Map<Integer, PassportData> findPassportData(List<Integer> fids) {
|
||||
|
|
@ -75,7 +76,7 @@ public class PersonService {
|
|||
List<PassportData> passports = mapper.findPassportData(fids);
|
||||
|
||||
Map<Integer, List<PassportData>> passportsMap = passports.stream()
|
||||
.collect(Collectors.groupingBy(PassportData::getFid));
|
||||
.collect(Collectors.groupingBy(PassportData::getFid));
|
||||
|
||||
fids.forEach(fid -> {
|
||||
PassportData passport = findPassport(passportsMap.get(fid));
|
||||
|
|
@ -87,13 +88,44 @@ public class PersonService {
|
|||
return resultMap;
|
||||
}
|
||||
|
||||
public void filledPassportsData(List<SubjectProfile> subjectProfiles) {
|
||||
List<Integer> fids = subjectProfiles.stream().map(SubjectProfile::getFid).collect(Collectors.toList());
|
||||
if (fids.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
List<PassportData> passports = mapper.findPassportData(fids);
|
||||
|
||||
Map<Integer, List<PassportData>> passportsMap = passports.stream()
|
||||
.collect(Collectors.groupingBy(PassportData::getFid));
|
||||
|
||||
subjectProfiles.forEach(subjectProfile -> {
|
||||
PassportData passport = findPassport(passportsMap.get(subjectProfile.getFid()));
|
||||
if (passport != null) {
|
||||
IdData idProfile = IdData.builder()
|
||||
.serNum(passport.getSerNum())
|
||||
.idNum(passport.getIdNum())
|
||||
.issDate(passport.getIssDate())
|
||||
.idType(passport.getIdType())
|
||||
.idType2(passport.getIdType2())
|
||||
.fileSinceDate(passport.getFileSinceDate())
|
||||
.build();
|
||||
subjectProfile.setIdData(idProfile);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private boolean isRequiredPassportDataPresent(PassportData passportData) {
|
||||
boolean isDocTypeNotNull = StringUtils.isNotBlank(passportData.idType) || StringUtils.isNotBlank(passportData.idType2);
|
||||
return StringUtils.isNotBlank(passportData.idNum) && isDocTypeNotNull;
|
||||
}
|
||||
|
||||
private PassportData findPassport(List<PassportData> passports) {
|
||||
if (passports == null || passports.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
List<PassportData> documents = passports.stream()
|
||||
.filter(this::isDUL)
|
||||
.toList(); // берем только ДУЛ
|
||||
.filter(this::isDUL)
|
||||
.toList(); // берем только ДУЛ
|
||||
|
||||
if (documents.isEmpty()) {
|
||||
return null;
|
||||
|
|
@ -101,16 +133,16 @@ public class PersonService {
|
|||
|
||||
// Отдаем приоритет паспортам РФ
|
||||
PassportData passport = documents.stream()
|
||||
.filter(p -> "21".equals(p.getIdType2()) || "21".equals(p.getIdType()))
|
||||
.sorted(
|
||||
Comparator.comparing(
|
||||
PassportData::getIssDate,
|
||||
Comparator.nullsLast(Comparator.reverseOrder())
|
||||
).thenComparing(
|
||||
PassportData::getFileSinceDate,
|
||||
Comparator.nullsLast(Comparator.reverseOrder())
|
||||
)
|
||||
).findFirst().orElse(null);
|
||||
.filter(p -> "21".equals(p.getIdType2()) || "21".equals(p.getIdType()))
|
||||
.sorted(
|
||||
Comparator.comparing(
|
||||
PassportData::getIssDate,
|
||||
Comparator.nullsLast(Comparator.reverseOrder())
|
||||
).thenComparing(
|
||||
PassportData::getFileSinceDate,
|
||||
Comparator.nullsLast(Comparator.reverseOrder())
|
||||
)
|
||||
).findFirst().orElse(null);
|
||||
|
||||
if (passport != null) {
|
||||
return passport;
|
||||
|
|
@ -118,15 +150,15 @@ public class PersonService {
|
|||
|
||||
// Если паспорт РФ не найден, смотри остальные ДУЛ'ы и берем первый
|
||||
passport = documents.stream()
|
||||
.sorted(
|
||||
Comparator.comparing(
|
||||
PassportData::getIssDate,
|
||||
Comparator.nullsLast(Comparator.reverseOrder())
|
||||
).thenComparing(
|
||||
PassportData::getFileSinceDate,
|
||||
Comparator.nullsLast(Comparator.reverseOrder())
|
||||
)
|
||||
).findFirst().orElse(null);
|
||||
.sorted(
|
||||
Comparator.comparing(
|
||||
PassportData::getIssDate,
|
||||
Comparator.nullsLast(Comparator.reverseOrder())
|
||||
).thenComparing(
|
||||
PassportData::getFileSinceDate,
|
||||
Comparator.nullsLast(Comparator.reverseOrder())
|
||||
)
|
||||
).findFirst().orElse(null);
|
||||
return passport;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,110 @@
|
|||
package ru.nbch.credit_tracker.service.indic;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import ru.nbch.credit_tracker.mapper.indic.PersonMapper;
|
||||
import ru.nbch.credit_tracker.model.IdData;
|
||||
import ru.nbch.credit_tracker.model.SubjectProfile;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class PersonServiceV2 {
|
||||
|
||||
private final PersonMapper mapper;
|
||||
|
||||
public PersonServiceV2(PersonMapper mapper) {
|
||||
this.mapper = mapper;
|
||||
}
|
||||
|
||||
public void filledPassportsData(List<SubjectProfile> subjectProfiles) {
|
||||
List<Integer> fids = subjectProfiles.stream().map(SubjectProfile::getFid).collect(Collectors.toList());
|
||||
if (fids.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
List<PersonService.PassportData> passports = mapper.findPassportData(fids);
|
||||
|
||||
Map<Integer, List<PersonService.PassportData>> passportsMap = passports.stream()
|
||||
.collect(Collectors.groupingBy(PersonService.PassportData::getFid));
|
||||
|
||||
subjectProfiles.forEach(subjectProfile -> {
|
||||
PersonService.PassportData passport = findPassport(passportsMap.get(subjectProfile.getFid()));
|
||||
if (passport != null) {
|
||||
IdData idProfile = IdData.builder()
|
||||
.serNum(passport.getSerNum())
|
||||
.idNum(passport.getIdNum())
|
||||
.issDate(passport.getIssDate())
|
||||
.idType(passport.getIdType())
|
||||
.idType2(passport.getIdType2())
|
||||
.fileSinceDate(passport.getFileSinceDate())
|
||||
.build();
|
||||
subjectProfile.setIdData(idProfile);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//todo подлежит рефакторингу
|
||||
private PersonService.PassportData findPassport(List<PersonService.PassportData> passports) {
|
||||
if (passports == null || passports.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
List<PersonService.PassportData> documents = passports.stream()
|
||||
.filter(this::isDUL)
|
||||
.toList(); // берем только ДУЛ
|
||||
|
||||
if (documents.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Отдаем приоритет паспортам РФ
|
||||
PersonService.PassportData passport = documents.stream()
|
||||
.filter(p -> "21".equals(p.getIdType2()) || "21".equals(p.getIdType()))
|
||||
.sorted(
|
||||
Comparator.comparing(
|
||||
PersonService.PassportData::getIssDate,
|
||||
Comparator.nullsLast(Comparator.reverseOrder())
|
||||
).thenComparing(
|
||||
PersonService.PassportData::getFileSinceDate,
|
||||
Comparator.nullsLast(Comparator.reverseOrder())
|
||||
)
|
||||
).findFirst().orElse(null);
|
||||
|
||||
if (passport != null) {
|
||||
return passport;
|
||||
}
|
||||
|
||||
// Если паспорт РФ не найден, смотри остальные ДУЛ'ы и берем первый
|
||||
passport = documents.stream()
|
||||
.sorted(
|
||||
Comparator.comparing(
|
||||
PersonService.PassportData::getIssDate,
|
||||
Comparator.nullsLast(Comparator.reverseOrder())
|
||||
).thenComparing(
|
||||
PersonService.PassportData::getFileSinceDate,
|
||||
Comparator.nullsLast(Comparator.reverseOrder())
|
||||
)
|
||||
).findFirst().orElse(null);
|
||||
return passport;
|
||||
}
|
||||
|
||||
//todo подлежит рефакторингу;
|
||||
private boolean isDUL(PersonService.PassportData p) {
|
||||
Integer idType;
|
||||
Double idType2;
|
||||
try {
|
||||
idType = p.getIdType() != null ? Integer.parseInt(p.getIdType()) : null;
|
||||
} catch (NumberFormatException e) {
|
||||
idType = null;
|
||||
}
|
||||
try {
|
||||
idType2 = p.getIdType2() != null ? Double.valueOf(p.getIdType2()) : null;
|
||||
} catch (NumberFormatException e) {
|
||||
idType2 = null;
|
||||
}
|
||||
return (idType != null && idType <= 30 && idType != 12) || (idType2 != null && (idType2 >= 1 || idType2 <= 999));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,58 +1,269 @@
|
|||
package ru.nbch.credit_tracker.service.indic;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.springframework.stereotype.Service;
|
||||
import ru.nbch.credit_tracker.entities.dto.PhoneFidMap;
|
||||
import ru.nbch.credit_tracker.mapper.indic.PhoneMapper;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import ru.nbch.credit_tracker.entities.app.PhoneFidMap;
|
||||
import ru.nbch.credit_tracker.entities.indic.Name;
|
||||
import ru.nbch.credit_tracker.repository.PhoneRepository;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@Deprecated
|
||||
//todo оставил для временного подсмотреть на старую логику
|
||||
public class PhoneService {
|
||||
|
||||
private final PhoneMapper mapper;
|
||||
private final PhoneRepository phoneRepository;
|
||||
|
||||
public PhoneService(PhoneMapper mapper) {
|
||||
this.mapper = mapper;
|
||||
public PhoneService(PhoneRepository phoneRepository) {
|
||||
this.phoneRepository = phoneRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ищет фиды по номерам телефона
|
||||
* @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) {
|
||||
List<String> phones = subjects.stream().
|
||||
map(subject -> "9" + subject.getPhone())
|
||||
.toList();
|
||||
|
||||
subjects.sort(
|
||||
Comparator.comparing(
|
||||
PhoneFidMap::getPhone,
|
||||
Comparator.naturalOrder()
|
||||
)
|
||||
);
|
||||
|
||||
List<PhoneRepository.FidsDataLong> phonesByFid = phoneRepository.findFidsLong(phones);
|
||||
|
||||
log.debug("Start merging fids");
|
||||
Map<Integer, PhoneFidMap> phoneFidMap = merger(subjects, phonesByFid);
|
||||
log.debug("End merging fids");
|
||||
|
||||
return phoneFidMap;
|
||||
}
|
||||
|
||||
private Map<String, Integer> convertFidsDataToMap(List<FidsData> fidsData) {
|
||||
if (fidsData == null || fidsData.isEmpty()) {
|
||||
return new HashMap<>();
|
||||
private Map<Integer, PhoneFidMap> merger(List<PhoneFidMap> subjects, List<PhoneRepository.FidsDataLong> phonesByFid) {
|
||||
Map<Integer, PhoneFidMap> phoneFidMap = new HashMap<>();
|
||||
|
||||
if (phonesByFid.isEmpty()) {
|
||||
return phoneFidMap;
|
||||
}
|
||||
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)
|
||||
)
|
||||
));
|
||||
|
||||
int index = 0;
|
||||
for (PhoneFidMap subject : subjects) {
|
||||
Long normalizedPhone = Long.parseLong("9" + subject.getPhone());
|
||||
|
||||
while (index < phonesByFid.size()) {
|
||||
PhoneRepository.FidsDataLong currentFidData = phonesByFid.get(index);
|
||||
|
||||
if (currentFidData.getPhone() < normalizedPhone) {
|
||||
index++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (currentFidData.getPhone().equals(normalizedPhone)) {
|
||||
phoneFidMap.put(currentFidData.getFid(), subject);
|
||||
index++;
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
if (index >= phonesByFid.size()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return phoneFidMap;
|
||||
}
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public static class FidsData {
|
||||
private Integer fid;
|
||||
private String phone;
|
||||
private LocalDate fileSinceDate;
|
||||
public void defineFidsAndNameByPhone2(List<PhoneFidMap> subjects) {
|
||||
List<String> phones = subjects.stream().
|
||||
map(subject -> "9" + subject.getPhone())
|
||||
.toList();
|
||||
|
||||
subjects.sort(
|
||||
Comparator.comparing(
|
||||
PhoneFidMap::getPhone,
|
||||
Comparator.naturalOrder()
|
||||
)
|
||||
);
|
||||
|
||||
List<PhoneRepository.FidsDataWithName> phonesByFid = phoneRepository.findFidsWithName(phones);
|
||||
|
||||
log.debug("Start merging fids and names");
|
||||
Map<Integer, List<PhoneRepository.FidsDataWithName>> fidFidsDataMap = phonesByFid.stream()
|
||||
.collect(Collectors.groupingBy(PhoneRepository.FidsDataWithName::getFid));
|
||||
|
||||
Map<String, Integer> phoneToFidsMap = new HashMap<>();
|
||||
for (PhoneRepository.FidsDataWithName fidsData : phonesByFid) {
|
||||
if (!phoneToFidsMap.containsKey(fidsData.getPhone())) {
|
||||
phoneToFidsMap.put(fidsData.getPhone(), fidsData.getFid());
|
||||
}
|
||||
}
|
||||
|
||||
for (PhoneFidMap phoneFid : subjects) {
|
||||
String phone = "9" + phoneFid.getPhone();
|
||||
Integer fid = phoneToFidsMap.get(phone);
|
||||
if (fid != null) {
|
||||
phoneFid.setFid(fid);
|
||||
|
||||
List<PhoneRepository.FidsDataWithName> fidsDataWithNames = fidFidsDataMap.get(fid);
|
||||
if (fidsDataWithNames != null) {
|
||||
PhoneRepository.FidsDataWithName nameData = defineNameData(fidsDataWithNames);
|
||||
|
||||
if (nameData != null) {
|
||||
phoneFid.setFirstName(nameData.getFirstName());
|
||||
phoneFid.setMiddleName(nameData.getMiddleName());
|
||||
phoneFid.setLastName(nameData.getLastName());
|
||||
phoneFid.setBirthDate(nameData.getBirthDate());
|
||||
phoneFid.setFileSinceDate(nameData.getFileSinceDateName());
|
||||
}
|
||||
}
|
||||
phoneToFidsMap.remove(phone);
|
||||
}
|
||||
}
|
||||
log.debug("End merging fids and names");
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
private PhoneRepository.FidsDataWithName defineNameData(List<PhoneRepository.FidsDataWithName> dataList) {
|
||||
PhoneRepository.FidsDataWithName currentData = null;
|
||||
for (PhoneRepository.FidsDataWithName fidsData : dataList) {
|
||||
if (currentData == null) {
|
||||
currentData = fidsData;
|
||||
continue;
|
||||
}
|
||||
LocalDate currentDate = currentData.getFileSinceDateName();
|
||||
LocalDate newDate = fidsData.getFileSinceDateName();
|
||||
if (currentDate != null && newDate != null) {
|
||||
if (currentDate.isBefore(newDate)) {
|
||||
currentData = fidsData;
|
||||
}
|
||||
} else if (currentDate == null) {
|
||||
currentData = fidsData;
|
||||
}
|
||||
}
|
||||
return currentData;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private Name defineNameDataV2(List<Name> dataList) {
|
||||
Name currentData = null;
|
||||
for (Name fidsData : dataList) {
|
||||
if (currentData == null) {
|
||||
currentData = fidsData;
|
||||
continue;
|
||||
}
|
||||
LocalDate currentDate = currentData.getFileSinceDate();
|
||||
LocalDate newDate = fidsData.getFileSinceDate();
|
||||
if (currentDate != null && newDate != null) {
|
||||
if (currentDate.isBefore(newDate)) {
|
||||
currentData = fidsData;
|
||||
}
|
||||
} else if (currentDate == null) {
|
||||
currentData = fidsData;
|
||||
}
|
||||
}
|
||||
return currentData;
|
||||
}
|
||||
|
||||
private Map<Integer, PhoneFidMap> merger2(List<PhoneFidMap> subjects, List<PhoneRepository.FidsData> phonesByFid) {
|
||||
Map<Integer, PhoneFidMap> phoneFidMap = new HashMap<>();
|
||||
Iterator<PhoneRepository.FidsData> iterator = phonesByFid.iterator();
|
||||
String lastUniquePhone = null;
|
||||
PhoneRepository.FidsData fidsData = null;
|
||||
for (PhoneFidMap phoneFid : subjects) {
|
||||
String normalizedPhone = 9 + phoneFid.getPhone();
|
||||
while (iterator.hasNext()) {
|
||||
fidsData = iterator.next();
|
||||
if (StringUtils.isNotEmpty(lastUniquePhone) && lastUniquePhone.equals(fidsData.getPhone())) {
|
||||
iterator.remove();
|
||||
continue;
|
||||
}
|
||||
if (fidsData.getPhone().equals(normalizedPhone) || normalizedPhone.equals(lastUniquePhone)) {
|
||||
lastUniquePhone = fidsData.getPhone();
|
||||
iterator.remove();
|
||||
break;
|
||||
}
|
||||
lastUniquePhone = fidsData.getPhone();
|
||||
break;
|
||||
}
|
||||
if (fidsData != null && normalizedPhone.equals(lastUniquePhone)) {
|
||||
phoneFidMap.put(fidsData.getFid(), phoneFid);
|
||||
}
|
||||
}
|
||||
return phoneFidMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ищет фиды по номерам телефона. Обогащает {@link PhoneFidMap} для найденных фидов данными из Name и фидом.
|
||||
* @return Map: key - FID, value - {@link PhoneFidMap}
|
||||
*/
|
||||
public Map<Integer, PhoneFidMap> defineFidsAndNameByPhone(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()
|
||||
)
|
||||
);
|
||||
|
||||
List<PhoneRepository.FidsDataWithName> phonesByFid = phoneRepository.findFidsWithName(phones);
|
||||
|
||||
log.debug("Start merging fids and names");
|
||||
Map<Integer, List<PhoneRepository.FidsDataWithName>> fidFidsDataMap = phonesByFid.stream()
|
||||
.collect(Collectors.groupingBy(PhoneRepository.FidsDataWithName::getFid));
|
||||
|
||||
Map<String, Integer> phoneToFidsMap = new HashMap<>();
|
||||
for (PhoneRepository.FidsDataWithName fidsData : phonesByFid) {
|
||||
if (!phoneToFidsMap.containsKey(fidsData.getPhone())) {
|
||||
phoneToFidsMap.put(fidsData.getPhone(), fidsData.getFid());
|
||||
}
|
||||
}
|
||||
|
||||
for (PhoneFidMap phoneFid : subjects) {
|
||||
String phone = "9" + phoneFid.getPhone();
|
||||
Integer fid = phoneToFidsMap.get(phone);
|
||||
if (fid != null) {
|
||||
phoneFid.setFid(fid);
|
||||
|
||||
List<PhoneRepository.FidsDataWithName> fidsDataWithNames = fidFidsDataMap.get(fid);
|
||||
if (fidsDataWithNames != null) {
|
||||
PhoneRepository.FidsDataWithName nameData = defineNameData(fidsDataWithNames);
|
||||
|
||||
if (nameData != null) {
|
||||
phoneFid.setFirstName(nameData.getFirstName());
|
||||
phoneFid.setMiddleName(nameData.getMiddleName());
|
||||
phoneFid.setLastName(nameData.getLastName());
|
||||
phoneFid.setBirthDate(nameData.getBirthDate());
|
||||
phoneFid.setFileSinceDate(nameData.getFileSinceDateName());
|
||||
}
|
||||
|
||||
fidFidsDataMap.remove(fid);
|
||||
}
|
||||
phoneFidMap.put(fid, phoneFid); // TODO можно перезатереть запись если у разных phone нашлись одинаковые фиды
|
||||
phoneToFidsMap.remove(phone);
|
||||
}
|
||||
}
|
||||
log.debug("End merging fids and names");
|
||||
|
||||
return phoneFidMap;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,49 @@
|
|||
package ru.nbch.credit_tracker.service.indic;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.BinaryOperator;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import ru.nbch.credit_tracker.entities.indic.Name;
|
||||
import ru.nbch.credit_tracker.model.SubjectProfile;
|
||||
import ru.nbch.credit_tracker.repository.PhoneRepository;
|
||||
import ru.nbch.credit_tracker.utils.CTUtil;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class PhoneService2 {
|
||||
|
||||
private final PhoneRepository phoneRepository;
|
||||
private final Comparator<Name> byDateThenIdNullSafe =
|
||||
Comparator.comparing(Name::getFileSinceDate, Comparator.nullsFirst(Comparator.naturalOrder()))
|
||||
.thenComparing(Name::getFid);
|
||||
|
||||
public PhoneService2(PhoneRepository phoneRepository) {
|
||||
this.phoneRepository = phoneRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Поиск данных по таблицам PHONE_NORM и NAME по списку номеров телефонов
|
||||
* @param subjects
|
||||
* @return - отображение NAME к номеру телефону, отфильтрованному по логике
|
||||
* FID с максимальным значением FILE_SINCE_DT. Если FILE_SINCE_DT оказался одинаковый, берется максимальный FID
|
||||
*/
|
||||
public Map<String, Name> findNameByNumber(List<SubjectProfile> subjects) {
|
||||
List<String> phones = subjects.stream()
|
||||
.map(subject -> CTUtil.normalizePhoneNumber(subject.getPhone()))
|
||||
.toList();
|
||||
|
||||
List<Name> names = phoneRepository.findNameByPhones(phones);
|
||||
|
||||
return names.stream()
|
||||
.collect(Collectors.toMap(
|
||||
Name::getNumber,
|
||||
Function.identity(),
|
||||
BinaryOperator.maxBy(byDateThenIdNullSafe)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
package ru.nbch.credit_tracker.service.signal;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
import ru.nbch.credit_tracker.entities.dto.Hit;
|
||||
import ru.nbch.credit_tracker.entities.app.Hit;
|
||||
import ru.nbch.credit_tracker.mapper.signal.HitsMapper;
|
||||
|
||||
import java.util.List;
|
||||
|
|
|
|||
|
|
@ -1,33 +1,84 @@
|
|||
package ru.nbch.credit_tracker.service.signal;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import org.apache.ibatis.session.ExecutorType;
|
||||
import org.apache.ibatis.session.SqlSession;
|
||||
import org.apache.ibatis.session.SqlSessionFactory;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.stereotype.Service;
|
||||
import ru.nbch.credit_tracker.entities.dto.PhoneFidMap;
|
||||
import ru.nbch.credit_tracker.entities.app.PhoneFidMap;
|
||||
import ru.nbch.credit_tracker.entities.indic.Name;
|
||||
import ru.nbch.credit_tracker.entities.user_request.monitoring_request.Subject;
|
||||
import ru.nbch.credit_tracker.mapper.signal.PhoneFidMapMapper;
|
||||
|
||||
import java.util.List;
|
||||
import ru.nbch.credit_tracker.model.SubjectProfile;
|
||||
import ru.nbch.credit_tracker.utils.CTUtil;
|
||||
|
||||
@Service
|
||||
public class PhoneFidMapService {
|
||||
|
||||
private final PhoneFidMapMapper mapper;
|
||||
private final SqlSessionFactory sqlSessionFactory;
|
||||
|
||||
public PhoneFidMapService(PhoneFidMapMapper mapper) {
|
||||
public PhoneFidMapService(PhoneFidMapMapper mapper,
|
||||
@Qualifier("sqlSessionSignal") SqlSessionFactory sqlSessionFactory) {
|
||||
this.mapper = mapper;
|
||||
this.sqlSessionFactory = sqlSessionFactory;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void registerSubjects(List<Subject> subjects, Long packageId) {
|
||||
mapper.registerSubjects(subjects, packageId);
|
||||
}
|
||||
|
||||
public List<PhoneFidMap> findFullPhoneFidMap(Long packageId, int offset, int limit) {
|
||||
/**
|
||||
* Регистрирует в базе список субъектов переданных в запросе
|
||||
*
|
||||
* @param subjects - список субъектов
|
||||
* @param packageId - уникальный идентификатор пакета
|
||||
*/
|
||||
public void register(List<SubjectProfile> subjects, Map<String, Name> nameByPhone, Long packageId) {
|
||||
List<PhoneFidMap> phoneFidMaps = subjects.stream().map(subjectProfile -> {
|
||||
Integer fid = Optional
|
||||
.ofNullable(nameByPhone.get(CTUtil.normalizePhoneNumber(subjectProfile.getPhone())))
|
||||
.map(Name::getFid)
|
||||
.orElse(null);
|
||||
|
||||
return PhoneFidMap.builder()
|
||||
.phone(subjectProfile.getPhone())
|
||||
.subjectId(subjectProfile.getIdentifier())
|
||||
.packageId(packageId)
|
||||
.fid(fid)
|
||||
.build();
|
||||
}
|
||||
).toList();
|
||||
mapper.register(phoneFidMaps);
|
||||
}
|
||||
|
||||
public List<PhoneFidMap> findFullPhoneFidMap(Long packageId, int offset, int limit) {
|
||||
return mapper.findFullPhoneFidMap(packageId, offset, limit);
|
||||
}
|
||||
|
||||
public void updatePhoneFidMaps(List<PhoneFidMap> subjects) {
|
||||
public void updatePhoneFidMaps(Collection<PhoneFidMap> subjects) {
|
||||
mapper.updatePhoneFidMaps(subjects);
|
||||
}
|
||||
|
||||
public void updatePhoneFidMaps2(Collection<PhoneFidMap> subjects) {
|
||||
final int batchSize = 1000;
|
||||
try (SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH)) {
|
||||
PhoneFidMapMapper mapper = session.getMapper(PhoneFidMapMapper.class);
|
||||
int i = 0;
|
||||
for (PhoneFidMap s : subjects) {
|
||||
mapper.updateOne(s);
|
||||
if (++i % batchSize == 0) session.flushStatements();
|
||||
}
|
||||
session.flushStatements();
|
||||
session.commit();
|
||||
}
|
||||
}
|
||||
|
||||
public void updatePhoneFidMapsErrors(List<PhoneFidMap> subjects) {
|
||||
mapper.updatePhoneFidMapsErrors(subjects);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -70,6 +70,10 @@ public class SignalRestService {
|
|||
*/
|
||||
public boolean setupMonitoring(Long packageId) throws Exception {
|
||||
String path = signalService.writeSignals(packageId);
|
||||
return setupMonitoring(path);
|
||||
}
|
||||
|
||||
public boolean setupMonitoring(String path) throws Exception {
|
||||
Resource request = compressFile(path);
|
||||
|
||||
MultiValueMap<String, Resource> body = new LinkedMultiValueMap<>();
|
||||
|
|
@ -95,8 +99,6 @@ public class SignalRestService {
|
|||
} else {
|
||||
log.error("неизвестная ошибка при постановке пакета на мониторинг в официальный сервис сигналов: {}", response.getBody());
|
||||
}
|
||||
CTUtil.removeFile(path);
|
||||
CTUtil.removeFile(path + ".gz");
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package ru.nbch.credit_tracker.service.signal;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
import ru.nbch.credit_tracker.entities.dto.UserPackage;
|
||||
import ru.nbch.credit_tracker.entities.app.UserPackage;
|
||||
import ru.nbch.credit_tracker.enums.Status;
|
||||
import ru.nbch.credit_tracker.mapper.signal.UserPackageMapper;
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
package ru.nbch.credit_tracker.service.task;
|
||||
|
||||
public interface Handler<X, Z> {
|
||||
Z handle(X taskPayload);
|
||||
default int preferredConcurrency() {return 10;}
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
package ru.nbch.credit_tracker.service.task;
|
||||
|
||||
import java.util.List;
|
||||
import ru.nbch.credit_tracker.service.task.setup.IPipeLineTerminator;
|
||||
|
||||
public class Pipeline {
|
||||
private Runnable starter;
|
||||
private IPipeLineTerminator<?,?> terminator;
|
||||
private List<Stage<?, ?>> stages;
|
||||
|
||||
public Pipeline() {
|
||||
}
|
||||
|
||||
public Stage<?, ?> getLast() {
|
||||
return stages.getLast();
|
||||
}
|
||||
|
||||
public Runnable getStarter() {
|
||||
return starter;
|
||||
}
|
||||
|
||||
public void setStarter(Runnable starter) {
|
||||
this.starter = starter;
|
||||
}
|
||||
|
||||
public IPipeLineTerminator<?, ?> getTerminator() {
|
||||
return terminator;
|
||||
}
|
||||
|
||||
public void setTerminator(IPipeLineTerminator<?, ?> terminator) {
|
||||
this.terminator = terminator;
|
||||
}
|
||||
|
||||
public List<Stage<?, ?>> getStages() {
|
||||
return stages;
|
||||
}
|
||||
|
||||
public void setStages(List<Stage<?, ?>> stages) {
|
||||
this.stages = stages;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
package ru.nbch.credit_tracker.service.task;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Semaphore;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Supplier;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.stereotype.Component;
|
||||
import ru.nbch.credit_tracker.config.settings.CreditTrackerSettings;
|
||||
import ru.nbch.credit_tracker.config.task.StageQueueFactory;
|
||||
import ru.nbch.credit_tracker.model.impl.PartFilePayload;
|
||||
import ru.nbch.credit_tracker.model.impl.SignalXmlFilePayload;
|
||||
import ru.nbch.credit_tracker.model.impl.SubjectPayload;
|
||||
import ru.nbch.credit_tracker.service.signal.SignalRestService;
|
||||
import ru.nbch.credit_tracker.service.task.impl.FidIdEnricher;
|
||||
import ru.nbch.credit_tracker.service.task.impl.FlagsEnricherV2;
|
||||
import ru.nbch.credit_tracker.service.task.impl.SaveSubjectHandler;
|
||||
import ru.nbch.credit_tracker.service.task.setup.IPipeLineStarter;
|
||||
import ru.nbch.credit_tracker.service.task.setup.IPipeLineTerminator;
|
||||
import ru.nbch.credit_tracker.service.task.setup.PipeLineBuilderV2;
|
||||
import ru.nbch.credit_tracker.utils.LogUtil;
|
||||
import ru.nbch.credit_tracker.utils.error.ExceptionUtils;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
public class PipelinePackageManager {
|
||||
private final SignalRestService signalRestService;
|
||||
private final FlagsEnricherV2 flagsEnricherV2;
|
||||
private final StageQueueFactory<SubjectPayload> saveSubjectQueueFactory;
|
||||
private final StageQueueFactory<SubjectPayload> idEnricherQueueFactory;
|
||||
private final StageQueueFactory<PartFilePayload> sendSignalQueueFactory;
|
||||
private final SaveSubjectHandler saveSubjectHandler;
|
||||
private final FidIdEnricher fidIdEnricherHandler;
|
||||
private final Supplier<IPipeLineTerminator<PartFilePayload, SignalXmlFilePayload>> pipelineTerminatorFactory;
|
||||
private final BiFunction<Long, String, IPipeLineStarter<SubjectPayload>> pipelineStarterFactory;
|
||||
private final CreditTrackerSettings creditTrackerSettings;
|
||||
private final ExecutorService executorService = Executors.newFixedThreadPool(10, r -> {
|
||||
Thread t = new Thread(r);
|
||||
t.setName("pipe-starter-%d".formatted(t.threadId()));
|
||||
return t;
|
||||
});
|
||||
private final Semaphore semaphore;
|
||||
private final static int MAX_PARALLEL_REQUESTS = 3;
|
||||
|
||||
public PipelinePackageManager(SignalRestService signalRestService,
|
||||
@Qualifier("saveSubjectQueueFactory") StageQueueFactory<SubjectPayload> saveSubjectQueueFactory,
|
||||
@Qualifier("idEnricherQueueFactory") StageQueueFactory<SubjectPayload> idEnricherQueueFactory,
|
||||
@Qualifier("partFileQueueFactory") StageQueueFactory<PartFilePayload> sendSignalQueueFactory,
|
||||
SaveSubjectHandler saveSubjectHandler, FidIdEnricher fidIdEnricherHandler,
|
||||
Supplier<IPipeLineTerminator<PartFilePayload, SignalXmlFilePayload>> pipelineTerminatorFactory,
|
||||
BiFunction<Long, String, IPipeLineStarter<SubjectPayload>> pipelineStarterFactory,
|
||||
FlagsEnricherV2 flagsEnricherV2,
|
||||
CreditTrackerSettings creditTrackerSettings) {
|
||||
this.signalRestService = signalRestService;
|
||||
this.saveSubjectQueueFactory = saveSubjectQueueFactory;
|
||||
this.idEnricherQueueFactory = idEnricherQueueFactory;
|
||||
this.sendSignalQueueFactory = sendSignalQueueFactory;
|
||||
this.saveSubjectHandler = saveSubjectHandler;
|
||||
this.fidIdEnricherHandler = fidIdEnricherHandler;
|
||||
this.pipelineTerminatorFactory = pipelineTerminatorFactory;
|
||||
this.pipelineStarterFactory = pipelineStarterFactory;
|
||||
this.flagsEnricherV2 = flagsEnricherV2;
|
||||
this.creditTrackerSettings = creditTrackerSettings;
|
||||
this.semaphore = new Semaphore(MAX_PARALLEL_REQUESTS);
|
||||
}
|
||||
|
||||
public void buildAndRun(Long packageId, String pkgPath) throws Exception {
|
||||
if (!semaphore.tryAcquire()) {
|
||||
throw new RuntimeException("Too many concurrent requests");
|
||||
}
|
||||
log.info("throttler permits after acquire: {}", semaphore.availablePermits());
|
||||
try {
|
||||
Pipeline pipeline = PipeLineBuilderV2.first(saveSubjectQueueFactory.getObject(), idEnricherQueueFactory.getObject())
|
||||
.handler(saveSubjectHandler)
|
||||
.poison(SubjectPayload.POISON)
|
||||
.handlerResult(flagsEnricherV2)
|
||||
.andThen()
|
||||
.stage(sendSignalQueueFactory.getObject())
|
||||
.handler(fidIdEnricherHandler)
|
||||
.poison(PartFilePayload.POISON)
|
||||
.andThen()
|
||||
.fillInitialQueue(pipelineStarterFactory.apply(packageId, pkgPath))
|
||||
.pollFinishingQueue(pipelineTerminatorFactory.get())
|
||||
.build();
|
||||
|
||||
executorService.submit(LogUtil.wrap(LogUtil.extractMdcMap(), pipeline.getStarter()));
|
||||
executorService.submit(LogUtil.wrap(LogUtil.extractMdcMap(), pipeline.getTerminator()));
|
||||
|
||||
pipeline.getTerminator()
|
||||
.doneFuture()
|
||||
.orTimeout(creditTrackerSettings.getTimeout(), TimeUnit.MINUTES)
|
||||
.whenComplete((v, e) -> {
|
||||
try {
|
||||
if (e != null) {
|
||||
log.error(ExceptionUtils.getStackTrace(e));
|
||||
return;
|
||||
}
|
||||
SignalXmlFilePayload res = (SignalXmlFilePayload) v;
|
||||
log.trace("Signal file by path: {} is completed", res.getSignalXmlFilePath());
|
||||
// signalRestService.setupMonitoring(res.getSignalXmlFilePath().toString());
|
||||
} catch (Exception ex) {
|
||||
log.error("pipeline finished with error{}", ExceptionUtils.getStackTrace(ex));
|
||||
} finally {
|
||||
log.info("packageId={}: pipeline finished", packageId);
|
||||
pipeline.getStages().forEach(Stage::close);
|
||||
semaphore.release();
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
semaphore.release();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
174
src/main/java/ru/nbch/credit_tracker/service/task/Stage.java
Normal file
174
src/main/java/ru/nbch/credit_tracker/service/task/Stage.java
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
package ru.nbch.credit_tracker.service.task;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||
import ru.nbch.credit_tracker.model.StagePayload;
|
||||
import ru.nbch.credit_tracker.utils.LogUtil;
|
||||
|
||||
@Slf4j
|
||||
public class Stage<X extends StagePayload, Z extends StagePayload> implements Runnable {
|
||||
private final Handler<X, Z> dispatcher;
|
||||
private final BlockingQueue<X> in;
|
||||
private final BlockingQueue<Z> out;
|
||||
private final Z POISON;
|
||||
private final ExecutorService ioPool;
|
||||
private Consumer<Z> consumer = null;
|
||||
private final String dispatcherName;
|
||||
|
||||
private static final ThreadPoolExecutor.CallerRunsPolicy callerRunsPolicy
|
||||
= new ThreadPoolExecutor.CallerRunsPolicy();
|
||||
|
||||
public Stage(Handler<X, Z> dispatcher,
|
||||
BlockingQueue<X> in,
|
||||
BlockingQueue<Z> out,
|
||||
Z POISON) {
|
||||
this.dispatcher = dispatcher;
|
||||
this.in = in;
|
||||
this.out = out;
|
||||
this.dispatcherName = dispatcher.getClass().getSimpleName();
|
||||
var mdcMap = LogUtil.extractMdcMap();
|
||||
this.ioPool = new ThreadPoolExecutor(dispatcher.preferredConcurrency(),
|
||||
dispatcher.preferredConcurrency(),
|
||||
0L, TimeUnit.MINUTES,
|
||||
new LinkedBlockingQueue<>(1),
|
||||
r -> {
|
||||
Thread t = new Thread(LogUtil.wrap(mdcMap, r));
|
||||
t.setName("pipe-handler-%s-%d".formatted(dispatcherName, t.threadId()));
|
||||
return t;
|
||||
},
|
||||
(r, executor) -> {
|
||||
log.info("stage {}: all threads are working; switch to CallerRunsPolicy", dispatcherName);
|
||||
callerRunsPolicy.rejectedExecution(r, executor);
|
||||
}
|
||||
);
|
||||
this.POISON = POISON;
|
||||
}
|
||||
|
||||
public void startRunning() {
|
||||
ioPool.submit(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
List<Future<Z>> inflight = new ArrayList<>();
|
||||
try {
|
||||
int counter = 0;
|
||||
while (true) {
|
||||
X b = takeQuiet(in, Duration.ofSeconds(5));
|
||||
if (wasInterrupted()) {
|
||||
break;
|
||||
}
|
||||
if (b == null) continue;
|
||||
if (b.isPoison()) {
|
||||
log.info("Got poison");
|
||||
// дождаться всех in-flight задач
|
||||
for (Future<Z> f : inflight) {
|
||||
if (wasInterrupted()) {
|
||||
log.info("stage {} was interrupted while waiting for tasks to finish",
|
||||
dispatcher.getClass().getSimpleName());
|
||||
break;
|
||||
}
|
||||
f.get();
|
||||
}
|
||||
putQuiet(out, POISON);
|
||||
log.info("stage {} finished.", dispatcher.getClass().getSimpleName());
|
||||
return;
|
||||
}
|
||||
counter++;
|
||||
int finalCounter = counter;
|
||||
Future<Z> f = ioPool.submit(() -> {
|
||||
try {
|
||||
if (wasInterrupted()) {
|
||||
log.info("stage {} task {} interrupted...", dispatcher.getClass().getSimpleName(), finalCounter);
|
||||
return null;
|
||||
}
|
||||
log.info("stage {} task {} started.", dispatcher.getClass().getSimpleName(), finalCounter);
|
||||
Z p = dispatcher.handle(b);
|
||||
putQuiet(out, p);
|
||||
log.info("stage {} task {} ended.", dispatcher.getClass().getSimpleName(), finalCounter);
|
||||
return p;
|
||||
} catch (Exception e) {
|
||||
log.error("{} ERROR: {}", dispatcherName, ExceptionUtils.getStackTrace(e));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
);
|
||||
inflight.add(f);
|
||||
if ((counter % 20) == 0) {
|
||||
inflight.removeIf(Future::isDone);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("Error for {}: {}; {}", dispatcherName, info(), ExceptionUtils.getStackTrace(e));
|
||||
putQuiet(out, POISON);
|
||||
}
|
||||
}
|
||||
|
||||
private String info() {
|
||||
ThreadPoolExecutor casted = (ThreadPoolExecutor) ioPool;
|
||||
return "executor active tasks: %d; queue size: %d, completed tasks: %d".formatted(
|
||||
casted.getActiveCount(),
|
||||
casted.getQueue().size(),
|
||||
casted.getCompletedTaskCount()
|
||||
);
|
||||
}
|
||||
|
||||
private boolean wasInterrupted() {
|
||||
if (Thread.currentThread().isInterrupted()) {
|
||||
log.warn("pipeline was interrupted...");
|
||||
putQuiet(out, POISON);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void close() {
|
||||
log.info("Stage {} is getting shutdown", dispatcher.getClass().getSimpleName());
|
||||
ioPool.shutdownNow(); // прерываем обработчики
|
||||
}
|
||||
|
||||
void putQuiet(BlockingQueue<Z> q, Z b) {
|
||||
try {
|
||||
if (consumer != null) {
|
||||
consumer.accept(b);
|
||||
}
|
||||
q.put(b);
|
||||
} catch (InterruptedException e) {
|
||||
log.error("Error for {}: {}; {}", dispatcherName, info(), ExceptionUtils.getStackTrace(e));
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
X takeQuiet(BlockingQueue<X> q, Duration timeout) {
|
||||
try {
|
||||
return q.poll(timeout.toMillis(), TimeUnit.MILLISECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
log.error("Error for {}: {}; {}", dispatcherName, info(), ExceptionUtils.getStackTrace(e));
|
||||
Thread.currentThread().interrupt();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public BlockingQueue<X> getIn() {
|
||||
return in;
|
||||
}
|
||||
|
||||
public Consumer<Z> getConsumer() {
|
||||
return consumer;
|
||||
}
|
||||
|
||||
public void setConsumer(Consumer<Z> consumer) {
|
||||
this.consumer = consumer;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
package ru.nbch.credit_tracker.service.task.impl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import ru.nbch.credit_tracker.builder.PersonFactoryBuilderV2;
|
||||
import ru.nbch.credit_tracker.config.settings.CreditTrackerSettings;
|
||||
import ru.nbch.credit_tracker.entities.external_request.signals.Person;
|
||||
import ru.nbch.credit_tracker.model.impl.PartFilePayload;
|
||||
import ru.nbch.credit_tracker.model.SubjectProfile;
|
||||
import ru.nbch.credit_tracker.model.impl.SubjectPayload;
|
||||
import ru.nbch.credit_tracker.service.indic.PersonServiceV2;
|
||||
import ru.nbch.credit_tracker.service.task.Handler;
|
||||
import ru.nbch.credit_tracker.service.xml.stax.SignalsStaxXmlWriterV2;
|
||||
|
||||
@Service
|
||||
@Slf4j
|
||||
public class FidIdEnricher implements Handler<SubjectPayload, PartFilePayload> {
|
||||
private final PersonServiceV2 personService;
|
||||
private final CreditTrackerSettings config;
|
||||
|
||||
public FidIdEnricher(PersonServiceV2 personService,
|
||||
CreditTrackerSettings config) {
|
||||
this.personService = personService;
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PartFilePayload handle(SubjectPayload taskPayload) {
|
||||
List<SubjectProfile> subjectsProfile = taskPayload.getSubjects();
|
||||
log.debug("FidIdEnricher handle by packageId: {}; size: {}", taskPayload.getPackageId(), subjectsProfile.size());
|
||||
personService.filledPassportsData(subjectsProfile);
|
||||
List<Person> personOnProcessing = subjectsProfile.stream()
|
||||
.filter(subjectProfile -> subjectProfile.getIdData() != null && subjectProfile.getNameData() != null)
|
||||
.map(subjectProfile -> PersonFactoryBuilderV2.builder().personData(subjectProfile).build())
|
||||
.toList();
|
||||
log.debug("personOnProcessing size: {}", personOnProcessing.size());
|
||||
Long packageId = taskPayload.getPackageId();
|
||||
Path partPath = writeSignals(personOnProcessing, packageId);
|
||||
return new PartFilePayload(partPath, packageId);
|
||||
}
|
||||
|
||||
private Path writeSignals(List<Person> persons, Long packageId) {
|
||||
Path dir = Paths.get(config.getLoadDir(), "tmp_folder");
|
||||
try {
|
||||
Files.createDirectories(dir);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
String base = UUID.randomUUID() + "_output.xml";
|
||||
log.debug("Write tmp xml by packageId: {}", packageId);
|
||||
Path tmp = dir.resolve(base + ".part");
|
||||
Path done = dir.resolve(base);
|
||||
|
||||
try (OutputStream os = Files.newOutputStream(tmp, StandardOpenOption.CREATE_NEW)) {
|
||||
SignalsStaxXmlWriterV2 writer = new SignalsStaxXmlWriterV2(persons, null, config.getEncoding());
|
||||
writer.writeWithStax(os); // перегрузи на OutputStream, если нужно
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
try {
|
||||
Files.move(tmp, done, StandardCopyOption.ATOMIC_MOVE);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return done;
|
||||
// try {
|
||||
// mergerQueue.put(new PartFilePayload(done, packageId, null));
|
||||
// } catch (InterruptedException e) {
|
||||
// throw new RuntimeException(e);
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,139 @@
|
|||
package ru.nbch.credit_tracker.service.task.impl;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||
import ru.nbch.credit_tracker.mapper.signal.PhoneFidMapMapper;
|
||||
import ru.nbch.credit_tracker.model.SubjectProfile;
|
||||
import ru.nbch.credit_tracker.model.impl.SubjectPayload;
|
||||
import ru.nbch.credit_tracker.service.indic.FlagsService2;
|
||||
|
||||
@Slf4j
|
||||
public class FlagsEnricher implements Runnable {
|
||||
|
||||
private final FlagsService2 flagsService;
|
||||
private final PhoneFidMapMapper phoneFidMapMapper;
|
||||
private BlockingQueue<SubjectPayload> in;
|
||||
private final CompletableFuture<Void> done = new CompletableFuture<>();
|
||||
private final ExecutorService ioPool;
|
||||
private final ObjectMapper mapper = new ObjectMapper()
|
||||
.registerModule(new JavaTimeModule())
|
||||
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
|
||||
.setSerializationInclusion(JsonInclude.Include.NON_NULL);
|
||||
|
||||
public FlagsEnricher(FlagsService2 flagsService,
|
||||
PhoneFidMapMapper phoneFidMapMapper) {
|
||||
this.flagsService = flagsService;
|
||||
this.phoneFidMapMapper = phoneFidMapMapper;
|
||||
this.ioPool = new ThreadPoolExecutor(10,
|
||||
10,
|
||||
0L, TimeUnit.MINUTES,
|
||||
new LinkedBlockingQueue<>(),
|
||||
r -> {
|
||||
Thread t = new Thread(r);
|
||||
t.setName("queue-handler-%s-%d".formatted(this.getClass().getSimpleName(), t.threadId()));
|
||||
return t;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
List<Future<?>> inFlight = new ArrayList<>();
|
||||
while (!Thread.currentThread().isInterrupted()) {
|
||||
try {
|
||||
SubjectPayload subjectPayload = in.poll(5, TimeUnit.SECONDS);
|
||||
if (subjectPayload == null) {
|
||||
continue;
|
||||
}
|
||||
if (subjectPayload.isPoison()) {
|
||||
log.debug("Got poison");
|
||||
for (Future<?> f : inFlight) {
|
||||
f.get();
|
||||
}
|
||||
done.complete(null);
|
||||
return;
|
||||
}
|
||||
inFlight.add(ioPool.submit(() -> process(subjectPayload)));
|
||||
} catch (InterruptedException ie) {
|
||||
Thread.currentThread().interrupt();
|
||||
} catch (Exception e) {
|
||||
log.error("Error: {}", ExceptionUtils.getStackTrace(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void process(SubjectPayload subjectPayload) {
|
||||
List<SubjectProfile> subjects = subjectPayload.getSubjects();
|
||||
log.debug("FlagsEnricher handle by packageId: {}, size: {}", subjectPayload.getPackageId(), subjects.size());
|
||||
|
||||
List<Integer> fids = subjects.stream()
|
||||
.map(SubjectProfile::getFid)
|
||||
.filter(Objects::nonNull)
|
||||
.toList();
|
||||
LocalDate yearAgo = LocalDate.now().minusYears(1);
|
||||
|
||||
Map<Integer, Integer> flagsDebt5000 = flagsService.findFlagsDebt5000(fids);
|
||||
Map<Integer, Integer> flag90ByPastDueArrear = flagsService.findFlag90ByPastDueArrear(fids, yearAgo);
|
||||
Map<Integer, Integer> flag90ByRuPaymt = flagsService.findFlag90ByRuPaymt(fids, yearAgo);
|
||||
|
||||
String json;
|
||||
try {
|
||||
json = mapper.writeValueAsString(subjects.stream().map(subject -> {
|
||||
Integer debt5000 = null;
|
||||
Integer flagDays90 = null;
|
||||
LocalDateTime calculatedAt = null;
|
||||
|
||||
if (subject.getFid() != null) {
|
||||
debt5000 = flagsDebt5000.getOrDefault(subject.getFid(), 0);
|
||||
int pastDue90 = flag90ByPastDueArrear.getOrDefault(subject.getFid(), 0);
|
||||
int paymt90 = flag90ByRuPaymt.getOrDefault(subject.getFid(), 0);
|
||||
flagDays90 = Math.max(pastDue90, paymt90);
|
||||
calculatedAt = LocalDateTime.now();
|
||||
}
|
||||
|
||||
Integer finalDebt500 = debt5000;
|
||||
Integer finalFlagDays9 = flagDays90;
|
||||
LocalDateTime finalCalculatedAt = calculatedAt;
|
||||
return new HashMap<String, Object>() {{
|
||||
put("subject_id", subject.getIdentifier());
|
||||
put("fid", subject.getFid());
|
||||
put("phone", subject.getPhone());
|
||||
put("package_id", subjectPayload.getPackageId());
|
||||
put("flag_debt5000", finalDebt500);
|
||||
put("flag_90_12", finalFlagDays9);
|
||||
put("calculated_at", finalCalculatedAt);
|
||||
}};
|
||||
}).toList());
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
phoneFidMapMapper.registerJson(json);
|
||||
}
|
||||
|
||||
public void setPollingQueue(BlockingQueue<SubjectPayload> queue) {
|
||||
this.in = queue;
|
||||
}
|
||||
|
||||
public CompletableFuture<Void> doneFuture() {
|
||||
return done;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
package ru.nbch.credit_tracker.service.task.impl;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import ru.nbch.credit_tracker.mapper.signal.PhoneFidMapMapper;
|
||||
import ru.nbch.credit_tracker.model.SubjectProfile;
|
||||
import ru.nbch.credit_tracker.model.impl.SubjectPayload;
|
||||
import ru.nbch.credit_tracker.service.indic.FlagsService2;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class FlagsEnricherV2 implements Consumer<SubjectPayload> {
|
||||
|
||||
private final FlagsService2 flagsService;
|
||||
private final PhoneFidMapMapper phoneFidMapMapper;
|
||||
private final ExecutorService ioPool;
|
||||
private final ObjectMapper mapper = new ObjectMapper()
|
||||
.registerModule(new JavaTimeModule())
|
||||
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
|
||||
.setSerializationInclusion(JsonInclude.Include.NON_NULL);
|
||||
|
||||
public FlagsEnricherV2(FlagsService2 flagsService,
|
||||
PhoneFidMapMapper phoneFidMapMapper) {
|
||||
this.flagsService = flagsService;
|
||||
this.phoneFidMapMapper = phoneFidMapMapper;
|
||||
this.ioPool = new ThreadPoolExecutor(10,
|
||||
10,
|
||||
0L, TimeUnit.MINUTES,
|
||||
new LinkedBlockingQueue<>(100),
|
||||
r -> {
|
||||
Thread t = new Thread(r);
|
||||
t.setName("flags-enricher-%d".formatted(t.threadId()));
|
||||
return t;
|
||||
},
|
||||
new ThreadPoolExecutor.CallerRunsPolicy()
|
||||
);
|
||||
}
|
||||
|
||||
public void process(SubjectPayload subjectPayload) {
|
||||
List<SubjectProfile> subjects = subjectPayload.getSubjects();
|
||||
log.debug("FlagsEnricher handle by packageId: {}, size: {}", subjectPayload.getPackageId(), subjects.size());
|
||||
|
||||
List<Integer> fids = subjects.stream()
|
||||
.map(SubjectProfile::getFid)
|
||||
.filter(Objects::nonNull)
|
||||
.toList();
|
||||
LocalDate yearAgo = LocalDate.now().minusYears(1);
|
||||
|
||||
Map<Integer, Integer> flagsDebt5000 = flagsService.findFlagsDebt5000(fids);
|
||||
Map<Integer, Integer> flag90ByPastDueArrear = flagsService.findFlag90ByPastDueArrear(fids, yearAgo);
|
||||
Map<Integer, Integer> flag90ByRuPaymt = flagsService.findFlag90ByRuPaymt(fids, yearAgo);
|
||||
|
||||
String json;
|
||||
try {
|
||||
json = mapper.writeValueAsString(subjects.stream().map(subject -> {
|
||||
Integer debt5000 = null;
|
||||
Integer flagDays90 = null;
|
||||
LocalDateTime calculatedAt = null;
|
||||
|
||||
if (subject.getFid() != null) {
|
||||
debt5000 = flagsDebt5000.getOrDefault(subject.getFid(), 0);
|
||||
int pastDue90 = flag90ByPastDueArrear.getOrDefault(subject.getFid(), 0);
|
||||
int paymt90 = flag90ByRuPaymt.getOrDefault(subject.getFid(), 0);
|
||||
flagDays90 = Math.max(pastDue90, paymt90);
|
||||
calculatedAt = LocalDateTime.now();
|
||||
}
|
||||
|
||||
Integer finalDebt500 = debt5000;
|
||||
Integer finalFlagDays9 = flagDays90;
|
||||
LocalDateTime finalCalculatedAt = calculatedAt;
|
||||
return new HashMap<String, Object>() {{
|
||||
put("subject_id", subject.getIdentifier());
|
||||
put("fid", subject.getFid());
|
||||
put("phone", subject.getPhone());
|
||||
put("package_id", subjectPayload.getPackageId());
|
||||
put("flag_debt5000", finalDebt500);
|
||||
put("flag_90_12", finalFlagDays9);
|
||||
put("calculated_at", finalCalculatedAt);
|
||||
}};
|
||||
}).toList());
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
phoneFidMapMapper.registerJson(json);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(SubjectPayload subjectPayload) {
|
||||
ThreadPoolExecutor casted = (ThreadPoolExecutor) ioPool;
|
||||
log.info("submitting payload. active tasks number: {}; queue size: {}", casted.getActiveCount(), casted.getQueue().size());
|
||||
this.ioPool.submit(() -> {
|
||||
process(subjectPayload);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
package ru.nbch.credit_tracker.service.task.impl;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.stream.Collectors;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import ru.nbch.credit_tracker.config.settings.CreditTrackerSettings;
|
||||
import ru.nbch.credit_tracker.entities.user_request.monitoring_request.MonitoringRequest;
|
||||
import ru.nbch.credit_tracker.model.SubjectProfile;
|
||||
import ru.nbch.credit_tracker.model.impl.SubjectPayload;
|
||||
import ru.nbch.credit_tracker.service.task.setup.IPipeLineStarter;
|
||||
import ru.nbch.credit_tracker.service.xml.stax.StaxXmlProcessor;
|
||||
import ru.nbch.credit_tracker.utils.error.ExceptionUtils;
|
||||
|
||||
public class KickPipeLineImpl implements IPipeLineStarter<SubjectPayload> {
|
||||
private final Logger log = LoggerFactory.getLogger(getClass());
|
||||
private final Long packageId;
|
||||
private final String pkgPath;
|
||||
|
||||
private final StaxXmlProcessor xmlProcessor;
|
||||
private final int BATCH_SIZE;
|
||||
|
||||
|
||||
public KickPipeLineImpl(StaxXmlProcessor xmlProcessor, Long packageId, String pkgPath,
|
||||
CreditTrackerSettings creditTrackerSettings) {
|
||||
this.packageId = packageId;
|
||||
this.pkgPath = pkgPath;
|
||||
this.xmlProcessor = xmlProcessor;
|
||||
this.BATCH_SIZE = creditTrackerSettings.getBatchSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startFillingQueue(BlockingQueue<SubjectPayload> firstQueue) {
|
||||
log.info("Start registering subjects. PackageId: {}", packageId);
|
||||
AtomicInteger counter = new AtomicInteger(0);
|
||||
try (BufferedInputStream in = new BufferedInputStream(Files.newInputStream(Path.of(pkgPath)), 8 * 1024 * 1024)) {
|
||||
xmlProcessor.read(in, MonitoringRequest.class,
|
||||
request -> {
|
||||
if (!request.getSubjects().getSubject().isEmpty()) {
|
||||
List<SubjectProfile> subjectProfileBatch = request.getSubjects().getSubject().stream().map(subject ->
|
||||
SubjectProfile.builder()
|
||||
.identifier(subject.getId())
|
||||
.phone(subject.getClientId())
|
||||
.build()
|
||||
|
||||
).collect(Collectors.toList());
|
||||
int count = counter.incrementAndGet();
|
||||
log.trace("Reading subject's {} batch. Total count: {}. PackageId: {}", count, request.getSubjects().getSubject().size(), packageId);
|
||||
try {
|
||||
firstQueue.put(new SubjectPayload(packageId, subjectProfileBatch));
|
||||
} catch (InterruptedException e) {
|
||||
log.error("{}", ExceptionUtils.getStackTrace(e));
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
request.getSubjects().getSubject().clear();
|
||||
}
|
||||
},
|
||||
request -> request.getSubjects().getSubject().isEmpty()
|
||||
,
|
||||
BATCH_SIZE, "/MonitoringRequest/Subjects/Subject");
|
||||
} catch (IOException e) {
|
||||
log.error("{}", ExceptionUtils.getStackTrace(e));
|
||||
} finally {
|
||||
log.debug("Send poison pill");
|
||||
try {
|
||||
firstQueue.put(new SubjectPayload(true));
|
||||
} catch (InterruptedException e) {
|
||||
log.error("couldn't send poison: {}", ExceptionUtils.getStackTrace(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
package ru.nbch.credit_tracker.service.task.impl;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import ru.nbch.credit_tracker.entities.indic.Name;
|
||||
import ru.nbch.credit_tracker.mapper.signal.PhoneFidMapMapper;
|
||||
import ru.nbch.credit_tracker.model.NameData;
|
||||
import ru.nbch.credit_tracker.model.SubjectProfile;
|
||||
import ru.nbch.credit_tracker.model.impl.SubjectPayload;
|
||||
import ru.nbch.credit_tracker.service.indic.PhoneService2;
|
||||
import ru.nbch.credit_tracker.service.task.Handler;
|
||||
import ru.nbch.credit_tracker.utils.CTUtil;
|
||||
|
||||
/**
|
||||
* Стадия для сопоставления FID к номеру телефона и регистрации пакета в базе данных.
|
||||
* Так же выполняется поиск и обогащение перс.данными из NAME для дальнейшей обработки.
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
//todo опять нейминг класса, варианты: ResolveSubject; DefineIcrsSubject; IcrsMapping/Resolving; PackageSaver; Filler...
|
||||
public class SaveSubjectHandler implements Handler<SubjectPayload, SubjectPayload> {
|
||||
private final PhoneService2 phoneService2;
|
||||
private final PhoneFidMapMapper phoneFidMapMapper;
|
||||
|
||||
public SaveSubjectHandler(PhoneService2 phoneService2,
|
||||
PhoneFidMapMapper phoneFidMapMapper) {
|
||||
this.phoneService2 = phoneService2;
|
||||
this.phoneFidMapMapper = phoneFidMapMapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SubjectPayload handle(SubjectPayload taskPayload) {
|
||||
List<SubjectProfile> subjects = taskPayload.getSubjects();
|
||||
Long packageId = taskPayload.getPackageId();
|
||||
log.debug("SaveSubjectStage handle by packageId: {}, size of bucket for save: {}", taskPayload.getPackageId(), subjects.size());
|
||||
|
||||
Map<String, Name> nameByPhone = phoneService2.findNameByNumber(subjects);
|
||||
//todo сделать один проход для установки FID для вставки в PHONE_FID_MAP и одновременного заполнения инфо из Name для дальнейшей обработки
|
||||
subjects.forEach(subject -> {
|
||||
Name name = nameByPhone.get(CTUtil.normalizePhoneNumber(subject.getPhone()));
|
||||
if (name != null) {
|
||||
NameData nameData = NameData.builder()
|
||||
.middleName(name.getMiddle())
|
||||
.firstName(name.getFirst())
|
||||
.lastName(name.getLast())
|
||||
.fileSinceDate(name.getFileSinceDate())
|
||||
.birthDate(name.getBirthDate())
|
||||
.build();
|
||||
|
||||
subject.setFid(name.getFid());
|
||||
subject.setNameData(nameData);
|
||||
}
|
||||
});
|
||||
//todo стоит ли делать имутабл для subject?
|
||||
return new SubjectPayload(taskPayload.getPackageId(), subjects);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,148 @@
|
|||
package ru.nbch.credit_tracker.service.task.impl;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import javax.xml.stream.XMLInputFactory;
|
||||
import javax.xml.stream.XMLOutputFactory;
|
||||
import javax.xml.stream.XMLStreamConstants;
|
||||
import javax.xml.stream.XMLStreamException;
|
||||
import javax.xml.stream.XMLStreamReader;
|
||||
import javax.xml.stream.XMLStreamWriter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||
import ru.nbch.credit_tracker.config.settings.CreditTrackerSettings;
|
||||
import ru.nbch.credit_tracker.model.impl.PartFilePayload;
|
||||
import ru.nbch.credit_tracker.model.impl.SignalXmlFilePayload;
|
||||
import ru.nbch.credit_tracker.service.task.setup.IPipeLineTerminator;
|
||||
|
||||
@Slf4j
|
||||
public final class XmlMergeCoordinator implements IPipeLineTerminator<PartFilePayload, SignalXmlFilePayload> {
|
||||
private final Path mergedDir;
|
||||
private final Charset encoding = Charset.forName("windows-1251");
|
||||
private final int batchSize = 10;
|
||||
private final CompletableFuture<SignalXmlFilePayload> done = new CompletableFuture<>();
|
||||
private Path out;
|
||||
private BlockingQueue<PartFilePayload> inQueue;
|
||||
|
||||
public XmlMergeCoordinator(CreditTrackerSettings config) {
|
||||
this.mergedDir = Path.of(config.getLoadDir() + File.separator + "tmp_folder" + File.separator + "merged" + File.separator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Files.createDirectories(mergedDir);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
while (!Thread.currentThread().isInterrupted()) {
|
||||
try {
|
||||
PartFilePayload part = inQueue.poll(5, TimeUnit.SECONDS);
|
||||
if (part == null) {
|
||||
continue;
|
||||
}
|
||||
if (part.isPoison()) {
|
||||
log.debug("Got poison");
|
||||
done.complete(new SignalXmlFilePayload(out));
|
||||
return;
|
||||
}
|
||||
mergeBatch(Collections.singletonList(part));
|
||||
} catch (InterruptedException ie) {
|
||||
Thread.currentThread().interrupt();
|
||||
} catch (Exception e) {
|
||||
log.error("Error: {}", ExceptionUtils.getStackTrace(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void mergeBatch(List<PartFilePayload> inputs) throws Exception {
|
||||
log.debug("Starting merge batch; size: {}", inputs.size());
|
||||
Long packageId = inputs.stream().findFirst().get().getPackageId();
|
||||
String outName = "merged_" + packageId + ".xml";
|
||||
if (out == null) {
|
||||
out = mergedDir.resolve(outName);
|
||||
}
|
||||
|
||||
XMLOutputFactory xof = XMLOutputFactory.newFactory();
|
||||
try (OutputStream os = Files.newOutputStream(out, StandardOpenOption.CREATE, StandardOpenOption.APPEND)) {
|
||||
XMLStreamWriter w = xof.createXMLStreamWriter(os, encoding.name());
|
||||
|
||||
for (PartFilePayload in : inputs) {
|
||||
log.debug("Copy person from file {}", in.getPartPath());
|
||||
copyPersonsChildren(in.getPartPath(), w);
|
||||
}
|
||||
|
||||
w.flush();
|
||||
w.close();
|
||||
}
|
||||
for (PartFilePayload p : inputs) Files.deleteIfExists(p.getPartPath());
|
||||
// inputs.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Копирует всё содержимое внутри <Persons>...</Persons> входного файла
|
||||
* в текущий writer, но без повторного открытия/закрытия корневого тега.
|
||||
*/
|
||||
private void copyPersonsChildren(Path input, XMLStreamWriter w) throws Exception {
|
||||
XMLInputFactory xif = XMLInputFactory.newFactory();
|
||||
try (InputStream is = Files.newInputStream(input)) {
|
||||
XMLStreamReader r = xif.createXMLStreamReader(is, encoding.name());
|
||||
|
||||
while (r.hasNext()) {
|
||||
int t = r.next();
|
||||
if (t == XMLStreamConstants.START_ELEMENT && "Persons".equals(r.getLocalName())) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int depth = 0; // 0 — внутри корневого контейнера
|
||||
while (r.hasNext()) {
|
||||
int t = r.next();
|
||||
if (t == XMLStreamConstants.START_ELEMENT) {
|
||||
depth++;
|
||||
writeStart(w, r);
|
||||
} else if (t == XMLStreamConstants.CHARACTERS) {
|
||||
if (!r.isWhiteSpace()) w.writeCharacters(r.getText());
|
||||
} else if (t == XMLStreamConstants.CDATA) {
|
||||
w.writeCData(r.getText());
|
||||
} else if (t == XMLStreamConstants.END_ELEMENT) {
|
||||
if (depth > 0) {
|
||||
w.writeEndElement();
|
||||
depth--;
|
||||
} else if ("Persons".equals(r.getLocalName())) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
r.close();
|
||||
}
|
||||
}
|
||||
|
||||
private void writeStart(XMLStreamWriter w, XMLStreamReader r) throws XMLStreamException {
|
||||
w.writeStartElement(r.getLocalName());
|
||||
for (int i = 0; i < r.getAttributeCount(); i++) {
|
||||
w.writeAttribute(r.getAttributeLocalName(i), r.getAttributeValue(i));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPollingQueue(BlockingQueue<PartFilePayload> mergerQueue) {
|
||||
this.inQueue = mergerQueue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<SignalXmlFilePayload> doneFuture() {
|
||||
return done;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package ru.nbch.credit_tracker.service.task.setup;
|
||||
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import ru.nbch.credit_tracker.model.StagePayload;
|
||||
|
||||
public interface IPipeLineStarter<Q extends StagePayload> {
|
||||
void startFillingQueue(BlockingQueue<Q> queue);
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package ru.nbch.credit_tracker.service.task.setup;
|
||||
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import ru.nbch.credit_tracker.model.StagePayload;
|
||||
import ru.nbch.credit_tracker.model.TerminatePayload;
|
||||
|
||||
public interface IPipeLineTerminator<Q extends StagePayload, X extends TerminatePayload> extends Runnable {
|
||||
void setPollingQueue(BlockingQueue<Q> queue);
|
||||
CompletableFuture<X> doneFuture();
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
package ru.nbch.credit_tracker.service.task.setup;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import ru.nbch.credit_tracker.model.StagePayload;
|
||||
import ru.nbch.credit_tracker.service.task.Pipeline;
|
||||
import ru.nbch.credit_tracker.service.task.Stage;
|
||||
import ru.nbch.credit_tracker.service.task.setup.step.HandlerStep;
|
||||
|
||||
public class PipeLineBuilderV2<FIRST extends StagePayload, LAST extends StagePayload> {
|
||||
private final List<Stage<?, ?>> stages;
|
||||
private Stage<FIRST, ?> firstStage;
|
||||
private BlockingQueue<LAST> lastQueue;
|
||||
private IPipeLineStarter<FIRST> pipeLineStarter;
|
||||
private IPipeLineTerminator<LAST, ?> pipeLineTerminator;
|
||||
|
||||
private PipeLineBuilderV2() {
|
||||
this.stages = new ArrayList<>();
|
||||
}
|
||||
|
||||
public static <I extends StagePayload, O extends StagePayload>
|
||||
HandlerStep<I, I, O> first(BlockingQueue<I> inputQueue, BlockingQueue<O> outputQueue) {
|
||||
PipeLineBuilderV2<I, O> builder = new PipeLineBuilderV2<>();
|
||||
builder.lastQueue = outputQueue;
|
||||
HandlerStep<I, I, O> step = StageBuilderV2.create(inputQueue, outputQueue);
|
||||
((StageBuilderV2<I, I, O>) step).setParent(builder);
|
||||
return step;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <NEXT extends StagePayload>
|
||||
HandlerStep<FIRST, LAST, NEXT> stage(BlockingQueue<NEXT> outputQueue) {
|
||||
HandlerStep<FIRST, LAST, NEXT> step = StageBuilderV2.create(lastQueue, outputQueue);
|
||||
lastQueue = (BlockingQueue<LAST>) outputQueue;
|
||||
((StageBuilderV2<FIRST, LAST, NEXT>) step).setParent((PipeLineBuilderV2<FIRST, NEXT>) this);
|
||||
return step;
|
||||
}
|
||||
|
||||
public PipeLineBuilderV2<FIRST, LAST> fillInitialQueue(IPipeLineStarter<FIRST> pipeLineStarter) {
|
||||
Objects.requireNonNull(pipeLineStarter, "pipeLineStarter is required");
|
||||
if (firstStage == null) {
|
||||
throw new IllegalStateException("Pipeline has no stages");
|
||||
}
|
||||
this.pipeLineStarter = pipeLineStarter;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PipeLineBuilderV2<FIRST, LAST> pollFinishingQueue(IPipeLineTerminator<LAST, ?> pipeLineTerminator) {
|
||||
Objects.requireNonNull(pipeLineTerminator, "pipeLineTerminator is required");
|
||||
this.pipeLineTerminator = pipeLineTerminator;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Pipeline build() {
|
||||
Pipeline p = new Pipeline();
|
||||
p.setStarter(() -> {
|
||||
stages.forEach(Stage::startRunning);
|
||||
pipeLineStarter.startFillingQueue(firstStage.getIn());
|
||||
});
|
||||
pipeLineTerminator.setPollingQueue(lastQueue);
|
||||
p.setTerminator(pipeLineTerminator);
|
||||
p.setStages(stages);
|
||||
return p;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
<I extends StagePayload, O extends StagePayload>
|
||||
void addStage(Stage<I, O> stage) {
|
||||
Objects.requireNonNull(stage, "stage is required");
|
||||
if (stages.isEmpty()) {
|
||||
this.firstStage = (Stage<FIRST, ?>) stage;
|
||||
}
|
||||
stages.add(stage);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
package ru.nbch.credit_tracker.service.task.setup;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.function.Consumer;
|
||||
import ru.nbch.credit_tracker.model.StagePayload;
|
||||
import ru.nbch.credit_tracker.service.task.Handler;
|
||||
import ru.nbch.credit_tracker.service.task.Stage;
|
||||
import ru.nbch.credit_tracker.service.task.setup.step.BuildStep;
|
||||
import ru.nbch.credit_tracker.service.task.setup.step.HandlerStep;
|
||||
import ru.nbch.credit_tracker.service.task.setup.step.PoisonStep;
|
||||
|
||||
/**
|
||||
* Step Builder для Stage:
|
||||
* Обязательная последовательность: handler(...) -> poison(...) -> buildStage()
|
||||
*/
|
||||
public class StageBuilderV2<FIRST extends StagePayload, I extends StagePayload, O extends StagePayload>
|
||||
implements HandlerStep<FIRST, I, O>, PoisonStep<FIRST, I, O>, BuildStep<FIRST, O> {
|
||||
|
||||
private PipeLineBuilderV2<FIRST, O> parent;
|
||||
private final BlockingQueue<I> inputQueue;
|
||||
private final BlockingQueue<O> outputQueue;
|
||||
private Handler<I, O> handler;
|
||||
private O poison;
|
||||
Consumer<O> consumer = null;
|
||||
|
||||
private StageBuilderV2(BlockingQueue<I> inputQueue, BlockingQueue<O> outputQueue) {
|
||||
this.inputQueue = Objects.requireNonNull(inputQueue, "inputQueue is required");
|
||||
this.outputQueue = Objects.requireNonNull(outputQueue, "outputQueue is required");
|
||||
}
|
||||
|
||||
/** Стартовая фабрика возвращает первый шаг — HandlerStep */
|
||||
public static <FIRST extends StagePayload, I extends StagePayload, O extends StagePayload>
|
||||
HandlerStep<FIRST, I, O> create(BlockingQueue<I> inputQueue, BlockingQueue<O> outputQueue) {
|
||||
return new StageBuilderV2<>(inputQueue, outputQueue);
|
||||
}
|
||||
|
||||
void setParent(PipeLineBuilderV2<FIRST, O> parent) {
|
||||
this.parent = Objects.requireNonNull(parent, "parent is required");
|
||||
}
|
||||
|
||||
// ===================== ШАГИ =====================
|
||||
|
||||
/** Шаг 1: задать обработчик */
|
||||
@Override
|
||||
public PoisonStep<FIRST, I, O> handler(Handler<I, O> handler) {
|
||||
this.handler = Objects.requireNonNull(handler, "handler is required");
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Шаг 2: задать poison pill */
|
||||
@Override
|
||||
public BuildStep<FIRST, O> poison(O poison) {
|
||||
this.poison = Objects.requireNonNull(poison, "poison is required");
|
||||
return this;
|
||||
}
|
||||
|
||||
public BuildStep<FIRST, O> handlerResult(Consumer<O> consumer) {
|
||||
this.consumer = Objects.requireNonNull(consumer, "consumer is required");
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Шаг 3: собрать Stage и вернуться к PipeLineBuilder */
|
||||
@Override
|
||||
public PipeLineBuilderV2<FIRST, O> andThen() {
|
||||
if (parent == null) {
|
||||
throw new IllegalStateException("parent PipeLineBuilder is not set");
|
||||
}
|
||||
if (handler == null) {
|
||||
throw new IllegalStateException("handler must be set before buildStage()");
|
||||
}
|
||||
if (poison == null) {
|
||||
throw new IllegalStateException("poison must be set before buildStage()");
|
||||
}
|
||||
|
||||
Stage<I, O> stage = new Stage<>(handler, inputQueue, outputQueue, poison);
|
||||
stage.setConsumer(consumer);
|
||||
parent.addStage(stage);
|
||||
return parent;
|
||||
}
|
||||
|
||||
// ===================== Step-interfaces =====================
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
package ru.nbch.credit_tracker.service.task.setup.step;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
import ru.nbch.credit_tracker.model.StagePayload;
|
||||
import ru.nbch.credit_tracker.service.task.setup.PipeLineBuilderV2;
|
||||
|
||||
public interface BuildStep<FIRST extends StagePayload, O extends StagePayload> {
|
||||
PipeLineBuilderV2<FIRST, O> andThen();
|
||||
BuildStep<FIRST, O> handlerResult(Consumer<O> consumer);
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package ru.nbch.credit_tracker.service.task.setup.step;
|
||||
|
||||
import ru.nbch.credit_tracker.model.StagePayload;
|
||||
import ru.nbch.credit_tracker.service.task.Handler;
|
||||
|
||||
public interface HandlerStep<FIRST extends StagePayload, I extends StagePayload, O extends StagePayload> {
|
||||
PoisonStep<FIRST, I, O> handler(Handler<I, O> handler);
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package ru.nbch.credit_tracker.service.task.setup.step;
|
||||
|
||||
import ru.nbch.credit_tracker.model.StagePayload;
|
||||
|
||||
public interface PoisonStep<FIRST extends StagePayload, I extends StagePayload, O extends StagePayload> {
|
||||
BuildStep<FIRST, O> poison(O poison);
|
||||
}
|
||||
|
|
@ -0,0 +1,114 @@
|
|||
package ru.nbch.credit_tracker.service.xml.stax;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.util.List;
|
||||
import javax.xml.stream.XMLEventFactory;
|
||||
import javax.xml.stream.XMLEventWriter;
|
||||
import javax.xml.stream.XMLOutputFactory;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import ru.nbch.credit_tracker.entities.external_request.signals.Auth;
|
||||
import ru.nbch.credit_tracker.entities.external_request.signals.Person;
|
||||
import ru.nbch.credit_tracker.entities.external_request.signals.Signals;
|
||||
|
||||
@Slf4j
|
||||
//todo сделать с этим что-то!!!
|
||||
public class SignalsStaxXmlWriterV2 {
|
||||
private final List<Person> personList;
|
||||
private final Signals signals;
|
||||
private final String charsetName;
|
||||
|
||||
public SignalsStaxXmlWriterV2(List<Person> personList,
|
||||
Signals signals,
|
||||
String charsetName) {
|
||||
this.personList = personList;
|
||||
this.signals = signals;
|
||||
this.charsetName = charsetName;
|
||||
}
|
||||
|
||||
public void writeWithStax(OutputStream ou) {
|
||||
try {
|
||||
XMLOutputFactory output = XMLOutputFactory.newInstance();
|
||||
XMLEventFactory xmlEventFactory = XMLEventFactory.newInstance();
|
||||
XMLEventWriter writer = output.createXMLEventWriter(ou, charsetName);
|
||||
writer = new IndentingXMLEventWriter(writer);
|
||||
|
||||
WriterWrapper writerWrapper = new WriterWrapper(xmlEventFactory, writer);
|
||||
// writerWrapper.addStartDocument(charsetName);
|
||||
//
|
||||
// writerWrapper.addStartElement("Signals");
|
||||
// writerWrapper.addAttribute("online", signals.getOnline());
|
||||
// writerWrapper.addAttribute("processWithErrors", signals.getProcessWithErrors());
|
||||
// writerWrapper.addSimplelement("Id", signals.getId());
|
||||
// writeAuth(writerWrapper, signals.getAuth());
|
||||
//
|
||||
// writerWrapper.addStartElement("Conditions");
|
||||
writerWrapper.addStartElement("Persons");
|
||||
|
||||
writePersons(writerWrapper, personList);
|
||||
|
||||
writerWrapper.addEndElement("Persons");
|
||||
// writerWrapper.addEndElement("Conditions");
|
||||
|
||||
// writerWrapper.addEndElement("Signals");
|
||||
|
||||
writerWrapper.addEndDocument();
|
||||
writerWrapper.flush();
|
||||
System.gc();
|
||||
} catch (Throwable e) {
|
||||
log.error("Error serializing object to XML string: {}", e.getMessage(), e);
|
||||
throw new IllegalStateException("cannot serialize object to XML");
|
||||
}
|
||||
}
|
||||
|
||||
private void writePersons(WriterWrapper writerWrapper, List<Person> persons) throws Exception {
|
||||
writerWrapper.writeCollection("p", persons, (obj, name) -> writePerson(writerWrapper, obj, name));
|
||||
}
|
||||
|
||||
|
||||
private void writeAuth(WriterWrapper writerWrapper, Auth auth) throws Exception {
|
||||
writerWrapper.addStartElement("Auth");
|
||||
writerWrapper.addSimplelement("User", auth.getUser());
|
||||
writerWrapper.addEndElement("Auth");
|
||||
}
|
||||
|
||||
public void writePerson(WriterWrapper writerWrapper, Person person, String name) throws Exception {
|
||||
if (person == null) {
|
||||
return;
|
||||
}
|
||||
writerWrapper.addStartElement(name);
|
||||
writerWrapper.addAttribute("uid", String.valueOf(person.getUid()));
|
||||
writerWrapper.addAttribute("oksm", person.getOksm());
|
||||
writerWrapper.addAttribute("s", person.getS());
|
||||
writerWrapper.addAttribute("n", person.getN());
|
||||
writerWrapper.addAttribute("m", person.getM());
|
||||
writerWrapper.addAttribute("d", person.getD());
|
||||
writerWrapper.addAttribute("pn", person.getPn());
|
||||
writerWrapper.addAttribute("pd", person.getPd());
|
||||
writerWrapper.addAttribute("dt", person.getDt());
|
||||
writerWrapper.addAttribute("odt", person.getOdt());
|
||||
writerWrapper.addAttribute("ip", person.getIp());
|
||||
writerWrapper.addAttribute("consentDate", person.getConsentDate());
|
||||
writerWrapper.addAttribute("consentPurpose", person.getConsentPurpose());
|
||||
writerWrapper.addAttribute("reportUser", person.getReportUser());
|
||||
writerWrapper.addAttribute("liability", person.getLiability());
|
||||
writerWrapper.addAttribute("consentPeriod", person.getConsentPeriod());
|
||||
writerWrapper.addAttribute("agrDate", person.getAgrDate());
|
||||
writerWrapper.addAttribute("reportUserRegNum", person.getReportUserRegNum());
|
||||
writerWrapper.addAttribute("reportUserTaxID", person.getReportUserTaxID());
|
||||
writerWrapper.addAttribute("consentHash", person.getConsentHash());
|
||||
writerWrapper.addAttribute("cS", person.getCs());
|
||||
writerWrapper.addAttribute("consentN", person.getConsentN());
|
||||
writerWrapper.addAttribute("consentM", person.getConsentM());
|
||||
writerWrapper.addAttribute("consentBd", person.getConsentBd());
|
||||
writerWrapper.addAttribute("consentPn", person.getConsentPn());
|
||||
writerWrapper.addAttribute("consentPd", person.getConsentPd());
|
||||
writerWrapper.addAttribute("consentDivCode", person.getConsentDivCode());
|
||||
writerWrapper.addAttribute("consentDt", person.getConsentDt());
|
||||
writerWrapper.addAttribute("consentOdt", person.getConsentOdt());
|
||||
writerWrapper.addAttribute("consentIssueAuthority", person.getConsentIssueAuthority());
|
||||
writerWrapper.addAttribute("inqPurpose", person.getInqPurpose());
|
||||
writerWrapper.addEndElement(name);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -104,7 +104,11 @@ public class XmlProcessorStaxImpl implements StaxXmlProcessor {
|
|||
}
|
||||
|
||||
@Override
|
||||
public <Z> Z read(InputStream inputStream, Class<Z> clazz, Consumer<Z> objProcessor, Function<Z, Boolean> emptyBatchCheckFunction, Integer batchSize, String endBatchElementPath) {
|
||||
public <Z> Z read(InputStream inputStream,
|
||||
Class<Z> clazz, Consumer<Z> objProcessor,
|
||||
Function<Z, Boolean> emptyBatchCheckFunction,
|
||||
Integer batchSize,
|
||||
String endBatchElementPath) {
|
||||
try {
|
||||
XMLStreamReader xmlStreamReader = xmlInputFactory.createXMLStreamReader(inputStream);
|
||||
Supplier<StaxReader<?>> staxReaderSupplier = staxReaders.get(clazz);
|
||||
|
|
|
|||
|
|
@ -101,4 +101,8 @@ public class CTUtil {
|
|||
log.warn("File {} hasn't been deleted", filePath);
|
||||
}
|
||||
}
|
||||
|
||||
public static String normalizePhoneNumber(String phoneNumber) {
|
||||
return "9" + phoneNumber;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
85
src/main/java/ru/nbch/credit_tracker/utils/LogUtil.java
Normal file
85
src/main/java/ru/nbch/credit_tracker/utils/LogUtil.java
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
package ru.nbch.credit_tracker.utils;
|
||||
|
||||
import java.util.Map;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.slf4j.MDC;
|
||||
import ru.nbch.credit_tracker.utils.error.ExceptionUtils;
|
||||
|
||||
|
||||
public class LogUtil {
|
||||
private final static Logger log = LoggerFactory.getLogger(LogUtil.class);
|
||||
public static final String mdcKey = "ru.nbch.credit_tracker.log.mdc.key";
|
||||
|
||||
public static void addMdcLogKey(String uuid) {
|
||||
addMdcLogKey(mdcKey, uuid);
|
||||
}
|
||||
|
||||
|
||||
public static void removeMdcLogKey() {
|
||||
removeMdcLogKey(mdcKey);
|
||||
}
|
||||
|
||||
public static void removeMdcLogKey(String key) {
|
||||
try {
|
||||
MDC.remove(key);
|
||||
} catch (Throwable e) {
|
||||
log.error(ExceptionUtils.getStackTrace(e));
|
||||
}
|
||||
}
|
||||
|
||||
public static void addMdcLogKey(String mdcKey, String uuid) {
|
||||
if (uuid != null) {
|
||||
try {
|
||||
MDC.put(mdcKey, "[" + uuid + "] ");
|
||||
} catch (Throwable e) {
|
||||
log.error("{}", ExceptionUtils.getStackTrace(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Map<String, String> extractMdcMap() {
|
||||
try {
|
||||
return MDC.getCopyOfContextMap();
|
||||
} catch (Throwable e) {
|
||||
log.error(ExceptionUtils.getStackTrace(e));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static Runnable wrap(Map<String, String> contextMap, Runnable task) {
|
||||
return () -> {
|
||||
Map<String, String> previous = null;
|
||||
try {
|
||||
try {
|
||||
previous = MDC.getCopyOfContextMap();
|
||||
} catch (Throwable e) {
|
||||
log.error(ExceptionUtils.getStackTrace(e));
|
||||
}
|
||||
// ставим захваченный контекст (может быть null)
|
||||
try {
|
||||
if (contextMap == null) {
|
||||
MDC.clear();
|
||||
} else {
|
||||
MDC.setContextMap(contextMap);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
log.error(ExceptionUtils.getStackTrace(e));
|
||||
}
|
||||
|
||||
task.run();
|
||||
} finally {
|
||||
// восстанавливаем предыдущий (может быть null)
|
||||
try {
|
||||
if (previous == null) {
|
||||
MDC.clear();
|
||||
} else {
|
||||
MDC.setContextMap(previous);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
log.error(ExceptionUtils.getStackTrace(e));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +1,15 @@
|
|||
package ru.nbch.credit_tracker.utils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
@Slf4j
|
||||
public class ZipUtils {
|
||||
|
|
@ -25,4 +30,27 @@ public class ZipUtils {
|
|||
}
|
||||
return zipEntry;
|
||||
}
|
||||
|
||||
public static boolean isZipFile(MultipartFile file) {
|
||||
return file.getContentType() != null &&
|
||||
(file.getContentType().equals("application/zip") || file.getContentType().equals("application/x-zip-compressed")) ||
|
||||
file.getOriginalFilename() != null && file.getOriginalFilename().toLowerCase().endsWith(".zip");
|
||||
}
|
||||
|
||||
public static String writeTmpFile(String path, MultipartFile file) throws IOException {
|
||||
CTUtil.createFolder(path);
|
||||
String filePath = path + UUID.randomUUID() + "monitoring_request.xml";
|
||||
|
||||
try (OutputStream out = Files.newOutputStream(Path.of(filePath))) {
|
||||
ZipInputStream zipInputStream = new ZipInputStream(file.getInputStream());
|
||||
zipInputStream.getNextEntry();
|
||||
byte[] buffer = new byte[8 * 1024 * 1024];
|
||||
int bytesRead;
|
||||
while ((bytesRead = zipInputStream.read(buffer)) > 0) {
|
||||
out.write(buffer, 0, bytesRead);
|
||||
}
|
||||
|
||||
return filePath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
package ru.nbch.credit_tracker.utils.error;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import ru.nbch.credit_tracker.enums.EnumKeyMessage;
|
||||
import ru.nbch.credit_tracker.enums.IEnumKeyCode;
|
||||
import ru.nbch.credit_tracker.enums.XmlErrorFormat;
|
||||
|
|
@ -8,6 +10,13 @@ import ru.nbch.credit_tracker.exceptions.SignalServerException;
|
|||
|
||||
public class ExceptionUtils {
|
||||
|
||||
public static String getStackTrace(Throwable throwable) {
|
||||
StringWriter sw = new StringWriter();
|
||||
PrintWriter pw = new PrintWriter(sw, true);
|
||||
throwable.printStackTrace(pw);
|
||||
return sw.getBuffer().toString();
|
||||
}
|
||||
|
||||
public static SignalClientException clientExcp(IEnumKeyCode enumKeyCode, XmlErrorFormat xmlErrorFormat, Object... args) {
|
||||
return clientExcp(enumKeyCode, null, xmlErrorFormat, args);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} %X{ru.nbch.credit_tracker.log.mdc.key} - %msg%n</pattern>
|
||||
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} %X{ru.nbch.credit_tracker.log.mdc.key}- %msg%n</pattern>
|
||||
<charset>utf-8</charset>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
|
@ -22,7 +22,7 @@
|
|||
<maxFileSize>100MB</maxFileSize>
|
||||
</triggeringPolicy>
|
||||
<encoder>
|
||||
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} %X{ru.nbch.credit_tracker.log.mdc.key} - %msg%n</pattern>
|
||||
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} %X{ru.nbch.credit_tracker.log.mdc.key}- %msg%n</pattern>
|
||||
<charset>utf-8</charset>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
|
@ -63,12 +63,18 @@
|
|||
</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.component" level="DEBUG" additivity="false">
|
||||
<appender-ref ref="FILE"/>
|
||||
<appender-ref ref="FILE-JSON"/>
|
||||
<appender-ref ref="CONSOLE"/>
|
||||
</logger>
|
||||
|
||||
<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,50 +5,85 @@
|
|||
|
||||
<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="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>
|
||||
|
||||
<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>
|
||||
|
||||
<resultMap id="PaymentsFlagResult" type="ru.nbch.credit_tracker.entities.indic.PaymentsFlag">
|
||||
<result property="fid" column="fid" />
|
||||
<result property="flag" column="flag" />
|
||||
</resultMap>
|
||||
|
||||
<select id="findFlagsDebt5000" resultMap="FlagsFidDataResultMap">
|
||||
<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="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="findFlagsDebt5000" resultMap="PaymentsFlagResult">
|
||||
SELECT
|
||||
FID as fid,
|
||||
(case when (sum(case when AMT_PAST_DUE > 5000 then 1 else 0 end) = 0) then 0 else 1 end) as flag
|
||||
FID as fid,
|
||||
(case when (sum(case when AMT_PAST_DUE > 5000 then 1 else 0 end) = 0) then 0 else 1 end) as flag
|
||||
FROM
|
||||
(select FID, AMT_PAST_DUE, rank() over (partition by FID, ACC_SERIAL_NUM order by REPORTED_DT desc) rn
|
||||
FROM RU_PASTDUEARREAR
|
||||
WHERE FID IN
|
||||
<foreach item="fid" collection="fids" open="(" separator="," close=")">
|
||||
#{fid}
|
||||
</foreach>
|
||||
)
|
||||
(select FID, AMT_PAST_DUE, rank() over (partition by FID, ACC_SERIAL_NUM order by REPORTED_DT desc) rn
|
||||
FROM RU_PASTDUEARREAR
|
||||
WHERE FID IN
|
||||
<foreach item="fid" collection="fids" open="(" separator="," close=")">
|
||||
#{fid}
|
||||
</foreach>
|
||||
)
|
||||
where rn=1
|
||||
group by FID
|
||||
WITH UR
|
||||
</select>
|
||||
|
||||
<select id="findFlag90ByPastDueArrear" resultMap="FlagsFidDataResultMap">
|
||||
<select id="findFlag90ByPastDueArrear" resultMap="PaymentsFlagResult">
|
||||
SELECT
|
||||
FID as fid,
|
||||
(case when (sum(case when (REPORTED_DT >= #{reportedDate} and DAYS_PAST_DUE > 90) then 1 else 0 end) = 0) then 0 else 1 end) as flag
|
||||
FID as fid,
|
||||
(case when (sum(case when (REPORTED_DT >= #{reportedDate} and DAYS_PAST_DUE > 90) then 1 else 0 end) = 0) then 0 else 1 end) as flag
|
||||
FROM RU_PASTDUEARREAR
|
||||
WHERE FID IN
|
||||
<foreach item="fid" collection="fids" open="(" separator="," close=")">
|
||||
#{fid}
|
||||
</foreach>
|
||||
<foreach item="fid" collection="fids" open="(" separator="," close=")">
|
||||
#{fid}
|
||||
</foreach>
|
||||
GROUP BY FID
|
||||
WITH UR
|
||||
</select>
|
||||
|
||||
<select id="findFlag90ByRuPaymt" resultMap="FlagsFidDataResultMap">
|
||||
<select id="findFlag90ByRuPaymt" resultMap="PaymentsFlagResult">
|
||||
SELECT
|
||||
FID as fid,
|
||||
(case when (sum(case when (REPORTED_DT >= #{reportedDate} and DAYS_PAST_DUE > 90) then 1 else 0 end) = 0) then 0 else 1 end) as flag
|
||||
FID as fid,
|
||||
(case when (sum(case when (REPORTED_DT >= #{reportedDate} and DAYS_PAST_DUE > 90) then 1 else 0 end) = 0) then 0 else 1 end) as flag
|
||||
FROM RU_PAYMT
|
||||
WHERE FID IN
|
||||
<foreach item="fid" collection="fids" open="(" separator="," close=")">
|
||||
#{fid}
|
||||
</foreach>
|
||||
<foreach item="fid" collection="fids" open="(" separator="," close=")">
|
||||
#{fid}
|
||||
</foreach>
|
||||
GROUP BY FID
|
||||
WITH UR
|
||||
</select>
|
||||
|
|
|
|||
|
|
@ -1,25 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
|
||||
<mapper namespace="ru.nbch.credit_tracker.mapper.indic.PhoneMapper">
|
||||
|
||||
<resultMap id="fidsDataResultMap" type="ru.nbch.credit_tracker.service.indic.PhoneService$FidsData">
|
||||
<result property="phone" column="number" />
|
||||
<result property="fid" column="fid" />
|
||||
<result property="fileSinceDate" column="fileSinceDate" />
|
||||
</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
|
||||
WHERE number in
|
||||
<foreach item="phone" collection="phones" open="(" separator="," close=")">
|
||||
#{phone}
|
||||
</foreach>
|
||||
ORDER BY fileSinceDate DESC, fid DESC
|
||||
WITH UR
|
||||
</select>
|
||||
</mapper>
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
<mapper namespace="ru.nbch.credit_tracker.mapper.signal.PhoneFidMapMapper">
|
||||
|
||||
<resultMap id="PhoneFidMapResultMap" type="ru.nbch.credit_tracker.entities.dto.PhoneFidMap">
|
||||
<resultMap id="PhoneFidMapResultMap" type="ru.nbch.credit_tracker.entities.app.PhoneFidMap">
|
||||
<result property="id" column="id"/>
|
||||
<result property="packageId" column="package_id"/>
|
||||
<result property="subjectId" column="subject_id"/>
|
||||
|
|
@ -25,6 +25,36 @@
|
|||
</foreach>
|
||||
</insert>
|
||||
|
||||
<insert id="register" parameterType="java.util.List">
|
||||
INSERT INTO signals_phone_fid_map (subject_id, fid, phone, package_id, flag_debt5000, flag_90_12, calculated_at)
|
||||
values
|
||||
<foreach collection="phoneFidMap" item="phoneFid" separator=",">
|
||||
(#{phoneFid.subjectId}, #{phoneFid.fid}, #{phoneFid.phone}, #{phoneFid.packageId}, #{phoneFid.flagDebt5000}, #{phoneFid.flag90days}, #{phoneFid.calculatedAt})
|
||||
</foreach>
|
||||
</insert>
|
||||
|
||||
<insert id="registerJson" parameterType="string">
|
||||
INSERT INTO signals_phone_fid_map
|
||||
(subject_id, fid, phone, package_id, flag_debt5000, flag_90_12, calculated_at)
|
||||
SELECT subject_id, fid, phone, package_id, flag_debt5000, flag_90_12, calculated_at
|
||||
FROM json_to_recordset(#{json}::json) AS t(
|
||||
subject_id varchar(255),
|
||||
fid int,
|
||||
phone text,
|
||||
package_id bigint,
|
||||
flag_debt5000 smallint,
|
||||
flag_90_12 smallint,
|
||||
calculated_at timestamp)
|
||||
</insert>
|
||||
|
||||
<insert id="registerWithId" parameterType="java.util.List" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
|
||||
INSERT INTO signals_phone_fid_map (subject_id, phone, package_id)
|
||||
values
|
||||
<foreach collection="phoneFidMap" item="phoneFid" separator=",">
|
||||
(#{phoneFid.subjectId}, #{phoneFid.phone}, #{phoneFid.packageId})
|
||||
</foreach>
|
||||
</insert>
|
||||
|
||||
|
||||
<update id="updatePhoneFidMaps" parameterType="java.util.List">
|
||||
<foreach collection="subjects" item="subject" separator=";">
|
||||
|
|
@ -42,12 +72,35 @@
|
|||
</foreach>
|
||||
</update>
|
||||
|
||||
<update id="updateOne" parameterType="ru.nbch.credit_tracker.entities.app.PhoneFidMap">
|
||||
UPDATE signals_phone_fid_map
|
||||
SET
|
||||
package_id = #{subject.packageId},
|
||||
subject_id = #{subject.subjectId},
|
||||
phone = #{subject.phone},
|
||||
fid = #{subject.fid2},
|
||||
flag_debt5000 = #{subject.flagDebt5000},
|
||||
flag_90_12 = #{subject.flag90days},
|
||||
calculated_at = #{subject.calculatedAt},
|
||||
error_text = #{subject.errorText}
|
||||
WHERE id = #{subject.id}
|
||||
</update>
|
||||
|
||||
<update id="updateFid" parameterType="java.util.List">
|
||||
<foreach collection="subjects" item="subject" separator=";">
|
||||
UPDATE signals_phone_fid_map
|
||||
SET
|
||||
fid = #{subject.fid}
|
||||
WHERE rec_uuid = #{subject.id}
|
||||
</foreach>
|
||||
</update>
|
||||
|
||||
<update id="updatePhoneFidMapsErrors" parameterType="java.util.List">
|
||||
<foreach collection="subjects" item="subject" separator=";">
|
||||
UPDATE signals_phone_fid_map
|
||||
SET
|
||||
error_text = #{subject.errorText}
|
||||
WHERE id = #{subject.id}
|
||||
WHERE id = #{subject.recUuid}
|
||||
</foreach>
|
||||
</update>
|
||||
|
||||
|
|
@ -67,7 +120,7 @@
|
|||
LIMIT #{limit} OFFSET #{offset}
|
||||
</select>
|
||||
|
||||
<select id="findPhoneFidMapById" resultMap="PhoneFidMapResultMap" resultType="ru.nbch.credit_tracker.entities.dto.PhoneFidMap">
|
||||
<select id="findPhoneFidMapById" resultMap="PhoneFidMapResultMap" resultType="ru.nbch.credit_tracker.entities.app.PhoneFidMap">
|
||||
SELECT id,
|
||||
package_id,
|
||||
subject_id,
|
||||
|
|
@ -88,11 +141,5 @@
|
|||
where package_id = #{packageId} AND fid is not null
|
||||
</select>
|
||||
|
||||
<select id="hasOldRow" resultType="java.lang.Boolean">
|
||||
select EXISTS(SELECT 1
|
||||
FROM signals_phone_fid_map
|
||||
WHERE package_id = #{packageId}
|
||||
AND calculated_at < now() - make_interval(days => #{daysCount})
|
||||
)
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
<mapper namespace="ru.nbch.credit_tracker.mapper.signal.UserPackageMapper">
|
||||
|
||||
<resultMap id="packageDataResultMap" type="ru.nbch.credit_tracker.entities.dto.UserPackage">
|
||||
<resultMap id="packageDataResultMap" type="ru.nbch.credit_tracker.entities.app.UserPackage">
|
||||
<result property="id" column="id"/>
|
||||
<result property="userId" column="membercode"/>
|
||||
<result property="packageName" column="package_name"/>
|
||||
|
|
@ -53,7 +53,7 @@
|
|||
</update>
|
||||
|
||||
<select id="insertPackage" resultType="java.lang.Long"
|
||||
parameterType="ru.nbch.credit_tracker.entities.dto.UserPackage">
|
||||
parameterType="ru.nbch.credit_tracker.entities.app.UserPackage">
|
||||
INSERT INTO signals_user_packages (membercode, package_name, created_at, status)
|
||||
values (#{userPackage.userId}, #{userPackage.packageName}, #{userPackage.createdAt}, #{userPackage.status}) RETURNING id
|
||||
</select>
|
||||
|
|
@ -77,7 +77,7 @@
|
|||
</update>
|
||||
|
||||
<select id="findPackage" resultMap="packageDataResultMap"
|
||||
resultType="ru.nbch.credit_tracker.entities.dto.UserPackage">
|
||||
resultType="ru.nbch.credit_tracker.entities.app.UserPackage">
|
||||
SELECT id, membercode, package_name, created_at, status, deactivated_at, last_downloaded_report, monitoring_at
|
||||
FROM signals_user_packages
|
||||
WHERE id = #{id}
|
||||
|
|
@ -85,7 +85,7 @@
|
|||
</select>
|
||||
|
||||
<select id="findUserPackages" resultMap="packageDataResultMap"
|
||||
resultType="ru.nbch.credit_tracker.entities.dto.UserPackage">
|
||||
resultType="ru.nbch.credit_tracker.entities.app.UserPackage">
|
||||
SELECT id, membercode, package_name, created_at, status, deactivated_at, last_downloaded_report, monitoring_at
|
||||
FROM signals_user_packages
|
||||
WHERE membercode IS NOT NULL
|
||||
|
|
@ -97,7 +97,7 @@
|
|||
</select>
|
||||
|
||||
<select id="findAllPackagesByUserId" resultMap="packageDataResultMap"
|
||||
resultType="ru.nbch.credit_tracker.entities.dto.UserPackage">
|
||||
resultType="ru.nbch.credit_tracker.entities.app.UserPackage">
|
||||
SELECT id, membercode, package_name, created_at, status, deactivated_at, last_downloaded_report, monitoring_at
|
||||
FROM signals_user_packages
|
||||
WHERE membercode IS NOT NULL
|
||||
|
|
@ -105,7 +105,7 @@
|
|||
</select>
|
||||
|
||||
<select id="findAllPackagesByStatus" resultMap="packageDataResultMap"
|
||||
resultType="ru.nbch.credit_tracker.entities.dto.UserPackage">
|
||||
resultType="ru.nbch.credit_tracker.entities.app.UserPackage">
|
||||
SELECT id, membercode, package_name, created_at, status, deactivated_at, last_downloaded_report, monitoring_at
|
||||
FROM signals_user_packages
|
||||
WHERE status = #{status}
|
||||
|
|
|
|||
|
|
@ -1,3 +0,0 @@
|
|||
CREATE INDEX IF NOT EXISTS
|
||||
idx_signals_phone_fid_map_calculated_at
|
||||
ON signals_phone_fid_map (calculated_at);
|
||||
24
src/main/resources/sql/v2.21.ddl
Normal file
24
src/main/resources/sql/v2.21.ddl
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
|
||||
CREATE TABLE signals_user_packages (
|
||||
DB_VERSION varchar(12)
|
||||
);
|
||||
|
||||
alter table signals_user_packages
|
||||
alter column LAST_SEARCHED_AT type timestamp;
|
||||
alter table signals_user_packages
|
||||
alter column REJECT_STATUS type varchar(255);
|
||||
|
||||
alter table signals_phone_fid_map
|
||||
alter column SEARCHED_AT type timestamp;
|
||||
|
||||
|
||||
CREATE TABLE signals_flags (
|
||||
ID SERIAL PRIMARY KEY,
|
||||
MAP_ID bigint,
|
||||
SUBJECT_ID varchar(255),
|
||||
PHONE varchar(255),
|
||||
FID int,
|
||||
FLAG_DEBT5000 smallint,
|
||||
FLAG_90_12 smallint,
|
||||
CALCULATED_AT timestamp
|
||||
);
|
||||
|
|
@ -0,0 +1,278 @@
|
|||
package ru.nbch.credit_tracker.service;
|
||||
|
||||
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 javax.sql.DataSource;
|
||||
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;
|
||||
|
||||
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 FlagsService 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 FlagsService(null, 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, null);
|
||||
|
||||
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, null);
|
||||
|
||||
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, null);
|
||||
|
||||
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,179 @@
|
|||
package ru.nbch.credit_tracker.service;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import org.mockito.Mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import ru.nbch.credit_tracker.entities.app.PhoneFidMap;
|
||||
import ru.nbch.credit_tracker.repository.PhoneRepository;
|
||||
import ru.nbch.credit_tracker.service.indic.PhoneService;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class PhoneService2Test {
|
||||
|
||||
@Mock
|
||||
PhoneRepository phoneRepository;
|
||||
|
||||
@Test
|
||||
public void defineFidByPhoneTest() {
|
||||
PhoneService phoneService = new PhoneService(phoneRepository);
|
||||
|
||||
fidsDataWithEqualSinceDt.sort(byDbOrder);
|
||||
when(phoneRepository.findFids(any())).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());
|
||||
phoneFidMap.add(PhoneFidMap.builder().phone("1115").build());
|
||||
|
||||
Map<Integer, PhoneFidMap> res = phoneService.defineFidByPhone(phoneFidMap);
|
||||
Assertions.assertEquals(4, res.size());
|
||||
Assertions.assertEquals("1111", res.get(124).getPhone());
|
||||
Assertions.assertEquals("1112", res.get(125).getPhone());
|
||||
Assertions.assertEquals("1114", res.get(126).getPhone());
|
||||
Assertions.assertEquals("1115", res.get(127).getPhone());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defineFidByPhoneTest2() {
|
||||
PhoneService phoneService = new PhoneService(phoneRepository);
|
||||
|
||||
fidsDataWithEqualSinceDt.sort(byDbOrder);
|
||||
when(phoneRepository.findFids(any())).thenReturn(fidsDataWithEqualSinceDt2);
|
||||
|
||||
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());
|
||||
phoneFidMap.add(PhoneFidMap.builder().phone("1115").build());
|
||||
|
||||
Map<Integer, PhoneFidMap> res = phoneService.defineFidByPhone(phoneFidMap);
|
||||
Assertions.assertEquals(3, res.size());
|
||||
Assertions.assertEquals("1112", res.get(125).getPhone());
|
||||
Assertions.assertEquals("1114", res.get(126).getPhone());
|
||||
Assertions.assertEquals("1115", res.get(127).getPhone());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defineFidByPhoneTest3() {
|
||||
PhoneService phoneService = new PhoneService(phoneRepository);
|
||||
|
||||
fidsDataWithEqualSinceDt.sort(byDbOrder);
|
||||
when(phoneRepository.findFids(any())).thenReturn(fidsDataWithEqualSinceDt3);
|
||||
|
||||
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());
|
||||
phoneFidMap.add(PhoneFidMap.builder().phone("1115").build());
|
||||
|
||||
Map<Integer, PhoneFidMap> res = phoneService.defineFidByPhone(phoneFidMap);
|
||||
Assertions.assertEquals(2, res.size());
|
||||
Assertions.assertEquals("1114", res.get(126).getPhone());
|
||||
Assertions.assertEquals("1115", res.get(127).getPhone());
|
||||
}
|
||||
|
||||
private final List<PhoneRepository.FidsData> fidsDataWithEqualSinceDt = new ArrayList<>(
|
||||
List.of(
|
||||
new PhoneRepository.FidsData(123, "91111", LocalDate.of(2025, 9, 10)),
|
||||
new PhoneRepository.FidsData(124, "91111", LocalDate.of(2025, 9, 10)),
|
||||
new PhoneRepository.FidsData(125, "91112", LocalDate.of(2024, 10, 10)),
|
||||
new PhoneRepository.FidsData(126, "91114", LocalDate.of(2024, 10, 11)),
|
||||
new PhoneRepository.FidsData(127, "91115", LocalDate.of(2025, 9, 11)),
|
||||
new PhoneRepository.FidsData(128, "91115", LocalDate.of(2025, 9, 10))
|
||||
)
|
||||
);
|
||||
|
||||
private final List<PhoneRepository.FidsData> fidsDataWithEqualSinceDt2 = new ArrayList<>(
|
||||
List.of(
|
||||
new PhoneRepository.FidsData(125, "91112", LocalDate.of(2024, 10, 10)),
|
||||
new PhoneRepository.FidsData(126, "91114", LocalDate.of(2024, 10, 11)),
|
||||
new PhoneRepository.FidsData(127, "91115", LocalDate.of(2025, 9, 11)),
|
||||
new PhoneRepository.FidsData(128, "91115", LocalDate.of(2025, 9, 10))
|
||||
)
|
||||
);
|
||||
|
||||
private final List<PhoneRepository.FidsData> fidsDataWithEqualSinceDt3 = new ArrayList<>(
|
||||
List.of(
|
||||
new PhoneRepository.FidsData(126, "91114", LocalDate.of(2024, 10, 11)),
|
||||
new PhoneRepository.FidsData(127, "91115", LocalDate.of(2025, 9, 11)),
|
||||
new PhoneRepository.FidsData(128, "91115", LocalDate.of(2025, 9, 10))
|
||||
)
|
||||
);
|
||||
|
||||
Comparator<PhoneRepository.FidsData> byDbOrder =
|
||||
Comparator.comparing(PhoneRepository.FidsData::getPhone, Comparator.nullsFirst(Comparator.naturalOrder())) // pn.number ASC
|
||||
.thenComparing(PhoneRepository.FidsData::getFileSinceDate, Comparator.nullsLast(Comparator.reverseOrder())) // file_since_dt DESC
|
||||
.thenComparing(PhoneRepository.FidsData::getFid, Comparator.reverseOrder());
|
||||
|
||||
@Test
|
||||
void defineFidsAndNameByPhoneTest() {
|
||||
PhoneService phoneService = new PhoneService(phoneRepository);
|
||||
|
||||
// fidsDataWithNameEqualSinceDt.sort(byDbOrderWithName);
|
||||
when(phoneRepository.findFidsWithName(any())).thenReturn(fidsDataWithNameEqualSinceDt);
|
||||
|
||||
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());
|
||||
phoneFidMap.add(PhoneFidMap.builder().phone("1115").build());
|
||||
|
||||
Map<Integer, PhoneFidMap> res = phoneService.defineFidsAndNameByPhone(phoneFidMap);
|
||||
Assertions.assertEquals(4, res.size());
|
||||
Assertions.assertEquals("1111", res.get(124).getPhone());
|
||||
Assertions.assertEquals("1112", res.get(125).getPhone());
|
||||
Assertions.assertEquals("1114", res.get(126).getPhone());
|
||||
Assertions.assertEquals("1115", res.get(127).getPhone());
|
||||
|
||||
Assertions.assertEquals("found124", res.get(124).getFirstName());
|
||||
Assertions.assertNull(res.get(125).getFirstName());
|
||||
Assertions.assertEquals("found126", res.get(126).getFirstName());
|
||||
Assertions.assertNull(res.get(128));
|
||||
|
||||
}
|
||||
private final List<PhoneRepository.FidsDataWithName> fidsDataWithNameEqualSinceDt = new ArrayList<>(
|
||||
List.of(
|
||||
new PhoneRepository.FidsDataWithName(123, "91111", LocalDate.of(2025, 9, 10), "skip", null, null, null, null),
|
||||
new PhoneRepository.FidsDataWithName(123, "91111", LocalDate.of(2025, 9, 10), "skip", null, null, null, LocalDate.of(2025, 10, 9)),
|
||||
new PhoneRepository.FidsDataWithName(124, "91111", LocalDate.of(2025, 9, 9), "found124", null, null, null, LocalDate.of(2025, 9, 9)),
|
||||
new PhoneRepository.FidsDataWithName(124, "91111", LocalDate.of(2025, 9, 10), "skip124", null, null, null, LocalDate.of(2025, 9, 8)),
|
||||
new PhoneRepository.FidsDataWithName(124, "91111", LocalDate.of(2025, 9, 9), "skip124", null, null, null, LocalDate.of(2025, 9, 8)),
|
||||
new PhoneRepository.FidsDataWithName(124, "91111", LocalDate.of(2025, 9, 10), "found124", null, null, null, LocalDate.of(2025, 9, 9)),
|
||||
|
||||
|
||||
|
||||
|
||||
new PhoneRepository.FidsDataWithName(125, "91112", LocalDate.of(2024, 10, 10), null, null, null, null, null),
|
||||
|
||||
new PhoneRepository.FidsDataWithName(126, "91114", LocalDate.of(2024, 10, 11), "skip126", null, null, null, null),
|
||||
new PhoneRepository.FidsDataWithName(126, "91114", LocalDate.of(2024, 10, 11), "found126", null, null, null, LocalDate.of(2025, 9, 9)),
|
||||
new PhoneRepository.FidsDataWithName(126, "91114", LocalDate.of(2024, 10, 5), "found126", null, null, null, LocalDate.of(2025, 9, 9)),
|
||||
new PhoneRepository.FidsDataWithName(126, "91114", LocalDate.of(2024, 10, 5), "skip126", null, null, null, null),
|
||||
|
||||
|
||||
new PhoneRepository.FidsDataWithName(127, "91115", LocalDate.of(2025, 9, 11), null, null, null, null, null),
|
||||
new PhoneRepository.FidsDataWithName(128, "91115", LocalDate.of(2025, 9, 10), "found128", null, null, null, LocalDate.of(2025, 9, 9))
|
||||
)
|
||||
);
|
||||
|
||||
//todo понять зачем в тесте использовался этот компоратор
|
||||
// Comparator<PhoneRepository.FidsDataWithName> byDbOrderWithName =
|
||||
// Comparator.comparing(PhoneRepository.FidsDataWithName::getPhone, Comparator.nullsFirst(Comparator.naturalOrder())) // pn.number ASC
|
||||
// .thenComparing(PhoneRepository.FidsDataWithName::getFileSinceDate, Comparator.nullsLast(Comparator.reverseOrder())) // file_since_dt DESC
|
||||
// .thenComparing(PhoneRepository.FidsDataWithName::getFid, Comparator.reverseOrder());
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,122 @@
|
|||
package ru.nbch.credit_tracker.service;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import ru.nbch.credit_tracker.entities.app.PhoneFidMap;
|
||||
import ru.nbch.credit_tracker.repository.PhoneRepository;
|
||||
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.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class PhoneServiceTest {
|
||||
|
||||
@Mock
|
||||
PhoneRepository phoneRepository;
|
||||
|
||||
@Test
|
||||
public void defineFidByPhoneTest() {
|
||||
PhoneService phoneService = new PhoneService(phoneRepository);
|
||||
|
||||
fidsDataWithEqualSinceDt.sort(byDbOrder);
|
||||
when(phoneRepository.findFidsLong(any())).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());
|
||||
phoneFidMap.add(PhoneFidMap.builder().phone("1115").build());
|
||||
|
||||
Map<Integer, PhoneFidMap> res = phoneService.defineFidByPhone(phoneFidMap);
|
||||
Assertions.assertEquals(4, res.size());
|
||||
Assertions.assertEquals("1111", res.get(124).getPhone());
|
||||
Assertions.assertEquals("1112", res.get(125).getPhone());
|
||||
Assertions.assertEquals("1114", res.get(126).getPhone());
|
||||
Assertions.assertEquals("1115", res.get(127).getPhone());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defineFidByPhoneTest2() {
|
||||
PhoneService phoneService = new PhoneService(phoneRepository);
|
||||
|
||||
fidsDataWithEqualSinceDt.sort(byDbOrder);
|
||||
when(phoneRepository.findFidsLong(any())).thenReturn(fidsDataWithEqualSinceDt2);
|
||||
|
||||
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());
|
||||
phoneFidMap.add(PhoneFidMap.builder().phone("1115").build());
|
||||
|
||||
Map<Integer, PhoneFidMap> res = phoneService.defineFidByPhone(phoneFidMap);
|
||||
Assertions.assertEquals(3, res.size());
|
||||
Assertions.assertEquals("1112", res.get(125).getPhone());
|
||||
Assertions.assertEquals("1114", res.get(126).getPhone());
|
||||
Assertions.assertEquals("1115", res.get(127).getPhone());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defineFidByPhoneTest3() {
|
||||
PhoneService phoneService = new PhoneService(phoneRepository);
|
||||
|
||||
fidsDataWithEqualSinceDt.sort(byDbOrder);
|
||||
when(phoneRepository.findFidsLong(any())).thenReturn(fidsDataWithEqualSinceDt3);
|
||||
|
||||
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());
|
||||
phoneFidMap.add(PhoneFidMap.builder().phone("1115").build());
|
||||
|
||||
Map<Integer, PhoneFidMap> res = phoneService.defineFidByPhone(phoneFidMap);
|
||||
Assertions.assertEquals(2, res.size());
|
||||
Assertions.assertEquals("1114", res.get(126).getPhone());
|
||||
Assertions.assertEquals("1115", res.get(127).getPhone());
|
||||
}
|
||||
|
||||
private final List<PhoneRepository.FidsDataLong> fidsDataWithEqualSinceDt = new ArrayList<>(
|
||||
List.of(
|
||||
new PhoneRepository.FidsDataLong(123, 91111L, LocalDate.of(2025, 9, 10)),
|
||||
new PhoneRepository.FidsDataLong(124, 91111L, LocalDate.of(2025, 9, 10)),
|
||||
new PhoneRepository.FidsDataLong(125, 91112L, LocalDate.of(2024, 10, 10)),
|
||||
new PhoneRepository.FidsDataLong(126, 91114L, LocalDate.of(2024, 10, 11)),
|
||||
new PhoneRepository.FidsDataLong(127, 91115L, LocalDate.of(2025, 9, 11)),
|
||||
new PhoneRepository.FidsDataLong(128, 91115L, LocalDate.of(2025, 9, 10))
|
||||
)
|
||||
);
|
||||
|
||||
private final List<PhoneRepository.FidsDataLong> fidsDataWithEqualSinceDt2 = new ArrayList<>(
|
||||
List.of(
|
||||
new PhoneRepository.FidsDataLong(125, 91112L, LocalDate.of(2024, 10, 10)),
|
||||
new PhoneRepository.FidsDataLong(126, 91114L, LocalDate.of(2024, 10, 11)),
|
||||
new PhoneRepository.FidsDataLong(127, 91115L, LocalDate.of(2025, 9, 11)),
|
||||
new PhoneRepository.FidsDataLong(128, 91115L, LocalDate.of(2025, 9, 10))
|
||||
)
|
||||
);
|
||||
|
||||
private final List<PhoneRepository.FidsDataLong> fidsDataWithEqualSinceDt3 = new ArrayList<>(
|
||||
List.of(
|
||||
new PhoneRepository.FidsDataLong(126, 91114L, LocalDate.of(2024, 10, 11)),
|
||||
new PhoneRepository.FidsDataLong(127, 91115L, LocalDate.of(2025, 9, 11)),
|
||||
new PhoneRepository.FidsDataLong(128, 91115L, LocalDate.of(2025, 9, 10))
|
||||
)
|
||||
);
|
||||
|
||||
Comparator<PhoneRepository.FidsDataLong> byDbOrder =
|
||||
Comparator.comparing(PhoneRepository.FidsDataLong::getPhone, Comparator.nullsFirst(Comparator.naturalOrder())) // pn.number ASC
|
||||
.thenComparing(PhoneRepository.FidsDataLong::getFileSinceDate, Comparator.nullsLast(Comparator.reverseOrder())) // file_since_dt DESC
|
||||
.thenComparing(PhoneRepository.FidsDataLong::getFid, Comparator.reverseOrder());
|
||||
}
|
||||
|
|
@ -6,7 +6,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import ru.nbch.credit_tracker.entities.dto.PhoneFidMap;
|
||||
import ru.nbch.credit_tracker.entities.app.PhoneFidMap;
|
||||
import ru.nbch.credit_tracker.entities.external_response.monitoring_report_error.MonitoringReportErrors;
|
||||
import ru.nbch.credit_tracker.entities.external_response.online_download.Person;
|
||||
import ru.nbch.credit_tracker.entities.external_response.online_download.Persons;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue