Compare commits

..

32 commits

Author SHA1 Message Date
etreschenkov
3e7bfa51a0 http://jira.mfd.msk:8088/browse/BKI-3341 2025-12-03 15:47:58 +03:00
etreschenkov
99222c740e http://jira.mfd.msk:8088/browse/BKI-3288 2025-11-06 12:20:58 +03:00
etreschenkov
5dc5e72496 http://jira.mfd.msk:8088/browse/BKI-3288 2025-11-06 12:20:38 +03:00
etreschenkov
b85cda38ef http://jira.mfd.msk:8088/browse/BKI-3287 2025-11-05 17:31:21 +03:00
etreschenkov
14138b4455 http://jira.mfd.msk:8088/browse/BKI-3287 2025-11-05 17:14:46 +03:00
etreschenkov
786cc89718 http://jira.mfd.msk:8088/browse/BKI-3286 2025-11-05 12:36:38 +03:00
etreschenkov
6b0f8668ae http://jira.mfd.msk:8088/browse/BKI-3286 2025-11-05 12:29:12 +03:00
Mikhail Trofimov
7bb08ebb59 Updated version to 1.20 2025-10-16 14:30:38 +03:00
Mikhail Trofimov
439b934c5c http://jira.mfd.msk:8088/browse/BKI-3264 rename ddls 2025-10-16 14:28:04 +03:00
Mikhail Trofimov
10257b9cb2 http://jira.mfd.msk:8088/browse/BKI-3264 Changed id types to bigint 2025-10-16 14:01:17 +03:00
Mikhail Trofimov
8784b4e4f3 Updated version to 1.19 2025-10-14 15:44:09 +03:00
Mikhail Trofimov
60cb6e9e8c http://jira.mfd.msk:8088/browse/BKI-3246 Get rid of update duplicate 2025-10-14 15:42:17 +03:00
etreschenkov
ddc4418b1f add nbki ca[fix image build] 2025-10-14 15:29:12 +03:00
Mikhail Trofimov
33723b60d5 Updated version to 1.18 2025-10-14 11:15:30 +03:00
Mikhail Trofimov
d963533a87 http://jira.mfd.msk:8088/browse/BKI-3236 Changed return error typy for /all_packages 2025-10-14 11:14:41 +03:00
Mikhail Trofimov
9253dd3f19 http://jira.mfd.msk:8088/browse/BKI-3246 Added signal_user_packages.monitoring_at column; Added monitoring_at setup logic 2025-10-10 14:21:41 +03:00
Mikhail Trofimov
f950f98536 http://jira.mfd.msk:8088/browse/BKI-3215 Moved member code logging logic to MemberCodeHttpLoggingFilter; Added CachedServletInputStream; Updated CachedHttpRequestWrapper 2025-10-06 17:11:03 +03:00
Mikhail Trofimov
3b9c2eaa15 http://jira.mfd.msk:8088/browse/BKI-3215 Added CachedHttpRequestWrapper to handle request body before doFilter invocation; Added memberCode ro logs; Updated version to 1.17 2025-10-06 15:37:27 +03:00
Mikhail Trofimov
b61970a71e http://jira.mfd.msk:8088/browse/BKI-3215 Changed log error lvl to warn for getErrorFileName method 2025-10-02 10:07:30 +03:00
Mikhail Trofimov
83bcea13b1 http://jira.mfd.msk:8088/browse/BKI-3231 Minor fix 2025-10-02 10:03:44 +03:00
Mikhail Trofimov
10180bb471 Get rid of pluginManagement 2025-10-01 16:24:38 +03:00
Mikhail Trofimov
9a44d8cd5e Updated version to 1.16 2025-10-01 15:52:46 +03:00
Mikhail Trofimov
6571e4d90f http://jira.mfd.msk:8088/browse/BKI-3215 Added packageName to SignalClientException for logging it on error 2025-10-01 15:50:46 +03:00
Mikhail Trofimov
dc89f17761 http://jira.mfd.msk:8088/browse/BKI-3215 Added reqParams to HttpLoggingFilter 2025-10-01 14:45:07 +03:00
Mikhail Trofimov
ea12395559 http://jira.mfd.msk:8088/browse/BKI-3231 Changed DETECTION_THRESHOLD_* property to LEAK_DETECTION_THRESHOLD_* 2025-10-01 10:12:02 +03:00
Mikhail Trofimov
d02b471466 http://jira.mfd.msk:8088/browse/BKI-3231 Moved db properties to .properties and .env file 2025-10-01 09:58:30 +03:00
Mikhail Trofimov
6b5f39c755 http://jira.mfd.msk:8088/browse/BKI-3215 Updated HttpLoggingFilter to log incoming requests 2025-09-30 16:36:42 +03:00
Mikhail Trofimov
e6bf35f337 http://jira.mfd.msk:8088/browse/BKI-3215 Use MDC for monitoring, updating flags, setup monitoring; Update ValidationUtil to a service 2025-09-30 16:20:24 +03:00
Mikhail Trofimov
061c24b89c http://jira.mfd.msk:8088/browse/BKI-3215 Changed log lvl to warn for error checking; Added SetAndClearMdc annotation for packageName logging 2025-09-30 14:35:21 +03:00
Mikhail Trofimov
2c3b2822c2 http://jira.mfd.msk:8088/browse/BKI-3215 Moved SignalServerException logs to GlobalExceptionHandler; Added warn logs to GlobalExceptionHandler for SignalClientException 2025-09-30 14:20:45 +03:00
Mikhail Trofimov
98622d7817 http://jira.mfd.msk:8088/browse/BKI-3215 Log http requests; Log slow mapper calls; Added spring AOP 2025-09-30 14:15:33 +03:00
Mikhail Trofimov
f888f9af4c http://jira.mfd.msk:8088/browse/BKI-3230 Get rid of SimpleDateFormat 2025-09-30 14:08:42 +03:00
67 changed files with 1234 additions and 1763 deletions

View file

@ -1,6 +1,5 @@
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
@ -14,6 +13,11 @@ 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"]

33
ca_nbki.cer Normal file
View file

@ -0,0 +1,33 @@
-----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,21 +8,45 @@ 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.100:2668/cprosd22:currentSchema=INDIC;} ct.indic-data-source-settings.url=${DB_URL_INDIC:jdbc:db2://10.230.227.101:2668/cprosd22:currentSchema=INDIC;}
ct.indic-data-source-settings.username=${USERNAME_INDIC:ros9appl} ct.indic-data-source-settings.username=${USERNAME_INDIC:tester}
ct.indic-data-source-settings.password=${PASSWORD_INDIC:R0s9Appl} ct.indic-data-source-settings.password=${PASSWORD_INDIC:tester}
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}
@ -42,7 +66,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-interval=${UPDATE_FLAGS_INTERVAL:1} ct.update-flags-cron-expression=${UPDATE_FLAGS_CRON_EXPRESSION:0 0 22 * * *}
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.15 APP_VERSION=1.24
REGISTRY_URL=rh8-vm3.nbki.msk:5000 REGISTRY_URL=rh8-vm3.nbki.msk:5000
JMX_PORT= JMX_PORT=
JMX_HOSTNAME= JMX_HOSTNAME=
@ -24,16 +24,41 @@ 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
@ -46,7 +71,7 @@ SIGNALS_USER=0001ZZ000001
# min # min
MONITORING_INTERVAL=15 MONITORING_INTERVAL=15
# days # days
UPDATE_FLAGS_INTERVAL=1 UPDATE_FLAGS_CRON_EXPRESSION=0 22 * * *
DAYS_TO_UPDATE_FLAGS=30 DAYS_TO_UPDATE_FLAGS=30

