Compare commits

...
Sign in to create a new pull request.

32 commits
mdc ... master

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
60 changed files with 1245 additions and 550 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

@ -11,18 +11,42 @@ ct.indic-data-source-settings.driver=com.ibm.db2.jcc.DB2Driver
ct.indic-data-source-settings.url=${DB_URL_INDIC:jdbc:db2://10.230.227.101:2668/cprosd22:currentSchema=INDIC;} ct.indic-data-source-settings.url=${DB_URL_INDIC:jdbc:db2://10.230.227.101:2668/cprosd22:currentSchema=INDIC;}
ct.indic-data-source-settings.username=${USERNAME_INDIC:tester} ct.indic-data-source-settings.username=${USERNAME_INDIC:tester}
ct.indic-data-source-settings.password=${PASSWORD_INDIC:tester} 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}

151
pom.xml
View file

@ -10,7 +10,7 @@
</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>
@ -18,7 +18,7 @@
<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>
@ -27,6 +27,10 @@
<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>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</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>
@ -122,8 +126,91 @@
<version>3.0.4</version> <version>3.0.4</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
</dependencies> </dependencies>
<profiles>
<profile>
<id>docker-push-dev</id>
<build>
<plugins>
<plugin>
<groupId>io.fabric8</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>0.45.0</version>
<executions>
<execution>
<id>Build and deploy docker container</id>
<phase>install</phase>
<goals>
<goal>build</goal>
<goal>push</goal>
</goals>
</execution>
</executions>
<configuration>
<images>
<image>
<name>${project.artifactId}:${project.version}</name>
<registry>${registry.url}</registry>
<build>
<dockerFile>${project.basedir}/Dockerfile</dockerFile>
<assembly>
<inline>
<files>
<file>
<source>
${project.build.directory}/${project.build.finalName}.jar
</source>
<outputDirectory>.</outputDirectory>
</file>
<file>
<source>
${project.basedir}/apm-agent/elastic-apm-agent-1.45.0.jar
</source>
<outputDirectory>.</outputDirectory>
</file>
<file>
<source>${project.basedir}/start-credit-tracker.sh</source>
<outputDirectory>.</outputDirectory>
</file>
<file>
<source>${project.basedir}/credit-tracker.properties</source>
<outputDirectory>.</outputDirectory>
</file>
<file>
<source>${project.basedir}/monitoring-request.xsd</source>
<outputDirectory>.</outputDirectory>
</file>
<file>
<source>${project.basedir}/signal-package.xsd</source>
<outputDirectory>.</outputDirectory>
</file>
<file>
<source>
${project.basedir}/ca_nbki.cer
</source>
<outputDirectory>.</outputDirectory>
</file>
</files>
</inline>
</assembly>
<tags>
<tag>${project.version}</tag>
</tags>
</build>
</image>
</images>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
<build> <build>
<finalName>credit-tracker</finalName> <finalName>credit-tracker</finalName>
<plugins> <plugins>
@ -181,65 +268,7 @@
</includeOnlyProperties> </includeOnlyProperties>
</configuration> </configuration>
</plugin> </plugin>
<plugin>
<groupId>io.fabric8</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>0.45.0</version>
<executions>
<execution>
<id>Build and deploy docker container</id>
<phase>install</phase>
<goals>
<goal>build</goal>
<goal>push</goal>
</goals>
</execution>
</executions>
<configuration>
<images>
<image>
<name>${project.artifactId}:${project.version}</name>
<registry>${registry.url}</registry>
<build>
<dockerFile>${project.basedir}/Dockerfile</dockerFile>
<assembly>
<inline>
<files>
<file>
<source>${project.build.directory}/${project.build.finalName}.jar</source>
<outputDirectory>.</outputDirectory>
</file>
<file>
<source>${project.basedir}/apm-agent/elastic-apm-agent-1.45.0.jar</source>
<outputDirectory>.</outputDirectory>
</file>
<file>
<source>${project.basedir}/start-credit-tracker.sh</source>
<outputDirectory>.</outputDirectory>
</file>
<file>
<source>${project.basedir}/credit-tracker.properties</source>
<outputDirectory>.</outputDirectory>
</file>
<file>
<source>${project.basedir}/monitoring-request.xsd</source>
<outputDirectory>.</outputDirectory>
</file>
<file>
<source>${project.basedir}/signal-package.xsd</source>
<outputDirectory>.</outputDirectory>
</file>
</files>
</inline>
</assembly>
<tags>
<tag>${project.version}</tag>
</tags>
</build>
</image>
</images>
</configuration>
</plugin>
</plugins> </plugins>
</build> </build>

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

