Compare commits

..

20 commits

Author SHA1 Message Date
Mikhail Trofimov
af824c7d07 Used jdbcTemplate for finding fids 2025-09-30 10:59:26 +03:00
Mikhail Trofimov
7a4695281e Removed unused methods; Uncommented schedulers 2025-09-29 13:12:00 +03:00
Mikhail Trofimov
e07b203686 Use FlagsService2 2025-09-29 13:04:39 +03:00
Mikhail Trofimov
8c8ee75292 Added test for FlagsService2 2025-09-29 11:51:46 +03:00
Mikhail Trofimov
4f55c0df42 Added flags fetching by jdbc template 2025-09-29 10:10:21 +03:00
Mikhail Trofimov
e70f07b04b Added debug logs 2025-09-25 18:03:07 +03:00
Mikhail Trofimov
45ca5f4440 Changed logic for loading flag90days from Ru_Paymt; Added tests 2025-09-25 18:02:05 +03:00
Mikhail Trofimov
d1064b6b9f Minor fix calc flag5000 2025-09-25 16:51:19 +03:00
Mikhail Trofimov
b42e4f70df Added finding flags for RU_PASTDUEARREAR; Updated tests 2025-09-25 15:46:04 +03:00
Mikhail Trofimov
c245790eb5 Added test variant of counting flags 500 and 90_days from RU_PASTDUEARREAR 2025-09-25 13:07:39 +03:00
Mikhail Trofimov
021d7e8615 Removed order by reported_dt 2025-09-24 15:59:04 +03:00
Mikhail Trofimov
4114576a78 all queries 2025-09-24 15:10:24 +03:00
Mikhail Trofimov
f083716071 optimize updating 2025-09-24 15:10:01 +03:00
Mikhail Trofimov
669236e696 Fix PhoneService 2025-09-23 18:18:48 +03:00
Mikhail Trofimov
3f9fff5baa Fix PhoneService 2025-09-23 17:45:22 +03:00
Mikhail Trofimov
a633a0d605 Fix tests; Added extra logs 2025-09-23 14:56:00 +03:00
Mikhail Trofimov
21bd541243 Moved SignalServerException logs to GlobalExceptionHandler; Added warn logs to GlobalExceptionHandler for SignalClientException 2025-09-23 14:20:57 +03:00
etreschenkov
d136c30739 collect fids for procession flag calc 2025-09-23 14:14:49 +03:00
Mikhail Trofimov
29b017c51c Minor fix 2025-09-23 13:43:57 +03:00
etreschenkov
bddc5bb1fa logging rest request; logging query time for every mapper; optimization query and refactoring some methods 2025-09-22 22:02:55 +03:00
67 changed files with 1751 additions and 1222 deletions

View file

@ -1,5 +1,6 @@
FROM eclipse-temurin:17-jre-alpine FROM eclipse-temurin:17-jre-alpine
MAINTAINER ru.nbch MAINTAINER ru.nbch
RUN apk add gcompat bash
VOLUME /opt/app/logs/app VOLUME /opt/app/logs/app
VOLUME /opt/app/logs/json VOLUME /opt/app/logs/json
@ -13,11 +14,6 @@ COPY maven/start-credit-tracker.sh /opt/app/start-credit-tracker.sh
COPY maven/credit-tracker.properties /opt/app/credit-tracker.properties COPY maven/credit-tracker.properties /opt/app/credit-tracker.properties
COPY maven/monitoring-request.xsd /opt/app/monitoring-request.xsd COPY maven/monitoring-request.xsd /opt/app/monitoring-request.xsd
COPY maven/signal-package.xsd /opt/app/signal-package.xsd COPY maven/signal-package.xsd /opt/app/signal-package.xsd
COPY maven/ca_nbki.cer /usr/local/share/ca-certificates/corp.crt
RUN apk add --no-cache ca-certificates \
&& update-ca-certificates \
RUN apk add gcompat bash
RUN chmod +x /opt/app/start-credit-tracker.sh RUN chmod +x /opt/app/start-credit-tracker.sh
ENTRYPOINT ["/opt/app/start-credit-tracker.sh"] ENTRYPOINT ["/opt/app/start-credit-tracker.sh"]

View file

@ -1,33 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIFpTCCA42gAwIBAgIQXRfulgQYrrRFEpmSkmctvzANBgkqhkiG9w0BAQsFADBH
MRMwEQYKCZImiZPyLGQBGRYDbXNrMRQwEgYKCZImiZPyLGQBGRYEbmJraTEaMBgG
A1UEAxMRTkJLSS1Sb290LUNBLTIwMzIwHhcNMTQwNTE1MDYxODMzWhcNMzcwMjAx
MTE1OTEyWjBHMRMwEQYKCZImiZPyLGQBGRYDbXNrMRQwEgYKCZImiZPyLGQBGRYE
bmJraTEaMBgGA1UEAxMRTkJLSS1Sb290LUNBLTIwMzIwggIiMA0GCSqGSIb3DQEB
AQUAA4ICDwAwggIKAoICAQCjS1l6kx+tjX2PGmQcVw9wg0Q6SCeWbiz7rxAYZPqE
OGckXWWjtfUOc8BhD3+BnC4BQ414X1HP2OWQ97pa2LogTksbpNpJ0+p82IPAYxV+
zpI0FnW09FpzjtB6l/1kE9gKecjBoFG3xDhhFmpzCeK7tsKt94j84XPV/Zo82gYM
egmqhOfQZ+2HAChBj7gaBJALybFN34wkvN9bWdBPmK/j1jQikEAT8QE9qwcdmCKB
snHw+heYX4uJyZEkXEzW8GxQfTaAL+3d8HdRm1ZT/dN9KScF3Tp13LnQPCcixYoB
dF9Pd3P8JDAQX1KUVceLq6e75Q36EQaHHfEA8iQjaKlEzD049QbfAfCzKkSTnxHk
rOGwhm/h3ipwdAL4eqWkXfRriEvQU2fXGYXS0bhDT0/vBVAhHUMZ15/Oww0QSlc9
clpau6rYG4m/Fmm8LPyEHROenbNwu35TqLl+CUFES25aE2oZcH6+SyE5dDgpX9e1
Plt3fT6DuEDuT+rWvm0N8NVPNlQP/1lcbvP1oQrby07LToPatdKF9V5g8MX/19bP
BFsQsUHLlfffWMqqX8W/OPt5da3XL6U7Z8aBdhS7VAqqrvNhAbJBxgyJAzSfSwtZ
K5dZTKrJMZqmosUSIrLVr+9oXlbqJjKGNWQjwCfRQmPPkCpmpy1SRg8NPDoa3OVU
3wIDAQABo4GMMIGJMBMGCSsGAQQBgjcUAgQGHgQAQwBBMAsGA1UdDwQEAwIBhjAP
BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSkqLxdQSQ2+UIWrqSnms7lhp5zLjAQ
BgkrBgEEAYI3FQEEAwIBATAjBgkrBgEEAYI3FQIEFgQUvM7jX4RA4XBrZqhNhpl6
EDcEMZEwDQYJKoZIhvcNAQELBQADggIBAINqzWxlGXFgMwSlWmH4ocDa0A3ip51C
nEaWB58aomzOZYHnNLXj4KiwY59TMDkiRiql4wWCw2AJBVv2//7JwOqrk8rixq02
95NVX9TNefMKY3oo/g9bSqEsxoHgOoXP0QiY4GrYwAL6Bl0d/V9KUw5+BzBx/DKo
ncWD6KHnwFPBnHhIWiFlRAy2dX55rg6YSpB5/IlTQxGTjeg0OjX5w0ZE3L/0GZBl
O5W2E0RvUSErbjzoisxrhG48Mo5TtoGTmazKHRDqRCuZVnV0uEL9XaUOOV3G+LlP
/ujADUkF4iiIgoynB0W30fpwEqQFBSUxe+KzMw10ojaYaSz2g/HEg9ZKxy1a/bDL
6Va+vUMrypg26+MFrEdUwpPuORV1chtah/t+OxSk1dO82cuhjmRHX79mRwqjmXUs
5I/fNey8JWLCAV+Acf24CepdeYzeBGdh94t9FqTWw7aJaN5vL9ghwyG7JT8ozAfp
wYa1vq3nq+7SiELenQYYEUl792V8OsRV6N9nJkoLC12R9HGHjxMja5O2+Sw9LU4p
TjQcJt+qhT/c5+i6tQMjJUCTpXtOkaEezwrPpbUcoluGsz9vxs84eDtHiwumUCGj
XgaMSjcnY35dgne/x3kXcLYazdUYDoyYOEkgKG7ncSFg0lnIgwtYpqW6tzL3t9KN
Tsm6UcBOJAnv
-----END CERTIFICATE-----

View file

@ -8,45 +8,21 @@ server.tomcat.accesslog.pattern=%h %l %u %t "%r" %s %b
# datasources # datasources
# indic # indic
ct.indic-data-source-settings.driver=com.ibm.db2.jcc.DB2Driver 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.url=${DB_URL_INDIC:jdbc:db2://10.230.227.100:2668/cprosd22:currentSchema=INDIC;}
ct.indic-data-source-settings.username=${USERNAME_INDIC:tester} ct.indic-data-source-settings.username=${USERNAME_INDIC:ros9appl}
ct.indic-data-source-settings.password=${PASSWORD_INDIC:tester} 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}
ct.indic-data-source-settings.validation-timeout=${VALIDATION_TIMEOUT_INDIC:5000}
ct.indic-data-source-settings.idle-timeout=${IDLE_TIMEOUT_INDIC:600000}
ct.indic-data-source-settings.max-lifetime=${MAX_LIFETIME_INDIC:1800000}
ct.indic-data-source-settings.keepalive-time=${KEEPALIVE_TIME_INDIC:0}
ct.indic-data-source-settings.leak-detection-threshold=${LEAK_DETECTION_THRESHOLD_INDIC:0}
# signal # signal
ct.signal-data-source-settings.driver=org.postgresql.Driver ct.signal-data-source-settings.driver=org.postgresql.Driver
ct.signal-data-source-settings.url=${DB_URL_SIGNAL:jdbc:postgresql://10.200.200.133:5432/credit_tracker} ct.signal-data-source-settings.url=${DB_URL_SIGNAL:jdbc:postgresql://10.200.200.133:5432/credit_tracker}
ct.signal-data-source-settings.username=${USERNAME_SIGNAL:credit_tracker_user} ct.signal-data-source-settings.username=${USERNAME_SIGNAL:credit_tracker_user}
ct.signal-data-source-settings.password=${PASSWORD_SIGNAL:-} ct.signal-data-source-settings.password=${PASSWORD_SIGNAL:-}
ct.signal-data-source-settings.maximum-pool-size=${MAXIMUM_POOL_SIZE_SIGNAL:10}
ct.signal-data-source-settings.minimum-idle=${MINIMUM_IDLE_SIGNAL:10}
ct.signal-data-source-settings.connection-timeout=${CONNECTION_TIMEOUT_SIGNAL:10000}
ct.signal-data-source-settings.validation-timeout=${VALIDATION_TIMEOUT_SIGNAL:5000}
ct.signal-data-source-settings.idle-timeout=${IDLE_TIMEOUT_SIGNAL:600000}
ct.signal-data-source-settings.max-lifetime=${MAX_LIFETIME_SIGNAL:1800000}
ct.signal-data-source-settings.keepalive-time=${KEEPALIVE_TIME_SIGNAL:0}
ct.signal-data-source-settings.leak-detection-threshold=${LEAK_DETECTION_THRESHOLD_SIGNAL:0}
# scoring # scoring
ct.scoring-data-source-settings.driver=com.ibm.db2.jcc.DB2Driver ct.scoring-data-source-settings.driver=com.ibm.db2.jcc.DB2Driver
ct.scoring-data-source-settings.url=${DB_URL_SCORING:jdbc:db2://10.230.227.101:2668/cprosd22:currentSchema=TEST_2_INDIC;} ct.scoring-data-source-settings.url=${DB_URL_SCORING:jdbc:db2://10.230.227.101:2668/cprosd22:currentSchema=TEST_2_INDIC;}
ct.scoring-data-source-settings.username=${USERNAME_SCORING:tester} ct.scoring-data-source-settings.username=${USERNAME_SCORING:tester}
ct.scoring-data-source-settings.password=${PASSWORD_SCORING:tester} ct.scoring-data-source-settings.password=${PASSWORD_SCORING:tester}
ct.scoring-data-source-settings.maximum-pool-size=${MAXIMUM_POOL_SIZE_SCORING:10}
ct.scoring-data-source-settings.minimum-idle=${MINIMUM_IDLE_SCORING:10}
ct.scoring-data-source-settings.connection-timeout=${CONNECTION_TIMEOUT_SCORING:10000}
ct.scoring-data-source-settings.validation-timeout=${VALIDATION_TIMEOUT_SCORING:5000}
ct.scoring-data-source-settings.idle-timeout=${IDLE_TIMEOUT_SCORING:600000}
ct.scoring-data-source-settings.max-lifetime=${MAX_LIFETIME_SCORING:1800000}
ct.scoring-data-source-settings.keepalive-time=${KEEPALIVE_TIME_SCORING:0}
ct.scoring-data-source-settings.leak-detection-threshold=${LEAK_DETECTION_THRESHOLD_SCORING:0}
ct.max_xml_file_size=${ACCEPTED_XML_SIZE:1024} ct.max_xml_file_size=${ACCEPTED_XML_SIZE:1024}
spring.servlet.multipart.max-file-size=${ACCEPTED_ARCHIVE_SIZE:2048MB} spring.servlet.multipart.max-file-size=${ACCEPTED_ARCHIVE_SIZE:2048MB}
@ -66,7 +42,7 @@ ct.load-dir=/opt/app/reports
# min # min
ct.monitoring-interval=${MONITORING_INTERVAL:15} ct.monitoring-interval=${MONITORING_INTERVAL:15}
# days # 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.days-to-update-flags=${DAYS_TO_UPDATE_FLAGS:30}
ct.encoding=windows-1251 ct.encoding=windows-1251

View file

@ -1,5 +1,5 @@
#commons #commons
APP_VERSION=1.24 APP_VERSION=1.15
REGISTRY_URL=rh8-vm3.nbki.msk:5000 REGISTRY_URL=rh8-vm3.nbki.msk:5000
JMX_PORT= JMX_PORT=
JMX_HOSTNAME= JMX_HOSTNAME=
@ -24,41 +24,16 @@ SERVER_PORT=8099
DB_URL_INDIC=jdbc:db2://10.230.227.101:2668/cprosd22:currentSchema=INDIC; DB_URL_INDIC=jdbc:db2://10.230.227.101:2668/cprosd22:currentSchema=INDIC;
USERNAME_INDIC=tester USERNAME_INDIC=tester
PASSWORD_INDIC=tester PASSWORD_INDIC=tester
MAXIMUM_POOL_SIZE_INDIC=10
MINIMUM_IDLE_INDIC=10
CONNECTION_TIMEOUT_INDIC=10000
VALIDATION_TIMEOUT_INDIC=5000
IDLE_TIMEOUT_INDIC=600000
MAX_LIFETIME_INDIC=1800000
KEEPALIVE_TIME_INDIC=0
LEAK_DETECTION_THRESHOLD_INDIC=0
# signal # signal
DB_URL_SIGNAL=jdbc:postgresql://10.200.200.133:5432/credit_tracker DB_URL_SIGNAL=jdbc:postgresql://10.200.200.133:5432/credit_tracker
USERNAME_SIGNAL=credit_tracker_user USERNAME_SIGNAL=credit_tracker_user
PASSWORD_SIGNAL= PASSWORD_SIGNAL=
MAXIMUM_POOL_SIZE_SIGNAL=10
MINIMUM_IDLE_SIGNAL=10
CONNECTION_TIMEOUT_SIGNAL=10000
VALIDATION_TIMEOUT_SIGNAL=5000
IDLE_TIMEOUT_SIGNAL=600000
MAX_LIFETIME_SIGNAL=1800000
KEEPALIVE_TIME_SIGNAL=0
LEAK_DETECTION_THRESHOLD_SIGNAL=0
# scoring # scoring
DB_URL_SCORING=jdbc:db2://10.230.227.101:2668/cprosd22:currentSchema=TEST_2_INDIC; DB_URL_SCORING=jdbc:db2://10.230.227.101:2668/cprosd22:currentSchema=TEST_2_INDIC;
USERNAME_SCORING=tester USERNAME_SCORING=tester
PASSWORD_SCORING=tester PASSWORD_SCORING=tester
MAXIMUM_POOL_SIZE_SCORING=10
MINIMUM_IDLE_SCORING=10
CONNECTION_TIMEOUT_SCORING=10000
VALIDATION_TIMEOUT_SCORING=5000
IDLE_TIMEOUT_SCORING=600000
MAX_LIFETIME_SCORING=1800000
KEEPALIVE_TIME_SCORING=0
LEAK_DETECTION_THRESHOLD_SCORING=0
# размер в mb # размер в mb
ACCEPTED_XML_SIZE=1024 ACCEPTED_XML_SIZE=1024
@ -71,7 +46,7 @@ SIGNALS_USER=0001ZZ000001
# min # min
MONITORING_INTERVAL=15 MONITORING_INTERVAL=15
# days # days
UPDATE_FLAGS_CRON_EXPRESSION=0 22 * * * UPDATE_FLAGS_INTERVAL=1
DAYS_TO_UPDATE_FLAGS=30 DAYS_TO_UPDATE_FLAGS=30

View file

@ -23,36 +23,12 @@ services:
- DB_URL_INDIC=${DB_URL_INDIC} - DB_URL_INDIC=${DB_URL_INDIC}
- USERNAME_INDIC=${USERNAME_INDIC} - USERNAME_INDIC=${USERNAME_INDIC}
- PASSWORD_INDIC=${PASSWORD_INDIC} - PASSWORD_INDIC=${PASSWORD_INDIC}
- MAXIMUM_POOL_SIZE_INDIC=${MAXIMUM_POOL_SIZE_INDIC}
- MINIMUM_IDLE_INDIC=${MINIMUM_IDLE_INDIC}
- CONNECTION_TIMEOUT_INDIC=${CONNECTION_TIMEOUT_INDIC}
- VALIDATION_TIMEOUT_INDIC=${VALIDATION_TIMEOUT_INDIC}
- IDLE_TIMEOUT_INDIC=${IDLE_TIMEOUT_INDIC}
- MAX_LIFETIME_INDIC=${MAX_LIFETIME_INDIC}
- KEEPALIVE_TIME_INDIC=${KEEPALIVE_TIME_INDIC}
- LEAK_DETECTION_THRESHOLD_INDIC=${LEAK_DETECTION_THRESHOLD_INDIC}
- DB_URL_SIGNAL=${DB_URL_SIGNAL} - DB_URL_SIGNAL=${DB_URL_SIGNAL}
- USERNAME_SIGNAL=${USERNAME_SIGNAL} - USERNAME_SIGNAL=${USERNAME_SIGNAL}
- PASSWORD_SIGNAL=${PASSWORD_SIGNAL} - PASSWORD_SIGNAL=${PASSWORD_SIGNAL}
- MAXIMUM_POOL_SIZE_SIGNAL=${MAXIMUM_POOL_SIZE_SIGNAL}
- MINIMUM_IDLE_SIGNAL=${MINIMUM_IDLE_SIGNAL}
- CONNECTION_TIMEOUT_SIGNAL=${CONNECTION_TIMEOUT_SIGNAL}
- VALIDATION_TIMEOUT_SIGNAL=${VALIDATION_TIMEOUT_SIGNAL}
- IDLE_TIMEOUT_SIGNAL=${IDLE_TIMEOUT_SIGNAL}
- MAX_LIFETIME_SIGNAL=${MAX_LIFETIME_SIGNAL}
- KEEPALIVE_TIME_SIGNAL=${KEEPALIVE_TIME_SIGNAL}
- LEAK_DETECTION_THRESHOLD_SIGNAL=${LEAK_DETECTION_THRESHOLD_SIGNAL}
- DB_URL_SCORING=${DB_URL_SCORING} - DB_URL_SCORING=${DB_URL_SCORING}
- USERNAME_SCORING=${USERNAME_SCORING} - USERNAME_SCORING=${USERNAME_SCORING}
- PASSWORD_SCORING=${PASSWORD_SCORING} - PASSWORD_SCORING=${PASSWORD_SCORING}
- MAXIMUM_POOL_SIZE_SCORING=${MAXIMUM_POOL_SIZE_SCORING}
- MINIMUM_IDLE_SCORING=${MINIMUM_IDLE_SCORING}
- CONNECTION_TIMEOUT_SCORING=${CONNECTION_TIMEOUT_SCORING}
- VALIDATION_TIMEOUT_SCORING=${VALIDATION_TIMEOUT_SCORING}
- IDLE_TIMEOUT_SCORING=${IDLE_TIMEOUT_SCORING}
- MAX_LIFETIME_SCORING=${MAX_LIFETIME_SCORING}
- KEEPALIVE_TIME_SCORING=${KEEPALIVE_TIME_SCORING}
- LEAK_DETECTION_THRESHOLD_SCORING=${LEAK_DETECTION_THRESHOLD_SCORING}
- ACCEPTED_XML_SIZE=${ACCEPTED_XML_SIZE} - ACCEPTED_XML_SIZE=${ACCEPTED_XML_SIZE}
- ACCEPTED_ARCHIVE_SIZE=${ACCEPTED_ARCHIVE_SIZE} - ACCEPTED_ARCHIVE_SIZE=${ACCEPTED_ARCHIVE_SIZE}
- SIGNALS_ROOT_URI=${SIGNALS_ROOT_URI} - SIGNALS_ROOT_URI=${SIGNALS_ROOT_URI}

531
pom.xml
View file

@ -1,275 +1,272 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId> <artifactId>spring-boot-starter-parent</artifactId>
<version>3.4.6</version> <version>3.4.6</version>
<relativePath/> <!-- lookup parent from repository --> <relativePath/> <!-- lookup parent from repository -->
</parent> </parent>
<groupId>ru.nbch</groupId> <groupId>ru.nbch</groupId>
<artifactId>credit-tracker</artifactId> <artifactId>credit-tracker</artifactId>
<version>1.24</version> <version>1.15</version>
<name>credit_tracker</name> <name>credit_tracker</name>
<description>Credit Tracker Application</description> <description>Credit Tracker Application</description>
<properties> <properties>
<java.version>17</java.version> <java.version>17</java.version>
<git.info.prefix>git_info</git.info.prefix> <git.info.prefix>git_info</git.info.prefix>
<git.info.filename>git.properties</git.info.filename> <git.info.filename>git.properties</git.info.filename>
<registry.url>10.230.238.56:5000</registry.url> <registry.url>rh8-vm3.nbki.msk:5000</registry.url>
<!-- <registry.url>rh8-vm3.nbki.msk:5000</registry.url>--> <!-- <registry.url>rh8-vm3.nbki.msk:5000</registry.url>-->
</properties> </properties>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId> <artifactId>spring-boot-starter-web</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId> <artifactId>spring-boot-starter-aop</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.mybatis.spring.boot</groupId> <groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId> <artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.4</version> <version>3.0.4</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.postgresql</groupId> <groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId> <artifactId>postgresql</artifactId>
<scope>runtime</scope> <scope>runtime</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.ibm.db2</groupId> <groupId>com.ibm.db2</groupId>
<artifactId>jcc</artifactId> <artifactId>jcc</artifactId>
<version>12.1.0.0</version> <version>12.1.0.0</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.projectlombok</groupId> <groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId> <artifactId>lombok</artifactId>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.slf4j</groupId> <groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId> <artifactId>slf4j-api</artifactId>
<version>2.0.7</version> <version>2.0.7</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>ch.qos.logback</groupId> <groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId> <artifactId>logback-classic</artifactId>
<version>1.4.12</version> <version>1.4.12</version>
<exclusions> <exclusions>
<exclusion> <exclusion>
<groupId>ch.qos.logback</groupId> <groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId> <artifactId>logback-core</artifactId>
</exclusion> </exclusion>
</exclusions> </exclusions>
</dependency> </dependency>
<dependency> <dependency>
<groupId>ch.qos.logback</groupId> <groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId> <artifactId>logback-core</artifactId>
<version>1.5.12</version> <version>1.5.12</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.apache.commons</groupId> <groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId> <artifactId>commons-lang3</artifactId>
<version>3.17.0</version> <version>3.17.0</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>commons-io</groupId> <groupId>commons-io</groupId>
<artifactId>commons-io</artifactId> <artifactId>commons-io</artifactId>
<version>2.18.0</version> <version>2.18.0</version>
</dependency> </dependency>
<!-- JAXB API --> <!-- JAXB API -->
<dependency> <dependency>
<groupId>jakarta.xml.bind</groupId> <groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId> <artifactId>jakarta.xml.bind-api</artifactId>
<version>4.0.2</version> <version>4.0.2</version>
</dependency> </dependency>
<!--JAXB runtime implementation: eclipse moxy--> <!--JAXB runtime implementation: eclipse moxy-->
<dependency> <dependency>
<groupId>org.eclipse.persistence</groupId> <groupId>org.eclipse.persistence</groupId>
<artifactId>org.eclipse.persistence.moxy</artifactId> <artifactId>org.eclipse.persistence.moxy</artifactId>
<version>4.0.2</version> <version>4.0.2</version>
</dependency> </dependency>
<!-- Activation dependency needed for JAXB --> <!-- Activation dependency needed for JAXB -->
<dependency> <dependency>
<groupId>com.sun.activation</groupId> <groupId>com.sun.activation</groupId>
<artifactId>jakarta.activation</artifactId> <artifactId>jakarta.activation</artifactId>
<version>2.0.1</version> <version>2.0.1</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>co.elastic.apm</groupId> <groupId>co.elastic.apm</groupId>
<artifactId>apm-agent-api</artifactId> <artifactId>apm-agent-api</artifactId>
<version>1.43.0</version> <version>1.43.0</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.internetitem</groupId> <groupId>com.internetitem</groupId>
<artifactId>logback-elasticsearch-appender</artifactId> <artifactId>logback-elasticsearch-appender</artifactId>
<version>1.6</version> <version>1.6</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>net.logstash.logback</groupId> <groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId> <artifactId>logstash-logback-encoder</artifactId>
<version>7.4</version> <version>7.4</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId> <artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.mybatis.spring.boot</groupId> <groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter-test</artifactId> <artifactId>mybatis-spring-boot-starter-test</artifactId>
<version>3.0.4</version> <version>3.0.4</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.mockito</groupId> <groupId>com.h2database</groupId>
<artifactId>mockito-junit-jupiter</artifactId> <artifactId>h2</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
</dependencies> <dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<profiles> <profiles>
<profile> <profile>
<id>docker-push-dev</id> <id>docker-push-dev</id>
<build> <build>
<plugins> <pluginManagement>
<plugin> <plugins>
<groupId>io.fabric8</groupId> <plugin>
<artifactId>docker-maven-plugin</artifactId> <groupId>io.fabric8</groupId>
<version>0.45.0</version> <artifactId>docker-maven-plugin</artifactId>
<executions> <version>0.45.0</version>
<execution> <executions>
<id>Build and deploy docker container</id> <execution>
<phase>install</phase> <id>Build and deploy docker container</id>
<goals> <phase>install</phase>
<goal>build</goal> <goals>
<goal>push</goal> <!--<goal>build</goal>-->
</goals> <goal>push</goal>
</execution> </goals>
</executions> </execution>
<configuration> </executions>
<images> <configuration>
<image> <images>
<name>${project.artifactId}:${project.version}</name> <image>
<registry>${registry.url}</registry> <name>${project.artifactId}:${project.version}</name>
<build> <registry>${registry.url}</registry>
<dockerFile>${project.basedir}/Dockerfile</dockerFile> <build>
<assembly> <dockerFile>${project.basedir}/Dockerfile</dockerFile>
<inline> <assembly>
<files> <inline>
<file> <files>
<source> <file>
${project.build.directory}/${project.build.finalName}.jar <source>${project.build.directory}/${project.build.finalName}.jar</source>
</source> <outputDirectory>.</outputDirectory>
<outputDirectory>.</outputDirectory> </file>
</file> <file>
<file> <source>${project.basedir}/apm-agent/elastic-apm-agent-1.45.0.jar</source>
<source> <outputDirectory>.</outputDirectory>
${project.basedir}/apm-agent/elastic-apm-agent-1.45.0.jar </file>
</source> <file>
<outputDirectory>.</outputDirectory> <source>${project.basedir}/start-credit-tracker.sh</source>
</file> <outputDirectory>.</outputDirectory>
<file> </file>
<source>${project.basedir}/start-credit-tracker.sh</source> <file>
<outputDirectory>.</outputDirectory> <source>${project.basedir}/credit-tracker.properties</source>
</file> <outputDirectory>.</outputDirectory>
<file> </file>
<source>${project.basedir}/credit-tracker.properties</source> <file>
<outputDirectory>.</outputDirectory> <source>${project.basedir}/monitoring-request.xsd</source>
</file> <outputDirectory>.</outputDirectory>
<file> </file>
<source>${project.basedir}/monitoring-request.xsd</source> <file>
<outputDirectory>.</outputDirectory> <source>${project.basedir}/signal-package.xsd</source>
</file> <outputDirectory>.</outputDirectory>
<file> </file>
<source>${project.basedir}/signal-package.xsd</source> </files>
<outputDirectory>.</outputDirectory> </inline>
</file> </assembly>
<file> <tags>
<source> <tag>${project.version}</tag>
${project.basedir}/ca_nbki.cer </tags>
</source> </build>
<outputDirectory>.</outputDirectory> </image>
</file> </images>
</files> </configuration>
</inline> </plugin>
</assembly> </plugins>
<tags> </pluginManagement>
<tag>${project.version}</tag> </build>
</tags> </profile>
</build> </profiles>
</image> <build>
</images> <finalName>credit-tracker</finalName>
</configuration> <plugins>
</plugin> <plugin>
</plugins> <groupId>org.apache.maven.plugins</groupId>
</build> <artifactId>maven-compiler-plugin</artifactId>
</profile> <configuration>
</profiles> <annotationProcessorPaths>
<build> <path>
<finalName>credit-tracker</finalName> <groupId>org.projectlombok</groupId>
<plugins> <artifactId>lombok</artifactId>
<plugin> </path>
<groupId>org.apache.maven.plugins</groupId> </annotationProcessorPaths>
<artifactId>maven-compiler-plugin</artifactId> </configuration>
<configuration> </plugin>
<annotationProcessorPaths> <plugin>
<path> <groupId>org.springframework.boot</groupId>
<groupId>org.projectlombok</groupId> <artifactId>spring-boot-maven-plugin</artifactId>
<artifactId>lombok</artifactId> <configuration>
</path> <excludes>
</annotationProcessorPaths> <exclude>
</configuration> <groupId>org.projectlombok</groupId>
</plugin> <artifactId>lombok</artifactId>
<plugin> </exclude>
<groupId>org.springframework.boot</groupId> </excludes>
<artifactId>spring-boot-maven-plugin</artifactId> </configuration>
<configuration> </plugin>
<excludes> <plugin>
<exclude> <groupId>pl.project13.maven</groupId>
<groupId>org.projectlombok</groupId> <artifactId>git-commit-id-plugin</artifactId>
<artifactId>lombok</artifactId> <version>4.9.10</version>
</exclude> <executions>
</excludes> <execution>
</configuration> <id>get-the-git-infos</id>
</plugin> <goals>
<plugin> <goal>revision</goal>
<groupId>pl.project13.maven</groupId> </goals>
<artifactId>git-commit-id-plugin</artifactId> <phase>compile</phase>
<version>4.9.10</version> </execution>
<executions> </executions>
<execution> <configuration>
<id>get-the-git-infos</id> <failOnNoGitDirectory>false</failOnNoGitDirectory>
<goals> <dotGitDirectory>${project.basedir}/${git.info.relative.dir}</dotGitDirectory>
<goal>revision</goal> <generateGitPropertiesFile>true</generateGitPropertiesFile>
</goals> <generateGitPropertiesFilename>
<phase>compile</phase> ${project.build.outputDirectory}/${git.info.filename}
</execution> </generateGitPropertiesFilename>
</executions> <prefix>${git.info.prefix}</prefix>
<configuration> <includeOnlyProperties>
<failOnNoGitDirectory>false</failOnNoGitDirectory> <includeOnlyProperty>${git.info.prefix}.commit.id</includeOnlyProperty>
<dotGitDirectory>${project.basedir}/${git.info.relative.dir}</dotGitDirectory> <includeOnlyProperty>${git.info.prefix}.branch</includeOnlyProperty>
<generateGitPropertiesFile>true</generateGitPropertiesFile> <includeOnlyProperty>${git.info.prefix}.build.version</includeOnlyProperty>
<generateGitPropertiesFilename> <includeOnlyProperty>${git.info.prefix}.build.time</includeOnlyProperty>
${project.build.outputDirectory}/${git.info.filename} <includeOnlyProperty>${git.info.prefix}.closest.tag.name</includeOnlyProperty>
</generateGitPropertiesFilename> </includeOnlyProperties>
<prefix>${git.info.prefix}</prefix> </configuration>
<includeOnlyProperties> </plugin>
<includeOnlyProperty>${git.info.prefix}.commit.id</includeOnlyProperty>
<includeOnlyProperty>${git.info.prefix}.branch</includeOnlyProperty>
<includeOnlyProperty>${git.info.prefix}.build.version</includeOnlyProperty>
<includeOnlyProperty>${git.info.prefix}.build.time</includeOnlyProperty>
<includeOnlyProperty>${git.info.prefix}.closest.tag.name</includeOnlyProperty>
</includeOnlyProperties>
</configuration>
</plugin>
</plugins> </plugins>
</build> </build>
</project> </project>

View file

@ -15,9 +15,9 @@ public class CreditTrackerApplication {
UpdateProcessingPackagesService updateProcessingPackagesService = ctx.getBean(UpdateProcessingPackagesService.class); UpdateProcessingPackagesService updateProcessingPackagesService = ctx.getBean(UpdateProcessingPackagesService.class);
MonitoringService monitoringService = ctx.getBean(MonitoringService.class); MonitoringService monitoringService = ctx.getBean(MonitoringService.class);
UpdatingFlagsService updatingFlagsService = ctx.getBean(UpdatingFlagsService.class); UpdatingFlagsService updatingFlagsService = ctx.getBean(UpdatingFlagsService.class);
updateProcessingPackagesService.run();
monitoringService.run(); monitoringService.run();
updatingFlagsService.run(); updatingFlagsService.run();
updateProcessingPackagesService.run();
} }
} }