View file

@ -23,12 +23,36 @@ 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,272 +1,275 @@
<?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.15</version> <version>1.24</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>rh8-vm3.nbki.msk:5000</registry.url> <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>-->
</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>com.h2database</groupId> <groupId>org.mockito</groupId>
<artifactId>h2</artifactId> <artifactId>mockito-junit-jupiter</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> </dependencies>
<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>
<pluginManagement> <plugins>
<plugins> <plugin>
<plugin> <groupId>io.fabric8</groupId>
<groupId>io.fabric8</groupId> <artifactId>docker-maven-plugin</artifactId>
<artifactId>docker-maven-plugin</artifactId> <version>0.45.0</version>
<version>0.45.0</version> <executions>
<executions> <execution>
<execution> <id>Build and deploy docker container</id>
<id>Build and deploy docker container</id> <phase>install</phase>
<phase>install</phase> <goals>
<goals> <goal>build</goal>
<!--<goal>build</goal>--> <goal>push</goal>
<goal>push</goal> </goals>
</goals> </execution>
</execution> </executions>
</executions> <configuration>
<configuration> <images>
<images> <image>
<image> <name>${project.artifactId}:${project.version}</name>
<name>${project.artifactId}:${project.version}</name> <registry>${registry.url}</registry>
<registry>${registry.url}</registry> <build>
<build> <dockerFile>${project.basedir}/Dockerfile</dockerFile>
<dockerFile>${project.basedir}/Dockerfile</dockerFile> <assembly>
<assembly> <inline>
<inline> <files>
<files> <file>
<file> <source>
<source>${project.build.directory}/${project.build.finalName}.jar</source> ${project.build.directory}/${project.build.finalName}.jar
<outputDirectory>.</outputDirectory> </source>
</file> <outputDirectory>.</outputDirectory>
<file> </file>
<source>${project.basedir}/apm-agent/elastic-apm-agent-1.45.0.jar</source> <file>
<outputDirectory>.</outputDirectory> <source>
</file> ${project.basedir}/apm-agent/elastic-apm-agent-1.45.0.jar
<file> </source>
<source>${project.basedir}/start-credit-tracker.sh</source> <outputDirectory>.</outputDirectory>
<outputDirectory>.</outputDirectory> </file>
</file> <file>
<file> <source>${project.basedir}/start-credit-tracker.sh</source>
<source>${project.basedir}/credit-tracker.properties</source> <outputDirectory>.</outputDirectory>
<outputDirectory>.</outputDirectory> </file>
</file> <file>
<file> <source>${project.basedir}/credit-tracker.properties</source>
<source>${project.basedir}/monitoring-request.xsd</source> <outputDirectory>.</outputDirectory>
<outputDirectory>.</outputDirectory> </file>
</file> <file>
<file> <source>${project.basedir}/monitoring-request.xsd</source>
<source>${project.basedir}/signal-package.xsd</source> <outputDirectory>.</outputDirectory>
<outputDirectory>.</outputDirectory> </file>
</file> <file>
</files> <source>${project.basedir}/signal-package.xsd</source>
</inline> <outputDirectory>.</outputDirectory>
</assembly> </file>
<tags> <file>
<tag>${project.version}</tag> <source>
</tags> ${project.basedir}/ca_nbki.cer
</build> </source>
</image> <outputDirectory>.</outputDirectory>
</images> </file>
</configuration> </files>
</plugin> </inline>
</plugins> </assembly>
</pluginManagement> <tags>
</build> <tag>${project.version}</tag>
</profile> </tags>
</profiles> </build>
<build> </image>
<finalName>credit-tracker</finalName> </images>
<plugins> </configuration>
<plugin> </plugin>
<groupId>org.apache.maven.plugins</groupId> </plugins>
<artifactId>maven-compiler-plugin</artifactId> </build>
<configuration> </profile>
<annotationProcessorPaths> </profiles>
<path> <build>
<groupId>org.projectlombok</groupId> <finalName>credit-tracker</finalName>
<artifactId>lombok</artifactId> <plugins>
</path> <plugin>
</annotationProcessorPaths> <groupId>org.apache.maven.plugins</groupId>
</configuration> <artifactId>maven-compiler-plugin</artifactId>
</plugin> <configuration>
<plugin> <annotationProcessorPaths>
<groupId>org.springframework.boot</groupId> <path>
<artifactId>spring-boot-maven-plugin</artifactId> <groupId>org.projectlombok</groupId>
<configuration> <artifactId>lombok</artifactId>
<excludes> </path>
<exclude> </annotationProcessorPaths>
<groupId>org.projectlombok</groupId> </configuration>
<artifactId>lombok</artifactId> </plugin>
</exclude> <plugin>
</excludes> <groupId>org.springframework.boot</groupId>
</configuration> <artifactId>spring-boot-maven-plugin</artifactId>
</plugin> <configuration>
<plugin> <excludes>
<groupId>pl.project13.maven</groupId> <exclude>
<artifactId>git-commit-id-plugin</artifactId> <groupId>org.projectlombok</groupId>
<version>4.9.10</version> <artifactId>lombok</artifactId>
<executions> </exclude>
<execution> </excludes>
<id>get-the-git-infos</id> </configuration>
<goals> </plugin>
<goal>revision</goal> <plugin>
</goals> <groupId>pl.project13.maven</groupId>
<phase>compile</phase> <artifactId>git-commit-id-plugin</artifactId>
</execution> <version>4.9.10</version>
</executions> <executions>
<configuration> <execution>
<failOnNoGitDirectory>false</failOnNoGitDirectory> <id>get-the-git-infos</id>
<dotGitDirectory>${project.basedir}/${git.info.relative.dir}</dotGitDirectory> <goals>
<generateGitPropertiesFile>true</generateGitPropertiesFile> <goal>revision</goal>
<generateGitPropertiesFilename> </goals>
${project.build.outputDirectory}/${git.info.filename} <phase>compile</phase>
</generateGitPropertiesFilename> </execution>
<prefix>${git.info.prefix}</prefix> </executions>
<includeOnlyProperties> <configuration>
<includeOnlyProperty>${git.info.prefix}.commit.id</includeOnlyProperty> <failOnNoGitDirectory>false</failOnNoGitDirectory>
<includeOnlyProperty>${git.info.prefix}.branch</includeOnlyProperty> <dotGitDirectory>${project.basedir}/${git.info.relative.dir}</dotGitDirectory>
<includeOnlyProperty>${git.info.prefix}.build.version</includeOnlyProperty> <generateGitPropertiesFile>true</generateGitPropertiesFile>
<includeOnlyProperty>${git.info.prefix}.build.time</includeOnlyProperty> <generateGitPropertiesFilename>
<includeOnlyProperty>${git.info.prefix}.closest.tag.name</includeOnlyProperty> ${project.build.outputDirectory}/${git.info.filename}
</includeOnlyProperties> </generateGitPropertiesFilename>
</configuration> <prefix>${git.info.prefix}</prefix>
</plugin> <includeOnlyProperties>
<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,13 +6,12 @@ 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 DateFormat DATE_FORMAT = new SimpleDateFormat("dd.MM.yyyy"); private final static DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("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";
@ -31,7 +30,7 @@ public class PersonFactoryBuilder {
private PersonService.PersonData personData; private PersonService.PersonData personData;
private Integer uid; private Long uid;
public static PersonFactoryBuilder builder() { public static PersonFactoryBuilder builder() {
return new PersonFactoryBuilder(); return new PersonFactoryBuilder();
@ -42,7 +41,7 @@ public class PersonFactoryBuilder {
return this; return this;
} }
public PersonFactoryBuilder uid(Integer uid) { public PersonFactoryBuilder uid(Long uid) {
this.uid = uid; this.uid = uid;
return this; return this;
} }
@ -57,10 +56,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(CTUtil.localDatetoDate(nameData.getBirthDate()))); person.setD(DATE_FORMAT.format(nameData.getBirthDate()));
person.setPn(getPn(passportData.getSerNum(), passportData.getIdNum())); person.setPn(getPn(passportData.getSerNum(), passportData.getIdNum()));
person.setPd(DATE_FORMAT.format(CTUtil.localDatetoDate(LocalDate.now()))); // Do not use passport iss_date. Signal service could reject the package person.setPd(DATE_FORMAT.format(LocalDate.now())); // Do not use passport iss_date. Signal service could reject the package
person.setDt(getDt(passportData.getIdType(), passportData.getIdType2())); 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);
@ -69,12 +68,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(CTUtil.localDatetoDate(LocalDate.now()))); person.setConsentDate(DATE_FORMAT.format(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(CTUtil.localDatetoDate(LocalDate.now()))); person.setAgrDate(DATE_FORMAT.format(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 DataSource createDataSource(DataSourceSettings settings) { protected HikariDataSource 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,5 +1,6 @@
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;
@ -9,8 +10,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;
@ -29,14 +30,25 @@ 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 {
DataSource dataSource = createDataSource(config.getIndicDataSourceSettings()); DataSourceSettings indicSetings = 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={}", config.getIndicDataSourceSettings().url(), e); log.error("Unable to connect to Indic url={}", indicSetings.url(), e);
throw e; throw e;
} }
log.info("Connected to Indic: url={}", config.getIndicDataSourceSettings().url()); log.info("Connected to Indic: url={}", indicSetings.url());
return dataSource; return dataSource;
} }
@ -48,10 +60,5 @@ 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,5 +1,6 @@
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,6 +11,7 @@ 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;
@ -28,14 +30,25 @@ 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 {
DataSource dataSource = createDataSource(config.getScoringDataSourceSettings()); DataSourceSettings scoringSetings = 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={}", config.getScoringDataSourceSettings().url(), e); log.error("Unable to connect to Scoring url={}", scoringSetings.url(), e);
throw e; throw e;
} }
log.info("Connected to Scoring: url={}", config.getScoringDataSourceSettings().url()); log.info("Connected to Scoring: url={}", scoringSetings.url());
return dataSource; return dataSource;
} }