@ -0,0 +1,28 @@
package ru.nbch.credit_tracker.component;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Aspect
@Slf4j
@Component
public class MapperTimingAspect {
@Around("within(@org.apache.ibatis.annotations.Mapper *)")
public Object timeMapper(ProceedingJoinPoint pjp) throws Throwable {
long start = System.nanoTime();
try {
return pjp.proceed();
} finally {
long tookMs = (System.nanoTime() - start) / 1_000_000;
String method = pjp.getSignature().toShortString();
if (tookMs > 1000) {
log.warn("Slow mapper call {} took {} ms", method, tookMs);
} else {
log.debug("Mapper call {} took {} ms", method, tookMs);
}
}
}
}

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;
@ -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 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;
} }

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

@ -1,5 +1,6 @@
package ru.nbch.credit_tracker.config.rest; package ru.nbch.credit_tracker.config.rest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
@ -12,10 +13,12 @@ import ru.nbch.credit_tracker.entities.user_response.errors.*;
import ru.nbch.credit_tracker.enums.SignalError; import ru.nbch.credit_tracker.enums.SignalError;
import ru.nbch.credit_tracker.exceptions.SignalClientException; import ru.nbch.credit_tracker.exceptions.SignalClientException;
import ru.nbch.credit_tracker.exceptions.SignalException; import ru.nbch.credit_tracker.exceptions.SignalException;
import ru.nbch.credit_tracker.exceptions.SignalServerException;
import ru.nbch.credit_tracker.utils.error.SimpleMessageResolver; import ru.nbch.credit_tracker.utils.error.SimpleMessageResolver;
import java.nio.charset.Charset; import java.nio.charset.Charset;
@Slf4j
@RestControllerAdvice @RestControllerAdvice
public class GlobalExceptionHandler { public class GlobalExceptionHandler {
@ -43,14 +46,25 @@ public class GlobalExceptionHandler {
public ResponseEntity<CommonError> handleClientException(SignalException ex) { public ResponseEntity<CommonError> handleClientException(SignalException ex) {
HttpHeaders headers = new HttpHeaders(); HttpHeaders headers = new HttpHeaders();
headers.setContentType(new MediaType("application", "xml", Charset.forName(encoding))); headers.setContentType(new MediaType("application", "xml", Charset.forName(encoding)));
String errorText = messageResolver.resolve(ex.getErrorMessage());
CommonError commonError = getCommonError(ex); CommonError commonError = getCommonError(ex);
commonError.setStatus("error"); commonError.setStatus("error");
commonError.setErrorCode(ex.getErrorMessage().getSubject().getCode()); commonError.setErrorCode(ex.getErrorMessage().getSubject().getCode());
commonError.setErrorText(messageResolver.resolve(ex.getErrorMessage())); commonError.setErrorText(errorText);
if (ex instanceof SignalClientException) { if (ex instanceof SignalClientException) {
String packageName = ((SignalClientException) ex).getPackageName();
if(packageName != null ) {
log.warn("Response error: {}. PackageName: {}", errorText, packageName);
} else {
log.warn("Response error: {}", errorText);
}
return new ResponseEntity<>(commonError, headers, HttpStatus.BAD_REQUEST); return new ResponseEntity<>(commonError, headers, HttpStatus.BAD_REQUEST);
} else { } else {
Exception cause = ((SignalServerException) ex).getCause();
log.error(cause.getMessage(), cause);
return new ResponseEntity<>(commonError, headers, HttpStatus.INTERNAL_SERVER_ERROR); return new ResponseEntity<>(commonError, headers, HttpStatus.INTERNAL_SERVER_ERROR);
} }
} }
@ -60,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

@ -0,0 +1,132 @@
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.HttpServletRequestWrapper;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.Part;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.ContentCachingRequestWrapper;
import org.springframework.web.util.ContentCachingResponseWrapper;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;
@Component
@Slf4j
@Order(0)
public class HttpLoggingFilter extends OncePerRequestFilter {
private static final Set<String> SENSITIVE_HEADERS = Set.of("authorization", "cookie", "set-cookie");
private static final int MAX_BODY = 10 * 1024;
@Override
protected boolean shouldNotFilter(HttpServletRequest request) {
String path = request.getRequestURI();
return path.startsWith("/actuator/health") || path.startsWith("/swagger")
|| path.startsWith("/v3/api-docs") || path.startsWith("/static/") || path.equals("/");
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
long startNs = System.nanoTime();
ContentCachingRequestWrapper req = wrapRequest(request);
ContentCachingResponseWrapper resp = new ContentCachingResponseWrapper(response);
String correlationId = Optional.ofNullable(request.getHeader("X-Correlation-Id"))
.filter(StringUtils::hasText)
.orElse(UUID.randomUUID().toString());
resp.setHeader("X-Correlation-Id", correlationId);
logIncomingRequest(req, correlationId);
try {
chain.doFilter(req, resp);
} finally {
long tookMs = (System.nanoTime() - startNs) / 1_000_000;
Map<String, String> reqParams = paramsMap(req);
Map<String, String> reqHeaders = headersMap(req);
Map<String, String> respHeaders = headersMap(resp);
mask(reqHeaders);
mask(respHeaders);
log.info(
"HTTP (RESPONSE) {} {} | status={} | took={}ms | cid={} | reqHeaders={} | reqParams={} | respHeaders={}",
req.getMethod(),
req.getRequestURI(),
resp.getStatus(),
tookMs,
correlationId,
reqHeaders,
reqParams,
respHeaders
);
resp.copyBodyToResponse();
}
}
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);
}
private static Map<String, String> headersMap(HttpServletRequest req) {
return Collections.list(req.getHeaderNames()).stream()
.collect(Collectors.toMap(h -> h, req::getHeader, (a, b) -> a, LinkedHashMap::new));
}
private static Map<String, String> 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) {
return resp.getHeaderNames().stream()
.collect(Collectors.toMap(h -> h, resp::getHeader, (a, b) -> a, LinkedHashMap::new));
}
private static void mask(Map<String, String> headers) {
headers.replaceAll((k, v) -> SENSITIVE_HEADERS.contains(k.toLowerCase()) ? "***" : v);
}
}

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