View file

@ -6,12 +6,13 @@ import ru.nbch.credit_tracker.entities.external_request.signals.Person;
import ru.nbch.credit_tracker.service.indic.PersonService; import ru.nbch.credit_tracker.service.indic.PersonService;
import ru.nbch.credit_tracker.utils.CTUtil; import ru.nbch.credit_tracker.utils.CTUtil;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
@Slf4j @Slf4j
public class PersonFactoryBuilder { public class PersonFactoryBuilder {
private final static DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("dd.MM.yyyy"); private final static DateFormat DATE_FORMAT = new SimpleDateFormat("dd.MM.yyyy");
private final static String DEFAULT_DATE = "01.01.2000"; private final static String DEFAULT_DATE = "01.01.2000";
private final static String IP = "0"; private final static String IP = "0";
@ -30,7 +31,7 @@ public class PersonFactoryBuilder {
private PersonService.PersonData personData; private PersonService.PersonData personData;
private Long uid; private Integer uid;
public static PersonFactoryBuilder builder() { public static PersonFactoryBuilder builder() {
return new PersonFactoryBuilder(); return new PersonFactoryBuilder();
@ -41,7 +42,7 @@ public class PersonFactoryBuilder {
return this; return this;
} }
public PersonFactoryBuilder uid(Long uid) { public PersonFactoryBuilder uid(Integer uid) {
this.uid = uid; this.uid = uid;
return this; return this;
} }
@ -56,10 +57,10 @@ public class PersonFactoryBuilder {
person.setS(nameData.getLastName()); person.setS(nameData.getLastName());
person.setN(nameData.getFirstName()); person.setN(nameData.getFirstName());
person.setM(StringUtils.isNotBlank(nameData.getMiddleName()) ? nameData.getMiddleName() : null); person.setM(StringUtils.isNotBlank(nameData.getMiddleName()) ? nameData.getMiddleName() : null);
person.setD(DATE_FORMAT.format(nameData.getBirthDate())); person.setD(DATE_FORMAT.format(CTUtil.localDatetoDate(nameData.getBirthDate())));
person.setPn(getPn(passportData.getSerNum(), passportData.getIdNum())); 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.setPd(DATE_FORMAT.format(CTUtil.localDatetoDate(LocalDate.now()))); // Do not use passport iss_date. Signal service could reject the package
person.setDt(getDt(passportData.getIdType(), passportData.getIdType2())); person.setDt(getDt(passportData.getIdType(), passportData.getIdType2()));
if (person.getDt() != null && person.getDt().equals("999")) { if (person.getDt() != null && person.getDt().equals("999")) {
person.setOdt(ODT); person.setOdt(ODT);
@ -68,12 +69,12 @@ public class PersonFactoryBuilder {
person.setIp(IP); person.setIp(IP);
person.setUid(uid); person.setUid(uid);
person.setOksm(OKSM); person.setOksm(OKSM);
person.setConsentDate(DATE_FORMAT.format(LocalDate.now())); person.setConsentDate(DATE_FORMAT.format(CTUtil.localDatetoDate(LocalDate.now())));
person.setConsentPurpose(CONSENT_PURPOSE); person.setConsentPurpose(CONSENT_PURPOSE);
person.setReportUser(REPORT_USER); person.setReportUser(REPORT_USER);
person.setLiability(LIABILITY); person.setLiability(LIABILITY);
person.setConsentPeriod(CONSENT_PERIOD); person.setConsentPeriod(CONSENT_PERIOD);
person.setAgrDate(DATE_FORMAT.format(LocalDate.now())); person.setAgrDate(DATE_FORMAT.format(CTUtil.localDatetoDate(LocalDate.now())));
person.setReportUserRegNum(REPORT_USER_REG_NUM); person.setReportUserRegNum(REPORT_USER_REG_NUM);
person.setReportUserTaxID(REPORT_USER_TAX_ID); person.setReportUserTaxID(REPORT_USER_TAX_ID);
person.setConsentHash(CONSENT_HASH); person.setConsentHash(CONSENT_HASH);

View file

@ -1,18 +1,18 @@
package ru.nbch.credit_tracker.config.datasource; package ru.nbch.credit_tracker.config.datasource;
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.boot.jdbc.DataSourceBuilder; import org.springframework.boot.jdbc.DataSourceBuilder;
import ru.nbch.credit_tracker.config.settings.DataSourceSettings; import ru.nbch.credit_tracker.config.settings.DataSourceSettings;
import javax.sql.DataSource;
public class DatabaseConfig { public class DatabaseConfig {
protected HikariDataSource createDataSource(DataSourceSettings settings) { protected DataSource createDataSource(DataSourceSettings settings) {
return DataSourceBuilder.create() return DataSourceBuilder.create()
.driverClassName(settings.driver()) .driverClassName(settings.driver())
.url(settings.url()) .url(settings.url())
.username(settings.username()) .username(settings.username())
.password(settings.password()) .password(settings.password())
.type(HikariDataSource.class)
.build(); .build();
} }

View file

@ -1,6 +1,5 @@
package ru.nbch.credit_tracker.config.datasource; package ru.nbch.credit_tracker.config.datasource;
import com.zaxxer.hikari.HikariDataSource;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.SqlSessionFactoryBean;
@ -10,8 +9,8 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource; 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.CreditTrackerSettings;
import ru.nbch.credit_tracker.config.settings.DataSourceSettings;
import javax.sql.DataSource; import javax.sql.DataSource;
import java.sql.Connection; import java.sql.Connection;
@ -30,25 +29,14 @@ public class IndicDataSourceConfig extends DatabaseConfig {
@Bean(name = "indicDataSource") @Bean(name = "indicDataSource")
public DataSource dataSource(CreditTrackerSettings config) throws SQLException { public DataSource dataSource(CreditTrackerSettings config) throws SQLException {
DataSourceSettings indicSetings = config.getIndicDataSourceSettings(); DataSource dataSource = createDataSource(config.getIndicDataSourceSettings());
HikariDataSource dataSource = createDataSource(indicSetings);
dataSource.setMaximumPoolSize(indicSetings.maximumPoolSize());
dataSource.setMinimumIdle(indicSetings.minimumIdle());
dataSource.setConnectionTimeout(indicSetings.connectionTimeout());
dataSource.setValidationTimeout(indicSetings.validationTimeout());
dataSource.setIdleTimeout(indicSetings.idleTimeout());
dataSource.setMaxLifetime(indicSetings.maxLifetime());
dataSource.setKeepaliveTime(indicSetings.keepaliveTime());
dataSource.setLeakDetectionThreshold(indicSetings.leakDetectionThreshold());
try (Connection connection = dataSource.getConnection()) { try (Connection connection = dataSource.getConnection()) {
connection.isValid(3); connection.isValid(3);
} catch (SQLException e) { } catch (SQLException e) {
log.error("Unable to connect to Indic url={}", indicSetings.url(), e); log.error("Unable to connect to Indic url={}", config.getIndicDataSourceSettings().url(), e);
throw e; throw e;
} }
log.info("Connected to Indic: url={}", indicSetings.url()); log.info("Connected to Indic: url={}", config.getIndicDataSourceSettings().url());
return dataSource; return dataSource;
} }
@ -60,5 +48,10 @@ public class IndicDataSourceConfig extends DatabaseConfig {
sqlSessionFactoryBean.setTypeHandlersPackage("ru.nbch.credit_tracker.config.datasource.handlers"); sqlSessionFactoryBean.setTypeHandlersPackage("ru.nbch.credit_tracker.config.datasource.handlers");
return sqlSessionFactoryBean.getObject(); return sqlSessionFactoryBean.getObject();
} }
@Bean(name = "jdbcTemplateIndic")
public NamedParameterJdbcTemplate getNamedParameterJdbcTemplate(@Qualifier("indicDataSource") DataSource dataSource) {
return new NamedParameterJdbcTemplate(dataSource);
}
} }

View file

@ -1,6 +1,5 @@
package ru.nbch.credit_tracker.config.datasource; package ru.nbch.credit_tracker.config.datasource;
import com.zaxxer.hikari.HikariDataSource;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.SqlSessionFactoryBean;
@ -11,7 +10,6 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import ru.nbch.credit_tracker.config.settings.CreditTrackerSettings; import ru.nbch.credit_tracker.config.settings.CreditTrackerSettings;
import ru.nbch.credit_tracker.config.settings.DataSourceSettings;
import javax.sql.DataSource; import javax.sql.DataSource;
import java.sql.Connection; import java.sql.Connection;
@ -30,25 +28,14 @@ public class ScoringDataSourceConfig extends DatabaseConfig {
@Bean(name = "scoringDataSource") @Bean(name = "scoringDataSource")
public DataSource dataSource(CreditTrackerSettings config) throws SQLException { public DataSource dataSource(CreditTrackerSettings config) throws SQLException {
DataSourceSettings scoringSetings = config.getScoringDataSourceSettings(); DataSource dataSource = createDataSource(config.getScoringDataSourceSettings());
HikariDataSource dataSource = createDataSource(scoringSetings);
dataSource.setMaximumPoolSize(scoringSetings.maximumPoolSize());
dataSource.setMinimumIdle(scoringSetings.minimumIdle());
dataSource.setConnectionTimeout(scoringSetings.connectionTimeout());
dataSource.setValidationTimeout(scoringSetings.validationTimeout());
dataSource.setIdleTimeout(scoringSetings.idleTimeout());
dataSource.setMaxLifetime(scoringSetings.maxLifetime());
dataSource.setKeepaliveTime(scoringSetings.keepaliveTime());
dataSource.setLeakDetectionThreshold(scoringSetings.leakDetectionThreshold());
try (Connection connection = dataSource.getConnection()) { try (Connection connection = dataSource.getConnection()) {
connection.isValid(3); connection.isValid(3);
} catch (SQLException e) { } catch (SQLException e) {
log.error("Unable to connect to Scoring url={}", scoringSetings.url(), e); log.error("Unable to connect to Scoring url={}", config.getScoringDataSourceSettings().url(), e);
throw e; throw e;
} }
log.info("Connected to Scoring: url={}", scoringSetings.url()); log.info("Connected to Scoring: url={}", config.getScoringDataSourceSettings().url());
return dataSource; return dataSource;
} }

View file

@ -1,6 +1,5 @@
package ru.nbch.credit_tracker.config.datasource; package ru.nbch.credit_tracker.config.datasource;
import com.zaxxer.hikari.HikariDataSource;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.SqlSessionFactoryBean;
@ -11,7 +10,6 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import ru.nbch.credit_tracker.config.settings.CreditTrackerSettings; import ru.nbch.credit_tracker.config.settings.CreditTrackerSettings;
import ru.nbch.credit_tracker.config.settings.DataSourceSettings;
import javax.sql.DataSource; import javax.sql.DataSource;
import java.sql.Connection; import java.sql.Connection;
@ -29,25 +27,14 @@ public class SignalDataSourceConfig extends DatabaseConfig {
@Bean(name = "signalDataSource") @Bean(name = "signalDataSource")
public DataSource dataSource(CreditTrackerSettings config) throws SQLException { public DataSource dataSource(CreditTrackerSettings config) throws SQLException {
DataSourceSettings signalSetings = config.getSignalDataSourceSettings(); DataSource dataSource = createDataSource(config.getSignalDataSourceSettings());
HikariDataSource dataSource = createDataSource(signalSetings);
dataSource.setMaximumPoolSize(signalSetings.maximumPoolSize());
dataSource.setMinimumIdle(signalSetings.minimumIdle());
dataSource.setConnectionTimeout(signalSetings.connectionTimeout());
dataSource.setValidationTimeout(signalSetings.validationTimeout());
dataSource.setIdleTimeout(signalSetings.idleTimeout());
dataSource.setMaxLifetime(signalSetings.maxLifetime());
dataSource.setKeepaliveTime(signalSetings.keepaliveTime());
dataSource.setLeakDetectionThreshold(signalSetings.leakDetectionThreshold());
try (Connection connection = dataSource.getConnection()) { try (Connection connection = dataSource.getConnection()) {
connection.isValid(3); connection.isValid(3);
} catch (SQLException e) { } catch (SQLException e) {
log.error("Unable to connect to Signal url={}", signalSetings.url(), e); log.error("Unable to connect to Signal url={}", config.getSignalDataSourceSettings().url(), e);
throw e; throw e;
} }
log.info("Connected to Signal: url={}", signalSetings.url()); log.info("Connected to Signal: url={}", config.getSignalDataSourceSettings().url());
return dataSource; return dataSource;
} }

View file

@ -1,19 +0,0 @@
package ru.nbch.credit_tracker.config.mdc;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@ConditionalOnClass(Aspect.class)
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class AopMdcConfig {
@Bean
public MdcAspect mdcAspect() {
return new MdcAspect();
}
}

View file

@ -1,49 +0,0 @@
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.user_request.monitoring_request.MonitoringRequest;
@Slf4j
@Aspect
public class MdcAspect {
// 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 {
Object[] args = pjp.getArgs();
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());
}
} else if (obj instanceof UserPackage userPackage) {
if (userPackage.getPackageName() != null) {
putMdcValue(setAndClearMdc.key(), userPackage.getPackageName());
}
} else if (obj instanceof String packageName) {
putMdcValue(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));
}
}
}

View file

@ -1,15 +0,0 @@
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;
// 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";
// The index of the method argument to use for the MDC value (defaults to the first argument)
int argIndex() default 0;
}

View file

@ -55,12 +55,7 @@ public class GlobalExceptionHandler {
commonError.setErrorText(errorText); commonError.setErrorText(errorText);
if (ex instanceof SignalClientException) { if (ex instanceof SignalClientException) {
String packageName = ((SignalClientException) ex).getPackageName(); log.warn("Response error: {}", errorText);
if(packageName != null ) {
log.warn("Response error: {}. PackageName: {}", errorText, packageName);
} else {
log.warn("Response error: {}", errorText);
}
return new ResponseEntity<>(commonError, headers, HttpStatus.BAD_REQUEST); return new ResponseEntity<>(commonError, headers, HttpStatus.BAD_REQUEST);
} else { } else {
Exception cause = ((SignalServerException) ex).getCause(); Exception cause = ((SignalServerException) ex).getCause();
@ -74,7 +69,6 @@ public class GlobalExceptionHandler {
switch (ex.getErrorFormat()) { switch (ex.getErrorFormat()) {
case MonitoringError -> commonError = new MonitoringError(); case MonitoringError -> commonError = new MonitoringError();
case MonitoringActivePackagesError -> commonError = new MonitoringActivePackagesError(); case MonitoringActivePackagesError -> commonError = new MonitoringActivePackagesError();
case MonitoringAllPackagesError -> commonError = new MonitoringAllPackagesError();
case MonitoringDownloadError -> commonError = new MonitoringDownloadError(); case MonitoringDownloadError -> commonError = new MonitoringDownloadError();
case MonitoringReportListError -> commonError = new MonitoringReportListError(); case MonitoringReportListError -> commonError = new MonitoringReportListError();
case MonitoringPackageDeleteError -> commonError = new MonitoringPackageDeleteError(); case MonitoringPackageDeleteError -> commonError = new MonitoringPackageDeleteError();

View file

@ -3,9 +3,17 @@ package ru.nbch.credit_tracker.config.rest.filters;
import jakarta.servlet.FilterChain; import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException; import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.Part; import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order; import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -14,10 +22,6 @@ import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.ContentCachingRequestWrapper; import org.springframework.web.util.ContentCachingRequestWrapper;
import org.springframework.web.util.ContentCachingResponseWrapper; import org.springframework.web.util.ContentCachingResponseWrapper;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;
@Component @Component
@Slf4j @Slf4j
@Order(0) @Order(0)
@ -30,12 +34,12 @@ public class HttpLoggingFilter extends OncePerRequestFilter {
protected boolean shouldNotFilter(HttpServletRequest request) { protected boolean shouldNotFilter(HttpServletRequest request) {
String path = request.getRequestURI(); String path = request.getRequestURI();
return path.startsWith("/actuator/health") || path.startsWith("/swagger") return path.startsWith("/actuator/health") || path.startsWith("/swagger")
|| path.startsWith("/v3/api-docs") || path.startsWith("/static/") || path.equals("/"); || path.startsWith("/v3/api-docs") || path.startsWith("/static/");
} }
@Override @Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException { throws ServletException, IOException {
long startNs = System.nanoTime(); long startNs = System.nanoTime();
@ -43,18 +47,17 @@ public class HttpLoggingFilter extends OncePerRequestFilter {
ContentCachingResponseWrapper resp = new ContentCachingResponseWrapper(response); ContentCachingResponseWrapper resp = new ContentCachingResponseWrapper(response);
String correlationId = Optional.ofNullable(request.getHeader("X-Correlation-Id")) String correlationId = Optional.ofNullable(request.getHeader("X-Correlation-Id"))
.filter(StringUtils::hasText) .filter(StringUtils::hasText)
.orElse(UUID.randomUUID().toString()); .orElse(UUID.randomUUID().toString());
resp.setHeader("X-Correlation-Id", correlationId); resp.setHeader("X-Correlation-Id", correlationId);
logIncomingRequest(req, correlationId);
try { try {
chain.doFilter(req, resp); chain.doFilter(req, resp);
} finally { } finally {
long tookMs = (System.nanoTime() - startNs) / 1_000_000; long tookMs = (System.nanoTime() - startNs) / 1_000_000;
Map<String, String> reqParams = paramsMap(req); String requestBody = readBody(req.getContentAsByteArray(), req.getCharacterEncoding());
String responseBody = readBody(resp.getContentAsByteArray(), resp.getCharacterEncoding());
Map<String, String> reqHeaders = headersMap(req); Map<String, String> reqHeaders = headersMap(req);
Map<String, String> respHeaders = headersMap(resp); Map<String, String> respHeaders = headersMap(resp);
@ -63,67 +66,43 @@ public class HttpLoggingFilter extends OncePerRequestFilter {
mask(respHeaders); mask(respHeaders);
log.info( log.info(
"HTTP (RESPONSE) {} {} | status={} | took={}ms | cid={} | reqHeaders={} | reqParams={} | respHeaders={}", "HTTP {} {}{} | status={} | took={}ms | cid={} | reqHeaders={} | reqBody={} | respHeaders={} | respBody={}",
req.getMethod(), req.getMethod(),
req.getRequestURI(), req.getRequestURI(),
resp.getStatus(), req.getQueryString() == null ? "" : "?" + req.getQueryString(),
tookMs, resp.getStatus(),
correlationId, tookMs,
reqHeaders, correlationId,
reqParams, reqHeaders,
respHeaders requestBody,
respHeaders,
responseBody
); );
resp.copyBodyToResponse(); resp.copyBodyToResponse();
} }
} }
private void logIncomingRequest(ContentCachingRequestWrapper req, String correlationId) { private static ContentCachingRequestWrapper wrapRequest(HttpServletRequest request) {
Map<String, String> reqParams = paramsMap(req);
Map<String, String> reqHeaders = headersMap(req);
mask(reqHeaders);
log.info(
"HTTP (REQUEST) {} {} | cid={} | reqHeaders={} | reqParams={}",
req.getMethod(),
req.getRequestURI(),
correlationId,
reqHeaders,
reqParams
);
}
private static ContentCachingRequestWrapper wrapRequest(HttpServletRequest request) throws IOException {
return (request instanceof ContentCachingRequestWrapper c) ? c : new ContentCachingRequestWrapper(request, MAX_BODY); return (request instanceof ContentCachingRequestWrapper c) ? c : new ContentCachingRequestWrapper(request, MAX_BODY);
} }
private static String readBody(byte[] buf, String encoding) {
if (buf == null || buf.length == 0) return "";
var bytes = buf.length > MAX_BODY ? Arrays.copyOf(buf, MAX_BODY) : buf;
String body = new String(bytes, encoding != null ? java.nio.charset.Charset.forName(encoding) : StandardCharsets.UTF_8);
if (buf.length > MAX_BODY) body += "…[truncated]";
return body;
}
private static Map<String, String> headersMap(HttpServletRequest req) { private static Map<String, String> headersMap(HttpServletRequest req) {
return Collections.list(req.getHeaderNames()).stream() return Collections.list(req.getHeaderNames()).stream()
.collect(Collectors.toMap(h -> h, req::getHeader, (a, b) -> a, LinkedHashMap::new)); .collect(Collectors.toMap(h -> h, req::getHeader, (a, b) -> a, LinkedHashMap::new));
}
private static Map<String, String> paramsMap(HttpServletRequestWrapper req) {
Map<String, String> paramsMap = new LinkedHashMap<>();
req.getParameterMap().forEach((k, v) -> {
String params = String.join(", ", v);
paramsMap.put(k, params);
});
try {
Collection<Part> fileParts = req.getParts();
fileParts.forEach(part -> {
paramsMap.put(part.getName(), part.getSubmittedFileName());
});
} catch (Exception e) {
// ignore
}
return paramsMap;
} }
private static Map<String, String> headersMap(HttpServletResponse resp) { private static Map<String, String> headersMap(HttpServletResponse resp) {
return resp.getHeaderNames().stream() return resp.getHeaderNames().stream()
.collect(Collectors.toMap(h -> h, resp::getHeader, (a, b) -> a, LinkedHashMap::new)); .collect(Collectors.toMap(h -> h, resp::getHeader, (a, b) -> a, LinkedHashMap::new));
} }
private static void mask(Map<String, String> headers) { private static void mask(Map<String, String> headers) {

View file

@ -1,55 +0,0 @@
package ru.nbch.credit_tracker.config.rest.filters;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import ru.nbch.credit_tracker.config.rest.wrappers.CachedHttpRequestWrapper;
import java.io.IOException;
@Component
@Slf4j
@Order(1)
public class MemberCodeHttpLoggingFilter extends OncePerRequestFilter {
@Override
protected boolean shouldNotFilter(HttpServletRequest request) {
String path = request.getRequestURI();
return !(path.startsWith("/api/v1/signal/all_packages") || path.startsWith("/api/v1/signal/active_packages"));
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
CachedHttpRequestWrapper req = wrapRequest(request);
String body = IOUtils.toString(req.getBody(), req.getCharacterEncoding());
if (StringUtils.isNotBlank(body)) {
try {
int startIndex = body.indexOf("<UserId>") + 8;
int endIndex = body.indexOf("</UserId>");
String userId = body.substring(startIndex, endIndex);
log.info(
"HTTP (REQUEST) {} {} | memberCode={}",
req.getMethod(),
req.getRequestURI(),
userId.substring(0, 6)
);
} catch (Exception e) {
// ignore
}
}
filterChain.doFilter(req, response);
}
private static CachedHttpRequestWrapper wrapRequest(HttpServletRequest request) throws IOException {
return new CachedHttpRequestWrapper(request);
}
}

View file

@ -1,25 +0,0 @@
package ru.nbch.credit_tracker.config.rest.wrappers;
import jakarta.servlet.ServletInputStream;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;
import lombok.Getter;
import java.io.IOException;
@Getter
public class CachedHttpRequestWrapper extends HttpServletRequestWrapper {
private final byte[] body;
public CachedHttpRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
this.body = request.getInputStream().readAllBytes();
}
@Override
public ServletInputStream getInputStream() {
return new CachedServletInputStream(body);
}
}

View file

@ -1,33 +0,0 @@
package ru.nbch.credit_tracker.config.rest.wrappers;
import jakarta.servlet.ReadListener;
import jakarta.servlet.ServletInputStream;
import java.io.ByteArrayInputStream;
public class CachedServletInputStream extends ServletInputStream {
private final ByteArrayInputStream byteArray;
public CachedServletInputStream(byte[] data) {
this.byteArray = new ByteArrayInputStream(data);
}
public int read() {
return byteArray.read();
}
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener listener) {
}
}

View file

@ -29,7 +29,7 @@ public class CreditTrackerSettings {
private Long maxXmlFileSize; private Long maxXmlFileSize;
private String loadDir; private String loadDir;
private Long monitoringInterval; private Long monitoringInterval;
private String updateFlagsCronExpression; private Long updateFlagsInterval;
private Long daysToUpdateFlags; private Long daysToUpdateFlags;
private String encoding; private String encoding;

View file

@ -4,13 +4,5 @@ public record DataSourceSettings(
String username, String username,
String password, String password,
String url, String url,
String driver, String driver
Integer maximumPoolSize,
Integer minimumIdle,
long connectionTimeout,
long validationTimeout,
long idleTimeout,
long maxLifetime,
long keepaliveTime,
long leakDetectionThreshold
) {} ) {}

View file

@ -6,8 +6,6 @@ import org.springframework.context.annotation.Configuration;
import ru.nbch.credit_tracker.config.settings.CreditTrackerSettings; import ru.nbch.credit_tracker.config.settings.CreditTrackerSettings;
import ru.nbch.credit_tracker.entities.external_response.monitoring_report_file.SgnlReportMonitorigSetupError; import ru.nbch.credit_tracker.entities.external_response.monitoring_report_file.SgnlReportMonitorigSetupError;
import ru.nbch.credit_tracker.entities.external_response.online_report.SgnlOnlineReport; import ru.nbch.credit_tracker.entities.external_response.online_report.SgnlOnlineReport;
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.errors.MonitoringError; import ru.nbch.credit_tracker.entities.user_response.errors.MonitoringError;
import ru.nbch.credit_tracker.entities.user_response.success.MonitoringSetupResponse; import ru.nbch.credit_tracker.entities.user_response.success.MonitoringSetupResponse;
import ru.nbch.credit_tracker.service.xml.jaxb.IXmlProcessor; import ru.nbch.credit_tracker.service.xml.jaxb.IXmlProcessor;
@ -28,8 +26,6 @@ public class XmlConfig {
// unmarshalling classes // unmarshalling classes
SgnlOnlineReport.class, SgnlOnlineReport.class,
SgnlReportMonitorigSetupError.class, SgnlReportMonitorigSetupError.class,
MonitoringActivePackagesRequest.class,
MonitoringAllPackagesRequest.class,
// marshalling classes // marshalling classes
MonitoringError.class, MonitoringError.class,
MonitoringSetupResponse.class); MonitoringSetupResponse.class);

View file

@ -8,8 +8,8 @@ import java.time.LocalDateTime;
@Getter @Getter
@Setter @Setter
public class Hit { public class Hit {
private Long id; private Integer id;
private Long packageId; private Integer packageId;
private String subjectId; private String subjectId;
private Integer signalType; private Integer signalType;
private LocalDateTime responseSentAt; private LocalDateTime responseSentAt;

View file

@ -10,8 +10,8 @@ import java.time.LocalDateTime;
@NoArgsConstructor @NoArgsConstructor
@Builder @Builder
public class PhoneFidMap { public class PhoneFidMap {
private Long id; private Integer id;
private Long packageId; private Integer packageId;
private String subjectId; private String subjectId;
private String phone; private String phone;
private Integer fid; private Integer fid;

View file

@ -10,12 +10,11 @@ import java.time.LocalDateTime;
@Setter @Setter
@Builder @Builder
public class UserPackage { public class UserPackage {
private Long id; private Integer id;
private String userId; private String userId;
private String packageName; private String packageName;
private LocalDateTime createdAt; private LocalDateTime createdAt;
private String status; private String status;
private LocalDateTime deactivatedAt; private LocalDateTime deactivatedAt;
private String lastDownloadedReport; private String lastDownloadedReport;
private LocalDateTime monitoringAt;
} }

View file

@ -15,7 +15,7 @@ public class Person {
@XmlValue @XmlValue
protected String value; protected String value;
@XmlAttribute(name = "uid", required = true) @XmlAttribute(name = "uid", required = true)
protected Long uid; protected Integer uid;
@XmlAttribute(name = "oksm", required = true) @XmlAttribute(name = "oksm", required = true)
protected String oksm; protected String oksm;
@XmlAttribute(name = "s", required = true) @XmlAttribute(name = "s", required = true)

View file

@ -63,7 +63,7 @@ public class MonitoringReportErrors {
public static class P { public static class P {
@XmlAttribute(name = "uid") @XmlAttribute(name = "uid")
protected Long uid; protected Integer uid;
@XmlValue @XmlValue
protected String value; protected String value;

View file

@ -16,7 +16,7 @@ import java.util.List;
public class Person { public class Person {
@XmlAttribute(name = "uid", required = true) @XmlAttribute(name = "uid", required = true)
protected Long uid; protected Integer uid;
@XmlElement(name = "Inquiry", required = true) @XmlElement(name = "Inquiry", required = true)
protected List<Inquiry> inquiries; protected List<Inquiry> inquiries;

View file

@ -1,25 +0,0 @@
package ru.nbch.credit_tracker.entities.user_response.errors;
import jakarta.xml.bind.annotation.*;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "MonitoringAllPackagesResponse", propOrder = {
"status",
"errorCode",
"errorText"
})
@XmlRootElement(name = "MonitoringAllPackagesResponse")
public class MonitoringAllPackagesError implements CommonError {
@XmlElement(name = "Status", required = true)
protected String status;
@XmlElement(name = "Code",required = true)
protected String errorCode;
@XmlElement(name = "Message",required = true)
protected String errorText;
}

View file

@ -2,7 +2,6 @@ package ru.nbch.credit_tracker.enums;
public enum XmlErrorFormat { public enum XmlErrorFormat {
MonitoringActivePackagesError, MonitoringActivePackagesError,
MonitoringAllPackagesError,
MonitoringDownloadError, MonitoringDownloadError,
MonitoringError, MonitoringError,
MonitoringPackageDeleteError, MonitoringPackageDeleteError,

View file

@ -7,10 +7,7 @@ import ru.nbch.credit_tracker.enums.XmlErrorFormat;
@Getter @Getter
public class SignalClientException extends SignalException { public class SignalClientException extends SignalException {
private final String packageName; public SignalClientException(EnumKeyMessage errorMessage, XmlErrorFormat errorFormat) {
public SignalClientException(EnumKeyMessage errorMessage, XmlErrorFormat errorFormat, String packageName) {
super(errorMessage, errorFormat); super(errorMessage, errorFormat);
this.packageName = packageName;
} }
} }

View file

@ -1,16 +1,12 @@
package ru.nbch.credit_tracker.facade; package ru.nbch.credit_tracker.facade;
import lombok.extern.slf4j.Slf4j; 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.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.core.io.FileUrlResource; import org.springframework.core.io.FileUrlResource;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile; 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.config.settings.CreditTrackerSettings;
import ru.nbch.credit_tracker.entities.dto.UserPackage; 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.active_packages.MonitoringActivePackagesRequest;
@ -35,7 +31,7 @@ import ru.nbch.credit_tracker.service.scoring.AuthorizationService;
import ru.nbch.credit_tracker.service.signal.SignalRestService; import ru.nbch.credit_tracker.service.signal.SignalRestService;
import ru.nbch.credit_tracker.service.xml.stax.StaxXmlProcessor; import ru.nbch.credit_tracker.service.xml.stax.StaxXmlProcessor;
import ru.nbch.credit_tracker.utils.CTUtil; import ru.nbch.credit_tracker.utils.CTUtil;
import ru.nbch.credit_tracker.utils.ValidationService; import ru.nbch.credit_tracker.utils.ValidationUtil;
import ru.nbch.credit_tracker.utils.ZipUtils; import ru.nbch.credit_tracker.utils.ZipUtils;
import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamException;
@ -47,7 +43,6 @@ import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
@ -59,17 +54,12 @@ import static ru.nbch.credit_tracker.utils.error.ExceptionUtils.serverExcp;
@Slf4j @Slf4j
@Service @Service
@EnableAspectJAutoProxy(exposeProxy = true)
public class SignalFacade { public class SignalFacade {
private SignalFacade self;
private final CreditTrackerSettings config; private final CreditTrackerSettings config;
private final AuthorizationService authorizationService; private final AuthorizationService authorizationService;
private final SignalService signalService; private final SignalService signalService;
private final SignalRestService signalRestService; private final SignalRestService signalRestService;
private final MonitoringErrorProcesingService monitoringErrorProcesingService; private final MonitoringErrorProcesingService monitoringErrorProcesingService;
private final ValidationService validationService;
private final StaxXmlProcessor staxXmlProcessor; private final StaxXmlProcessor staxXmlProcessor;
private final ExecutorService executor; private final ExecutorService executor;
@ -81,7 +71,6 @@ public class SignalFacade {
SignalService signalService, SignalService signalService,
SignalRestService signalRestService, SignalRestService signalRestService,
MonitoringErrorProcesingService monitoringErrorProcesingService, MonitoringErrorProcesingService monitoringErrorProcesingService,
ValidationService validationService,
StaxXmlProcessor staxXmlProcessor, StaxXmlProcessor staxXmlProcessor,
@Qualifier("executorService") ExecutorService executor) { @Qualifier("executorService") ExecutorService executor) {
this.config = config; this.config = config;
@ -89,7 +78,6 @@ public class SignalFacade {
this.signalService = signalService; this.signalService = signalService;
this.signalRestService = signalRestService; this.signalRestService = signalRestService;
this.monitoringErrorProcesingService = monitoringErrorProcesingService; this.monitoringErrorProcesingService = monitoringErrorProcesingService;
this.validationService = validationService;
this.staxXmlProcessor = staxXmlProcessor; this.staxXmlProcessor = staxXmlProcessor;
this.executor = executor; this.executor = executor;
this.maxXmlFileSize = config.getMaxXmlFileSize() * 1024 * 1024; this.maxXmlFileSize = config.getMaxXmlFileSize() * 1024 * 1024;
@ -103,7 +91,7 @@ public class SignalFacade {
// Валидация Архива на количетсво файлов, размер и расширение // Валидация Архива на количетсво файлов, размер и расширение
MonitoringRequest monitoringRequest; MonitoringRequest monitoringRequest;
Long packageId; Integer packageId;
ZipInputStream zipInputStream = null; ZipInputStream zipInputStream = null;
String filePath; String filePath;
try { try {
@ -156,13 +144,13 @@ public class SignalFacade {
AuthorizationService.AuthResult authResult = authorizationService.findAuthInfo(monitoringRequest.getUserId(), monitoringRequest.getPassword()); AuthorizationService.AuthResult authResult = authorizationService.findAuthInfo(monitoringRequest.getUserId(), monitoringRequest.getPassword());
if (!authResult.isSuccess()) { if (!authResult.isSuccess()) {
signalService.rejectPackage(monitoringRequest); signalService.rejectPackage(monitoringRequest);
throw clientExcp(authResult.getError(), monitoringRequest.getPackageName(), XmlErrorFormat.MonitoringError); throw clientExcp(authResult.getError(), XmlErrorFormat.MonitoringError);
} }
// проверка на уникальность пакета // проверка на уникальность пакета
if (signalService.isPackageNotUnique(monitoringRequest)) { if (signalService.isPackageNotUnique(monitoringRequest)) {
signalService.rejectPackage(monitoringRequest); signalService.rejectPackage(monitoringRequest);
throw clientExcp(SignalError.ERROR_007, monitoringRequest.getPackageName(), XmlErrorFormat.MonitoringError); throw clientExcp(SignalError.ERROR_007, XmlErrorFormat.MonitoringError);
} }
packageId = signalService.registerPackage(monitoringRequest); packageId = signalService.registerPackage(monitoringRequest);
@ -176,7 +164,7 @@ public class SignalFacade {
} }
// в отдельном потоке запускаем регистрацию субъектов, поиск фидов и расчет флагов просрочек // в отдельном потоке запускаем регистрацию субъектов, поиск фидов и расчет флагов просрочек
getCurrentProxy().registerFidsAndDebts(packageId, monitoringRequest.getPackageName(), filePath); registerFidsAndDebts(packageId, monitoringRequest.getPackageName(), filePath);
return ResponseEntity.ok().body(new MonitoringSetupResponse()); return ResponseEntity.ok().body(new MonitoringSetupResponse());
} }
@ -187,7 +175,7 @@ public class SignalFacade {
monitoringRequest = staxXmlProcessor.read(in, MonitoringRequest.class, monitoringRequest = staxXmlProcessor.read(in, MonitoringRequest.class,
request -> { request -> {
try { try {
validationService.validateMonitoringRequest(request); ValidationUtil.validateMonitoringRequest(request);
} catch (SignalClientException e) { } catch (SignalClientException e) {
if (request != null) { if (request != null) {
signalService.rejectPackage(request); signalService.rejectPackage(request);
@ -223,13 +211,8 @@ public class SignalFacade {
} }
} }
@SetAndClearMdc(argIndex = 1) private void registerFidsAndDebts(Integer packageId, String packageName, String filePath) {
public void registerFidsAndDebts(Long packageId, String packageName, String filePath) {
Map<String, String> context = MDC.getCopyOfContextMap();
executor.execute(() -> { executor.execute(() -> {
if (context != null) {
MDC.setContextMap(context);
}
try { try {
signalService.registerSubjects(filePath, packageId); signalService.registerSubjects(filePath, packageId);
CTUtil.removeFile(filePath); CTUtil.removeFile(filePath);
@ -240,9 +223,8 @@ public class SignalFacade {
return; return;
} }
if (signalRestService.setupMonitoring(packageId)) { if (signalRestService.setupMonitoring(packageId)) {
LocalDateTime monitoringAt = LocalDateTime.now(); log.info("Package was successfully registered in signal service. PackageId: {} PackageName: {}", packageId, packageName);
log.info("Package was successfully registered in signal service. PackageId: {} PackageName: {}, MonitoringAt: {}", packageId, packageName, monitoringAt); signalService.updatePackageStatus(packageId, Status.ON_MONITORING);
signalService.updatePackageOnMonitoringSuccess(packageId, monitoringAt);
monitoringErrorProcesingService.findErrors(packageId); monitoringErrorProcesingService.findErrors(packageId);
} else { } else {
log.info("Package registration was rejected by signal service. PackageId: {} PackageName: {}", packageId, packageName); log.info("Package registration was rejected by signal service. PackageId: {} PackageName: {}", packageId, packageName);
@ -298,7 +280,7 @@ public class SignalFacade {
try { try {
AuthorizationService.AuthResult authResult = authorizationService.findAuthInfo(allPackagesRequest.getUserId(), allPackagesRequest.getPassword()); AuthorizationService.AuthResult authResult = authorizationService.findAuthInfo(allPackagesRequest.getUserId(), allPackagesRequest.getPassword());
if (!authResult.isSuccess()) { if (!authResult.isSuccess()) {
throw clientExcp(authResult.getError(), XmlErrorFormat.MonitoringAllPackagesError); throw clientExcp(authResult.getError(), XmlErrorFormat.MonitoringActivePackagesError);
} }
List<UserPackage> activePackages = signalService.findAllPackages(allPackagesRequest.getUserId()); List<UserPackage> activePackages = signalService.findAllPackages(allPackagesRequest.getUserId());
@ -320,13 +302,13 @@ public class SignalFacade {
response.setPackages(packages); response.setPackages(packages);
return ResponseEntity.ok(response); return ResponseEntity.ok(response);
} else { } else {
throw clientExcp(SignalError.ERROR_019, XmlErrorFormat.MonitoringAllPackagesError); throw clientExcp(SignalError.ERROR_019, XmlErrorFormat.MonitoringActivePackagesError);
} }
} catch (Exception e) { } catch (Exception e) {
if (e instanceof SignalClientException) { if (e instanceof SignalClientException) {
throw (SignalClientException) e; throw (SignalClientException) e;
} }
throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringAllPackagesError, e); throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringActivePackagesError, e);
} }
} }
@ -426,11 +408,4 @@ public class SignalFacade {
throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringDownloadError, e); throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringDownloadError, e);
} }
} }
private SignalFacade getCurrentProxy() {
if (self == null) {
self = (SignalFacade) AopContext.currentProxy();
}
return self;
}
} }

View file

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

View file

@ -10,16 +10,15 @@ import java.util.List;
@Mapper @Mapper
public interface PhoneFidMapMapper { public interface PhoneFidMapMapper {
void registerSubjects(@Param("subjects") List<Subject> subjects, @Param("packageId") Long packageId); void registerSubjects(@Param("subjects") List<Subject> subjects, @Param("packageId") Integer packageId);
List<PhoneFidMap> findFullPhoneFidMap(@Param("packageId") Long packageId, @Param("offset") int offset, @Param("limit") int limit); List<PhoneFidMap> findFullPhoneFidMap(@Param("packageId") Integer packageId, @Param("offset") int offset, @Param("limit") int limit);
void updatePhoneFidMaps(@Param("subjects") List<PhoneFidMap> subjects); void updatePhoneFidMaps(@Param("subjects") List<PhoneFidMap> subjects);
void updatePhoneFidMapsErrors(@Param("subjects") List<PhoneFidMap> subjects); void updatePhoneFidMapsErrors(@Param("subjects") List<PhoneFidMap> subjects);
PhoneFidMap findPhoneFidMapById(@Param("id") Long packageId); PhoneFidMap findPhoneFidMapById(@Param("id") Integer packageId);
boolean hasNonNullFids(@Param("packageId") Long packageId); boolean hasNonNullFids(@Param("packageId") Integer packageId);
boolean hasOldRow(@Param("packageId") Long packageId, @Param("daysCount") Integer daysCount);
} }

View file

@ -18,15 +18,13 @@ public interface UserPackageMapper {
void deletePackage(@Param("userId") String userId, @Param("packageName") String packageName, @Param("status") String newStatus, @Param("previousStatus") String previousStatus, @Param("deactivatedAt") LocalDateTime deactivatedAt); void deletePackage(@Param("userId") String userId, @Param("packageName") String packageName, @Param("status") String newStatus, @Param("previousStatus") String previousStatus, @Param("deactivatedAt") LocalDateTime deactivatedAt);
Long insertPackage(@Param("userPackage") UserPackage userPackage); Integer insertPackage(@Param("userPackage") UserPackage userPackage);
void updatePackageStatus(@Param("packageId") Long packageId, @Param("status") String status); void updatePackageStatus(@Param("packageId") Integer packageId, @Param("status") String status);
void updatePackageLastDownloadedReport(@Param("id") Long id, @Param("fileName") String fileName); void updatePackageLastDownloadedReport(@Param("id") Integer id, @Param("fileName") String fileName);
void updatePackageOnMonitoringSuccess(@Param("id") Long id, @Param("monitoringAt") LocalDateTime monitoringAt); UserPackage findPackage(@Param("id") Integer id);
UserPackage findPackage(@Param("id") Long id);
List<UserPackage> findUserPackages(@Param("userId") String userId, @Param("statuses") List<String> status); List<UserPackage> findUserPackages(@Param("userId") String userId, @Param("statuses") List<String> status);

View file

@ -2,21 +2,15 @@ package ru.nbch.credit_tracker.service;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.aop.framework.AopContext;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Service; 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.dto.UserPackage;
import ru.nbch.credit_tracker.service.signal.SignalRestService; import ru.nbch.credit_tracker.service.signal.SignalRestService;
import ru.nbch.credit_tracker.utils.CTUtil; import ru.nbch.credit_tracker.utils.CTUtil;
@Slf4j @Slf4j
@Service @Service
@EnableAspectJAutoProxy(exposeProxy = true)
public class MonitoringErrorProcesingService { public class MonitoringErrorProcesingService {
private MonitoringErrorProcesingService self;
private final SignalService signalService; private final SignalService signalService;
private final SignalRestService signalRestService; private final SignalRestService signalRestService;
@ -25,13 +19,8 @@ public class MonitoringErrorProcesingService {
this.signalRestService = signalRestService; this.signalRestService = signalRestService;
} }
public void findErrors(Long packageId) { public void findErrors(Integer packageId) {
UserPackage userPackage = signalService.findPackage(packageId); UserPackage userPackage = signalService.findPackage(packageId);
getCurrentProxy().processPackage(userPackage, packageId);
}
@SetAndClearMdc
public void processPackage(UserPackage userPackage, Long packageId) {
if (userPackage != null) { if (userPackage != null) {
String packCode = userPackage.getUserId().substring(0, 6) + "_" + userPackage.getPackageName(); String packCode = userPackage.getUserId().substring(0, 6) + "_" + userPackage.getPackageName();
log.info("Проверка на ошибки при постановке на мониторинг. packCode={}, packageId={}", packCode, packageId); log.info("Проверка на ошибки при постановке на мониторинг. packCode={}, packageId={}", packCode, packageId);
@ -49,11 +38,4 @@ public class MonitoringErrorProcesingService {
} }
} }
} }
private MonitoringErrorProcesingService getCurrentProxy() {
if (self == null) {
self = (MonitoringErrorProcesingService) AopContext.currentProxy();
}
return self;
}
} }

View file

@ -2,7 +2,6 @@ package ru.nbch.credit_tracker.service;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.slf4j.MDC;
import org.springframework.scheduling.concurrent.CustomizableThreadFactory; import org.springframework.scheduling.concurrent.CustomizableThreadFactory;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import ru.nbch.credit_tracker.config.settings.CreditTrackerSettings; import ru.nbch.credit_tracker.config.settings.CreditTrackerSettings;
@ -70,87 +69,81 @@ public class MonitoringService {
log.info("Start monitoring"); log.info("Start monitoring");
List<UserPackage> packages = signalService.findPackagesOnMonitoring(); List<UserPackage> packages = signalService.findPackagesOnMonitoring();
for (UserPackage userPackage : packages) { for (UserPackage userPackage : packages) {
MDC.put("ru.nbch.credit_tracker.log.mdc.key", "[" + userPackage.getPackageName() +"]"); log.info("Processing package {}", userPackage.getPackageName());
processPackage(userPackage); if (!signalService.hasNonNullfids(userPackage.getId())) {
MDC.remove("ru.nbch.credit_tracker.log.mdc.key"); log.info("Package {} has no fids. Package will be skipped.", userPackage.getPackageName());
} continue;
log.info("Finish monitoring"); }
} SgnlOnlineReport onlineReports = signalRestService.getReports(userPackage.getUserId().substring(0, 6) + "_" + userPackage.getPackageName());
private void processPackage(UserPackage userPackage) { if (onlineReports == null) {
log.info("Processing package"); log.info("No reports found for package {}", userPackage.getPackageName());
if (!signalService.hasNonNullfids(userPackage.getId())) { continue;
log.info("Package has no fids. Package will be skipped."); }
return;
}
SgnlOnlineReport onlineReports = signalRestService.getReports(userPackage.getUserId().substring(0, 6) + "_" + userPackage.getPackageName());
if (onlineReports == null) { // Формируем список имен файлов, по которым сработали сигналы. Сюда входят только те имена, у которых парсится дата формирования отчета
log.info("No reports found for package"); List<String> fileNames = onlineReports.getOperationReport().getReports().getReports().stream().map(Report::getFilename)
return; .filter(fileName -> CTUtil.parseDateFromReportFileName(fileName) != null).toList();
}
// Формируем список имен файлов, по которым сработали сигналы. Сюда входят только те имена, у которых парсится дата формирования отчета if (fileNames.isEmpty()) {
List<String> fileNames = onlineReports.getOperationReport().getReports().getReports().stream().map(Report::getFilename) log.info("No new report found for package {}", userPackage.getPackageName());
.filter(fileName -> CTUtil.parseDateFromReportFileName(fileName) != null).toList(); continue;
}
if (fileNames.isEmpty()) { LocalDateTime lastDownloadedReportDate = CTUtil.parseDateFromReportFileName(userPackage.getLastDownloadedReport());
log.info("No new report found for package");
return;
}
LocalDateTime lastDownloadedReportDate = CTUtil.parseDateFromReportFileName(userPackage.getLastDownloadedReport()); if (lastDownloadedReportDate != null) {
// Отбираем только те отчеты, дата которых позже даты последнего загруженного отчета
fileNames = fileNames.stream()
.filter(fileName -> CTUtil.parseDateFromReportFileName(fileName).isAfter(lastDownloadedReportDate)) // отфильтровали только те имена, которые парсятся в дату, так что null тут не придет
.sorted(Comparator.comparing(CTUtil::parseDateFromReportFileName)) // Сортируем по дате
.toList();
} else {
fileNames = fileNames.stream()
.sorted(Comparator.comparing(CTUtil::parseDateFromReportFileName)) // Сортируем по дате
.toList();
}
if (lastDownloadedReportDate != null) { if (StringUtils.isNotBlank(userPackage.getLastDownloadedReport())) {
// Отбираем только те отчеты, дата которых позже даты последнего загруженного отчета log.info("Last downloaded report found for package {}. Last Downloaded Report Name: {}", userPackage.getPackageName(), userPackage.getLastDownloadedReport());
fileNames = fileNames.stream() } else {
.filter(fileName -> CTUtil.parseDateFromReportFileName(fileName).isAfter(lastDownloadedReportDate)) // отфильтровали только те имена, которые парсятся в дату, так что null тут не придет log.info("Last Downloaded Report Name is empty");
.sorted(Comparator.comparing(CTUtil::parseDateFromReportFileName)) // Сортируем по дате }
.toList();
} else {
fileNames = fileNames.stream()
.sorted(Comparator.comparing(CTUtil::parseDateFromReportFileName)) // Сортируем по дате
.toList();
}
if (StringUtils.isNotBlank(userPackage.getLastDownloadedReport())) { for (String newReport : fileNames) {
log.info("Last downloaded report found for package. Last Downloaded Report Name: {}", userPackage.getLastDownloadedReport()); try {
} else { String loadedReportPath = null;
log.info("Last Downloaded Report Name is empty"); if (StringUtils.isBlank(userPackage.getLastDownloadedReport())) {
} loadedReportPath = loadReport(userPackage.getId(), newReport); // по этому пакету еще не было загруженных отчетов, продолжаем работу с полученным
} else if (lastDownloadedReportDate != null) {
for (String newReport : fileNames) { LocalDateTime newReportDate = CTUtil.parseDateFromReportFileName(newReport);
try { if (newReportDate.isAfter(lastDownloadedReportDate)) {
String loadedReportPath = null; loadedReportPath = loadReport(userPackage.getId(), newReport); // полученный отчет новее записанного в базу
if (StringUtils.isBlank(userPackage.getLastDownloadedReport())) { }
loadedReportPath = loadReport(userPackage.getId(), newReport); // по этому пакету еще не было загруженных отчетов, продолжаем работу с полученным }
} else if (lastDownloadedReportDate != null) { if (loadedReportPath == null) {
LocalDateTime newReportDate = CTUtil.parseDateFromReportFileName(newReport); log.info("Report was not loaded for package {}", userPackage.getPackageName());
if (newReportDate.isAfter(lastDownloadedReportDate)) { continue;
loadedReportPath = loadReport(userPackage.getId(), newReport); // полученный отчет новее записанного в базу
} }
}
if (loadedReportPath == null) {
log.info("Report was not loaded for package");
continue;
}
String responsePath = generateResponse(userPackage, loadedReportPath); String responsePath = generateResponse(userPackage, loadedReportPath);
String fileName = null; String fileName = null;
if (responsePath != null) { if (responsePath != null) {
fileName = createResponseFile(userPackage, responsePath); fileName = createResponseFile(userPackage, responsePath);
}
if (fileName != null) {
log.info("New report fileName: {}", fileName);
updateSignalHits(userPackage, responsePath, fileName);
signalService.updateLastDownloadedReport(userPackage.getId(), newReport);
} else {
log.info("Report was not created for package {}", userPackage.getPackageName());
}
} catch (Exception e) {
log.error(e.getMessage(), e);
} }
if (fileName != null) {
log.info("New report fileName: {}", fileName);
updateSignalHits(userPackage, responsePath, fileName);
signalService.updateLastDownloadedReport(userPackage.getId(), newReport);
} else {
log.info("Report was not created for package");
}
} catch (Exception e) {
log.error(e.getMessage(), e);
} }
} }
log.info("Finish monitoring");
} }
/** /**
@ -304,7 +297,7 @@ public class MonitoringService {
10000, 10000,
"/MonitoringSignalResponse/Subjects/Subject"); "/MonitoringSignalResponse/Subjects/Subject");
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException("Error while updating hits", e); throw new RuntimeException("Error while and updating hits", e);
} }
CTUtil.removeFile(responsePath); // удалям результирующий xml файл с диска (Он уже заархивирован к этому моменту) CTUtil.removeFile(responsePath); // удалям результирующий xml файл с диска (Он уже заархивирован к этому моменту)
@ -312,7 +305,7 @@ public class MonitoringService {
log.info("Signal hits updated for package {}", userPackage.getPackageName()); log.info("Signal hits updated for package {}", userPackage.getPackageName());
} }
public String loadReport(Long packageId, String reportName) { public String loadReport(Integer packageId, String reportName) {
return signalRestService.downloadReport(packageId, reportName); return signalRestService.downloadReport(packageId, reportName);
} }

View file

@ -16,8 +16,9 @@ import ru.nbch.credit_tracker.entities.external_response.monitoring_report_error
import ru.nbch.credit_tracker.entities.user_request.monitoring_request.MonitoringRequest; import ru.nbch.credit_tracker.entities.user_request.monitoring_request.MonitoringRequest;
import ru.nbch.credit_tracker.enums.Status; import ru.nbch.credit_tracker.enums.Status;
import ru.nbch.credit_tracker.service.indic.FlagsService; import ru.nbch.credit_tracker.service.indic.FlagsService;
import ru.nbch.credit_tracker.service.indic.FlagsService2;
import ru.nbch.credit_tracker.service.indic.PersonService; import ru.nbch.credit_tracker.service.indic.PersonService;
import ru.nbch.credit_tracker.service.indic.PhoneService; import ru.nbch.credit_tracker.service.indic.PhoneService2;
import ru.nbch.credit_tracker.service.signal.HitsService; import ru.nbch.credit_tracker.service.signal.HitsService;
import ru.nbch.credit_tracker.service.signal.PhoneFidMapService; import ru.nbch.credit_tracker.service.signal.PhoneFidMapService;
import ru.nbch.credit_tracker.service.signal.UserPackageService; import ru.nbch.credit_tracker.service.signal.UserPackageService;
@ -43,8 +44,8 @@ public class SignalService {
private final HitsService hitsService; private final HitsService hitsService;
private final UserPackageService userPackageService; private final UserPackageService userPackageService;
private final PhoneFidMapService phoneFidMapService; private final PhoneFidMapService phoneFidMapService;
private final PhoneService phoneService; private final PhoneService2 phoneService;
private final FlagsService flagsService; private final FlagsService2 flagsService;
private final PersonService personService; private final PersonService personService;
private final StaxXmlProcessor staxXmlProcessor; private final StaxXmlProcessor staxXmlProcessor;
@ -54,8 +55,8 @@ public class SignalService {
HitsService hitsService, HitsService hitsService,
UserPackageService userPackageService, UserPackageService userPackageService,
PhoneFidMapService phoneFidMapService, PhoneFidMapService phoneFidMapService,
PhoneService phoneService, PhoneService2 phoneService,
FlagsService flagsService, FlagsService2 flagsService,
PersonService personService, PersonService personService,
StaxXmlProcessor staxXmlProcessor) { StaxXmlProcessor staxXmlProcessor) {
this.config = config; this.config = config;
@ -72,7 +73,7 @@ public class SignalService {
return userPackageService.isPackageNotUnique(request.getUserId(), request.getPackageName()); return userPackageService.isPackageNotUnique(request.getUserId(), request.getPackageName());
} }
public Long registerPackage(MonitoringRequest request) { public Integer registerPackage(MonitoringRequest request) {
UserPackage userPackage = UserPackage.builder() UserPackage userPackage = UserPackage.builder()
.userId(request.getUserId()) .userId(request.getUserId())
.packageName(request.getPackageName()) .packageName(request.getPackageName())
@ -82,7 +83,7 @@ public class SignalService {
return userPackageService.insertPackage(userPackage); return userPackageService.insertPackage(userPackage);
} }
public void registerSubjects(String filePath, Long packageId) { public void registerSubjects(String filePath, Integer packageId) {
log.info("Start registering subjects. PackageId: {}", packageId); log.info("Start registering subjects. PackageId: {}", packageId);
try (BufferedInputStream in = new BufferedInputStream(Files.newInputStream(Path.of(filePath)), 8 * 1024 * 1024)) { try (BufferedInputStream in = new BufferedInputStream(Files.newInputStream(Path.of(filePath)), 8 * 1024 * 1024)) {
staxXmlProcessor.read(in, MonitoringRequest.class, staxXmlProcessor.read(in, MonitoringRequest.class,
@ -105,11 +106,12 @@ public class SignalService {
/** /**
* Обрабатывает все PhoneFidMap по packageId, выгружая их пачками и фильтруя по переданному фильтру * Обрабатывает все PhoneFidMap по packageId, выгружая их пачками и фильтруя по переданному фильтру
*/ */
public UpdateResult updateSubjects(Long packageId, Predicate<PhoneFidMap> filter) { public UpdateResult updateSubjects(Integer packageId, Predicate<PhoneFidMap> filter) {
boolean updated = false; boolean updated = false;
boolean noFidsFound = true; boolean noFidsFound = true;
int offset = 0; int offset = 0;
List<PhoneFidMap> batch; List<PhoneFidMap> batch;
Map<Integer, PhoneFidMap> phonesByFidToProcessing = new HashMap<>();
do { do {
batch = phoneFidMapService.findFullPhoneFidMap(packageId, offset, batchSize); batch = phoneFidMapService.findFullPhoneFidMap(packageId, offset, batchSize);
@ -125,15 +127,30 @@ public class SignalService {
} }
if (!filteredItems.isEmpty()) { if (!filteredItems.isEmpty()) {
boolean fidsFound = updateFlags(filteredItems, packageId); Map<Integer, PhoneFidMap> fidsMapByBatch = phoneService.defineFidByPhone(filteredItems);
if (noFidsFound) { log.debug("Actual fids size by phone's batch: {}", fidsMapByBatch.size());
noFidsFound = !fidsFound;
if (fidsMapByBatch.size() + phonesByFidToProcessing.size() <= batchSize) {
phonesByFidToProcessing.putAll(fidsMapByBatch);
} else {
boolean fidsFound = updateFlags(phonesByFidToProcessing, packageId);
if (noFidsFound) {
noFidsFound = !fidsFound;
}
phonesByFidToProcessing = new HashMap<>(fidsMapByBatch);
} }
} }
offset += batchSize; offset += batchSize;
} while (!batch.isEmpty()); } while (!batch.isEmpty());
if (!phonesByFidToProcessing.isEmpty()) {
boolean fidsFound = updateFlags(phonesByFidToProcessing, packageId);
if (noFidsFound) {
noFidsFound = !fidsFound;
}
}
return new UpdateResult(updated, noFidsFound); return new UpdateResult(updated, noFidsFound);
} }
@ -141,50 +158,37 @@ public class SignalService {
* Обрабатывает пачку PhoneFidMap: обновляет фиды и флаги просрочек * Обрабатывает пачку PhoneFidMap: обновляет фиды и флаги просрочек
* @return fidsFound true - если хотя бы по одному номеру был найден фид, false - в противном случае * @return fidsFound true - если хотя бы по одному номеру был найден фид, false - в противном случае
*/ */
public boolean updateFlags(List<PhoneFidMap> subjects, Long packageId) { // TODO Добавить отдельный метод. Если не нашелся фид, надо обновить в базе fid = null и флаги = null calculated_at=now()
log.info("Start calculating fids and flags for subjects batch. PackageId: {}", packageId); public boolean updateFlags(Map<Integer, PhoneFidMap> fidsMap, Integer packageId) {
Map<String, Integer> fidsMap = phoneService.findFidsPhoneData(subjects); log.info("Start calculating flags for subjects batch. PackageId: {}", packageId);
LocalDateTime calculatedAt = LocalDateTime.now(); Map<Integer, FlagsService.FlagsData> flagsDataMap = flagsService.findFlags(fidsMap.keySet().stream().toList());
boolean fidsFound = fidsMap.values().stream().anyMatch(Objects::nonNull);
Map<Integer, FlagsService.FlagsData> flagsDataMap = flagsService.findFlags(fidsMap.values().stream().filter(Objects::nonNull).toList());
List<PhoneFidMap> subjectToUpdate = new ArrayList<>(); 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 -> { phoneFidMap.setFid(fid);
Integer fid = fidsMap.get("9" + subject.getPhone()); phoneFidMap.setFlagDebt5000(flagsData.getFlagDebt5000());
if (fid != null) { phoneFidMap.setFlag90days(flagsData.getFlag90days());
FlagsService.FlagsData flagsData = flagsDataMap.get(fid); phoneFidMap.setCalculatedAt(LocalDateTime.now());
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);
});
int updateBatchSize = batchSize / 9; // 9 параметров на одну запись для обновления. Максимальное количество параметров ~65000 subjectToUpdate.add(phoneFidMap);
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.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) { public void rejectPackage(MonitoringRequest request) {
@ -197,20 +201,16 @@ public class SignalService {
userPackageService.insertPackage(userPackage); userPackageService.insertPackage(userPackage);
} }
public void updatePackageStatus(Long packageId, Status status) { public void updatePackageStatus(Integer packageId, Status status) {
userPackageService.updatePackageStatus(packageId, status); userPackageService.updatePackageStatus(packageId, status);
} }
public void updatePackageOnMonitoringSuccess(Long packageId, LocalDateTime monitoringAt) {
userPackageService.updatePackageOnMonitoringSuccess(packageId, monitoringAt);
}
/** /**
* Формирует файл запроса в официальный сервис сигналов, выгружая паспортные данные пачками и сразу записывая их в файл * Формирует файл запроса в официальный сервис сигналов, выгружая паспортные данные пачками и сразу записывая их в файл
* *
* @return Путь к файлу запроса к официальному сервису сигналов на постановку на мониторинг * @return Путь к файлу запроса к официальному сервису сигналов на постановку на мониторинг
*/ */
public String writeSignals(Long packageId) { public String writeSignals(Integer packageId) {
String folderPath = config.getLoadDir() + File.separator + "tmp_folder" + File.separator; String folderPath = config.getLoadDir() + File.separator + "tmp_folder" + File.separator;
String filePath = folderPath + UUID.randomUUID() + "_output.xml"; String filePath = folderPath + UUID.randomUUID() + "_output.xml";
CTUtil.createFolder(folderPath); CTUtil.createFolder(folderPath);
@ -226,7 +226,7 @@ public class SignalService {
return filePath; return filePath;
} }
private Signals buildSignals(Long packageId) { private Signals buildSignals(Integer packageId) {
UserPackage userPackage = userPackageService.findPackage(packageId); UserPackage userPackage = userPackageService.findPackage(packageId);
Signals signals = new Signals(); Signals signals = new Signals();
signals.setOnline("2"); // Всегда=2 (только онлайн-сигналы) signals.setOnline("2"); // Всегда=2 (только онлайн-сигналы)
@ -242,7 +242,7 @@ public class SignalService {
* Обрабатывает все PhoneFidMap по packageId, выгружая их пачками и фильтруя по переданному фильтру. * Обрабатывает все PhoneFidMap по packageId, выгружая их пачками и фильтруя по переданному фильтру.
* writer записывает пачку сразу в файл * writer записывает пачку сразу в файл
*/ */
private void buildPersons(Long packageId, Consumer<List<Person>> writer) { private void buildPersons(Integer packageId, Consumer<List<Person>> writer) {
int offset = 0; int offset = 0;
List<PhoneFidMap> batch; List<PhoneFidMap> batch;
do { do {
@ -282,14 +282,14 @@ public class SignalService {
return persons; return persons;
} }
public void updateSubjectsErrors(String filePath, Long packageId) { public void updateSubjectsErrors(String filePath, Integer packageId) {
log.info("Start updating subjects errors. PackageId: {}", packageId); log.info("Start updating subjects errors. PackageId: {}", packageId);
try (BufferedInputStream in = new BufferedInputStream(Files.newInputStream(Path.of(filePath)), 8 * 1024 * 1024)) { try (BufferedInputStream in = new BufferedInputStream(Files.newInputStream(Path.of(filePath)), 8 * 1024 * 1024)) {
staxXmlProcessor.read(in, MonitoringReportErrors.class, staxXmlProcessor.read(in, MonitoringReportErrors.class,
request -> { request -> {
if (!request.getPersons().isEmpty()) { if (!request.getPersons().isEmpty()) {
List<PhoneFidMap> subjects = request.getPersons().stream().map(p -> { List<PhoneFidMap> subjects = request.getPersons().stream().map(p -> {
log.warn("Ошибка при формировании субъекта. package_id = {}, id = {}. Описание: {}", packageId, p.getUid(), p.getValue()); log.error("Ошибка при формировании субъекта. package_id = {}, id = {}. Описание: {}", packageId, p.getUid(), p.getValue());
return PhoneFidMap.builder() return PhoneFidMap.builder()
.id(p.getUid()) .id(p.getUid())
.errorText(p.getValue()) .errorText(p.getValue())
@ -341,19 +341,19 @@ public class SignalService {
hitsService.insertHits(hits); hitsService.insertHits(hits);
} }
public void updateLastDownloadedReport(Long id, String fileName) { public void updateLastDownloadedReport(Integer id, String fileName) {
userPackageService.updatePackageLastDownloadedReport(id, fileName); userPackageService.updatePackageLastDownloadedReport(id, fileName);
} }
public PhoneFidMap findPhoneFidMapById(Long id) { public PhoneFidMap findPhoneFidMapById(Integer id) {
return phoneFidMapService.findPhoneFidMapById(id); return phoneFidMapService.findPhoneFidMapById(id);
} }
public boolean hasNonNullfids(Long packageId) { public boolean hasNonNullfids(Integer packageId) {
return phoneFidMapService.hasNonNullfids(packageId); return phoneFidMapService.hasNonNullfids(packageId);
} }
public UserPackage findPackage(Long packageId) { public UserPackage findPackage(Integer packageId) {
return userPackageService.findPackage(packageId); return userPackageService.findPackage(packageId);
} }

View file

@ -1,10 +1,7 @@
package ru.nbch.credit_tracker.service; package ru.nbch.credit_tracker.service;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.framework.AopContext;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Service; 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.dto.UserPackage;
import ru.nbch.credit_tracker.enums.Status; import ru.nbch.credit_tracker.enums.Status;
import ru.nbch.credit_tracker.service.signal.SignalRestService; import ru.nbch.credit_tracker.service.signal.SignalRestService;
@ -17,11 +14,8 @@ import java.util.List;
*/ */
@Slf4j @Slf4j
@Service @Service
@EnableAspectJAutoProxy(exposeProxy = true)
public class UpdateProcessingPackagesService { public class UpdateProcessingPackagesService {
private UpdateProcessingPackagesService self;
private final SignalService signalService; private final SignalService signalService;
private final SignalRestService signalRestService; private final SignalRestService signalRestService;
private final MonitoringErrorProcesingService monitoringErrorProcesingService; private final MonitoringErrorProcesingService monitoringErrorProcesingService;
@ -39,26 +33,25 @@ public class UpdateProcessingPackagesService {
List<UserPackage> packages = signalService.findPackagesOnProcessing(); List<UserPackage> packages = signalService.findPackagesOnProcessing();
for (UserPackage userPackage : packages) { for (UserPackage userPackage : packages) {
log.info("Updating flags for Package {}", userPackage.getPackageName()); log.info("Updating flags for Package {}", userPackage.getPackageName());
getCurrentProxy().process(userPackage.getId(), userPackage.getPackageName()); process(userPackage.getId(), userPackage.getPackageName());
} }
log.info("Finish updating processing packages"); log.info("Finish updating processing packages");
} }
@SetAndClearMdc(argIndex = 1) private void process(Integer packageId, String packageName) {
public void process(Long packageId, String packageName) {
try { try {
SignalService.UpdateResult updateResult = signalService.updateSubjects(packageId, null); SignalService.UpdateResult updateResult = signalService.updateSubjects(packageId, null);
if (updateResult.isNoFidsFound()) { if (updateResult.isNoFidsFound()) {
log.info("No fids found for PackageId: {} . Package status ON_MONITORING. No registration in signal service.", packageId); 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); signalService.updatePackageStatus(packageId, Status.ON_MONITORING);
return; return;
} }
if (signalRestService.setupMonitoring(packageId)) { if (signalRestService.setupMonitoring(packageId)) {
log.info("Package was successfully registered in signal service. PackageId: {}", packageId); log.info("Package was successfully registered in signal service. PackageId: {} PackageName: {}", packageId, packageName);
signalService.updatePackageStatus(packageId, Status.ON_MONITORING); signalService.updatePackageStatus(packageId, Status.ON_MONITORING);
monitoringErrorProcesingService.findErrors(packageId); monitoringErrorProcesingService.findErrors(packageId);
} else { } else {
log.info("Package registration was rejected by signal service. PackageId: {}", packageId); log.info("Package registration was rejected by signal service. PackageId: {} PackageName: {}", packageId, packageName);
signalService.updatePackageStatus(packageId, Status.ERROR); signalService.updatePackageStatus(packageId, Status.ERROR);
} }
} catch (Exception e) { } catch (Exception e) {
@ -66,11 +59,4 @@ public class UpdateProcessingPackagesService {
log.error("Ошибка при поиске фидов или расчете флагов просрочек. Package_id {}", packageId, e); log.error("Ошибка при поиске фидов или расчете флагов просрочек. Package_id {}", packageId, e);
} }
} }
private UpdateProcessingPackagesService getCurrentProxy() {
if (self == null) {
self = (UpdateProcessingPackagesService) AopContext.currentProxy();
}
return self;
}
} }

View file

@ -1,18 +1,21 @@
package ru.nbch.credit_tracker.service; package ru.nbch.credit_tracker.service;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.concurrent.CustomizableThreadFactory;
import org.springframework.stereotype.Service;
import ru.nbch.credit_tracker.config.settings.CreditTrackerSettings;
import ru.nbch.credit_tracker.entities.dto.PhoneFidMap;
import ru.nbch.credit_tracker.entities.dto.UserPackage;
import ru.nbch.credit_tracker.enums.Status;
import ru.nbch.credit_tracker.service.signal.SignalRestService;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit; import java.time.temporal.ChronoUnit;
import java.util.List; import java.util.List;
import lombok.extern.slf4j.Slf4j; import java.util.concurrent.Executors;
import org.slf4j.MDC; import java.util.concurrent.ScheduledExecutorService;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; import java.util.concurrent.TimeUnit;
import org.springframework.scheduling.support.CronTrigger; import java.util.function.Predicate;
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;
/** /**
* Сервис периодического обновления фидов и флагов просрочек по пакетам находящимся в статусе ON_MONITORING * Сервис периодического обновления фидов и флагов просрочек по пакетам находящимся в статусе ON_MONITORING
@ -26,56 +29,43 @@ public class UpdatingFlagsService {
private final SignalRestService signalRestService; private final SignalRestService signalRestService;
private final MonitoringErrorProcesingService monitoringErrorProcesingService; private final MonitoringErrorProcesingService monitoringErrorProcesingService;
private final Long daysToUpdateFlags; private final Long daysToUpdateFlags;
private final String updateFlagsCronExpression; private final Long updateFlagsInterval;
private final PhoneFidMapMapper phoneFidMapMapper;
public UpdatingFlagsService(SignalService signalService, public UpdatingFlagsService(SignalService signalService,
SignalRestService signalRestService, SignalRestService signalRestService,
MonitoringErrorProcesingService monitoringErrorProcesingService, MonitoringErrorProcesingService monitoringErrorProcesingService,
CreditTrackerSettings config, CreditTrackerSettings config) {
PhoneFidMapMapper phoneFidMapMapper) {
this.signalService = signalService; this.signalService = signalService;
this.signalRestService = signalRestService; this.signalRestService = signalRestService;
this.monitoringErrorProcesingService = monitoringErrorProcesingService; this.monitoringErrorProcesingService = monitoringErrorProcesingService;
this.daysToUpdateFlags = config.getDaysToUpdateFlags() != null ? config.getDaysToUpdateFlags() : 1; this.daysToUpdateFlags = config.getDaysToUpdateFlags() != null ? config.getDaysToUpdateFlags() : 1;
this.updateFlagsCronExpression = config.getUpdateFlagsCronExpression() != null ? config.getUpdateFlagsCronExpression() : "*/10 * * * * *"; this.updateFlagsInterval = config.getUpdateFlagsInterval() != null ? config.getUpdateFlagsInterval() : 1;
this.phoneFidMapMapper = phoneFidMapMapper;
} }
public void run() { public void run() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(new CustomizableThreadFactory("updating-flags-"));
scheduler.setPoolSize(1); scheduler.scheduleAtFixedRate(this::update, 0, updateFlagsInterval, TimeUnit.DAYS);
scheduler.initialize();
scheduler.schedule(this::update, new CronTrigger(updateFlagsCronExpression));
} }
public void update() { private void update() {
log.info("Start updating flags"); log.info("Start updating flags");
List<UserPackage> packages = signalService.findPackagesOnMonitoring(); List<UserPackage> packages = signalService.findPackagesOnMonitoring();
for (UserPackage userPackage : packages) { for (UserPackage userPackage : packages) {
MDC.put("ru.nbch.credit_tracker.log.mdc.key", "[" + userPackage.getPackageName() +"]"); log.info("Updating flags for Package {}", userPackage.getPackageName());
log.info("Check on updating flags for Package {}", userPackage.getPackageName()); updateFlags(userPackage.getId(), 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);
}
MDC.remove("ru.nbch.credit_tracker.log.mdc.key");
} }
log.info("Finish updating flags"); log.info("Finish updating flags");
} }
private void updateFlags(UserPackage userPackage) { private void updateFlags(Integer packageId, String packageName) {
Long packageId = userPackage.getId();
String packageName = userPackage.getPackageName();
try { try {
signalService.updatePackageStatus(packageId, Status.PROCESSING); 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 (updateResult.isUpdated()) {
if (signalRestService.setupMonitoring(packageId)) { if (signalRestService.setupMonitoring(packageId)) {
LocalDateTime monitoringAt = userPackage.getMonitoringAt() != null ? userPackage.getMonitoringAt() : LocalDateTime.now(); log.info("Flags were updated successfully. PackageId: {} PackageName: {}. Package status changed to ON_MONITORING", packageId, packageName);
log.info("Flags were updated successfully. PackageId: {} PackageName: {} MonitoringAt: {}. Package status changed to ON_MONITORING", packageId, packageName, monitoringAt); signalService.updatePackageStatus(packageId, Status.ON_MONITORING);
signalService.updatePackageOnMonitoringSuccess(packageId, monitoringAt);
monitoringErrorProcesingService.findErrors(packageId); monitoringErrorProcesingService.findErrors(packageId);
} else { } else {
log.info("Updated package registration was rejected by signal service. PackageId: {} PackageName: {}", packageId, packageName); log.info("Updated package registration was rejected by signal service. PackageId: {} PackageName: {}", packageId, packageName);

View file

@ -2,19 +2,22 @@ package ru.nbch.credit_tracker.service.indic;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter; import lombok.Setter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import ru.nbch.credit_tracker.mapper.indic.FlagsMapper; import ru.nbch.credit_tracker.mapper.indic.FlagsMapper;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.HashMap; import java.util.*;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Service @Service
public class FlagsService { public class FlagsService {
private static final Logger log = LoggerFactory.getLogger(FlagsService.class);
private final FlagsMapper mapper; private final FlagsMapper mapper;
public FlagsService(FlagsMapper mapper) { public FlagsService(FlagsMapper mapper) {
@ -25,11 +28,19 @@ public class FlagsService {
if (fids.isEmpty()) { if (fids.isEmpty()) {
return new HashMap<>(); return new HashMap<>();
} }
Map<Integer, FlagsData> result = new HashMap<>(); List<Integer> distinctFids = fids.stream()
Map<Integer, Integer> flags5000 = calcFlag5000(fids); .distinct()
Map<Integer, Integer> flags90DaysByPastDueArrear = calcFlag90DaysByPastDueArrear(fids); .sorted(Comparator.naturalOrder())
Map<Integer, Integer> flags90DaysByRuPaymt = calcFlag90DaysByRuPaymt(fids); .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 -> { fids.forEach(fid -> {
Integer flagDebt5000 = flags5000.get(fid); Integer flagDebt5000 = flags5000.get(fid);
if (flagDebt5000 == null) { if (flagDebt5000 == null) {
@ -37,7 +48,7 @@ public class FlagsService {
} }
Integer flag90days = flags90DaysByPastDueArrear.get(fid); Integer flag90days = flags90DaysByPastDueArrear.get(fid);
if (flag90days == null) { if (flag90days == null || flag90days == 0) {
flag90days = flags90DaysByRuPaymt.get(fid); flag90days = flags90DaysByRuPaymt.get(fid);
if (flag90days == null) { if (flag90days == null) {
flag90days = 0; flag90days = 0;
@ -50,51 +61,118 @@ public class FlagsService {
} }
/** /**
* Выбирает данные из RU_PASTDUEARREAR и ищет по ним флаги просрочек
* По FID из RU_PASTDUEARREAR выбираются все уникальные ACC_SERIAL_NUM с максимальной датой REPORTED_DT. * По FID из RU_PASTDUEARREAR выбираются все уникальные ACC_SERIAL_NUM с максимальной датой REPORTED_DT.
* Если по хотя бы одной такой записи AMT_PAST_DUE > 5000, то флаг устанавливается в 1. * Если по хотя бы одной такой записи AMT_PAST_DUE > 5000, то flag500 устанавливается в 1.
* Если ни по одному счёту условие не выполняется или счетов вообще не найдено, флаг = 0. * Если ни по одному счёту условие не выполняется или счетов вообще не найдено, флаг = 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) { private void findFlagsRuPastDueArrear(List<Integer> fids, Map<Integer, Integer> flag5000Map, Map<Integer, Integer> flag90Days) {
return mapper.findFlagsDebt5000(fids).stream() log.debug("Start calculating flag 5000 and 90 days by RU_PASTDUEARREAR");
.collect(Collectors.toMap(FlagFidData::getFid, FlagFidData::getFlag, List<RuPastDuarearData> ruPastDuarearDataList = mapper.findRuPastDuarearData(fids);
(existing, replacement) -> {
if (existing == 1) { ruPastDuarearDataList.sort(Comparator.comparing(FlagsService.RuPastDuarearData::getFid) // сортируем сначала по фиду
return existing; .thenComparing(FlagsService.RuPastDuarearData::getReportedDate, Comparator.reverseOrder())); // Затем сортируем по REPORTED_DT по убыванию
} else {
return replacement; 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, если выполняется хотя бы одно из условий: * Выбирает данные из RU_PAYMT и ищет по ним флаги просрочек 90Days
* В INDIC.RU_PASTDUEARREAR или RU_PAYMT у данного FID есть запись с DAYS_PAST_DUE 90 и REPORTED_DT >= текущая дата 12 месяцев. *
* flag90Days = 1, если выполняется хотя бы одно из условий:
* В INDIC.RU_PAYMT у данного FID есть запись с DAYS_PAST_DUE 90 и REPORTED_DT >= текущая дата 12 месяцев.
* Если ни по одной записи условия не выполняются или записей вообще не найдено, флаг = 0. * Если ни по одной записи условия не выполняются или записей вообще не найдено, флаг = 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); LocalDate yearAgo = LocalDate.now().minusYears(1);
return mapper.findFlag90ByPastDueArrear(fids, yearAgo).stream() List<RuPaymtData> ruPaymtDataList = mapper.findRuPaymtData(fids, yearAgo);
.collect(Collectors.toMap(FlagFidData::getFid, FlagFidData::getFlag, ruPaymtDataList.sort(Comparator.comparing(RuPaymtData::getFid, Comparator.naturalOrder()));
(existing, replacement) -> {
if (existing == 1) {
return existing;
} else {
return replacement;
}
}));
}
private Map<Integer, Integer> calcFlag90DaysByRuPaymt(List<Integer> fids) { for (Integer fid : fids) {
LocalDate yearAgo = LocalDate.now().minusYears(1); Iterator<RuPaymtData> iterator = ruPaymtDataList.iterator();
return mapper.findFlag90ByRuPaymt(fids, yearAgo).stream() while (iterator.hasNext()) {
.collect(Collectors.toMap(FlagFidData::getFid, FlagFidData::getFlag, RuPaymtData data = iterator.next();
(existing, replacement) -> { if (fid.equals(data.getFid())) {
if (existing == 1) { Integer flag90 = flag90Days.get(fid);
return existing; 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 { } else {
return replacement; flag90Days.put(fid, 0);
iterator.remove();
} }
})); } else {
iterator.remove();
}
}
}
}
flag90Days.putIfAbsent(fid, 0);
}
log.debug("Complete calculating flag 90 days by RU_PAPAYMT");
} }
@Getter @Getter
@ -107,8 +185,23 @@ public class FlagsService {
@Getter @Getter
@Setter @Setter
public static class FlagFidData { @AllArgsConstructor
@NoArgsConstructor
public static class RuPastDuarearData {
private Integer fid; 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;
} }
} }

View file

@ -0,0 +1,256 @@
package ru.nbch.credit_tracker.service.indic;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.time.LocalDate;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Slf4j
@Service
public class FlagsService2 {
private static final String SELECT_RU_PASDUEARREAR_DATA =
"SELECT FID, ACC_SERIAL_NUM, REPORTED_DT, AMT_PAST_DUE, DAYS_PAST_DUE " +
"FROM RU_PASTDUEARREAR " +
"WHERE FID IN (:fids) " +
"ORDER BY FID, REPORTED_DT DESC " +
"WITH UR";
private static final String SELECT_RU_PAYMT_DATA =
"SELECT FID, DAYS_PAST_DUE, REPORTED_DT " +
"FROM RU_PAYMT " +
"WHERE FID IN (:fids) " +
"AND REPORTED_DT >= :reportedDate " +
"WITH UR";
private final NamedParameterJdbcTemplate jdbcTemplate;
public FlagsService2(@Qualifier("jdbcTemplateIndic") NamedParameterJdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public Map<Integer, FlagsService.FlagsData> findFlags(List<Integer> fids) {
if (fids.isEmpty()) {
return new HashMap<>();
}
List<Integer> distinctFids = fids.stream()
.distinct()
.sorted(Comparator.naturalOrder())
.toList();
log.debug("Fids count: {}", distinctFids.size());
Map<Integer, Integer> flags5000 = new HashMap<>();
Map<Integer, Integer> flags90DaysByPastDueArrear = new HashMap<>();
Map<Integer, Integer> flags90DaysByRuPaymt = new HashMap<>();
findFlagsRuPastDueArrear(distinctFids, flags5000, flags90DaysByPastDueArrear);
findFlagsRuPaymt(distinctFids, flags90DaysByRuPaymt);
Map<Integer, FlagsService.FlagsData> result = new HashMap<>();
fids.forEach(fid -> {
Integer flagDebt5000 = flags5000.get(fid);
if (flagDebt5000 == null) {
flagDebt5000 = 0;
}
Integer flag90days = flags90DaysByPastDueArrear.get(fid);
if (flag90days == null || flag90days == 0) {
flag90days = flags90DaysByRuPaymt.get(fid);
if (flag90days == null) {
flag90days = 0;
}
}
result.put(fid, new FlagsService.FlagsData(flagDebt5000, flag90days));
});
return result;
}
/**
* Выбирает данные из RU_PASTDUEARREAR и ищет по ним флаги просрочек
* По FID из RU_PASTDUEARREAR выбираются все уникальные ACC_SERIAL_NUM с максимальной датой REPORTED_DT.
* Если по хотя бы одной такой записи AMT_PAST_DUE > 5000, то flag500 устанавливается в 1.
* Если ни по одному счёту условие не выполняется или счетов вообще не найдено, флаг = 0.
* <p>
* flag90Days = 1, если выполняется хотя бы одно из условий:
* В INDIC.RU_PASTDUEARREAR у данного FID есть запись с DAYS_PAST_DUE 90 и REPORTED_DT >= текущая дата 12 месяцев.
* Если ни по одной записи условия не выполняются или записей вообще не найдено, флаг = 0.
*
* @param fids - отсортирован по возрастанию фидов
* @param flag5000Map - рещультирующая мапа флагов просрочек debt5000
* @param flag90Days - рещультирующая мапа флагов просрочек 90_days
*/
private void findFlagsRuPastDueArrear(List<Integer> fids, Map<Integer, Integer> flag5000Map, Map<Integer, Integer> flag90Days) {
log.debug("Start calculating flag 5000 and 90 days by RU_PASTDUEARREAR");
MapSqlParameterSource params = new MapSqlParameterSource();
params.addValue("fids", fids);
jdbcTemplate.query(SELECT_RU_PASDUEARREAR_DATA, params, (ResultSet rs) -> {
Integer currentFid = null;
BigInteger prevAccSerialNum = null;
boolean processed5000ForCurrentId = false;
boolean processed90DaysForCurrentId = false;
LocalDate yearAgo = LocalDate.now().minusYears(1);
while (rs.next()) {
Integer fid = rs.getInt("FID");
// Переход к новому fid. Сброс состояния
if (!fid.equals(currentFid)) {
currentFid = fid;
prevAccSerialNum = null;
processed5000ForCurrentId = false;
processed90DaysForCurrentId = false;
// Инициализируем значения по умолчанию
flag5000Map.putIfAbsent(fid, 0);
flag90Days.putIfAbsent(fid, 0);
}
// Если для текущего FID уже обработали оба флага, пропускаем остальные записи
if (processed5000ForCurrentId && processed90DaysForCurrentId) {
continue;
}
// Обработка флага 5000
if (!processed5000ForCurrentId && flag5000Map.get(fid) == 0) {
BigInteger currentAccSerialNum = getBigInteger(rs, "ACC_SERIAL_NUM");
BigDecimal amtPastDue = getBigDecimal(rs, "AMT_PAST_DUE");
if (prevAccSerialNum == null || !prevAccSerialNum.equals(currentAccSerialNum)) {
if (amtPastDue != null && amtPastDue.compareTo(BigDecimal.valueOf(5000)) >= 0) {
flag5000Map.put(fid, 1);
processed5000ForCurrentId = true;
}
prevAccSerialNum = currentAccSerialNum;
}
}
// Обработка флага 90 дней
if (!processed90DaysForCurrentId) {
Integer currentFlag90 = flag90Days.get(fid);
if (currentFlag90 != null && currentFlag90 == 1) {
processed90DaysForCurrentId = true;
continue;
}
if (currentFlag90 == null || currentFlag90 == 0) {
BigInteger days = getBigInteger(rs, "DAYS_PAST_DUE");
LocalDate reportedDate = rs.getObject("REPORTED_DT", LocalDate.class);
if (days != null) {
boolean has90daysFlag = days.compareTo(BigInteger.valueOf(90)) >= 0
&& reportedDate != null && !reportedDate.isBefore(yearAgo);
if (has90daysFlag) {
flag90Days.put(fid, 1);
processed90DaysForCurrentId = true;
} else {
flag90Days.put(fid, 0);
}
} else {
flag90Days.put(fid, 0);
}
}
}
}
return null;
});
log.debug("Complete calculating flag 5000 and 90 days by RU_PASTDUEARREAR");
}
/**
* Выбирает данные из RU_PAYMT и ищет по ним флаги просрочек 90Days
* <p>
* flag90Days = 1, если выполняется хотя бы одно из условий:
* В INDIC.RU_PAYMT у данного FID есть запись с DAYS_PAST_DUE 90 и REPORTED_DT >= текущая дата 12 месяцев.
* Если ни по одной записи условия не выполняются или записей вообще не найдено, флаг = 0.
*
* @param fids - отсортирован по возрастанию фидов
* @param flag90Days - рещультирующая мапа флагов просрочек 90_days
*/
private void findFlagsRuPaymt(List<Integer> fids, Map<Integer, Integer> flag90Days) {
log.debug("Start calculating flag 90 days by RU_PAPAYMT");
LocalDate yearAgo = LocalDate.now().minusYears(1);
MapSqlParameterSource params = new MapSqlParameterSource();
params.addValue("fids", fids);
params.addValue("reportedDate", yearAgo);
jdbcTemplate.query(SELECT_RU_PAYMT_DATA, params, rs -> {
Integer currentFid = null;
boolean processed90DaysForCurrentId = false;
while (rs.next()) {
Integer fid = rs.getInt("FID");
// Переход к новому fid. Сброс состояния
if (!fid.equals(currentFid)) {
currentFid = fid;
processed90DaysForCurrentId = false;
// Инициализируем значения по умолчанию
flag90Days.putIfAbsent(fid, 0);
}
// Обработка флага 90 дней
if (!processed90DaysForCurrentId) {
Integer currentFlag90 = flag90Days.get(fid);
if (currentFlag90 != null && currentFlag90 == 1) {
processed90DaysForCurrentId = true;
continue;
}
if (currentFlag90 == null || currentFlag90 == 0) {
BigInteger days = getBigInteger(rs, "DAYS_PAST_DUE");
LocalDate date = rs.getObject("REPORTED_DT", LocalDate.class);
if (days != null) {
boolean has90daysFlag = days.compareTo(BigInteger.valueOf(90)) >= 0
&& date != null && !date.isBefore(yearAgo);
if (has90daysFlag) {
flag90Days.put(fid, 1);
processed90DaysForCurrentId = true;
} else {
flag90Days.put(fid, 0);
}
} else {
flag90Days.put(fid, 0);
}
}
}
}
return null;
});
log.debug("Complete calculating flag 90 days by RU_PAPAYMT");
}
private BigInteger getBigInteger(ResultSet rs, String columnName) {
try {
String value = rs.getString(columnName);
return value != null ? new BigInteger(value) : null;
} catch (SQLException e) {
return null;
}
}
private BigDecimal getBigDecimal(ResultSet rs, String columnName) {
try {
return rs.getBigDecimal(columnName);
} catch (SQLException e) {
return null;
}
}
}

View file

@ -1,18 +1,18 @@
package ru.nbch.credit_tracker.service.indic; package ru.nbch.credit_tracker.service.indic;
import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter; import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import ru.nbch.credit_tracker.entities.dto.PhoneFidMap; import ru.nbch.credit_tracker.entities.dto.PhoneFidMap;
import ru.nbch.credit_tracker.mapper.indic.PhoneMapper; import ru.nbch.credit_tracker.mapper.indic.PhoneMapper;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.Comparator; import java.util.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Slf4j
@Service @Service
public class PhoneService { public class PhoneService {
@ -24,32 +24,45 @@ public class PhoneService {
/** /**
* Ищет фиды по номерам телефона * Ищет фиды по номерам телефона
* @return Map: key - номер телефона; value - fid * @return Map: key - fid; value - {@link PhoneFidMap}
*/ */
public Map<String, Integer> findFidsPhoneData(List<PhoneFidMap> subjects) { public Map<Integer, PhoneFidMap> defineFidByPhone(List<PhoneFidMap> subjects) {
List<String> phones = subjects.stream().map(subject -> "9" + subject.getPhone()).toList(); Map<Integer, PhoneFidMap> phoneFidMap = new HashMap<>();
return convertFidsDataToMap(mapper.findFids(phones)); List<String> phones = subjects.stream().
} map(subject -> "9" + subject.getPhone())
.toList();
private Map<String, Integer> convertFidsDataToMap(List<FidsData> fidsData) { subjects.sort(
if (fidsData == null || fidsData.isEmpty()) { Comparator.comparing(
return new HashMap<>(); PhoneFidMap::getPhone,
Comparator.naturalOrder()
)
);
//отсортированный список по NUMBER, FID и FILE_SINCE_DT
List<FidsData> phonesByFid = mapper.findFids(phones);
log.debug("Start merging fids");
for (PhoneFidMap phoneFid : subjects) {
Iterator<FidsData> iterator = phonesByFid.iterator();
while (iterator.hasNext()) {
FidsData fidsData = iterator.next();
if (fidsData.getPhone().equals(9 + phoneFid.getPhone())) {
//fixme доступ по ссылке, страшно, придумать что-то еще
phoneFidMap.put(fidsData.getFid(), phoneFid);
iterator.remove();
break;
}
}
} }
return fidsData.stream() log.debug("End merging fids");
.collect(Collectors.groupingBy(
FidsData::getPhone, return phoneFidMap;
Collectors.collectingAndThen(
Collectors.maxBy(
Comparator.comparing(FidsData::getFileSinceDate)
.thenComparing(FidsData::getFid)
),
data -> data.map(FidsData::getFid).orElse(null)
)
));
} }
@Getter @Getter
@Setter @Setter
@AllArgsConstructor
@NoArgsConstructor
public static class FidsData { public static class FidsData {
private Integer fid; private Integer fid;
private String phone; private String phone;

View file

@ -0,0 +1,96 @@
package ru.nbch.credit_tracker.service.indic;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Service;
import ru.nbch.credit_tracker.entities.dto.PhoneFidMap;
import java.time.LocalDate;
import java.util.*;
@Slf4j
@Service
public class PhoneService2 {
private static final String SELCT_FIDS = """
SELECT
pn.number,
pn.fid,
pn.file_since_dt
FROM PHONE_NORM pn
JOIN (SELECT DISTINCT fid FROM NAME) nf
ON nf.fid = pn.fid
WHERE number in (:phones)
ORDER BY
pn.number ASC,
pn.file_since_dt DESC,
pn.fid DESC
WITH UR
""";
private final NamedParameterJdbcTemplate jdbcTemplate;
public PhoneService2(@Qualifier("jdbcTemplateIndic") NamedParameterJdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
/**
* Ищет фиды по номерам телефона
*
* @return Map: key - fid; value - {@link PhoneFidMap}
*/
public Map<Integer, PhoneFidMap> defineFidByPhone(List<PhoneFidMap> subjects) {
Map<Integer, PhoneFidMap> phoneFidMap = new HashMap<>();
List<String> phones = subjects.stream().
map(subject -> "9" + subject.getPhone())
.toList();
subjects.sort(
Comparator.comparing(
PhoneFidMap::getPhone,
Comparator.naturalOrder()
)
);
MapSqlParameterSource params = new MapSqlParameterSource();
params.addValue("phones", phones);
List<FidsData> phonesByFid = jdbcTemplate.query(SELCT_FIDS, params, (rs, rowNum) ->
new FidsData(rs.getInt("fid"), rs.getString("number"), rs.getObject("file_since_dt", LocalDate.class))
);
log.debug("Start merging fids");
for (PhoneFidMap phoneFid : subjects) {
Iterator<FidsData> iterator = phonesByFid.iterator();
while (iterator.hasNext()) {
FidsData fidsData = iterator.next();
if (fidsData.getPhone().equals(9 + phoneFid.getPhone())) {
//fixme доступ по ссылке, страшно, придумать что-то еще
phoneFidMap.put(fidsData.getFid(), phoneFid);
iterator.remove();
break;
}
}
}
log.debug("End merging fids");
return phoneFidMap;
}
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public static class FidsData {
private Integer fid;
private String phone;
private LocalDate fileSinceDate;
}
}

View file

@ -16,11 +16,11 @@ public class PhoneFidMapService {
this.mapper = mapper; this.mapper = mapper;
} }
public void registerSubjects(List<Subject> subjects, Long packageId) { public void registerSubjects(List<Subject> subjects, Integer packageId) {
mapper.registerSubjects(subjects, packageId); mapper.registerSubjects(subjects, packageId);
} }
public List<PhoneFidMap> findFullPhoneFidMap(Long packageId, int offset, int limit) { public List<PhoneFidMap> findFullPhoneFidMap(Integer packageId, int offset, int limit) {
return mapper.findFullPhoneFidMap(packageId, offset, limit); return mapper.findFullPhoneFidMap(packageId, offset, limit);
} }
@ -32,11 +32,11 @@ public class PhoneFidMapService {
mapper.updatePhoneFidMapsErrors(subjects); mapper.updatePhoneFidMapsErrors(subjects);
} }
public PhoneFidMap findPhoneFidMapById(Long id) { public PhoneFidMap findPhoneFidMapById(Integer id) {
return mapper.findPhoneFidMapById(id); return mapper.findPhoneFidMapById(id);
} }
public boolean hasNonNullfids(Long packageId) { public boolean hasNonNullfids(Integer packageId) {
return mapper.hasNonNullFids(packageId); return mapper.hasNonNullFids(packageId);
} }
} }

View file

@ -68,7 +68,7 @@ public class SignalRestService {
* Ставит пакет на мониторинг в официальный сервис сигналов * Ставит пакет на мониторинг в официальный сервис сигналов
* @return true - если пакет успешно поставился на мониторинг. false - в противном случае * @return true - если пакет успешно поставился на мониторинг. false - в противном случае
*/ */
public boolean setupMonitoring(Long packageId) throws Exception { public boolean setupMonitoring(Integer packageId) throws Exception {
String path = signalService.writeSignals(packageId); String path = signalService.writeSignals(packageId);
Resource request = compressFile(path); Resource request = compressFile(path);
@ -105,7 +105,7 @@ public class SignalRestService {
/** /**
* @return имя файла с ошибками по поставленному на мониторинг отчету * @return имя файла с ошибками по поставленному на мониторинг отчету
*/ */
public String getErrorFileName(Long packageId, String packCode) { public String getErrorFileName(Integer packageId, String packCode) {
String url = GET_REPORT_URI + "?packCode=" + packCode; String url = GET_REPORT_URI + "?packCode=" + packCode;
HttpHeaders headers = new HttpHeaders(); HttpHeaders headers = new HttpHeaders();
@ -131,7 +131,7 @@ public class SignalRestService {
SgnlReportMonitorigSetupError report = xmlProcessor.read(answer, SgnlReportMonitorigSetupError.class); SgnlReportMonitorigSetupError report = xmlProcessor.read(answer, SgnlReportMonitorigSetupError.class);
if (report.getOperationReport().getErrorsForNextReport() != null) { if (report.getOperationReport().getErrorsForNextReport() != null) {
String fileName = report.getOperationReport().getErrorsForNextReport().getFilename(); String fileName = report.getOperationReport().getErrorsForNextReport().getFilename();
log.warn("Найдены ошибки по пакету. packCode={}, packageId={}, fileName = {}", packCode, packageId, fileName); log.error("Найдены ошибки по пакету. packCode={}, packageId={}, fileName = {}", packCode, packageId, fileName);
return fileName; return fileName;
} else { } else {
log.info("Пакет был поставлен на мониторинг без ошибок. packCode={}, packageId={}", packCode, packageId); log.info("Пакет был поставлен на мониторинг без ошибок. packCode={}, packageId={}", packCode, packageId);
@ -148,7 +148,7 @@ public class SignalRestService {
* *
* @return путь к разархивированному файлу. Пример: <loadDir>/tmp_folder/<package_id>/0001XX_MY_BATCH_223112.1.errors.xml.gz * @return путь к разархивированному файлу. Пример: <loadDir>/tmp_folder/<package_id>/0001XX_MY_BATCH_223112.1.errors.xml.gz
*/ */
public String loadErrorFile(Long packageId, String fileName) { public String loadErrorFile(Integer packageId, String fileName) {
String url = DOWNLOAD_REPORT_URI + "?fNameRequest=" + fileName; String url = DOWNLOAD_REPORT_URI + "?fNameRequest=" + fileName;
HttpHeaders headers = new HttpHeaders(); HttpHeaders headers = new HttpHeaders();
@ -229,7 +229,7 @@ public class SignalRestService {
* *
* @return путь к разархивированному файлу. Пример: <loadDir>/tmp_folder/<package_id>/0001XX_MY_BATCH_204.1.online.2025-07-08T20-00-46.xml.gz.xml * @return путь к разархивированному файлу. Пример: <loadDir>/tmp_folder/<package_id>/0001XX_MY_BATCH_204.1.online.2025-07-08T20-00-46.xml.gz.xml
*/ */
public String downloadReport(Long packageId, String reportName) { public String downloadReport(Integer packageId, String reportName) {
String url = DOWNLOAD_ONLINE_REPORT_URI + "?fNameReport=" + reportName; String url = DOWNLOAD_ONLINE_REPORT_URI + "?fNameReport=" + reportName;
HttpHeaders headers = new HttpHeaders(); HttpHeaders headers = new HttpHeaders();
@ -247,7 +247,7 @@ public class SignalRestService {
return unzipReport(response,packageId, reportName); return unzipReport(response,packageId, reportName);
} }
private String unzipReport(ResponseEntity<byte[]> response, Long packageId, String reportName) { private String unzipReport(ResponseEntity<byte[]> response, Integer packageId, String reportName) {
boolean isGzipped = response.getHeaders().getContentType() != null boolean isGzipped = response.getHeaders().getContentType() != null
&& response.getHeaders().getContentType().toString().equals("application/x-gzip"); && response.getHeaders().getContentType().toString().equals("application/x-gzip");

View file

@ -34,23 +34,19 @@ public class UserPackageService {
mapper.deletePackage(userId.substring(0, 6), packageName, newStatus.name(), previousStatus.name(), deactivatedAt); mapper.deletePackage(userId.substring(0, 6), packageName, newStatus.name(), previousStatus.name(), deactivatedAt);
} }
public Long insertPackage(UserPackage userPackage) { public Integer insertPackage(UserPackage userPackage) {
return mapper.insertPackage(userPackage); return mapper.insertPackage(userPackage);
} }
public void updatePackageStatus(Long packageId, Status status) { public void updatePackageStatus(Integer packageId, Status status) {
mapper.updatePackageStatus(packageId, status.name()); mapper.updatePackageStatus(packageId, status.name());
} }
public void updatePackageLastDownloadedReport(Long id, String fileName) { public void updatePackageLastDownloadedReport(Integer id, String fileName) {
mapper.updatePackageLastDownloadedReport(id, fileName); mapper.updatePackageLastDownloadedReport(id, fileName);
} }
public void updatePackageOnMonitoringSuccess(Long id, LocalDateTime monitoringAt) { public UserPackage findPackage(Integer id) {
mapper.updatePackageOnMonitoringSuccess(id, monitoringAt);
}
public UserPackage findPackage(Long id) {
return mapper.findPackage(id); return mapper.findPackage(id);
} }

View file

@ -55,7 +55,7 @@ public class MonitoringReportErrorsStaxtReader extends BaseStaxReader<Monitoring
switch (reader.getAttributeLocalName(i)) { switch (reader.getAttributeLocalName(i)) {
case "uid" -> { case "uid" -> {
try { try {
Long uid = Long.parseLong(text); int uid = Integer.parseInt(text);
obj.getCurrentPerson().setUid(uid); obj.getCurrentPerson().setUid(uid);
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
log.error("Couldn't parse uid: {}", text); log.error("Couldn't parse uid: {}", text);

View file

@ -46,7 +46,7 @@ public class OnlineDownloadReportPersonsStaxReader extends BaseStaxReader<Person
switch (reader.getAttributeLocalName(i)) { switch (reader.getAttributeLocalName(i)) {
case "uid" -> { case "uid" -> {
try { try {
Long uid = Long.parseLong(text); int uid = Integer.parseInt(text);
obj.getCurrentPerson().setUid(uid); obj.getCurrentPerson().setUid(uid);
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
log.error("Couldn't parse uid: {}", text); log.error("Couldn't parse uid: {}", text);

View file

@ -1,8 +1,6 @@
package ru.nbch.credit_tracker.utils; package ru.nbch.credit_tracker.utils;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import ru.nbch.credit_tracker.config.mdc.SetAndClearMdc;
import ru.nbch.credit_tracker.entities.user_request.monitoring_request.MonitoringRequest; import ru.nbch.credit_tracker.entities.user_request.monitoring_request.MonitoringRequest;
import ru.nbch.credit_tracker.entities.user_request.monitoring_request.Subject; import ru.nbch.credit_tracker.entities.user_request.monitoring_request.Subject;
import ru.nbch.credit_tracker.enums.SignalError; import ru.nbch.credit_tracker.enums.SignalError;
@ -14,15 +12,13 @@ import java.util.regex.Pattern;
import static ru.nbch.credit_tracker.utils.error.ExceptionUtils.clientExcp; import static ru.nbch.credit_tracker.utils.error.ExceptionUtils.clientExcp;
@Slf4j @Slf4j
@Service public class ValidationUtil {
public class ValidationService {
private static final String phoneRegex = "^\\d{9}$"; private static final String phoneRegex = "^\\d{9}$";
private static final String packageNameRegex = "^[A-Z0-9_]{1,22}$"; private static final String packageNameRegex = "^[A-Z0-9_]{1,22}$";
private static final String npIdRegex = "^\\S{1,36}$"; private static final String npIdRegex = "^\\S{1,36}$";
@SetAndClearMdc public static void validateMonitoringRequest(MonitoringRequest request) throws SignalClientException {
public void validateMonitoringRequest(MonitoringRequest request) throws SignalClientException {
if (request == null) { if (request == null) {
throw clientExcp(SignalError.ERROR_011, XmlErrorFormat.MonitoringError); throw clientExcp(SignalError.ERROR_011, XmlErrorFormat.MonitoringError);
} }
@ -31,34 +27,32 @@ public class ValidationService {
throw clientExcp(SignalError.ERROR_002, XmlErrorFormat.MonitoringError, "PackageName"); throw clientExcp(SignalError.ERROR_002, XmlErrorFormat.MonitoringError, "PackageName");
} }
String packageName = request.getPackageName();
if (!validatePackageName(request.getPackageName())) { if (!validatePackageName(request.getPackageName())) {
throw clientExcp(SignalError.ERROR_008, packageName, XmlErrorFormat.MonitoringError); throw clientExcp(SignalError.ERROR_008, XmlErrorFormat.MonitoringError);
} }
if (request.getUserId() == null) { if (request.getUserId() == null) {
throw clientExcp(SignalError.ERROR_002, packageName, XmlErrorFormat.MonitoringError, "UserId"); throw clientExcp(SignalError.ERROR_002, XmlErrorFormat.MonitoringError, "UserId");
} }
if (request.getPassword() == null) { if (request.getPassword() == null) {
throw clientExcp(SignalError.ERROR_002, packageName, XmlErrorFormat.MonitoringError, "Password"); throw clientExcp(SignalError.ERROR_002, XmlErrorFormat.MonitoringError, "Password");
} }
if (request.getSubjects() == null) { if (request.getSubjects() == null) {
throw clientExcp(SignalError.ERROR_002, packageName, XmlErrorFormat.MonitoringError, "Subjects"); throw clientExcp(SignalError.ERROR_002, XmlErrorFormat.MonitoringError, "Subjects");
} }
if (request.getSubjects().getSubject().isEmpty()) { if (request.getSubjects().getSubject().isEmpty()) {
throw clientExcp(SignalError.ERROR_002, packageName, XmlErrorFormat.MonitoringError, "Subject"); throw clientExcp(SignalError.ERROR_002, XmlErrorFormat.MonitoringError, "Subject");
} }
for (Subject subject : request.getSubjects().getSubject()) { for (Subject subject : request.getSubjects().getSubject()) {
if (!validateNPId(subject.getId())) { if (!validateNPId(subject.getId())) {
throw clientExcp(SignalError.ERROR_010, packageName, XmlErrorFormat.MonitoringError, subject.getId()); throw clientExcp(SignalError.ERROR_010, XmlErrorFormat.MonitoringError, subject.getId());
} }
if (!validatePhone(subject.getClientId())) { if (!validatePhone(subject.getClientId())) {
throw clientExcp(SignalError.ERROR_003, packageName, XmlErrorFormat.MonitoringError, subject.getId()); throw clientExcp(SignalError.ERROR_003, XmlErrorFormat.MonitoringError, subject.getId());
} }
} }
} }

View file

@ -9,11 +9,7 @@ import ru.nbch.credit_tracker.exceptions.SignalServerException;
public class ExceptionUtils { public class ExceptionUtils {
public static SignalClientException clientExcp(IEnumKeyCode enumKeyCode, XmlErrorFormat xmlErrorFormat, Object... args) { public static SignalClientException clientExcp(IEnumKeyCode enumKeyCode, XmlErrorFormat xmlErrorFormat, Object... args) {
return clientExcp(enumKeyCode, null, xmlErrorFormat, args); return new SignalClientException(new EnumKeyMessage(enumKeyCode, args), xmlErrorFormat);
}
public static SignalClientException clientExcp(IEnumKeyCode enumKeyCode, String packageName, XmlErrorFormat xmlErrorFormat, Object... args) {
return new SignalClientException(new EnumKeyMessage(enumKeyCode, args), xmlErrorFormat, packageName);
} }
public static SignalServerException serverExcp(IEnumKeyCode enumKeyCode, XmlErrorFormat xmlErrorFormat, Exception cause, Object... args) { public static SignalServerException serverExcp(IEnumKeyCode enumKeyCode, XmlErrorFormat xmlErrorFormat, Exception cause, Object... args) {

View file

@ -8,7 +8,7 @@
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder> <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} - %msg%n</pattern>
<charset>utf-8</charset> <charset>utf-8</charset>
</encoder> </encoder>
</appender> </appender>
@ -22,7 +22,7 @@
<maxFileSize>100MB</maxFileSize> <maxFileSize>100MB</maxFileSize>
</triggeringPolicy> </triggeringPolicy>
<encoder> <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} [%thread] %-5level %logger{36} - %msg%n</pattern>
<charset>utf-8</charset> <charset>utf-8</charset>
</encoder> </encoder>
</appender> </appender>
@ -63,12 +63,12 @@
</appender> </appender>
<!--SQL запросы и результаты их выполнения--> <!--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"/>
<appender-ref ref="FILE-JSON"/> <appender-ref ref="FILE-JSON"/>
</logger> </logger>
<logger name="ru.nbch.credit_tracker.service" level="INFO" additivity="false"> <logger name="ru.nbch.credit_tracker.service" level="TRACE" additivity="false">
<appender-ref ref="FILE"/> <appender-ref ref="FILE"/>
<appender-ref ref="FILE-JSON"/> <appender-ref ref="FILE-JSON"/>
<appender-ref ref="CONSOLE"/> <appender-ref ref="CONSOLE"/>

View file

@ -5,51 +5,39 @@
<mapper namespace="ru.nbch.credit_tracker.mapper.indic.FlagsMapper"> <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="fid" column="fid" />
<result property="flag" column="flag" /> <result property="accSerialNum" column="ACC_SERIAL_NUM" />
<result property="reportedDate" column="REPORTED_DT" />
<result property="amtPastDue" column="AMT_PAST_DUE" />
<result property="daysPastDue" column="DAYS_PAST_DUE" />
</resultMap> </resultMap>
<select id="findFlagsDebt5000" resultMap="FlagsFidDataResultMap"> <resultMap id="RuPaymtResultMap" type="ru.nbch.credit_tracker.service.indic.FlagsService$RuPaymtData">
SELECT <result property="fid" column="fid" />
FID as fid, <result property="daysPastDue" column="DAYS_PAST_DUE" />
(case when (sum(case when AMT_PAST_DUE > 5000 then 1 else 0 end) = 0) then 0 else 1 end) as flag <result property="reportedDate" column="REPORTED_DT" />
FROM </resultMap>
(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="findRuPastDuarearData" resultMap="RuPastDuarearResultMap">
SELECT SELECT FID, ACC_SERIAL_NUM, REPORTED_DT, AMT_PAST_DUE, DAYS_PAST_DUE
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 FROM RU_PASTDUEARREAR
WHERE FID IN WHERE FID IN
<foreach item="fid" collection="fids" open="(" separator="," close=")"> <foreach item="fid" collection="fids" open="(" separator="," close=")">
#{fid} #{fid}
</foreach> </foreach>
GROUP BY FID with UR
WITH UR
</select> </select>
<select id="findFlag90ByRuPaymt" resultMap="FlagsFidDataResultMap"> <select id="findRuPaymtData" resultMap="RuPaymtResultMap">
SELECT SELECT FID, DAYS_PAST_DUE, REPORTED_DT
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 FROM RU_PAYMT
WHERE FID IN WHERE FID IN
<foreach item="fid" collection="fids" open="(" separator="," close=")"> <foreach item="fid" collection="fids" open="(" separator="," close=")">
#{fid} #{fid}
</foreach> </foreach>
GROUP BY FID AND REPORTED_DT >= #{reportedDate}
WITH UR with UR
</select> </select>
</mapper> </mapper>

View file

@ -12,14 +12,21 @@
</resultMap> </resultMap>
<select id="findFids" resultMap="fidsDataResultMap"> <select id="findFids" resultMap="fidsDataResultMap">
SELECT pn.NUMBER AS number, pn.FID AS fid, pn.FILE_SINCE_DT AS fileSinceDate SELECT
FROM PHONE_NORM pn pn.number,
INNER JOIN NAME n ON n.FID = pn.FID pn.fid,
pn.file_since_dt
FROM indic.PHONE_NORM pn
JOIN (SELECT DISTINCT fid FROM indic.NAME) nf
ON nf.fid = pn.fid
WHERE number in WHERE number in
<foreach item="phone" collection="phones" open="(" separator="," close=")"> <foreach item="phone" collection="phones" open="(" separator="," close=")">
#{phone} #{phone}
</foreach> </foreach>
ORDER BY fileSinceDate DESC, fid DESC ORDER BY
pn.number ASC,
pn.file_since_dt DESC,
pn.fid DESC
WITH UR WITH UR
</select> </select>
</mapper> </mapper>

View file

@ -88,11 +88,5 @@
where package_id = #{packageId} AND fid is not null where package_id = #{packageId} AND fid is not null
</select> </select>
<select id="hasOldRow" resultType="java.lang.Boolean">
select EXISTS(SELECT 1
FROM signals_phone_fid_map
WHERE package_id = #{packageId}
AND calculated_at &lt; now() - make_interval(days => #{daysCount})
)
</select>
</mapper> </mapper>

View file

@ -13,7 +13,6 @@
<result property="status" column="status"/> <result property="status" column="status"/>
<result property="deactivatedAt" column="deactivated_at"/> <result property="deactivatedAt" column="deactivated_at"/>
<result property="lastDownloadedReport" column="last_downloaded_report"/> <result property="lastDownloadedReport" column="last_downloaded_report"/>
<result property="monitoringAt" column="monitoring_at"/>
</resultMap> </resultMap>
<select id="isPackageNotUnique" resultType="boolean"> <select id="isPackageNotUnique" resultType="boolean">
@ -52,7 +51,7 @@
#{previousStatus} #{previousStatus}
</update> </update>
<select id="insertPackage" resultType="java.lang.Long" <select id="insertPackage" resultType="java.lang.Integer"
parameterType="ru.nbch.credit_tracker.entities.dto.UserPackage"> parameterType="ru.nbch.credit_tracker.entities.dto.UserPackage">
INSERT INTO signals_user_packages (membercode, package_name, created_at, status) INSERT INTO signals_user_packages (membercode, package_name, created_at, status)
values (#{userPackage.userId}, #{userPackage.packageName}, #{userPackage.createdAt}, #{userPackage.status}) RETURNING id values (#{userPackage.userId}, #{userPackage.packageName}, #{userPackage.createdAt}, #{userPackage.status}) RETURNING id
@ -70,15 +69,9 @@
WHERE id = #{id} WHERE id = #{id}
</update> </update>
<update id="updatePackageOnMonitoringSuccess">
UPDATE signals_user_packages
SET monitoring_at = #{monitoringAt}, status = 'ON_MONITORING'
WHERE id = #{id}
</update>
<select id="findPackage" resultMap="packageDataResultMap" <select id="findPackage" resultMap="packageDataResultMap"
resultType="ru.nbch.credit_tracker.entities.dto.UserPackage"> resultType="ru.nbch.credit_tracker.entities.dto.UserPackage">
SELECT id, membercode, package_name, created_at, status, deactivated_at, last_downloaded_report, monitoring_at SELECT id, membercode, package_name, created_at, status, deactivated_at, last_downloaded_report
FROM signals_user_packages FROM signals_user_packages
WHERE id = #{id} WHERE id = #{id}
LIMIT 1 LIMIT 1
@ -86,7 +79,7 @@
<select id="findUserPackages" resultMap="packageDataResultMap" <select id="findUserPackages" resultMap="packageDataResultMap"
resultType="ru.nbch.credit_tracker.entities.dto.UserPackage"> resultType="ru.nbch.credit_tracker.entities.dto.UserPackage">
SELECT id, membercode, package_name, created_at, status, deactivated_at, last_downloaded_report, monitoring_at SELECT id, membercode, package_name, created_at, status, deactivated_at, last_downloaded_report
FROM signals_user_packages FROM signals_user_packages
WHERE membercode IS NOT NULL WHERE membercode IS NOT NULL
AND LEFT(membercode, 6) = #{userId} AND LEFT(membercode, 6) = #{userId}
@ -98,7 +91,7 @@
<select id="findAllPackagesByUserId" resultMap="packageDataResultMap" <select id="findAllPackagesByUserId" resultMap="packageDataResultMap"
resultType="ru.nbch.credit_tracker.entities.dto.UserPackage"> resultType="ru.nbch.credit_tracker.entities.dto.UserPackage">
SELECT id, membercode, package_name, created_at, status, deactivated_at, last_downloaded_report, monitoring_at SELECT id, membercode, package_name, created_at, status, deactivated_at, last_downloaded_report
FROM signals_user_packages FROM signals_user_packages
WHERE membercode IS NOT NULL WHERE membercode IS NOT NULL
AND LEFT(membercode, 6) = #{userId} AND LEFT(membercode, 6) = #{userId}
@ -106,7 +99,7 @@
<select id="findAllPackagesByStatus" resultMap="packageDataResultMap" <select id="findAllPackagesByStatus" resultMap="packageDataResultMap"
resultType="ru.nbch.credit_tracker.entities.dto.UserPackage"> resultType="ru.nbch.credit_tracker.entities.dto.UserPackage">
SELECT id, membercode, package_name, created_at, status, deactivated_at, last_downloaded_report, monitoring_at SELECT id, membercode, package_name, created_at, status, deactivated_at, last_downloaded_report
FROM signals_user_packages FROM signals_user_packages
WHERE status = #{status} WHERE status = #{status}
</select> </select>

View file

@ -1,2 +0,0 @@
alter table signals_phone_fid_map
add error_text text;

View file

@ -1,2 +0,0 @@
alter table signals_user_packages
add "monitoring_at" timestamp;

View file

@ -1,14 +0,0 @@
alter table signals_user_packages
alter column id type bigint using id::bigint;
alter table signals_phone_fid_map
alter column id type bigint using id::bigint;
alter table signals_phone_fid_map
alter column package_id type bigint using package_id::bigint;
alter table signals_hits
alter column id type bigint using id::bigint;
alter table signals_hits
alter column package_id type bigint using package_id::bigint;

View file

@ -1,3 +0,0 @@
CREATE INDEX IF NOT EXISTS
idx_signals_phone_fid_map_calculated_at
ON signals_phone_fid_map (calculated_at);

View file

@ -0,0 +1,280 @@
package ru.nbch.credit_tracker.service;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import ru.nbch.credit_tracker.service.indic.FlagsService;
import ru.nbch.credit_tracker.service.indic.FlagsService2;
import javax.sql.DataSource;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class FlagsService2Test {
private static final LocalDate INITIAL_DATE = LocalDate.parse("2025-09-25");
private static final String RU_PASTDUEAREAR_CREATE = """
create table RU_PASTDUEARREAR
(
FID INTEGER not null,
SERIAL_NUM BIGINT not null primary key,
ACC_SERIAL_NUM BIGINT not null,
REPORTED_DT DATE not null,
AMT_PAST_DUE DECIMAL(17, 2),
DAYS_PAST_DUE BIGINT
);
""";
private static final String RU_PASTDUEAREAR_DROP = """
DROP TABLE IF EXISTS RU_PASTDUEARREAR;
""";
private static final String RU_PAYMT_DROP = """
DROP TABLE IF EXISTS RU_PAYMT;
""";
private static final String RU_PAYMT_CREATE = """
create table RU_PAYMT
(
FID INTEGER not null,
SERIAL_NUM BIGINT not null primary key,
DAYS_PAST_DUE INTEGER,
REPORTED_DT DATE not null
);
""";
private static final String FILL_RU_PASTDUEARREAR = String.format("""
INSERT INTO RU_PASTDUEARREAR VALUES
(1, 1, 1, '%s', 36000.00, 159), -- просрочка 5000 и 90_days по этой записи
(2, 2, 2, '%s', 3200.00, null), -- не макс reported_date для того же acc_serial_num
(2, 3, 2, '%s', 6700.00, null), -- не макс reported_date для того же acc_serial_num
(2, 4, 2, '%s', 5700.00, null), -- просрочка 5000 по этой записи
(3, 5, 3, '%s', 300000000000.00, null),
(2, 6, 2, '%s', 0.00, null), -- не макс reported_date для того же acc_serial_num
(4, 7, 4, '%s', 4000.00, null), -- нет просрочек
(5, 8, 5, '%s', 2000.31, 182), -- не макс reported_date для того же acc_serial_num
(5, 9, 5, '%s', 2000.31, 212), -- не макс reported_date для того же acc_serial_num
(5, 10, 5, '%s', 2000.31, null), -- не макс reported_date для того же acc_serial_num
(5, 11, 5, '%s', 2000.31, 273), -- не макс reported_date для того же acc_serial_num
(5, 12, 5, '%s', 2000.31, null), -- не макс reported_date для того же acc_serial_num
(5, 13, 5, '%s', 7000.31, 307), -- не макс reported_date для того же acc_serial_num
(5, 14, 5, '%s', 2000.31, 338), -- не макс reported_date для того же acc_serial_num, просрочка 90_days будет взята у этой записи
(5, 15, 5, '%s', 7000.31, 368), -- Просрочка 5000 будет взята у этой записи. Уникальный acc_serial_num с макс reported_dt
(5, 16, 5, '%s', 2000.31, 399), -- не макс reported_date для того же acc_serial_num
(5, 17, 5, '%s', 5000.31, 5), -- не макс reported_date для того же acc_serial_num
(6, 18, 6, '%s', 3000, null), -- Нет просрочки 5000, нет просрочки 90_days (90_days будет в RU_PAYMT)
(7, 19, 7, '%s', 5500, 90), -- не макс reported_date для того же acc_serial_num. 90_days будет взята отсюда
(7, 20, 7, '%s', 3000, null) -- Нет просрочки 5000. Уникальный acc_serial_num с макс reported_dt
""",
calcLocalDateFromInitialDate("2025-05-31"),
calcLocalDateFromInitialDate("2021-12-29"),
calcLocalDateFromInitialDate("2022-01-28"),
calcLocalDateFromInitialDate("2025-04-06"),
calcLocalDateFromInitialDate("2025-01-14"),
calcLocalDateFromInitialDate("2021-11-29"),
calcLocalDateFromInitialDate("2025-12-06"),
calcLocalDateFromInitialDate("2021-04-01"),
calcLocalDateFromInitialDate("2021-05-01"),
calcLocalDateFromInitialDate("2021-05-31"),
calcLocalDateFromInitialDate("2021-07-01"),
calcLocalDateFromInitialDate("2021-07-31"),
calcLocalDateFromInitialDate("2021-08-04"),
calcLocalDateFromInitialDate("2025-09-08"),
calcLocalDateFromInitialDate("2025-09-09"),
calcLocalDateFromInitialDate("2021-11-04"),
calcLocalDateFromInitialDate("2022-01-04"),
calcLocalDateFromInitialDate("2025-09-09"),
calcLocalDateFromInitialDate("2025-09-09"),
calcLocalDateFromInitialDate("2025-09-10"));
private static final String FILL_RU_PAYMT = String.format("""
INSERT INTO RU_PAYMT VALUES
(1, 1, null, '%s'),
(2, 2, 30, '%s'),
(3, 3, null, '%s'),
(3, 4, 100, '%s'),
(4, 5, 10, '%s'),
(5, 6, 10, '%s'),
(6, 7, 90, '%s'),
(6, 8, 80, '%s'),
(7, 9, null, '%s'),
(8, 10, null, '%s'),
(8, 11, 83, '%s'),
(8, 12, 91, '%s'),
(9, 13, 100, '%s')
""",
calcLocalDateFromInitialDate("2024-11-09"),
calcLocalDateFromInitialDate("2024-11-09"),
calcLocalDateFromInitialDate("2024-11-10"),
calcLocalDateFromInitialDate("2024-11-15"),
calcLocalDateFromInitialDate("2024-11-15"),
calcLocalDateFromInitialDate("2023-11-15"),
calcLocalDateFromInitialDate("2024-11-09"),
calcLocalDateFromInitialDate("2024-10-09"),
calcLocalDateFromInitialDate("2024-10-09"),
calcLocalDateFromInitialDate("2024-10-09"),
calcLocalDateFromInitialDate("2024-10-09"),
calcLocalDateFromInitialDate("2024-10-09"),
calcLocalDateFromInitialDate("2024-10-09"));
private DataSource dataSource;
private NamedParameterJdbcTemplate jdbcTemplate;
private FlagsService2 flagsService;
@BeforeEach
void setUp() {
dataSource = DataSourceBuilder.create()
.url("jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;MODE=DB2;DATABASE_TO_LOWER=TRUE")
.driverClassName("org.h2.Driver")
.username("sa")
.password("")
.build();
jdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
jdbcTemplate.getJdbcTemplate().execute(RU_PASTDUEAREAR_DROP);
jdbcTemplate.getJdbcTemplate().execute(RU_PAYMT_DROP);
jdbcTemplate.getJdbcTemplate().execute(RU_PASTDUEAREAR_CREATE);
jdbcTemplate.getJdbcTemplate().execute(RU_PAYMT_CREATE);
flagsService = new FlagsService2(jdbcTemplate);
}
@Test
public void testfindFlags() {
jdbcTemplate.getJdbcTemplate().execute(FILL_RU_PASTDUEARREAR);
jdbcTemplate.getJdbcTemplate().execute(FILL_RU_PAYMT);
List<Integer> fids = new ArrayList<>(List.of(
1, // есть просрочка 5000, есть просрочка 90_days в RU_PASTDUEARREAR
2, // есть просрочка 5000, нет просрочки 90_days в RU_PASTDUEARREAR. Нет просрочки 90_days в RU_PAYMT
3, // есть просрочка 5000, нет просрочки 90_days в RU_PASTDUEARREAR. Есть просрочка 90_days в RU_PAYMT
4, // нет никаких просрочек
7, // нет просрочки 5000, есть просрочка 90_days в RU_PASTDUEARREAR
5, // есть просрочка 5000, есть просрочка 90_days в RU_PASTDUEARREAR
6, // нет просрочки 5000, нет просрочки 90_days в RU_PASTDUEARREAR. Есть просрочка 90_days в RU_PAYMT
8, // нету в RU_PASTDUEARREAR, есть в RU_PAYMT
9) // нету в RU_PASTDUEARREAR, нету в RU_PAYMT
);
Map<Integer, FlagsService.FlagsData> resultMap = flagsService.findFlags(fids);
Assertions.assertEquals(9, resultMap.size());
Assertions.assertEquals(1, resultMap.get(1).getFlagDebt5000());
Assertions.assertEquals(1, resultMap.get(2).getFlagDebt5000());
Assertions.assertEquals(1, resultMap.get(3).getFlagDebt5000());
Assertions.assertEquals(0, resultMap.get(4).getFlagDebt5000());
Assertions.assertEquals(1, resultMap.get(5).getFlagDebt5000());
Assertions.assertEquals(0, resultMap.get(6).getFlagDebt5000());
Assertions.assertEquals(0, resultMap.get(7).getFlagDebt5000());
Assertions.assertEquals(0, resultMap.get(8).getFlagDebt5000());
Assertions.assertEquals(0, resultMap.get(9).getFlagDebt5000());
Assertions.assertEquals(1, resultMap.get(1).getFlag90days());
Assertions.assertEquals(0, resultMap.get(2).getFlag90days());
Assertions.assertEquals(1, resultMap.get(3).getFlag90days());
Assertions.assertEquals(0, resultMap.get(4).getFlag90days());
Assertions.assertEquals(1, resultMap.get(5).getFlag90days());
Assertions.assertEquals(1, resultMap.get(6).getFlag90days());
Assertions.assertEquals(1, resultMap.get(7).getFlag90days());
Assertions.assertEquals(1, resultMap.get(8).getFlag90days());
Assertions.assertEquals(1, resultMap.get(9).getFlag90days());
}
@Test
public void testfindFlagsWithoutRuPaymt() {
jdbcTemplate.getJdbcTemplate().execute(FILL_RU_PASTDUEARREAR);
List<Integer> fids = new ArrayList<>(List.of(
1, // есть просрочка 5000, есть просрочка 90_days в RU_PASTDUEARREAR
2, // есть просрочка 5000, нет просрочки 90_days в RU_PASTDUEARREAR. Нет просрочки 90_days в RU_PAYMT
3, // есть просрочка 5000, нет просрочки 90_days в RU_PASTDUEARREAR. Есть просрочка 90_days в RU_PAYMT
4, // нет никаких просрочек
7, // нет просрочки 5000, есть просрочка 90_days в RU_PASTDUEARREAR
5, // есть просрочка 5000, есть просрочка 90_days в RU_PASTDUEARREAR
6, // нет просрочки 5000, нет просрочки 90_days в RU_PASTDUEARREAR. Есть просрочка 90_days в RU_PAYMT
8, // нету в RU_PASTDUEARREAR, есть в RU_PAYMT
9) // нету в RU_PASTDUEARREAR, нету в RU_PAYMT
);
Map<Integer, FlagsService.FlagsData> resultMap = flagsService.findFlags(fids);
Assertions.assertEquals(9, resultMap.size());
Assertions.assertEquals(1, resultMap.get(1).getFlagDebt5000());
Assertions.assertEquals(1, resultMap.get(2).getFlagDebt5000());
Assertions.assertEquals(1, resultMap.get(3).getFlagDebt5000());
Assertions.assertEquals(0, resultMap.get(4).getFlagDebt5000());
Assertions.assertEquals(1, resultMap.get(5).getFlagDebt5000());
Assertions.assertEquals(0, resultMap.get(6).getFlagDebt5000());
Assertions.assertEquals(0, resultMap.get(7).getFlagDebt5000());
Assertions.assertEquals(0, resultMap.get(8).getFlagDebt5000());
Assertions.assertEquals(0, resultMap.get(9).getFlagDebt5000());
Assertions.assertEquals(1, resultMap.get(1).getFlag90days());
Assertions.assertEquals(0, resultMap.get(2).getFlag90days());
Assertions.assertEquals(0, resultMap.get(3).getFlag90days());
Assertions.assertEquals(0, resultMap.get(4).getFlag90days());
Assertions.assertEquals(1, resultMap.get(5).getFlag90days());
Assertions.assertEquals(0, resultMap.get(6).getFlag90days());
Assertions.assertEquals(1, resultMap.get(7).getFlag90days());
Assertions.assertEquals(0, resultMap.get(8).getFlag90days());
Assertions.assertEquals(0, resultMap.get(9).getFlag90days());
}
@Test
public void testfindFlagsWithoutRuPastduearreae() {
jdbcTemplate.getJdbcTemplate().execute(FILL_RU_PAYMT);
List<Integer> fids = new ArrayList<>(List.of(
1, // есть просрочка 5000, есть просрочка 90_days в RU_PASTDUEARREAR
2, // есть просрочка 5000, нет просрочки 90_days в RU_PASTDUEARREAR. Нет просрочки 90_days в RU_PAYMT
3, // есть просрочка 5000, нет просрочки 90_days в RU_PASTDUEARREAR. Есть просрочка 90_days в RU_PAYMT
4, // нет никаких просрочек
7, // нет просрочки 5000, есть просрочка 90_days в RU_PASTDUEARREAR
5, // есть просрочка 5000, есть просрочка 90_days в RU_PASTDUEARREAR
6, // нет просрочки 5000, нет просрочки 90_days в RU_PASTDUEARREAR. Есть просрочка 90_days в RU_PAYMT
8, // нету в RU_PASTDUEARREAR, есть в RU_PAYMT
9) // нету в RU_PASTDUEARREAR, нету в RU_PAYMT
);
Map<Integer, FlagsService.FlagsData> resultMap = flagsService.findFlags(fids);
Assertions.assertEquals(9, resultMap.size());
Assertions.assertEquals(0, resultMap.get(1).getFlagDebt5000());
Assertions.assertEquals(0, resultMap.get(2).getFlagDebt5000());
Assertions.assertEquals(0, resultMap.get(3).getFlagDebt5000());
Assertions.assertEquals(0, resultMap.get(4).getFlagDebt5000());
Assertions.assertEquals(0, resultMap.get(5).getFlagDebt5000());
Assertions.assertEquals(0, resultMap.get(6).getFlagDebt5000());
Assertions.assertEquals(0, resultMap.get(7).getFlagDebt5000());
Assertions.assertEquals(0, resultMap.get(8).getFlagDebt5000());
Assertions.assertEquals(0, resultMap.get(9).getFlagDebt5000());
Assertions.assertEquals(0, resultMap.get(1).getFlag90days());
Assertions.assertEquals(0, resultMap.get(2).getFlag90days());
Assertions.assertEquals(1, resultMap.get(3).getFlag90days());
Assertions.assertEquals(0, resultMap.get(4).getFlag90days());
Assertions.assertEquals(0, resultMap.get(5).getFlag90days());
Assertions.assertEquals(1, resultMap.get(6).getFlag90days());
Assertions.assertEquals(0, resultMap.get(7).getFlag90days());
Assertions.assertEquals(1, resultMap.get(8).getFlag90days());
Assertions.assertEquals(1, resultMap.get(9).getFlag90days());
}
private static LocalDate calcLocalDateFromInitialDate(String date) {
LocalDateTime now = LocalDateTime.now();
long daysPassed = ChronoUnit.DAYS.between(INITIAL_DATE, now);
return LocalDate.parse(date).plusDays(daysPassed);
}
}

View file

@ -0,0 +1,218 @@
package ru.nbch.credit_tracker.service;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import ru.nbch.credit_tracker.mapper.indic.FlagsMapper;
import ru.nbch.credit_tracker.service.indic.FlagsService;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
public class FlagsServiceTest {
private static final LocalDate INITIAL_DATE = LocalDate.parse("2025-09-25");
@Mock
FlagsMapper flagsMapper;
@Test
public void testfindFlags() {
FlagsService flagsService = new FlagsService(flagsMapper);
when(flagsMapper.findRuPastDuarearData(Mockito.anyList())).thenReturn(ruPastDuarearDataList);
when(flagsMapper.findRuPaymtData(Mockito.anyList(), Mockito.any())).thenReturn(ruPaymtDataList);
List<Integer> fids = new ArrayList<>(List.of(
1, // есть просрочка 5000, есть просрочка 90_days в RU_PASTDUEARREAR
2, // есть просрочка 5000, нет просрочки 90_days в RU_PASTDUEARREAR. Нет просрочки 90_days в RU_PAYMT
3, // есть просрочка 5000, нет просрочки 90_days в RU_PASTDUEARREAR. Есть просрочка 90_days в RU_PAYMT
4, // нет никаких просрочек
7, // нет просрочки 5000, есть просрочка 90_days в RU_PASTDUEARREAR
5, // есть просрочка 5000, есть просрочка 90_days в RU_PASTDUEARREAR
6, // нет просрочки 5000, нет просрочки 90_days в RU_PASTDUEARREAR. Есть просрочка 90_days в RU_PAYMT
8, // нету в RU_PASTDUEARREAR, есть в RU_PAYMT
9) // нету в RU_PASTDUEARREAR, нету в RU_PAYMT
);
Map<Integer, FlagsService.FlagsData> resultMap = flagsService.findFlags(fids);
Assertions.assertEquals(9, resultMap.size());
Assertions.assertEquals(1, resultMap.get(1).getFlagDebt5000());
Assertions.assertEquals(1, resultMap.get(2).getFlagDebt5000());
Assertions.assertEquals(1, resultMap.get(3).getFlagDebt5000());
Assertions.assertEquals(0, resultMap.get(4).getFlagDebt5000());
Assertions.assertEquals(1, resultMap.get(5).getFlagDebt5000());
Assertions.assertEquals(0, resultMap.get(6).getFlagDebt5000());
Assertions.assertEquals(0, resultMap.get(7).getFlagDebt5000());
Assertions.assertEquals(0, resultMap.get(8).getFlagDebt5000());
Assertions.assertEquals(0, resultMap.get(9).getFlagDebt5000());
Assertions.assertEquals(1, resultMap.get(1).getFlag90days());
Assertions.assertEquals(0, resultMap.get(2).getFlag90days());
Assertions.assertEquals(1, resultMap.get(3).getFlag90days());
Assertions.assertEquals(0, resultMap.get(4).getFlag90days());
Assertions.assertEquals(1, resultMap.get(5).getFlag90days());
Assertions.assertEquals(1, resultMap.get(6).getFlag90days());
Assertions.assertEquals(1, resultMap.get(7).getFlag90days());
Assertions.assertEquals(1, resultMap.get(8).getFlag90days());
Assertions.assertEquals(1, resultMap.get(9).getFlag90days());
}
@Test
public void testfindFlagsWithoutRuPaymt() {
FlagsService flagsService = new FlagsService(flagsMapper);
when(flagsMapper.findRuPastDuarearData(Mockito.anyList())).thenReturn(ruPastDuarearDataList);
when(flagsMapper.findRuPaymtData(Mockito.anyList(), Mockito.any())).thenReturn(new ArrayList<>());
List<Integer> fids = new ArrayList<>(List.of(
1, // есть просрочка 5000, есть просрочка 90_days в RU_PASTDUEARREAR
2, // есть просрочка 5000, нет просрочки 90_days в RU_PASTDUEARREAR. Нет просрочки 90_days в RU_PAYMT
3, // есть просрочка 5000, нет просрочки 90_days в RU_PASTDUEARREAR. Есть просрочка 90_days в RU_PAYMT
4, // нет никаких просрочек
7, // нет просрочки 5000, есть просрочка 90_days в RU_PASTDUEARREAR
5, // есть просрочка 5000, есть просрочка 90_days в RU_PASTDUEARREAR
6, // нет просрочки 5000, нет просрочки 90_days в RU_PASTDUEARREAR. Есть просрочка 90_days в RU_PAYMT
8, // нету в RU_PASTDUEARREAR, есть в RU_PAYMT
9) // нету в RU_PASTDUEARREAR, нету в RU_PAYMT
);
Map<Integer, FlagsService.FlagsData> resultMap = flagsService.findFlags(fids);
Assertions.assertEquals(9, resultMap.size());
Assertions.assertEquals(1, resultMap.get(1).getFlagDebt5000());
Assertions.assertEquals(1, resultMap.get(2).getFlagDebt5000());
Assertions.assertEquals(1, resultMap.get(3).getFlagDebt5000());
Assertions.assertEquals(0, resultMap.get(4).getFlagDebt5000());
Assertions.assertEquals(1, resultMap.get(5).getFlagDebt5000());
Assertions.assertEquals(0, resultMap.get(6).getFlagDebt5000());
Assertions.assertEquals(0, resultMap.get(7).getFlagDebt5000());
Assertions.assertEquals(0, resultMap.get(8).getFlagDebt5000());
Assertions.assertEquals(0, resultMap.get(9).getFlagDebt5000());
Assertions.assertEquals(1, resultMap.get(1).getFlag90days());
Assertions.assertEquals(0, resultMap.get(2).getFlag90days());
Assertions.assertEquals(0, resultMap.get(3).getFlag90days());
Assertions.assertEquals(0, resultMap.get(4).getFlag90days());
Assertions.assertEquals(1, resultMap.get(5).getFlag90days());
Assertions.assertEquals(0, resultMap.get(6).getFlag90days());
Assertions.assertEquals(1, resultMap.get(7).getFlag90days());
Assertions.assertEquals(0, resultMap.get(8).getFlag90days());
Assertions.assertEquals(0, resultMap.get(9).getFlag90days());
}
@Test
public void testfindFlagsWithoutRuPastduearreae() {
FlagsService flagsService = new FlagsService(flagsMapper);
when(flagsMapper.findRuPastDuarearData(Mockito.anyList())).thenReturn(new ArrayList<>());
when(flagsMapper.findRuPaymtData(Mockito.anyList(), Mockito.any())).thenReturn(ruPaymtDataList);
List<Integer> fids = new ArrayList<>(List.of(
1, // есть просрочка 5000, есть просрочка 90_days в RU_PASTDUEARREAR
2, // есть просрочка 5000, нет просрочки 90_days в RU_PASTDUEARREAR. Нет просрочки 90_days в RU_PAYMT
3, // есть просрочка 5000, нет просрочки 90_days в RU_PASTDUEARREAR. Есть просрочка 90_days в RU_PAYMT
4, // нет никаких просрочек
7, // нет просрочки 5000, есть просрочка 90_days в RU_PASTDUEARREAR
5, // есть просрочка 5000, есть просрочка 90_days в RU_PASTDUEARREAR
6, // нет просрочки 5000, нет просрочки 90_days в RU_PASTDUEARREAR. Есть просрочка 90_days в RU_PAYMT
8, // нету в RU_PASTDUEARREAR, есть в RU_PAYMT
9) // нету в RU_PASTDUEARREAR, нету в RU_PAYMT
);
Map<Integer, FlagsService.FlagsData> resultMap = flagsService.findFlags(fids);
Assertions.assertEquals(9, resultMap.size());
Assertions.assertEquals(0, resultMap.get(1).getFlagDebt5000());
Assertions.assertEquals(0, resultMap.get(2).getFlagDebt5000());
Assertions.assertEquals(0, resultMap.get(3).getFlagDebt5000());
Assertions.assertEquals(0, resultMap.get(4).getFlagDebt5000());
Assertions.assertEquals(0, resultMap.get(5).getFlagDebt5000());
Assertions.assertEquals(0, resultMap.get(6).getFlagDebt5000());
Assertions.assertEquals(0, resultMap.get(7).getFlagDebt5000());
Assertions.assertEquals(0, resultMap.get(8).getFlagDebt5000());
Assertions.assertEquals(0, resultMap.get(9).getFlagDebt5000());
Assertions.assertEquals(0, resultMap.get(1).getFlag90days());
Assertions.assertEquals(0, resultMap.get(2).getFlag90days());
Assertions.assertEquals(1, resultMap.get(3).getFlag90days());
Assertions.assertEquals(0, resultMap.get(4).getFlag90days());
Assertions.assertEquals(0, resultMap.get(5).getFlag90days());
Assertions.assertEquals(1, resultMap.get(6).getFlag90days());
Assertions.assertEquals(0, resultMap.get(7).getFlag90days());
Assertions.assertEquals(1, resultMap.get(8).getFlag90days());
Assertions.assertEquals(1, resultMap.get(9).getFlag90days());
}
private final List<FlagsService.RuPastDuarearData> ruPastDuarearDataList = new ArrayList<>(
List.of(
new FlagsService.RuPastDuarearData(1, BigInteger.valueOf(1), calcLocalDateFromInitialDate("2025-05-31"), BigDecimal.valueOf(36000.00), BigInteger.valueOf(159)), // просрочка 5000 и 90_days по этой записи
new FlagsService.RuPastDuarearData(2, BigInteger.valueOf(2), calcLocalDateFromInitialDate("2021-12-29"), BigDecimal.valueOf(3200.00), null), // не макс reported_date для того же acc_serial_num
new FlagsService.RuPastDuarearData(2, BigInteger.valueOf(2), calcLocalDateFromInitialDate("2022-01-28"), BigDecimal.valueOf(6700.00), null), // не макс reported_date для того же acc_serial_num
new FlagsService.RuPastDuarearData(2, BigInteger.valueOf(2), calcLocalDateFromInitialDate("2025-04-06"), BigDecimal.valueOf(5700.00), null), // просрочка 5000 по этой записи
new FlagsService.RuPastDuarearData(3, BigInteger.valueOf(3), calcLocalDateFromInitialDate("2025-01-14"), BigDecimal.valueOf(300000000000.00), null),
new FlagsService.RuPastDuarearData(2, BigInteger.valueOf(2), calcLocalDateFromInitialDate("2021-11-29"), BigDecimal.valueOf(0.00), null), // не макс reported_date для того же acc_serial_num
new FlagsService.RuPastDuarearData(4, BigInteger.valueOf(4), calcLocalDateFromInitialDate("2025-12-06"), BigDecimal.valueOf(4000.00), null), // нет просрочек
new FlagsService.RuPastDuarearData(5, BigInteger.valueOf(5), calcLocalDateFromInitialDate("2021-04-01"), BigDecimal.valueOf(2000.31), BigInteger.valueOf(182)), // не макс reported_date для того же acc_serial_num
new FlagsService.RuPastDuarearData(5, BigInteger.valueOf(5), calcLocalDateFromInitialDate("2021-05-01"), BigDecimal.valueOf(2000.31), BigInteger.valueOf(212)), // не макс reported_date для того же acc_serial_num
new FlagsService.RuPastDuarearData(5, BigInteger.valueOf(5), calcLocalDateFromInitialDate("2021-05-31"), BigDecimal.valueOf(2000.31), null), // не макс reported_date для того же acc_serial_num
new FlagsService.RuPastDuarearData(5, BigInteger.valueOf(5), calcLocalDateFromInitialDate("2021-07-01"), BigDecimal.valueOf(2000.31), BigInteger.valueOf(273)), // не макс reported_date для того же acc_serial_num
new FlagsService.RuPastDuarearData(5, BigInteger.valueOf(5), calcLocalDateFromInitialDate("2021-07-31"), BigDecimal.valueOf(2000.31), null), // не макс reported_date для того же acc_serial_num
new FlagsService.RuPastDuarearData(5, BigInteger.valueOf(5), calcLocalDateFromInitialDate("2021-08-04"), BigDecimal.valueOf(7000.31), BigInteger.valueOf(307)), // не макс reported_date для того же acc_serial_num
new FlagsService.RuPastDuarearData(5, BigInteger.valueOf(5), calcLocalDateFromInitialDate("2025-09-08"), BigDecimal.valueOf(2000.31), BigInteger.valueOf(338)), // не макс reported_date для того же acc_serial_num, просрочка 90_days будет взята у этой записи
new FlagsService.RuPastDuarearData(5, BigInteger.valueOf(51), calcLocalDateFromInitialDate("2025-09-09"), BigDecimal.valueOf(7000.31), BigInteger.valueOf(368)), // Просрочка 5000 будет взята у этой записи. Уникальный acc_serial_num с макс reported_dt
new FlagsService.RuPastDuarearData(5, BigInteger.valueOf(5), calcLocalDateFromInitialDate("2021-11-04"), BigDecimal.valueOf(2000.31), BigInteger.valueOf(399)), // не макс reported_date для того же acc_serial_num
new FlagsService.RuPastDuarearData(5, BigInteger.valueOf(5), calcLocalDateFromInitialDate("2022-01-04"), BigDecimal.valueOf(5000.31), BigInteger.valueOf(5)), // не макс reported_date для того же acc_serial_num
new FlagsService.RuPastDuarearData(6, BigInteger.valueOf(6), calcLocalDateFromInitialDate("2025-09-09"), BigDecimal.valueOf(3000), null), // Нет просрочки 5000, нет просрочки 90_days (90_days будет в RU_PAYMT)
new FlagsService.RuPastDuarearData(7, BigInteger.valueOf(7), calcLocalDateFromInitialDate("2025-09-09"), BigDecimal.valueOf(5500), BigInteger.valueOf(90)), // не макс reported_date для того же acc_serial_num. 90_days будет взята отсюда
new FlagsService.RuPastDuarearData(7, BigInteger.valueOf(7), calcLocalDateFromInitialDate("2025-09-10"), BigDecimal.valueOf(3000), null) // Нет просрочки 5000. Уникальный acc_serial_num с макс reported_dt
)
);
private final List<FlagsService.RuPaymtData> ruPaymtDataList = new ArrayList<>(
List.of(
new FlagsService.RuPaymtData(1, null, calcLocalDateFromInitialDate("2024-11-09")),
new FlagsService.RuPaymtData(2, BigInteger.valueOf(30), calcLocalDateFromInitialDate("2024-11-09")),
new FlagsService.RuPaymtData(3, null, calcLocalDateFromInitialDate("2024-11-10")),
new FlagsService.RuPaymtData(3, BigInteger.valueOf(100), calcLocalDateFromInitialDate("2024-11-15")),
new FlagsService.RuPaymtData(4, BigInteger.valueOf(10), calcLocalDateFromInitialDate("2024-11-15")),
new FlagsService.RuPaymtData(5, BigInteger.valueOf(10), calcLocalDateFromInitialDate("2023-11-15")),
new FlagsService.RuPaymtData(6, BigInteger.valueOf(90), calcLocalDateFromInitialDate("2024-11-09")),
new FlagsService.RuPaymtData(6, BigInteger.valueOf(80), calcLocalDateFromInitialDate("2024-10-09")),
new FlagsService.RuPaymtData(7, null, calcLocalDateFromInitialDate("2024-10-09")),
new FlagsService.RuPaymtData(8, null, calcLocalDateFromInitialDate("2024-10-09")),
new FlagsService.RuPaymtData(8, BigInteger.valueOf(83), calcLocalDateFromInitialDate("2024-10-09")),
new FlagsService.RuPaymtData(8, BigInteger.valueOf(91), calcLocalDateFromInitialDate("2024-10-09")),
new FlagsService.RuPaymtData(9, BigInteger.valueOf(100), calcLocalDateFromInitialDate("2024-10-09"))
)
);
private LocalDate calcLocalDateFromInitialDate(String date) {
LocalDateTime now = LocalDateTime.now();
long daysPassed = ChronoUnit.DAYS.between(INITIAL_DATE, now);
return LocalDate.parse(date).plusDays(daysPassed);
}
}

View file

@ -0,0 +1,63 @@
package ru.nbch.credit_tracker.service;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import ru.nbch.credit_tracker.entities.dto.PhoneFidMap;
import ru.nbch.credit_tracker.service.indic.PhoneService2;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
public class PhoneService2Test {
@Mock
NamedParameterJdbcTemplate jdbcTemplate;
@Test
public void defineFidByPhoneTest() {
PhoneService2 phoneService = new PhoneService2(jdbcTemplate);
fidsDataWithEqualSinceDt.sort(byDbOrder);
when(jdbcTemplate.query(anyString(), any(MapSqlParameterSource.class), any(RowMapper.class))).thenReturn(fidsDataWithEqualSinceDt);
List<PhoneFidMap> phoneFidMap = new ArrayList<>();
phoneFidMap.add(PhoneFidMap.builder().phone("1111").build());
phoneFidMap.add(PhoneFidMap.builder().phone("1112").build());
phoneFidMap.add(PhoneFidMap.builder().phone("1113").build());
phoneFidMap.add(PhoneFidMap.builder().phone("1114").build());
Map<Integer, PhoneFidMap> res = phoneService.defineFidByPhone(phoneFidMap);
Assertions.assertEquals(3, res.size());
Assertions.assertEquals("1111", res.get(124).getPhone());
Assertions.assertEquals("1112", res.get(125).getPhone());
Assertions.assertEquals("1114", res.get(126).getPhone());
}
private final List<PhoneService2.FidsData> fidsDataWithEqualSinceDt = new ArrayList<>(
List.of(
new PhoneService2.FidsData(123, "91111", LocalDate.of(2025, 9, 10)),
new PhoneService2.FidsData(124, "91111", LocalDate.of(2025, 9, 10)),
new PhoneService2.FidsData(125, "91112", LocalDate.of(2024, 10, 10)),
new PhoneService2.FidsData(126, "91114", LocalDate.of(2024, 10, 11))
)
);
Comparator<PhoneService2.FidsData> byDbOrder =
Comparator.comparing(PhoneService2.FidsData::getPhone, Comparator.nullsFirst(Comparator.naturalOrder())) // pn.number ASC
.thenComparing(PhoneService2.FidsData::getFileSinceDate, Comparator.nullsLast(Comparator.reverseOrder())) // file_since_dt DESC
.thenComparing(PhoneService2.FidsData::getFid, Comparator.reverseOrder());
}

View file

@ -0,0 +1,60 @@
package ru.nbch.credit_tracker.service;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import ru.nbch.credit_tracker.entities.dto.PhoneFidMap;
import ru.nbch.credit_tracker.mapper.indic.PhoneMapper;
import ru.nbch.credit_tracker.service.indic.PhoneService;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
public class PhoneServiceTest {
@Mock
PhoneMapper phoneMapper;
@Test
public void defineFidByPhoneTest() {
PhoneService phoneService = new PhoneService(phoneMapper);
fidsDataWithEqualSinceDt.sort(byDbOrder);
when(phoneMapper.findFids(Mockito.anyList())).thenReturn(fidsDataWithEqualSinceDt);
List<PhoneFidMap> phoneFidMap = new ArrayList<>();
phoneFidMap.add(PhoneFidMap.builder().phone("1111").build());
phoneFidMap.add(PhoneFidMap.builder().phone("1112").build());
phoneFidMap.add(PhoneFidMap.builder().phone("1113").build());
phoneFidMap.add(PhoneFidMap.builder().phone("1114").build());
Map<Integer, PhoneFidMap> res = phoneService.defineFidByPhone(phoneFidMap);
Assertions.assertEquals(3, res.size());
Assertions.assertEquals("1111", res.get(124).getPhone());
Assertions.assertEquals("1112", res.get(125).getPhone());
Assertions.assertEquals("1114", res.get(126).getPhone());
}
private final List<PhoneService.FidsData> fidsDataWithEqualSinceDt = new ArrayList<>(
List.of(
new PhoneService.FidsData(123, "91111", LocalDate.of(2025, 9, 10)),
new PhoneService.FidsData(124, "91111", LocalDate.of(2025, 9, 10)),
new PhoneService.FidsData(125, "91112", LocalDate.of(2024, 10, 10)),
new PhoneService.FidsData(126, "91114", LocalDate.of(2024, 10, 11))
)
);
Comparator<PhoneService.FidsData> byDbOrder =
Comparator.comparing(PhoneService.FidsData::getPhone, Comparator.nullsFirst(Comparator.naturalOrder())) // pn.number ASC
.thenComparing(PhoneService.FidsData::getFileSinceDate, Comparator.nullsLast(Comparator.reverseOrder())) // file_since_dt DESC
.thenComparing(PhoneService.FidsData::getFid, Comparator.reverseOrder());
}

View file

@ -28,17 +28,14 @@ import java.util.zip.ZipInputStream;
import static ru.nbch.credit_tracker.utils.error.ExceptionUtils.clientExcp; import static ru.nbch.credit_tracker.utils.error.ExceptionUtils.clientExcp;
@SpringBootTest(classes = {ValidationServiceTest.TestConfig.class}) @SpringBootTest(classes = {ValidationUtilTest.TestConfig.class})
public class ValidationServiceTest { public class ValidationUtilTest {
private final StaxXmlProcessor staxXmlProcessor; private final StaxXmlProcessor staxXmlProcessor;
private final ValidationService validationService;
@Autowired @Autowired
public ValidationServiceTest(StaxXmlProcessor staxXmlProcessor, public ValidationUtilTest(StaxXmlProcessor staxXmlProcessor) {
ValidationService validationService) {
this.staxXmlProcessor = staxXmlProcessor; this.staxXmlProcessor = staxXmlProcessor;
this.validationService = validationService;
} }
@Configuration @Configuration
@ -49,11 +46,6 @@ public class ValidationServiceTest {
staxReader.add(MonitoringRequestStaxReader::new); staxReader.add(MonitoringRequestStaxReader::new);
return new XmlProcessorStaxImpl(staxReader); return new XmlProcessorStaxImpl(staxReader);
} }
@Bean
public ValidationService validationService() {
return new ValidationService();
}
} }
private static Stream<Arguments> validationTestCases() { private static Stream<Arguments> validationTestCases() {
@ -115,7 +107,7 @@ public class ValidationServiceTest {
if(ex == null) { if(ex == null) {
try { try {
validationService.validateMonitoringRequest(monitoringRequest); ValidationUtil.validateMonitoringRequest(monitoringRequest);
} catch (SignalClientException e) { } catch (SignalClientException e) {
ex = e; ex = e;
} }