View file

@ -1,5 +1,6 @@
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,6 +11,7 @@ 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;
@ -27,14 +29,25 @@ 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 {
DataSource dataSource = createDataSource(config.getSignalDataSourceSettings()); DataSourceSettings signalSetings = 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={}", config.getSignalDataSourceSettings().url(), e); log.error("Unable to connect to Signal url={}", signalSetings.url(), e);
throw e; throw e;
} }
log.info("Connected to Signal: url={}", config.getSignalDataSourceSettings().url()); log.info("Connected to Signal: url={}", signalSetings.url());
return dataSource; return dataSource;
} }

View file

@ -0,0 +1,19 @@
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

@ -0,0 +1,49 @@
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

@ -0,0 +1,15 @@
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,7 +55,12 @@ public class GlobalExceptionHandler {
commonError.setErrorText(errorText); commonError.setErrorText(errorText);
if (ex instanceof SignalClientException) { if (ex instanceof SignalClientException) {
log.warn("Response error: {}", errorText); String packageName = ((SignalClientException) ex).getPackageName();
if(packageName != null ) {
log.warn("Response error: {}. PackageName: {}", errorText, packageName);
} else {
log.warn("Response error: {}", errorText);
}
return new ResponseEntity<>(commonError, headers, HttpStatus.BAD_REQUEST); return new ResponseEntity<>(commonError, headers, HttpStatus.BAD_REQUEST);
} else { } else {
Exception cause = ((SignalServerException) ex).getCause(); Exception cause = ((SignalServerException) ex).getCause();
@ -69,6 +74,7 @@ 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,17 +3,9 @@ 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 java.io.IOException; import jakarta.servlet.http.Part;
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;
@ -22,6 +14,10 @@ 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)
@ -34,12 +30,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.startsWith("/v3/api-docs") || path.startsWith("/static/") || path.equals("/");
} }
@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();
@ -47,17 +43,18 @@ 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;
String requestBody = readBody(req.getContentAsByteArray(), req.getCharacterEncoding()); Map<String, String> reqParams = paramsMap(req);
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);
@ -66,43 +63,67 @@ public class HttpLoggingFilter extends OncePerRequestFilter {
mask(respHeaders); mask(respHeaders);
log.info( log.info(
"HTTP {} {}{} | status={} | took={}ms | cid={} | reqHeaders={} | reqBody={} | respHeaders={} | respBody={}", "HTTP (RESPONSE) {} {} | status={} | took={}ms | cid={} | reqHeaders={} | reqParams={} | respHeaders={}",
req.getMethod(), req.getMethod(),
req.getRequestURI(), req.getRequestURI(),
req.getQueryString() == null ? "" : "?" + req.getQueryString(), resp.getStatus(),
resp.getStatus(), tookMs,
tookMs, correlationId,
correlationId, reqHeaders,
reqHeaders, reqParams,
requestBody, respHeaders
respHeaders,
responseBody
); );
resp.copyBodyToResponse(); resp.copyBodyToResponse();
} }
} }
private static ContentCachingRequestWrapper wrapRequest(HttpServletRequest request) { private void logIncomingRequest(ContentCachingRequestWrapper req, String correlationId) {
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

@ -0,0 +1,55 @@
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

@ -0,0 +1,25 @@
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

@ -0,0 +1,33 @@
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 Long updateFlagsInterval; private String updateFlagsCronExpression;
private Long daysToUpdateFlags; private Long daysToUpdateFlags;
private String encoding; private String encoding;

View file

@ -4,5 +4,13 @@ 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,6 +6,8 @@ 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;
@ -26,6 +28,8 @@ 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 Integer id; private Long id;
private Integer packageId; private Long 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 Integer id; private Long id;
private Integer packageId; private Long packageId;
private String subjectId; private String subjectId;
private String phone; private String phone;
private Integer fid; private Integer fid;

View file

@ -10,11 +10,12 @@ import java.time.LocalDateTime;
@Setter @Setter
@Builder @Builder
public class UserPackage { public class UserPackage {
private Integer id; private Long 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 Integer uid; protected Long 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 Integer uid; protected Long 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 Integer uid; protected Long uid;
@XmlElement(name = "Inquiry", required = true) @XmlElement(name = "Inquiry", required = true)
protected List<Inquiry> inquiries; protected List<Inquiry> inquiries;

View file

@ -0,0 +1,25 @@
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,6 +2,7 @@ package ru.nbch.credit_tracker.enums;
public enum XmlErrorFormat { public enum XmlErrorFormat {
MonitoringActivePackagesError, MonitoringActivePackagesError,
MonitoringAllPackagesError,
MonitoringDownloadError, MonitoringDownloadError,
MonitoringError, MonitoringError,
MonitoringPackageDeleteError, MonitoringPackageDeleteError,

View file

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

View file

@ -1,12 +1,16 @@
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;
@ -31,7 +35,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.ValidationUtil; import ru.nbch.credit_tracker.utils.ValidationService;
import ru.nbch.credit_tracker.utils.ZipUtils; import ru.nbch.credit_tracker.utils.ZipUtils;
import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamException;
@ -43,6 +47,7 @@ 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;
@ -54,12 +59,17 @@ 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;
@ -71,6 +81,7 @@ 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;
@ -78,6 +89,7 @@ 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;
@ -91,7 +103,7 @@ public class SignalFacade {
// Валидация Архива на количетсво файлов, размер и расширение // Валидация Архива на количетсво файлов, размер и расширение
MonitoringRequest monitoringRequest; MonitoringRequest monitoringRequest;
Integer packageId; Long packageId;
ZipInputStream zipInputStream = null; ZipInputStream zipInputStream = null;
String filePath; String filePath;
try { try {
@ -144,13 +156,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(), XmlErrorFormat.MonitoringError); throw clientExcp(authResult.getError(), monitoringRequest.getPackageName(), XmlErrorFormat.MonitoringError);
} }
// проверка на уникальность пакета // проверка на уникальность пакета
if (signalService.isPackageNotUnique(monitoringRequest)) { if (signalService.isPackageNotUnique(monitoringRequest)) {
signalService.rejectPackage(monitoringRequest); signalService.rejectPackage(monitoringRequest);
throw clientExcp(SignalError.ERROR_007, XmlErrorFormat.MonitoringError); throw clientExcp(SignalError.ERROR_007, monitoringRequest.getPackageName(), XmlErrorFormat.MonitoringError);
} }
packageId = signalService.registerPackage(monitoringRequest); packageId = signalService.registerPackage(monitoringRequest);
@ -164,7 +176,7 @@ public class SignalFacade {
} }
// в отдельном потоке запускаем регистрацию субъектов, поиск фидов и расчет флагов просрочек // в отдельном потоке запускаем регистрацию субъектов, поиск фидов и расчет флагов просрочек
registerFidsAndDebts(packageId, monitoringRequest.getPackageName(), filePath); getCurrentProxy().registerFidsAndDebts(packageId, monitoringRequest.getPackageName(), filePath);
return ResponseEntity.ok().body(new MonitoringSetupResponse()); return ResponseEntity.ok().body(new MonitoringSetupResponse());
} }
@ -175,7 +187,7 @@ public class SignalFacade {
monitoringRequest = staxXmlProcessor.read(in, MonitoringRequest.class, monitoringRequest = staxXmlProcessor.read(in, MonitoringRequest.class,
request -> { request -> {
try { try {
ValidationUtil.validateMonitoringRequest(request); validationService.validateMonitoringRequest(request);
} catch (SignalClientException e) { } catch (SignalClientException e) {
if (request != null) { if (request != null) {
signalService.rejectPackage(request); signalService.rejectPackage(request);
@ -211,8 +223,13 @@ public class SignalFacade {
} }
} }
private void registerFidsAndDebts(Integer packageId, String packageName, String filePath) { @SetAndClearMdc(argIndex = 1)
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);
@ -223,8 +240,9 @@ public class SignalFacade {
return; return;
} }
if (signalRestService.setupMonitoring(packageId)) { if (signalRestService.setupMonitoring(packageId)) {
log.info("Package was successfully registered in signal service. PackageId: {} PackageName: {}", packageId, packageName); LocalDateTime monitoringAt = LocalDateTime.now();
signalService.updatePackageStatus(packageId, Status.ON_MONITORING); log.info("Package was successfully registered in signal service. PackageId: {} PackageName: {}, MonitoringAt: {}", packageId, packageName, monitoringAt);
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);
@ -280,7 +298,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.MonitoringActivePackagesError); throw clientExcp(authResult.getError(), XmlErrorFormat.MonitoringAllPackagesError);
} }
List<UserPackage> activePackages = signalService.findAllPackages(allPackagesRequest.getUserId()); List<UserPackage> activePackages = signalService.findAllPackages(allPackagesRequest.getUserId());
@ -302,13 +320,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.MonitoringActivePackagesError); throw clientExcp(SignalError.ERROR_019, XmlErrorFormat.MonitoringAllPackagesError);
} }
} 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.MonitoringActivePackagesError, e); throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringAllPackagesError, e);
} }
} }
@ -408,4 +426,11 @@ 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,8 +10,10 @@ import java.util.List;
@Mapper @Mapper
public interface FlagsMapper { public interface FlagsMapper {
List<FlagsService.RuPastDuarearData> findRuPastDuarearData(@Param("fids") List<Integer> fids); List<FlagsService.FlagFidData> findFlagsDebt5000(@Param("fids") List<Integer> fids);
List<FlagsService.RuPaymtData> findRuPaymtData(@Param("fids") List<Integer> fids, @Param("reportedDate") LocalDate reportedDate); List<FlagsService.FlagFidData> findFlag90ByPastDueArrear(@Param("fids") List<Integer> fids, @Param("reportedDate") LocalDate reportedDate);
List<FlagsService.FlagFidData> findFlag90ByRuPaymt(@Param("fids") List<Integer> fids, @Param("reportedDate") LocalDate reportedDate);
} }