@ -3,13 +3,13 @@ package ru.nbch.credit_tracker.enums;
public enum SignalError implements IEnumKeyCode { public enum SignalError implements IEnumKeyCode {
ERROR_001("001", "Ошибка распаковки архива"), ERROR_001("001", "Ошибка распаковки архива"),
ERROR_002("002", "Отсутствует обязательный элемент: {}"), ERROR_002("002", "Отсутствует обязательный элемент: {}"),
ERROR_003("003", "Элемент ClientId не соответствует формату XXXXXXXXX"), ERROR_003("003", "Элемент ClientId не соответствует формату XXXXXXXXX. Id: {}"),
ERROR_005("005", "Неверный логин или пароль"), ERROR_005("005", "Неверный логин или пароль"),
ERROR_006("006", "Доступ к услуге закрыт, обратитесь к курирующему менеджеру"), ERROR_006("006", "Доступ к услуге закрыт, обратитесь к курирующему менеджеру"),
ERROR_007("007", "В системе уже существует пакет с таким именем"), ERROR_007("007", "В системе уже существует пакет с таким именем"),
ERROR_008("008", "PackageName не соответствует допустимому формату"), ERROR_008("008", "PackageName не соответствует допустимому формату"),
ERROR_009("009", "Превышен допустимый размер пакета"), ERROR_009("009", "Превышен допустимый размер пакета"),
ERROR_010("010", "Идентификатор субъекта (Id) не соответствует допустимому формату"), ERROR_010("010", "Идентификатор субъекта (Id) не соответствует допустимому формату: {}"),
ERROR_011("011", "Получен невалидный XML"), ERROR_011("011", "Получен невалидный XML"),
ERROR_012("012", "Пакет {} не найден"), ERROR_012("012", "Пакет {} не найден"),
ERROR_013("013", "На стороне сервиса произошла ошибка, попробуйте повторить запрос позже или сообщите в техническую поддержку"), ERROR_013("013", "На стороне сервиса произошла ошибка, попробуйте повторить запрос позже или сообщите в техническую поддержку"),

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,17 @@
package ru.nbch.credit_tracker.exceptions; package ru.nbch.credit_tracker.exceptions;
import lombok.Getter;
import ru.nbch.credit_tracker.enums.EnumKeyMessage; import ru.nbch.credit_tracker.enums.EnumKeyMessage;
import ru.nbch.credit_tracker.enums.XmlErrorFormat; import ru.nbch.credit_tracker.enums.XmlErrorFormat;
@Getter
public class SignalServerException extends SignalException { public class SignalServerException extends SignalException {
public SignalServerException(EnumKeyMessage errorMessage, XmlErrorFormat errorFormat) { private final Exception cause;
public SignalServerException(EnumKeyMessage errorMessage, XmlErrorFormat errorFormat, Exception cause) {
super(errorMessage, errorFormat); super(errorMessage, errorFormat);
this.cause = cause;
} }
} }

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 {
@ -109,21 +121,17 @@ public class SignalFacade {
zipInputStream.getNextEntry(); zipInputStream.getNextEntry();
filePath = writeTmpFile(zipInputStream); filePath = writeTmpFile(zipInputStream);
if (filePath == null) {
throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringError);
}
} catch (Exception e) { } catch (Exception e) {
if (e instanceof SignalClientException) { if (e instanceof SignalClientException) {
throw (SignalClientException) e; throw (SignalClientException) e;
} }
log.error(e.getMessage(), e); throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringError, e);
throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringError);
} finally { } finally {
if (zipInputStream != null) { if (zipInputStream != null) {
try { try {
zipInputStream.close(); zipInputStream.close();
} catch (IOException e) { } catch (IOException e) {
throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringError); throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringError, e);
} }
} }
} }
@ -140,8 +148,7 @@ public class SignalFacade {
if (e instanceof SignalClientException) { if (e instanceof SignalClientException) {
throw (SignalClientException) e; throw (SignalClientException) e;
} }
log.error(e.getMessage(), e); throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringError, e);
throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringError);
} }
try { try {
@ -149,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);
@ -165,12 +172,11 @@ public class SignalFacade {
if (e instanceof SignalClientException) { if (e instanceof SignalClientException) {
throw (SignalClientException) e; throw (SignalClientException) e;
} }
log.error(e.getMessage(), e); throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringError, e);
throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringError);
} }
// в отдельном потоке запускаем регистрацию субъектов, поиск фидов и расчет флагов просрочек // в отдельном потоке запускаем регистрацию субъектов, поиск фидов и расчет флагов просрочек
registerFidsAndDebts(packageId, monitoringRequest.getPackageName(), filePath); getCurrentProxy().registerFidsAndDebts(packageId, monitoringRequest.getPackageName(), filePath);
return ResponseEntity.ok().body(new MonitoringSetupResponse()); return ResponseEntity.ok().body(new MonitoringSetupResponse());
} }
@ -181,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);
@ -200,7 +206,7 @@ public class SignalFacade {
return monitoringRequest; return monitoringRequest;
} }
private String writeTmpFile(ZipInputStream zipInputStream) { private String writeTmpFile(ZipInputStream zipInputStream) throws IOException {
String folderPath = config.getLoadDir() + File.separator + "tmp_folder" + File.separator; String folderPath = config.getLoadDir() + File.separator + "tmp_folder" + File.separator;
String filePath = folderPath + UUID.randomUUID() + "monitoring_request.xml"; String filePath = folderPath + UUID.randomUUID() + "monitoring_request.xml";
CTUtil.createFolder(folderPath); CTUtil.createFolder(folderPath);
@ -214,14 +220,16 @@ public class SignalFacade {
} }
return filePath; return filePath;
} catch (Exception e) {
log.error("Ошибка при записи файла запроса постановки на мониторинг на диск", e);
return null;
} }
} }
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);
@ -232,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);
@ -281,8 +290,7 @@ public class SignalFacade {
if (e instanceof SignalClientException) { if (e instanceof SignalClientException) {
throw (SignalClientException) e; throw (SignalClientException) e;
} }
log.error(e.getMessage(), e); throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringActivePackagesError, e);
throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringActivePackagesError);
} }
} }
@ -290,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());
@ -312,14 +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;
} }
log.error(e.getMessage(), e); throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringAllPackagesError, e);
throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringActivePackagesError);
} }
} }
@ -344,8 +351,7 @@ public class SignalFacade {
if (e instanceof SignalClientException) { if (e instanceof SignalClientException) {
throw (SignalClientException) e; throw (SignalClientException) e;
} }
log.error(e.getMessage(), e); throw serverExcp(SignalError.ERROR_017, XmlErrorFormat.MonitoringPackageDeleteError, e);
throw serverExcp(SignalError.ERROR_017, XmlErrorFormat.MonitoringPackageDeleteError);
} }
} }
@ -391,8 +397,7 @@ public class SignalFacade {
if (e instanceof SignalClientException) { if (e instanceof SignalClientException) {
throw (SignalClientException) e; throw (SignalClientException) e;
} }
log.error(e.getMessage(), e); throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringReportListError, e);
throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringReportListError);
} }
} }
@ -418,8 +423,14 @@ public class SignalFacade {
if (e instanceof SignalClientException) { if (e instanceof SignalClientException) {
throw (SignalClientException) e; throw (SignalClientException) e;
} }
log.error(e.getMessage(), e); throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringDownloadError, e);
throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringDownloadError);
} }
} }
private SignalFacade getCurrentProxy() {
if (self == null) {
self = (SignalFacade) AopContext.currentProxy();
}
return self;
}
} }

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,16 +70,24 @@ 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() +"]");
processPackage(userPackage);
MDC.remove("ru.nbch.credit_tracker.log.mdc.key");
}
log.info("Finish monitoring");
}
private void processPackage(UserPackage userPackage) {
log.info("Processing package");
if (!signalService.hasNonNullfids(userPackage.getId())) { if (!signalService.hasNonNullfids(userPackage.getId())) {
log.info("Package {} has no fids. Package will be skipped.", userPackage.getPackageName()); log.info("Package has no fids. Package will be skipped.");
continue; return;
} }
SgnlOnlineReport onlineReports = signalRestService.getReports(userPackage.getUserId().substring(0, 6) + "_" + userPackage.getPackageName()); SgnlOnlineReport onlineReports = signalRestService.getReports(userPackage.getUserId().substring(0, 6) + "_" + userPackage.getPackageName());
if (onlineReports == null) { if (onlineReports == null) {
log.info("No reports found for package {}", userPackage.getPackageName()); log.info("No reports found for package");
continue; return;
} }
// Формируем список имен файлов, по которым сработали сигналы. Сюда входят только те имена, у которых парсится дата формирования отчета // Формируем список имен файлов, по которым сработали сигналы. Сюда входят только те имена, у которых парсится дата формирования отчета
@ -86,8 +95,8 @@ public class MonitoringService {
.filter(fileName -> CTUtil.parseDateFromReportFileName(fileName) != null).toList(); .filter(fileName -> CTUtil.parseDateFromReportFileName(fileName) != null).toList();
if (fileNames.isEmpty()) { if (fileNames.isEmpty()) {
log.info("No new report found for package {}", userPackage.getPackageName()); log.info("No new report found for package");
continue; return;
} }
LocalDateTime lastDownloadedReportDate = CTUtil.parseDateFromReportFileName(userPackage.getLastDownloadedReport()); LocalDateTime lastDownloadedReportDate = CTUtil.parseDateFromReportFileName(userPackage.getLastDownloadedReport());
@ -105,7 +114,7 @@ public class MonitoringService {
} }
if (StringUtils.isNotBlank(userPackage.getLastDownloadedReport())) { if (StringUtils.isNotBlank(userPackage.getLastDownloadedReport())) {
log.info("Last downloaded report found for package {}. Last Downloaded Report Name: {}", userPackage.getPackageName(), userPackage.getLastDownloadedReport()); log.info("Last downloaded report found for package. Last Downloaded Report Name: {}", userPackage.getLastDownloadedReport());
} else { } else {
log.info("Last Downloaded Report Name is empty"); log.info("Last Downloaded Report Name is empty");
} }
@ -122,7 +131,7 @@ public class MonitoringService {
} }
} }
if (loadedReportPath == null) { if (loadedReportPath == null) {
log.info("Report was not loaded for package {}", userPackage.getPackageName()); log.info("Report was not loaded for package");
continue; continue;
} }
@ -136,15 +145,13 @@ public class MonitoringService {
updateSignalHits(userPackage, responsePath, fileName); updateSignalHits(userPackage, responsePath, fileName);
signalService.updateLastDownloadedReport(userPackage.getId(), newReport); signalService.updateLastDownloadedReport(userPackage.getId(), newReport);
} else { } else {
log.info("Report was not created for package {}", userPackage.getPackageName()); log.info("Report was not created for package");
} }
} catch (Exception e) { } catch (Exception e) {
log.error(e.getMessage(), e); log.error(e.getMessage(), e);
} }
} }
} }
log.info("Finish monitoring");
}
/** /**
* Формирует результирующий xml файл для конечного пользователя на основе файла из официального сервиса сигналов * Формирует результирующий xml файл для конечного пользователя на основе файла из официального сервиса сигналов
@ -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

@ -72,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())
@ -82,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,
@ -105,7 +105,7 @@ 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;
@ -141,7 +141,7 @@ public class SignalService {
* Обрабатывает пачку PhoneFidMap: обновляет фиды и флаги просрочек * Обрабатывает пачку PhoneFidMap: обновляет фиды и флаги просрочек
* @return fidsFound true - если хотя бы по одному номеру был найден фид, false - в противном случае * @return fidsFound true - если хотя бы по одному номеру был найден фид, false - в противном случае
*/ */
public boolean updateFlags(List<PhoneFidMap> subjects, Integer packageId) { public boolean updateFlags(List<PhoneFidMap> subjects, Long packageId) {
log.info("Start calculating fids and flags for subjects batch. PackageId: {}", packageId); log.info("Start calculating fids and flags for subjects batch. PackageId: {}", packageId);
Map<String, Integer> fidsMap = phoneService.findFidsPhoneData(subjects); Map<String, Integer> fidsMap = phoneService.findFidsPhoneData(subjects);
LocalDateTime calculatedAt = LocalDateTime.now(); LocalDateTime calculatedAt = LocalDateTime.now();
@ -197,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);
@ -222,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 (только онлайн-сигналы)
@ -238,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 {
@ -278,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())
@ -337,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

@ -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,5 +1,8 @@
package ru.nbch.credit_tracker.utils; package ru.nbch.credit_tracker.utils;
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;
@ -10,47 +13,52 @@ 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;
public class ValidationUtil { @Slf4j
@Service
public class ValidationService {
private static String phoneRegex = "^\\d{9}$"; private static final String phoneRegex = "^\\d{9}$";
private static String packageNameRegex = "^[A-Z0-9_]{1,22}$"; private static final String packageNameRegex = "^[A-Z0-9_]{1,22}$";
private static 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);
} }
if (request.getUserId() == null) {
throw clientExcp(SignalError.ERROR_002, XmlErrorFormat.MonitoringError, "UserId");
}
if (request.getPackageName() == null) { if (request.getPackageName() == null) {
throw clientExcp(SignalError.ERROR_002, XmlErrorFormat.MonitoringError, "PackageName"); throw clientExcp(SignalError.ERROR_002, XmlErrorFormat.MonitoringError, "PackageName");
} }
String packageName = request.getPackageName();
if (!validatePackageName(request.getPackageName())) {
throw clientExcp(SignalError.ERROR_008, packageName, XmlErrorFormat.MonitoringError);
}
if (request.getUserId() == null) {
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");
}
if (!validatePackageName(request.getPackageName())) {
throw clientExcp(SignalError.ERROR_008, XmlErrorFormat.MonitoringError);
} }
for (Subject subject : request.getSubjects().getSubject()) { for (Subject subject : request.getSubjects().getSubject()) {
if (!validatePhone(subject.getClientId())) {
throw clientExcp(SignalError.ERROR_003, XmlErrorFormat.MonitoringError);
}
if (!validateNPId(subject.getId())) { if (!validateNPId(subject.getId())) {
throw clientExcp(SignalError.ERROR_010, XmlErrorFormat.MonitoringError); throw clientExcp(SignalError.ERROR_010, packageName, XmlErrorFormat.MonitoringError, subject.getId());
}
if (!validatePhone(subject.getClientId())) {
throw clientExcp(SignalError.ERROR_003, packageName, XmlErrorFormat.MonitoringError, subject.getId());
} }
} }
} }