View file

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

View file

@ -18,13 +18,15 @@ 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);
Integer insertPackage(@Param("userPackage") UserPackage userPackage); Long insertPackage(@Param("userPackage") UserPackage userPackage);
void updatePackageStatus(@Param("packageId") Integer packageId, @Param("status") String status); void updatePackageStatus(@Param("packageId") Long packageId, @Param("status") String status);
void updatePackageLastDownloadedReport(@Param("id") Integer id, @Param("fileName") String fileName); void updatePackageLastDownloadedReport(@Param("id") Long id, @Param("fileName") String fileName);
UserPackage findPackage(@Param("id") Integer id); void updatePackageOnMonitoringSuccess(@Param("id") Long id, @Param("monitoringAt") LocalDateTime monitoringAt);
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,15 +2,21 @@ 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;
@ -19,8 +25,13 @@ public class MonitoringErrorProcesingService {
this.signalRestService = signalRestService; this.signalRestService = signalRestService;
} }
public void findErrors(Integer packageId) { public void findErrors(Long 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);
@ -38,4 +49,11 @@ public class MonitoringErrorProcesingService {
} }
} }
} }
private MonitoringErrorProcesingService getCurrentProxy() {
if (self == null) {
self = (MonitoringErrorProcesingService) AopContext.currentProxy();
}
return self;
}
} }

View file

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

View file