View file

@ -9,10 +9,14 @@ 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 SignalServerException serverExcp(IEnumKeyCode enumKeyCode, XmlErrorFormat xmlErrorFormat, Object... args) { public static SignalClientException clientExcp(IEnumKeyCode enumKeyCode, String packageName, XmlErrorFormat xmlErrorFormat, Object... args) {
return new SignalServerException(new EnumKeyMessage(enumKeyCode, args), xmlErrorFormat); return new SignalClientException(new EnumKeyMessage(enumKeyCode, args), xmlErrorFormat, packageName);
}
public static SignalServerException serverExcp(IEnumKeyCode enumKeyCode, XmlErrorFormat xmlErrorFormat, Exception cause, Object... args) {
return new SignalServerException(new EnumKeyMessage(enumKeyCode, args), xmlErrorFormat, cause);
} }
} }

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} [%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>

View file

@ -11,70 +11,45 @@
</resultMap> </resultMap>
<select id="findFlagsDebt5000" resultMap="FlagsFidDataResultMap"> <select id="findFlagsDebt5000" resultMap="FlagsFidDataResultMap">
SELECT rp.FID as fid, SELECT
CASE FID as fid,
WHEN EXISTS (SELECT 1 (case when (sum(case when AMT_PAST_DUE > 5000 then 1 else 0 end) = 0) then 0 else 1 end) as flag
FROM RU_PASTDUEARREAR rp2 FROM
WHERE rp2.FID = rp.FID (select FID, AMT_PAST_DUE, rank() over (partition by FID, ACC_SERIAL_NUM order by REPORTED_DT desc) rn
AND rp2.ACC_SERIAL_NUM = rp.ACC_SERIAL_NUM
AND rp2.REPORTED_DT = rp.MAX_REP_DATE
AND rp2.AMT_PAST_DUE > 5000) THEN 1
ELSE 0
END as flag
FROM (SELECT FID,
ACC_SERIAL_NUM,
MAX(REPORTED_DT) as MAX_REP_DATE
FROM RU_PASTDUEARREAR FROM RU_PASTDUEARREAR
WHERE FID IN WHERE FID IN
<foreach item="fid" collection="fids" open="(" separator="," close=")"> <foreach item="fid" collection="fids" open="(" separator="," close=")">
#{fid} #{fid}
</foreach> </foreach>
GROUP BY FID, ACC_SERIAL_NUM) rp )
GROUP BY rp.FID, rp.ACC_SERIAL_NUM, rp.MAX_REP_DATE where rn=1
group by FID
WITH UR WITH UR
</select> </select>
<select id="findFlag90ByPastDueArrear" resultMap="FlagsFidDataResultMap"> <select id="findFlag90ByPastDueArrear" resultMap="FlagsFidDataResultMap">
SELECT rp.FID as fid, SELECT
CASE FID as fid,
WHEN EXISTS (SELECT 1 (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 rp2
WHERE rp2.FID = rp.FID
AND rp2.REPORTED_DT >= #{reportedDate}
AND rp2.DAYS_PAST_DUE > 90) THEN 1
ELSE 0
END as flag
FROM (SELECT FID,
DAYS_PAST_DUE
FROM RU_PASTDUEARREAR FROM RU_PASTDUEARREAR
WHERE FID IN WHERE FID IN
<foreach item="fid" collection="fids" open="(" separator="," close=")"> <foreach item="fid" collection="fids" open="(" separator="," close=")">
#{fid} #{fid}
</foreach> </foreach>
GROUP BY FID, DAYS_PAST_DUE) rp GROUP BY FID
GROUP BY rp.FID, rp.DAYS_PAST_DUE
WITH UR WITH UR
</select> </select>
<select id="findFlag90ByRuPaymt" resultMap="FlagsFidDataResultMap"> <select id="findFlag90ByRuPaymt" resultMap="FlagsFidDataResultMap">
SELECT rp.FID as fid, SELECT
CASE FID as fid,
WHEN EXISTS (SELECT 1 (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 rp2
WHERE rp2.FID = rp.FID
AND rp2.REPORTED_DT >= #{reportedDate}
AND rp2.DAYS_PAST_DUE > 90) THEN 1
ELSE 0
END as flag
FROM (SELECT FID,
DAYS_PAST_DUE
FROM RU_PAYMT FROM RU_PAYMT
WHERE FID IN WHERE FID IN
<foreach item="fid" collection="fids" open="(" separator="," close=")"> <foreach item="fid" collection="fids" open="(" separator="," close=")">
#{fid} #{fid}
</foreach> </foreach>
GROUP BY FID, DAYS_PAST_DUE) rp GROUP BY FID
GROUP BY rp.FID, rp.DAYS_PAST_DUE
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

@ -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() {
@ -69,12 +77,12 @@ public class ValidationUtilTest {
Arguments.of( Arguments.of(
"/monitoring_request/invalid_phone_request.zip", "/monitoring_request/invalid_phone_request.zip",
SignalError.ERROR_003, SignalError.ERROR_003,
Collections.emptyList().toArray(new Object[0]) new Object[]{"abc123"}
), ),
Arguments.of( Arguments.of(
"/monitoring_request/invalid_id_request.zip", "/monitoring_request/invalid_id_request.zip",
SignalError.ERROR_010, SignalError.ERROR_010,
Collections.emptyList().toArray(new Object[0]) new Object[]{"#####"}
) )
); );
} }
@ -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;
} }