@ -16,9 +16,8 @@ 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.PhoneService2; import ru.nbch.credit_tracker.service.indic.PhoneService;
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;
@ -44,8 +43,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 PhoneService2 phoneService; private final PhoneService phoneService;
private final FlagsService2 flagsService; private final FlagsService flagsService;
private final PersonService personService; private final PersonService personService;
private final StaxXmlProcessor staxXmlProcessor; private final StaxXmlProcessor staxXmlProcessor;
@ -55,8 +54,8 @@ public class SignalService {
HitsService hitsService, HitsService hitsService,
UserPackageService userPackageService, UserPackageService userPackageService,
PhoneFidMapService phoneFidMapService, PhoneFidMapService phoneFidMapService,
PhoneService2 phoneService, PhoneService phoneService,
FlagsService2 flagsService, FlagsService flagsService,
PersonService personService, PersonService personService,
StaxXmlProcessor staxXmlProcessor) { StaxXmlProcessor staxXmlProcessor) {
this.config = config; this.config = config;
@ -73,7 +72,7 @@ public class SignalService {
return userPackageService.isPackageNotUnique(request.getUserId(), request.getPackageName()); return userPackageService.isPackageNotUnique(request.getUserId(), request.getPackageName());
} }
public Integer registerPackage(MonitoringRequest request) { public Long registerPackage(MonitoringRequest request) {
UserPackage userPackage = UserPackage.builder() UserPackage userPackage = UserPackage.builder()
.userId(request.getUserId()) .userId(request.getUserId())
.packageName(request.getPackageName()) .packageName(request.getPackageName())
@ -83,7 +82,7 @@ public class SignalService {
return userPackageService.insertPackage(userPackage); return userPackageService.insertPackage(userPackage);
} }
public void registerSubjects(String filePath, Integer packageId) { public void registerSubjects(String filePath, Long 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,
@ -106,12 +105,11 @@ public class SignalService {
/** /**
* Обрабатывает все PhoneFidMap по packageId, выгружая их пачками и фильтруя по переданному фильтру * Обрабатывает все PhoneFidMap по packageId, выгружая их пачками и фильтруя по переданному фильтру
*/ */
public UpdateResult updateSubjects(Integer packageId, Predicate<PhoneFidMap> filter) { public UpdateResult updateSubjects(Long 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);
@ -127,30 +125,15 @@ public class SignalService {
} }
if (!filteredItems.isEmpty()) { if (!filteredItems.isEmpty()) {
Map<Integer, PhoneFidMap> fidsMapByBatch = phoneService.defineFidByPhone(filteredItems); boolean fidsFound = updateFlags(filteredItems, packageId);
log.debug("Actual fids size by phone's batch: {}", fidsMapByBatch.size()); if (noFidsFound) {
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);
} }
@ -158,37 +141,50 @@ public class SignalService {
* Обрабатывает пачку PhoneFidMap: обновляет фиды и флаги просрочек * Обрабатывает пачку PhoneFidMap: обновляет фиды и флаги просрочек
* @return fidsFound true - если хотя бы по одному номеру был найден фид, false - в противном случае * @return fidsFound true - если хотя бы по одному номеру был найден фид, false - в противном случае
*/ */
// TODO Добавить отдельный метод. Если не нашелся фид, надо обновить в базе fid = null и флаги = null calculated_at=now() public boolean updateFlags(List<PhoneFidMap> subjects, Long packageId) {
public boolean updateFlags(Map<Integer, PhoneFidMap> fidsMap, Integer packageId) { log.info("Start calculating fids and flags for subjects batch. PackageId: {}", packageId);
log.info("Start calculating flags for subjects batch. PackageId: {}", packageId); Map<String, Integer> fidsMap = phoneService.findFidsPhoneData(subjects);
Map<Integer, FlagsService.FlagsData> flagsDataMap = flagsService.findFlags(fidsMap.keySet().stream().toList()); LocalDateTime calculatedAt = LocalDateTime.now();
boolean fidsFound = fidsMap.values().stream().anyMatch(Objects::nonNull);
Map<Integer, FlagsService.FlagsData> flagsDataMap = flagsService.findFlags(fidsMap.values().stream().filter(Objects::nonNull).toList());
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);
phoneFidMap.setFid(fid); subjects.forEach(subject -> {
phoneFidMap.setFlagDebt5000(flagsData.getFlagDebt5000()); Integer fid = fidsMap.get("9" + subject.getPhone());
phoneFidMap.setFlag90days(flagsData.getFlag90days()); if (fid != null) {
phoneFidMap.setCalculatedAt(LocalDateTime.now()); FlagsService.FlagsData flagsData = flagsDataMap.get(fid);
subject = PhoneFidMap.builder()
subjectToUpdate.add(phoneFidMap); .id(subject.getId())
} .packageId(packageId)
.subjectId(subject.getSubjectId())
log.debug("Start updating subject's batch"); .phone(subject.getPhone())
if (!subjectToUpdate.isEmpty()) { .fid(fid)
int updateBatchSize = 7000; // 9 параметров на одну запись для обновления. Максимальное количество параметров ~65000 .flagDebt5000(flagsData.getFlagDebt5000())
for (int j = 0; j < subjectToUpdate.size(); j += updateBatchSize) { .flag90days(flagsData.getFlag90days())
int endIndex = Math.min(j + updateBatchSize, subjectToUpdate.size()); .calculatedAt(calculatedAt)
phoneFidMapService.updatePhoneFidMaps(subjectToUpdate.subList(j, endIndex)); .build();
log.trace("Updated fids and flags for subject's batch from: {} to: {}. Total count: {}. PackageId: {}", j, endIndex, subjectToUpdate.size(), packageId); } else {
subject = PhoneFidMap.builder()
.id(subject.getId())
.packageId(packageId)
.subjectId(subject.getSubjectId())
.phone(subject.getPhone())
.calculatedAt(calculatedAt)
.build();
} }
} subjectToUpdate.add(subject);
log.debug("Complete updating subject's batch"); });
log.info("Finish calculating flags for subjects. PackageId: {}. Batch size: {}", packageId, fidsMap.size()); int updateBatchSize = batchSize / 9; // 9 параметров на одну запись для обновления. Максимальное количество параметров ~65000
return !fidsMap.isEmpty(); 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());
return fidsFound;
} }
public void rejectPackage(MonitoringRequest request) { public void rejectPackage(MonitoringRequest request) {
@ -201,16 +197,20 @@ public class SignalService {
userPackageService.insertPackage(userPackage); userPackageService.insertPackage(userPackage);
} }
public void updatePackageStatus(Integer packageId, Status status) { public void updatePackageStatus(Long 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(Integer packageId) { public String writeSignals(Long 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(Integer packageId) { private Signals buildSignals(Long 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(Integer packageId, Consumer<List<Person>> writer) { private void buildPersons(Long 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, Integer packageId) { public void updateSubjectsErrors(String filePath, Long 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.error("Ошибка при формировании субъекта. package_id = {}, id = {}. Описание: {}", packageId, p.getUid(), p.getValue()); log.warn("Ошибка при формировании субъекта. 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(Integer id, String fileName) { public void updateLastDownloadedReport(Long id, String fileName) {
userPackageService.updatePackageLastDownloadedReport(id, fileName); userPackageService.updatePackageLastDownloadedReport(id, fileName);
} }
public PhoneFidMap findPhoneFidMapById(Integer id) { public PhoneFidMap findPhoneFidMapById(Long id) {
return phoneFidMapService.findPhoneFidMapById(id); return phoneFidMapService.findPhoneFidMapById(id);
} }
public boolean hasNonNullfids(Integer packageId) { public boolean hasNonNullfids(Long packageId) {
return phoneFidMapService.hasNonNullfids(packageId); return phoneFidMapService.hasNonNullfids(packageId);
} }
public UserPackage findPackage(Integer packageId) { public UserPackage findPackage(Long packageId) {
return userPackageService.findPackage(packageId); return userPackageService.findPackage(packageId);
} }

View file

@ -1,7 +1,10 @@
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;
@ -14,8 +17,11 @@ 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;
@ -33,25 +39,26 @@ 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());
process(userPackage.getId(), userPackage.getPackageName()); getCurrentProxy().process(userPackage.getId(), userPackage.getPackageName());
} }
log.info("Finish updating processing packages"); log.info("Finish updating processing packages");
} }
private void process(Integer packageId, String packageName) { @SetAndClearMdc(argIndex = 1)
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: {} PackageName: {}. Package status ON_MONITORING. No registration in signal service.", packageId, packageName); log.info("No fids found for PackageId: {} . Package status ON_MONITORING. No registration in signal service.", packageId);
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: {} PackageName: {}", packageId, packageName); log.info("Package was successfully registered in signal service. PackageId: {}", packageId);
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: {} PackageName: {}", packageId, packageName); log.info("Package registration was rejected by signal service. PackageId: {}", packageId);
signalService.updatePackageStatus(packageId, Status.ERROR); signalService.updatePackageStatus(packageId, Status.ERROR);
} }
} catch (Exception e) { } catch (Exception e) {
@ -59,4 +66,11 @@ 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,21 +1,18 @@
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 java.util.concurrent.Executors; import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.ScheduledExecutorService; import org.slf4j.MDC;
import java.util.concurrent.TimeUnit; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import java.util.function.Predicate; import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Service;
import ru.nbch.credit_tracker.config.settings.CreditTrackerSettings;
import ru.nbch.credit_tracker.entities.dto.UserPackage;
import ru.nbch.credit_tracker.enums.Status;
import ru.nbch.credit_tracker.mapper.signal.PhoneFidMapMapper;
import ru.nbch.credit_tracker.service.signal.SignalRestService;
/** /**
* Сервис периодического обновления фидов и флагов просрочек по пакетам находящимся в статусе ON_MONITORING * Сервис периодического обновления фидов и флагов просрочек по пакетам находящимся в статусе ON_MONITORING
@ -29,43 +26,56 @@ 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 Long updateFlagsInterval; private final String updateFlagsCronExpression;
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.updateFlagsInterval = config.getUpdateFlagsInterval() != null ? config.getUpdateFlagsInterval() : 1; this.updateFlagsCronExpression = config.getUpdateFlagsCronExpression() != null ? config.getUpdateFlagsCronExpression() : "*/10 * * * * *";
this.phoneFidMapMapper = phoneFidMapMapper;
} }
public void run() { public void run() {
ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(new CustomizableThreadFactory("updating-flags-")); ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.scheduleAtFixedRate(this::update, 0, updateFlagsInterval, TimeUnit.DAYS); scheduler.setPoolSize(1);
scheduler.initialize();
scheduler.schedule(this::update, new CronTrigger(updateFlagsCronExpression));
} }
private void update() { public 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) {
log.info("Updating flags for Package {}", userPackage.getPackageName()); MDC.put("ru.nbch.credit_tracker.log.mdc.key", "[" + userPackage.getPackageName() +"]");
updateFlags(userPackage.getId(), userPackage.getPackageName()); log.info("Check on updating flags for Package {}", userPackage.getPackageName());
boolean hasOldRow = phoneFidMapMapper.hasOldRow(userPackage.getId(), daysToUpdateFlags.intValue());
log.info("There are {} old rows", hasOldRow ? "exist" : "doesn't exist");
if (hasOldRow) {
updateFlags(userPackage);
}
MDC.remove("ru.nbch.credit_tracker.log.mdc.key");
} }
log.info("Finish updating flags"); log.info("Finish updating flags");
} }
private void updateFlags(Integer packageId, String packageName) { private void updateFlags(UserPackage userPackage) {
Long packageId = userPackage.getId();
String packageName = userPackage.getPackageName();
try { try {
signalService.updatePackageStatus(packageId, Status.PROCESSING); signalService.updatePackageStatus(packageId, Status.PROCESSING);
Predicate<PhoneFidMap> filter = subject -> subject.getCalculatedAt() != null && hasDaysPassed(subject.getCalculatedAt()); SignalService.UpdateResult updateResult = signalService.updateSubjects(packageId, null);
SignalService.UpdateResult updateResult = signalService.updateSubjects(packageId, filter);
if (updateResult.isUpdated()) { if (updateResult.isUpdated()) {
if (signalRestService.setupMonitoring(packageId)) { if (signalRestService.setupMonitoring(packageId)) {
log.info("Flags were updated successfully. PackageId: {} PackageName: {}. Package status changed to ON_MONITORING", packageId, packageName); LocalDateTime monitoringAt = userPackage.getMonitoringAt() != null ? userPackage.getMonitoringAt() : LocalDateTime.now();
signalService.updatePackageStatus(packageId, Status.ON_MONITORING); log.info("Flags were updated successfully. PackageId: {} PackageName: {} MonitoringAt: {}. Package status changed to ON_MONITORING", packageId, packageName, monitoringAt);
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,22 +2,19 @@ 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.*; import java.util.HashMap;
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) {
@ -28,19 +25,11 @@ public class FlagsService {
if (fids.isEmpty()) { if (fids.isEmpty()) {
return new HashMap<>(); 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, FlagsData> result = new HashMap<>(); Map<Integer, FlagsData> result = new HashMap<>();
Map<Integer, Integer> flags5000 = calcFlag5000(fids);
Map<Integer, Integer> flags90DaysByPastDueArrear = calcFlag90DaysByPastDueArrear(fids);
Map<Integer, Integer> flags90DaysByRuPaymt = calcFlag90DaysByRuPaymt(fids);
fids.forEach(fid -> { fids.forEach(fid -> {
Integer flagDebt5000 = flags5000.get(fid); Integer flagDebt5000 = flags5000.get(fid);
if (flagDebt5000 == null) { if (flagDebt5000 == null) {
@ -48,7 +37,7 @@ public class FlagsService {
} }
Integer flag90days = flags90DaysByPastDueArrear.get(fid); Integer flag90days = flags90DaysByPastDueArrear.get(fid);
if (flag90days == null || flag90days == 0) { if (flag90days == null) {
flag90days = flags90DaysByRuPaymt.get(fid); flag90days = flags90DaysByRuPaymt.get(fid);
if (flag90days == null) { if (flag90days == null) {
flag90days = 0; flag90days = 0;
@ -61,118 +50,51 @@ 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, то flag500 устанавливается в 1. * Если по хотя бы одной такой записи AMT_PAST_DUE > 5000, то флаг устанавливается в 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 void findFlagsRuPastDueArrear(List<Integer> fids, Map<Integer, Integer> flag5000Map, Map<Integer, Integer> flag90Days) { private Map<Integer, Integer> calcFlag5000(List<Integer> fids) {
log.debug("Start calculating flag 5000 and 90 days by RU_PASTDUEARREAR"); return mapper.findFlagsDebt5000(fids).stream()
List<RuPastDuarearData> ruPastDuarearDataList = mapper.findRuPastDuarearData(fids); .collect(Collectors.toMap(FlagFidData::getFid, FlagFidData::getFlag,
(existing, replacement) -> {
ruPastDuarearDataList.sort(Comparator.comparing(FlagsService.RuPastDuarearData::getFid) // сортируем сначала по фиду if (existing == 1) {
.thenComparing(FlagsService.RuPastDuarearData::getReportedDate, Comparator.reverseOrder())); // Затем сортируем по REPORTED_DT по убыванию return existing;
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 { } else {
flag90Days.put(fid, 0); return replacement;
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");
} }
/** /**
* Выбирает данные из RU_PAYMT и ищет по ним флаги просрочек 90Days * Флаг = 1, если выполняется хотя бы одно из условий:
* * В 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 void findFlagsRuPaymt(List<Integer> fids, Map<Integer, Integer> flag90Days) { private Map<Integer, Integer> calcFlag90DaysByPastDueArrear(List<Integer> fids) {
log.debug("Start calculating flag 90 days by RU_PAPAYMT");
LocalDate yearAgo = LocalDate.now().minusYears(1); LocalDate yearAgo = LocalDate.now().minusYears(1);
List<RuPaymtData> ruPaymtDataList = mapper.findRuPaymtData(fids, yearAgo); return mapper.findFlag90ByPastDueArrear(fids, yearAgo).stream()
ruPaymtDataList.sort(Comparator.comparing(RuPaymtData::getFid, Comparator.naturalOrder())); .collect(Collectors.toMap(FlagFidData::getFid, FlagFidData::getFlag,
(existing, replacement) -> {
for (Integer fid : fids) { if (existing == 1) {
Iterator<RuPaymtData> iterator = ruPaymtDataList.iterator(); return existing;
while (iterator.hasNext()) {
RuPaymtData data = iterator.next();
if (fid.equals(data.getFid())) {
Integer flag90 = flag90Days.get(fid);
if (flag90 != null && flag90 == 1) {
iterator.remove();
break;
}
if (flag90 == null || flag90 == 0) {
if (data.getDaysPastDue() != null) {
boolean has90daysFlag = data.getDaysPastDue().compareTo(BigInteger.valueOf(90)) >= 0 && !data.getReportedDate().isBefore(yearAgo);
if (has90daysFlag) {
flag90Days.put(fid, 1);
iterator.remove();
break;
} else { } else {
flag90Days.put(fid, 0); return replacement;
iterator.remove();
} }
} else { }));
iterator.remove(); }
}
} private Map<Integer, Integer> calcFlag90DaysByRuPaymt(List<Integer> fids) {
} LocalDate yearAgo = LocalDate.now().minusYears(1);
} return mapper.findFlag90ByRuPaymt(fids, yearAgo).stream()
flag90Days.putIfAbsent(fid, 0); .collect(Collectors.toMap(FlagFidData::getFid, FlagFidData::getFlag,
} (existing, replacement) -> {
log.debug("Complete calculating flag 90 days by RU_PAPAYMT"); if (existing == 1) {
return existing;
} else {
return replacement;
}
}));
} }
@Getter @Getter
@ -185,23 +107,8 @@ public class FlagsService {
@Getter @Getter
@Setter @Setter
@AllArgsConstructor public static class FlagFidData {
@NoArgsConstructor
public static class RuPastDuarearData {
private Integer fid; private Integer fid;
private BigInteger accSerialNum; private Integer flag;
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

@ -1,256 +0,0 @@
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.*; import java.util.Comparator;
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,45 +24,32 @@ public class PhoneService {
/** /**
* Ищет фиды по номерам телефона * Ищет фиды по номерам телефона
* @return Map: key - fid; value - {@link PhoneFidMap} * @return Map: key - номер телефона; value - fid
*/ */
public Map<Integer, PhoneFidMap> defineFidByPhone(List<PhoneFidMap> subjects) { public Map<String, Integer> findFidsPhoneData(List<PhoneFidMap> subjects) {
Map<Integer, PhoneFidMap> phoneFidMap = new HashMap<>(); List<String> phones = subjects.stream().map(subject -> "9" + subject.getPhone()).toList();
List<String> phones = subjects.stream(). return convertFidsDataToMap(mapper.findFids(phones));
map(subject -> "9" + subject.getPhone()) }
.toList();
subjects.sort( private Map<String, Integer> convertFidsDataToMap(List<FidsData> fidsData) {
Comparator.comparing( if (fidsData == null || fidsData.isEmpty()) {
PhoneFidMap::getPhone, return new HashMap<>();
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;
}
}
} }
log.debug("End merging fids"); return fidsData.stream()
.collect(Collectors.groupingBy(
return phoneFidMap; FidsData::getPhone,
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

@ -1,96 +0,0 @@
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, Integer packageId) { public void registerSubjects(List<Subject> subjects, Long packageId) {
mapper.registerSubjects(subjects, packageId); mapper.registerSubjects(subjects, packageId);
} }
public List<PhoneFidMap> findFullPhoneFidMap(Integer packageId, int offset, int limit) { public List<PhoneFidMap> findFullPhoneFidMap(Long 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(Integer id) { public PhoneFidMap findPhoneFidMapById(Long id) {
return mapper.findPhoneFidMapById(id); return mapper.findPhoneFidMapById(id);
} }
public boolean hasNonNullfids(Integer packageId) { public boolean hasNonNullfids(Long 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(Integer packageId) throws Exception { public boolean setupMonitoring(Long 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(Integer packageId, String packCode) { public String getErrorFileName(Long 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.error("Найдены ошибки по пакету. packCode={}, packageId={}, fileName = {}", packCode, packageId, fileName); log.warn("Найдены ошибки по пакету. 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(Integer packageId, String fileName) { public String loadErrorFile(Long 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(Integer packageId, String reportName) { public String downloadReport(Long 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, Integer packageId, String reportName) { private String unzipReport(ResponseEntity<byte[]> response, Long 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,19 +34,23 @@ 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 Integer insertPackage(UserPackage userPackage) { public Long insertPackage(UserPackage userPackage) {
return mapper.insertPackage(userPackage); return mapper.insertPackage(userPackage);
} }
public void updatePackageStatus(Integer packageId, Status status) { public void updatePackageStatus(Long packageId, Status status) {
mapper.updatePackageStatus(packageId, status.name()); mapper.updatePackageStatus(packageId, status.name());
} }
public void updatePackageLastDownloadedReport(Integer id, String fileName) { public void updatePackageLastDownloadedReport(Long id, String fileName) {
mapper.updatePackageLastDownloadedReport(id, fileName); mapper.updatePackageLastDownloadedReport(id, fileName);
} }
public UserPackage findPackage(Integer id) { public void updatePackageOnMonitoringSuccess(Long id, LocalDateTime monitoringAt) {
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 {
int uid = Integer.parseInt(text); Long uid = Long.parseLong(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 {
int uid = Integer.parseInt(text); Long uid = Long.parseLong(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,6 +1,8 @@
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;
@ -12,13 +14,15 @@ 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
public class ValidationUtil { @Service
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}$";
public static void validateMonitoringRequest(MonitoringRequest request) throws SignalClientException { @SetAndClearMdc
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);
} }
@ -27,32 +31,34 @@ public class ValidationUtil {
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, XmlErrorFormat.MonitoringError); throw clientExcp(SignalError.ERROR_008, packageName, XmlErrorFormat.MonitoringError);
} }
if (request.getUserId() == null) { if (request.getUserId() == null) {
throw clientExcp(SignalError.ERROR_002, XmlErrorFormat.MonitoringError, "UserId"); throw clientExcp(SignalError.ERROR_002, packageName, XmlErrorFormat.MonitoringError, "UserId");
} }
if (request.getPassword() == null) { if (request.getPassword() == null) {
throw clientExcp(SignalError.ERROR_002, XmlErrorFormat.MonitoringError, "Password"); throw clientExcp(SignalError.ERROR_002, packageName, XmlErrorFormat.MonitoringError, "Password");
} }
if (request.getSubjects() == null) { if (request.getSubjects() == null) {
throw clientExcp(SignalError.ERROR_002, XmlErrorFormat.MonitoringError, "Subjects"); throw clientExcp(SignalError.ERROR_002, packageName, XmlErrorFormat.MonitoringError, "Subjects");
} }
if (request.getSubjects().getSubject().isEmpty()) { if (request.getSubjects().getSubject().isEmpty()) {
throw clientExcp(SignalError.ERROR_002, XmlErrorFormat.MonitoringError, "Subject"); throw clientExcp(SignalError.ERROR_002, packageName, 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, XmlErrorFormat.MonitoringError, subject.getId()); throw clientExcp(SignalError.ERROR_010, packageName, XmlErrorFormat.MonitoringError, subject.getId());
} }
if (!validatePhone(subject.getClientId())) { if (!validatePhone(subject.getClientId())) {
throw clientExcp(SignalError.ERROR_003, XmlErrorFormat.MonitoringError, subject.getId()); throw clientExcp(SignalError.ERROR_003, packageName, XmlErrorFormat.MonitoringError, subject.getId());
} }
} }
} }

View file

@ -9,7 +9,11 @@ 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 new SignalClientException(new EnumKeyMessage(enumKeyCode, args), xmlErrorFormat); return clientExcp(enumKeyCode, null, xmlErrorFormat, args);
}
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} - %msg%n</pattern> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} %X{ru.nbch.credit_tracker.log.mdc.key} - %msg%n</pattern>
<charset>utf-8</charset> <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} [%thread] %-5level %logger{36} - %msg%n</pattern> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} %X{ru.nbch.credit_tracker.log.mdc.key} - %msg%n</pattern>
<charset>utf-8</charset> <charset>utf-8</charset>
</encoder> </encoder>
</appender> </appender>
@ -63,12 +63,12 @@
</appender> </appender>
<!--SQL запросы и результаты их выполнения--> <!--SQL запросы и результаты их выполнения-->
<logger name="ru.nbch.credit_tracker.mapper" level="DEBUG" additivity="false"> <logger name="ru.nbch.credit_tracker.mapper" level="INFO" 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="TRACE" additivity="false"> <logger name="ru.nbch.credit_tracker.service" level="INFO" 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,39 +5,51 @@
<mapper namespace="ru.nbch.credit_tracker.mapper.indic.FlagsMapper"> <mapper namespace="ru.nbch.credit_tracker.mapper.indic.FlagsMapper">
<resultMap id="RuPastDuarearResultMap" type="ru.nbch.credit_tracker.service.indic.FlagsService$RuPastDuarearData"> <resultMap id="FlagsFidDataResultMap" type="ru.nbch.credit_tracker.service.indic.FlagsService$FlagFidData">
<result property="fid" column="fid" /> <result property="fid" column="fid" />
<result property="accSerialNum" column="ACC_SERIAL_NUM" /> <result property="flag" column="flag" />
<result property="reportedDate" column="REPORTED_DT" />
<result property="amtPastDue" column="AMT_PAST_DUE" />
<result property="daysPastDue" column="DAYS_PAST_DUE" />
</resultMap> </resultMap>
<resultMap id="RuPaymtResultMap" type="ru.nbch.credit_tracker.service.indic.FlagsService$RuPaymtData"> <select id="findFlagsDebt5000" resultMap="FlagsFidDataResultMap">
<result property="fid" column="fid" /> SELECT
<result property="daysPastDue" column="DAYS_PAST_DUE" /> FID as fid,
<result property="reportedDate" column="REPORTED_DT" /> (case when (sum(case when AMT_PAST_DUE > 5000 then 1 else 0 end) = 0) then 0 else 1 end) as flag
</resultMap> FROM
(select FID, AMT_PAST_DUE, rank() over (partition by FID, ACC_SERIAL_NUM order by REPORTED_DT desc) rn
FROM RU_PASTDUEARREAR
WHERE FID IN
<foreach item="fid" collection="fids" open="(" separator="," close=")">
#{fid}
</foreach>
)
where rn=1
group by FID
WITH UR
</select>
<select id="findRuPastDuarearData" resultMap="RuPastDuarearResultMap"> <select id="findFlag90ByPastDueArrear" resultMap="FlagsFidDataResultMap">
SELECT FID, ACC_SERIAL_NUM, REPORTED_DT, AMT_PAST_DUE, DAYS_PAST_DUE SELECT
FID as fid,
(case when (sum(case when (REPORTED_DT >= #{reportedDate} and DAYS_PAST_DUE > 90) then 1 else 0 end) = 0) then 0 else 1 end) as flag
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>
with UR GROUP BY FID
WITH UR
</select> </select>
<select id="findRuPaymtData" resultMap="RuPaymtResultMap"> <select id="findFlag90ByRuPaymt" resultMap="FlagsFidDataResultMap">
SELECT FID, DAYS_PAST_DUE, REPORTED_DT SELECT
FID as fid,
(case when (sum(case when (REPORTED_DT >= #{reportedDate} and DAYS_PAST_DUE > 90) then 1 else 0 end) = 0) then 0 else 1 end) as flag
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>
AND REPORTED_DT >= #{reportedDate} GROUP BY FID
with UR WITH UR
</select> </select>
</mapper> </mapper>

View file

@ -12,21 +12,14 @@
</resultMap> </resultMap>
<select id="findFids" resultMap="fidsDataResultMap"> <select id="findFids" resultMap="fidsDataResultMap">
SELECT SELECT pn.NUMBER AS number, pn.FID AS fid, pn.FILE_SINCE_DT AS fileSinceDate
pn.number, FROM PHONE_NORM pn
pn.fid, INNER JOIN NAME n ON n.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 ORDER BY fileSinceDate DESC, fid DESC
pn.number ASC,
pn.file_since_dt DESC,
pn.fid DESC
WITH UR WITH UR
</select> </select>
</mapper> </mapper>

View file

@ -88,5 +88,11 @@
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,6 +13,7 @@
<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">
@ -51,7 +52,7 @@
#{previousStatus} #{previousStatus}
</update> </update>
<select id="insertPackage" resultType="java.lang.Integer" <select id="insertPackage" resultType="java.lang.Long"
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
@ -69,9 +70,15 @@
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 SELECT id, membercode, package_name, created_at, status, deactivated_at, last_downloaded_report, monitoring_at
FROM signals_user_packages FROM signals_user_packages
WHERE id = #{id} WHERE id = #{id}
LIMIT 1 LIMIT 1
@ -79,7 +86,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 SELECT id, membercode, package_name, created_at, status, deactivated_at, last_downloaded_report, monitoring_at
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}
@ -91,7 +98,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 SELECT id, membercode, package_name, created_at, status, deactivated_at, last_downloaded_report, monitoring_at
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}
@ -99,7 +106,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 SELECT id, membercode, package_name, created_at, status, deactivated_at, last_downloaded_report, monitoring_at
FROM signals_user_packages FROM signals_user_packages
WHERE status = #{status} WHERE status = #{status}
</select> </select>

View file

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

View file

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

View file

@ -0,0 +1,14 @@
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

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

View file

@ -1,280 +0,0 @@
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

@ -1,218 +0,0 @@
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

@ -1,63 +0,0 @@
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

@ -1,60 +0,0 @@
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,14 +28,17 @@ 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 = {ValidationUtilTest.TestConfig.class}) @SpringBootTest(classes = {ValidationServiceTest.TestConfig.class})
public class ValidationUtilTest { public class ValidationServiceTest {
private final StaxXmlProcessor staxXmlProcessor; private final StaxXmlProcessor staxXmlProcessor;
private final ValidationService validationService;
@Autowired @Autowired
public ValidationUtilTest(StaxXmlProcessor staxXmlProcessor) { public ValidationServiceTest(StaxXmlProcessor staxXmlProcessor,
ValidationService validationService) {
this.staxXmlProcessor = staxXmlProcessor; this.staxXmlProcessor = staxXmlProcessor;
this.validationService = validationService;
} }
@Configuration @Configuration
@ -46,6 +49,11 @@ public class ValidationUtilTest {
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() {
@ -107,7 +115,7 @@ public class ValidationUtilTest {
if(ex == null) { if(ex == null) {
try { try {
ValidationUtil.validateMonitoringRequest(monitoringRequest); validationService.validateMonitoringRequest(monitoringRequest);
} catch (SignalClientException e) { } catch (SignalClientException e) {
ex = e; ex = e;
} }