Compare commits

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

123 commits

Author SHA1 Message Date
etreschenkov
3e7bfa51a0 http://jira.mfd.msk:8088/browse/BKI-3341 2025-12-03 15:47:58 +03:00
etreschenkov
99222c740e http://jira.mfd.msk:8088/browse/BKI-3288 2025-11-06 12:20:58 +03:00
etreschenkov
5dc5e72496 http://jira.mfd.msk:8088/browse/BKI-3288 2025-11-06 12:20:38 +03:00
etreschenkov
b85cda38ef http://jira.mfd.msk:8088/browse/BKI-3287 2025-11-05 17:31:21 +03:00
etreschenkov
14138b4455 http://jira.mfd.msk:8088/browse/BKI-3287 2025-11-05 17:14:46 +03:00
etreschenkov
786cc89718 http://jira.mfd.msk:8088/browse/BKI-3286 2025-11-05 12:36:38 +03:00
etreschenkov
6b0f8668ae http://jira.mfd.msk:8088/browse/BKI-3286 2025-11-05 12:29:12 +03:00
Mikhail Trofimov
7bb08ebb59 Updated version to 1.20 2025-10-16 14:30:38 +03:00
Mikhail Trofimov
439b934c5c http://jira.mfd.msk:8088/browse/BKI-3264 rename ddls 2025-10-16 14:28:04 +03:00
Mikhail Trofimov
10257b9cb2 http://jira.mfd.msk:8088/browse/BKI-3264 Changed id types to bigint 2025-10-16 14:01:17 +03:00
Mikhail Trofimov
8784b4e4f3 Updated version to 1.19 2025-10-14 15:44:09 +03:00
Mikhail Trofimov
60cb6e9e8c http://jira.mfd.msk:8088/browse/BKI-3246 Get rid of update duplicate 2025-10-14 15:42:17 +03:00
etreschenkov
ddc4418b1f add nbki ca[fix image build] 2025-10-14 15:29:12 +03:00
Mikhail Trofimov
33723b60d5 Updated version to 1.18 2025-10-14 11:15:30 +03:00
Mikhail Trofimov
d963533a87 http://jira.mfd.msk:8088/browse/BKI-3236 Changed return error typy for /all_packages 2025-10-14 11:14:41 +03:00
Mikhail Trofimov
9253dd3f19 http://jira.mfd.msk:8088/browse/BKI-3246 Added signal_user_packages.monitoring_at column; Added monitoring_at setup logic 2025-10-10 14:21:41 +03:00
Mikhail Trofimov
f950f98536 http://jira.mfd.msk:8088/browse/BKI-3215 Moved member code logging logic to MemberCodeHttpLoggingFilter; Added CachedServletInputStream; Updated CachedHttpRequestWrapper 2025-10-06 17:11:03 +03:00
Mikhail Trofimov
3b9c2eaa15 http://jira.mfd.msk:8088/browse/BKI-3215 Added CachedHttpRequestWrapper to handle request body before doFilter invocation; Added memberCode ro logs; Updated version to 1.17 2025-10-06 15:37:27 +03:00
Mikhail Trofimov
b61970a71e http://jira.mfd.msk:8088/browse/BKI-3215 Changed log error lvl to warn for getErrorFileName method 2025-10-02 10:07:30 +03:00
Mikhail Trofimov
83bcea13b1 http://jira.mfd.msk:8088/browse/BKI-3231 Minor fix 2025-10-02 10:03:44 +03:00
Mikhail Trofimov
10180bb471 Get rid of pluginManagement 2025-10-01 16:24:38 +03:00
Mikhail Trofimov
9a44d8cd5e Updated version to 1.16 2025-10-01 15:52:46 +03:00
Mikhail Trofimov
6571e4d90f http://jira.mfd.msk:8088/browse/BKI-3215 Added packageName to SignalClientException for logging it on error 2025-10-01 15:50:46 +03:00
Mikhail Trofimov
dc89f17761 http://jira.mfd.msk:8088/browse/BKI-3215 Added reqParams to HttpLoggingFilter 2025-10-01 14:45:07 +03:00
Mikhail Trofimov
ea12395559 http://jira.mfd.msk:8088/browse/BKI-3231 Changed DETECTION_THRESHOLD_* property to LEAK_DETECTION_THRESHOLD_* 2025-10-01 10:12:02 +03:00
Mikhail Trofimov
d02b471466 http://jira.mfd.msk:8088/browse/BKI-3231 Moved db properties to .properties and .env file 2025-10-01 09:58:30 +03:00
Mikhail Trofimov
6b5f39c755 http://jira.mfd.msk:8088/browse/BKI-3215 Updated HttpLoggingFilter to log incoming requests 2025-09-30 16:36:42 +03:00
Mikhail Trofimov
e6bf35f337 http://jira.mfd.msk:8088/browse/BKI-3215 Use MDC for monitoring, updating flags, setup monitoring; Update ValidationUtil to a service 2025-09-30 16:20:24 +03:00
Mikhail Trofimov
061c24b89c http://jira.mfd.msk:8088/browse/BKI-3215 Changed log lvl to warn for error checking; Added SetAndClearMdc annotation for packageName logging 2025-09-30 14:35:21 +03:00
Mikhail Trofimov
2c3b2822c2 http://jira.mfd.msk:8088/browse/BKI-3215 Moved SignalServerException logs to GlobalExceptionHandler; Added warn logs to GlobalExceptionHandler for SignalClientException 2025-09-30 14:20:45 +03:00
Mikhail Trofimov
98622d7817 http://jira.mfd.msk:8088/browse/BKI-3215 Log http requests; Log slow mapper calls; Added spring AOP 2025-09-30 14:15:33 +03:00
Mikhail Trofimov
f888f9af4c http://jira.mfd.msk:8088/browse/BKI-3230 Get rid of SimpleDateFormat 2025-09-30 14:08:42 +03:00
Mikhail Trofimov
fdbec299d6 http://jira.mfd.msk:8088/browse/BKI-3210 Changed request and response entities for all_packages method; Updated version to 1.15 2025-09-16 17:27:48 +03:00
Mikhail Trofimov
a3fc3f8329 http://jira.mfd.msk:8088/browse/BKI-3208 Updated version to 1.14 2025-09-16 15:52:05 +03:00
Mikhail Trofimov
7ffcd874f6 http://jira.mfd.msk:8088/browse/BKI-3208 Change ERROR_14; Return only ON_MONITORING packages for active_packages method 2025-09-16 15:50:43 +03:00
Mikhail Trofimov
d84bdf608c http://jira.mfd.msk:8088/browse/BKI-3208 Added new Errors; Added catching new errors for report_list method 2025-09-16 15:39:27 +03:00
Mikhail Trofimov
26a1467af5 http://jira.mfd.msk:8088/browse/BKI-3207 Updated version to 1.13 2025-09-15 16:23:44 +03:00
Mikhail Trofimov
f921b25453 http://jira.mfd.msk:8088/browse/BKI-3207 Added with ur for all indic mappers 2025-09-15 16:22:44 +03:00
Mikhail Trofimov
f34a7be0a1 http://jira.mfd.msk:8088/browse/BKI-3207 Get rid of tmp folders 2025-09-15 16:10:43 +03:00
Mikhail Trofimov
a1a602d9e8 http://jira.mfd.msk:8088/browse/BKI-3210 Added find all packages logic; Updated version to 1.12 2025-09-15 12:35:25 +03:00
Mikhail Trofimov
5ec5c407ab http://jira.mfd.msk:8088/browse/BKI-3203 Do not save empty reports from monitoring service; Updated version to 1.11 2025-09-12 13:37:47 +03:00
Mikhail Trofimov
7c6a5fef6b http://jira.mfd.msk:8088/browse/BKI-3199 Updated version to 1.10 2025-09-11 16:56:29 +03:00
Mikhail Trofimov
3c3e340bcd http://jira.mfd.msk:8088/browse/BKI-3199 Added test for MonitoringReportErrorsStaxtReader 2025-09-11 16:38:31 +03:00
Mikhail Trofimov
0cc25b5a4b http://jira.mfd.msk:8088/browse/BKI-3199 Moved findErrors into separate service 2025-09-11 16:06:04 +03:00
Mikhail Trofimov
d4bef965c3 http://jira.mfd.msk:8088/browse/BKI-3199 Added MonitoringReportErrorsStaxtReader and updating error_text column 2025-09-11 15:43:02 +03:00
Mikhail Trofimov
6a350bfd17 http://jira.mfd.msk:8088/browse/BKI-3199 Added loading error file for monitoring 2025-09-11 15:43:01 +03:00
Mikhail Trofimov
2e42b956ef http://jira.mfd.msk:8088/browse/BKI-3199 Added new column signals_phone_fid_map.error_text 2025-09-11 15:43:01 +03:00
Mikhail Trofimov
f24a6a978a http://jira.mfd.msk:8088/browse/BKI-3201 Fix updating flag condition; Update version to 1.9 2025-09-11 10:29:39 +03:00
Mikhail Trofimov
1aff345768 http://jira.mfd.msk:8088/browse/BKI-3200 Fix MonitoringService to load all reports after last report (Not only latest); Updated version to 1.8 2025-09-10 14:45:21 +03:00
Mikhail Trofimov
d0c20abb96 Updated version to 1.7 2025-09-09 11:35:08 +03:00
Mikhail Trofimov
5acda45438 http://jira.mfd.msk:8088/browse/BKI-3198 Allow users get list of reports for packages in PROCESSING status 2025-09-09 11:33:23 +03:00
Mikhail Trofimov
53f36a47b0 http://jira.mfd.msk:8088/browse/BKI-3197 Swap pd attribut value with current date 2025-09-09 11:10:00 +03:00
Mikhail Trofimov
b8848f1c36 Updated version to 1.6 2025-09-04 12:58:40 +03:00
Mikhail Trofimov
0fd7bba97e Minor fix 2025-09-04 12:55:20 +03:00
Mikhail Trofimov
111d8b876b http://jira.mfd.msk:8088/browse/BKI-3191 fix concurrency bug in XmlProcessorStaxImpl 2025-09-04 12:48:59 +03:00
Mikhail Trofimov
3fe63a93ec Added emptyBatchCheckFunction for StaxXmlProcessor to not process empty lists of data 2025-09-04 12:04:06 +03:00
Mikhail Trofimov
af2cd72647 Fix empty inserting while registering subjects; Update version to 1.5 2025-09-03 15:42:36 +03:00
Mikhail Trofimov
9ce3b91a1e http://jira.mfd.msk:8088/browse/BKI-3189 Added attribute processWithErrors for package; Update version to 1.4 2025-09-02 11:02:38 +03:00
Mikhail Trofimov
ac82f57e23 http://jira.mfd.msk:8088/browse/BKI-3186 Added changes for empty fids processing in monitoring setup step; Update version to 1.3 2025-09-01 12:33:33 +03:00
Mikhail Trofimov
a29fe1e0f8 Update version to 1.2 2025-08-15 11:07:30 +03:00
Mikhail Trofimov
f8967fa337 Added extra check for userId 2025-08-15 11:05:47 +03:00
Mikhail Trofimov
a428ba9d49 http://jira.mfd.msk:8088/browse/BKI-3174 Removed . from access_log prefix; Updated version to 1.1 2025-08-12 17:25:25 +03:00
Mikhail Trofimov
834c6bdebb http://jira.mfd.msk:8088/browse/BKI-3174 Added tomcat access logs; Updated version to 1.0 2025-08-11 16:42:30 +03:00
Mikhail Trofimov
2a19a8da87 Do not wrap SignalClientException into RuntimeException in XmlProcessorStaxImpl; Updated version to 0.9 2025-08-06 13:02:31 +03:00
Mikhail Trofimov
87a4a0e334 Updated version to 0.8 2025-07-25 15:25:25 +03:00
Mikhail Trofimov
f5595141a3 Optimize memory usage 2025-07-25 15:24:14 +03:00
Mikhail Trofimov
0c7adde053 Added comments for SignalRestService, UpdatingFlagsService, UpdateProcessingPackagesService, MonitoringService 2025-07-24 16:22:59 +03:00
Mikhail Trofimov
aa106350b5 Refactored PhoneService and SignalService 2025-07-24 15:54:59 +03:00
Mikhail Trofimov
997bcc6c4a Added comments for SignalService 2025-07-24 15:40:02 +03:00
Mikhail Trofimov
7120fd9dd2 Removed unused method findPhoneFidMaps 2025-07-24 15:12:39 +03:00
Mikhail Trofimov
bb429511d3 Cover MonitoringService with comments 2025-07-24 13:09:13 +03:00
Mikhail Trofimov
5493fc4094 Splited UtilTest to ZipUtilsTest and ValidationUtilTest; Added CTUtilTest; Updated CTUtil 2025-07-24 12:44:08 +03:00
Mikhail Trofimov
10d7f00308 Splited entities to user and external 2025-07-24 11:53:09 +03:00
Mikhail Trofimov
33d796ef6f Minor fix 2025-07-24 11:13:35 +03:00
Mikhail Trofimov
a827bd0f6b Moved encoding to properties 2025-07-24 11:00:48 +03:00
Mikhail Trofimov
c77e22e641 Refactored datasource configs 2025-07-24 10:19:10 +03:00
Mikhail Trofimov
f9f026d815 Added null check for bus_category; Updated version to 0.7 2025-07-23 10:22:47 +03:00
Mikhail Trofimov
f1e2ef608c Update version to 0.6 2025-07-21 11:46:01 +03:00
Mikhail Trofimov
887c2ff06e Change active packeges selecting 2025-07-21 11:41:37 +03:00
Mikhail Trofimov
82a028c664 update version to 0.5 2025-07-21 11:25:29 +03:00
Mikhail Trofimov
bec4250cf5 Change logging lvl for XMLStreamException 2025-07-21 11:24:23 +03:00
Mikhail Trofimov
692dacc553 Added UpdateProcessingPackagesService 2025-07-21 11:15:20 +03:00
Mikhail Trofimov
48264bbdb5 Added max_xml_file_size 2025-07-21 10:47:05 +03:00
Mikhail Trofimov
b8975cb415 Use signals_phone_fid_map.id instead of subjectId for nbki service mapping; Remove unique id validation 2025-07-21 10:39:42 +03:00
Mikhail Trofimov
5c824ef0cd Update verson to 0.4 2025-07-21 09:51:31 +03:00
Mikhail Trofimov
ebb9cd0dc8 Load and unzip archive to disk 2025-07-21 09:40:38 +03:00
Mikhail Trofimov
cb5f0d9a9b Added MonitoringSignalResponseStaxXmlReader and MonitoringSignalResponseStaxXmlWriter; Used batches to write response file and update hits. 2025-07-18 18:10:48 +03:00
Mikhail Trofimov
f052841954 Update MonitoringService to read nbki responses by batches 2025-07-17 17:05:39 +03:00
Mikhail Trofimov
f2a827c8f2 Minor fix 2025-07-17 11:26:16 +03:00
Mikhail Trofimov
70bb792fa4 Update version to 0.3 2025-07-17 11:18:38 +03:00
Mikhail Trofimov
7986fc002d Decrease memory usage for loading and creating reports 2025-07-17 11:17:56 +03:00
Mikhail Trofimov
f4f3cf7151 Minor Refactoring 2025-07-16 17:17:05 +03:00
Mikhail Trofimov
09ee8e15fa Minor fix 2025-07-16 16:39:43 +03:00
Mikhail Trofimov
21c0731aa0 Optimize loading fids and calculating flags 2025-07-16 15:01:49 +03:00
Mikhail Trofimov
1815596a7b Added new method for stax reader to read from inputstream 2025-07-16 10:20:57 +03:00
Mikhail Trofimov
41582e676d Remove APM_SERVICE_VERSION from credit_tracker_config.env 2025-07-14 15:58:42 +03:00
Mikhail Trofimov
1ea8d12aab Added missed appender-ref for FILE-JSON 2025-07-11 16:53:46 +03:00
Mikhail Trofimov
63afa9b8ae Updated volumes for logs 2025-07-11 15:11:52 +03:00
Mikhail Trofimov
1e88776f2b Updated env and docker-compose.yml to have port section 2025-07-11 14:08:13 +03:00
Mikhail Trofimov
86bbfe16eb Added handler for MaxUploadSizeExceededException; hardcoded for MonitoringError 2025-07-11 13:21:16 +03:00
Mikhail Trofimov
a67478e350 Added mount for json logs 2025-07-11 13:01:47 +03:00
Mikhail Trofimov
0f49bfd27e Update properties, docker-compose.yml and .env config 2025-07-11 12:52:46 +03:00
Mikhail Trofimov
f91fee9a18 Changed isDUL method 2025-07-11 10:12:34 +03:00
Mikhail Trofimov
51ec8a098d Get rid of error 100; Added 013 error to set method; Changed 015 error message 2025-07-10 11:51:15 +03:00
Mikhail Trofimov
430ae8497a Fill dt attribute with GUTDF mapping 2025-07-10 11:45:42 +03:00
Mikhail Trofimov
9b2e723f4c Updated BaseStaxReader; Updated tests; Fix issue with stax reader; 2025-07-10 11:30:13 +03:00
Mikhail Trofimov
9a1ead682d Added consentIssueAuthority attribute 2025-07-08 16:23:17 +03:00
Mikhail Trofimov
1a6b13680c Fixed nullpointer in PersonService 2025-07-08 16:01:33 +03:00
Mikhail Trofimov
28e362231f setup Logback 2025-07-08 15:52:43 +03:00
Mikhail Trofimov
0ac6b9fdef Added docker with apm 2025-07-08 15:38:19 +03:00
Mikhail Trofimov
7c6a0c1696 Update Person attributes 2025-07-07 16:41:42 +03:00
Mikhail Trofimov
f0d5381b64 Update sql for fids finding; Minor refactoring; Optimize searching for passport data 2025-07-07 16:34:45 +03:00
Mikhail Trofimov
fecb4d467f Added batchs for updating hits 2025-07-07 12:18:12 +03:00
Mikhail Trofimov
2edecd538e Fix normalizing doc num value 2025-07-07 12:15:14 +03:00
Mikhail Trofimov
ba317986b0 Added SignalsStaxXmlWriter; Write Signals using stax; 2025-07-04 18:01:06 +03:00
Mikhail Trofimov
adfd2d4d28 Added MonitoringRequestStaxReader; Do not use jaxb to read MonitoringRequest 2025-07-04 17:45:10 +03:00
Mikhail Trofimov
29d57017ab Fix throwing nullpointer after validation 2025-07-04 13:02:26 +03:00
Mikhail Trofimov
119e93a9ff Fix UserPackageMapper; Remove tag for ERROR_008 2025-07-04 12:39:49 +03:00
Mikhail Trofimov
216fe7adbb Minor fix 2025-07-03 18:07:25 +03:00
Mikhail Trofimov
da024aaa3e Return empty reports list if package was found, but folder is empty 2025-07-03 17:49:29 +03:00
Mikhail Trofimov
7c4e81b18f Changed exception responses elemnt names 2025-07-03 17:24:31 +03:00
Mikhail Trofimov
a72930d1af Merge branch 'review' into 'dev'
Refactoring after review

See merge request nbki/credit_tracker!1
2025-07-03 14:06:20 +00:00
Mikhail Trofimov
3ed2902219 Refactoring after review 2025-07-03 14:06:20 +00:00
159 changed files with 5497 additions and 1378 deletions

23
Dockerfile Normal file
View file

@ -0,0 +1,23 @@
FROM eclipse-temurin:17-jre-alpine
MAINTAINER ru.nbch
VOLUME /opt/app/logs/app
VOLUME /opt/app/logs/json
VOLUME /opt/app/logs/access
VOLUME /opt/app/reports
WORKDIR /opt/app
COPY maven/elastic-apm-agent-1.45.0.jar /opt/app/elastic-apm-agent.jar
COPY maven/credit-tracker.jar /opt/app/credit-tracker.jar
COPY maven/start-credit-tracker.sh /opt/app/start-credit-tracker.sh
COPY maven/credit-tracker.properties /opt/app/credit-tracker.properties
COPY maven/monitoring-request.xsd /opt/app/monitoring-request.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
ENTRYPOINT ["/opt/app/start-credit-tracker.sh"]

Binary file not shown.

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

@ -1,38 +1,72 @@
server.port=8099 server.port=${SERVER_PORT:8099}
server.tomcat.accesslog.enabled=true
server.tomcat.accesslog.directory=/opt/app/logs/access
server.tomcat.accesslog.prefix=access_log
server.tomcat.accesslog.suffix=.log
server.tomcat.accesslog.pattern=%h %l %u %t "%r" %s %b
# datasources # datasources
# indic # indic
ct.indic-data-source-settings.driver=com.ibm.db2.jcc.DB2Driver ct.indic-data-source-settings.driver=com.ibm.db2.jcc.DB2Driver
ct.indic-data-source-settings.url=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=tester ct.indic-data-source-settings.username=${USERNAME_INDIC:tester}
ct.indic-data-source-settings.password=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=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=credit_tracker_user ct.signal-data-source-settings.username=${USERNAME_SIGNAL:credit_tracker_user}
ct.signal-data-source-settings.password= 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=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=tester ct.scoring-data-source-settings.username=${USERNAME_SCORING:tester}
ct.scoring-data-source-settings.password=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}
spring.servlet.multipart.max-file-size=2048MB ct.max_xml_file_size=${ACCEPTED_XML_SIZE:1024}
spring.servlet.multipart.max-request-size=2048MB spring.servlet.multipart.max-file-size=${ACCEPTED_ARCHIVE_SIZE:2048MB}
spring.servlet.multipart.max-request-size=${ACCEPTED_ARCHIVE_SIZE:2048MB}
# xsd paths # xsd paths
ct.xsd-validation.monitoring-request-path=monitoring-request.xsd ct.xsd-validation.monitoring-request-path=monitoring-request.xsd
ct.xsd-validation.signal-package-path=signal-package.xsd ct.xsd-validation.signal-package-path=signal-package.xsd
ct.signals-root-uri=http://10.230.227.102:11094/signalRUalfa/ ct.signals-root-uri=${SIGNALS_ROOT_URI:http://10.230.227.102:11094/signalRUalfa/}
ct.signals-token=gttcHxwcGojoAvZK ct.signals-token=${SIGNALS_TOKEN:gttcHxwcGojoAvZK}
ct.signals-user=0001ZZ000001 ct.signals-user=${SIGNALS_USER:0001ZZ000001}
ct.load-dir=/opt/app/reports
#ct.load-dir=D:\\folder_name\\signals
ct.load-dir=D:\\folder_name\\signals
# min # min
ct.monitoring-interval=15 ct.monitoring-interval=${MONITORING_INTERVAL:15}
# days # days
ct.update-flags-interval=1 ct.update-flags-cron-expression=${UPDATE_FLAGS_CRON_EXPRESSION:0 0 22 * * *}
ct.days-to-update-flags=30 ct.days-to-update-flags=${DAYS_TO_UPDATE_FLAGS:30}
ct.encoding=windows-1251

View file

@ -0,0 +1,77 @@
#commons
APP_VERSION=1.24
REGISTRY_URL=rh8-vm3.nbki.msk:5000
JMX_PORT=
JMX_HOSTNAME=
#volumes
REPORT_DIR=D:\folder_name\signals
LOGS_DIR=D:\folder_name\signals\logs\app
JSON_LOGS_DIR=D:\folder_name\signals\logs\json
ACCESS_LOGS_DIR=D:\folder_name\signals\logs\access
#APM settings
APM_SERVICE_NAME=credit-tracker
APM_SERVER_URLS=test
APM_ENVIRONMENT=test
APM_SERVICE_NODE_NAME=test
HOST_PORT=8081
SERVER_PORT=8099
# datasources
# indic
DB_URL_INDIC=jdbc:db2://10.230.227.101:2668/cprosd22:currentSchema=INDIC;
USERNAME_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
DB_URL_SIGNAL=jdbc:postgresql://10.200.200.133:5432/credit_tracker
USERNAME_SIGNAL=credit_tracker_user
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
DB_URL_SCORING=jdbc:db2://10.230.227.101:2668/cprosd22:currentSchema=TEST_2_INDIC;
USERNAME_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
ACCEPTED_XML_SIZE=1024
ACCEPTED_ARCHIVE_SIZE=2048MB
SIGNALS_ROOT_URI=http://10.230.227.102:11094/signalRUalfa/
SIGNALS_TOKEN=gttcHxwcGojoAvZK
SIGNALS_USER=0001ZZ000001
# min
MONITORING_INTERVAL=15
# days
UPDATE_FLAGS_CRON_EXPRESSION=0 22 * * *
DAYS_TO_UPDATE_FLAGS=30

View file

@ -0,0 +1,63 @@
services:
credit-tracker:
image: ${REGISTRY_URL}/credit-tracker:${APP_VERSION}
container_name: credit-tracker
restart: always
volumes:
- ${LOGS_DIR}:/opt/app/logs/app
- ${JSON_LOGS_DIR}:/opt/app/logs/json
- ${REPORT_DIR}:/opt/app/reports
- ${ACCESS_LOGS_DIR}:/opt/app/logs/access
ports:
- "${HOST_PORT}:${SERVER_PORT}"
environment:
- TZ=Europe/Moscow
- APM_SERVICE_NAME=${APM_SERVICE_NAME}
- APM_SERVER_URLS=${APM_SERVER_URLS}
- APM_ENVIRONMENT=${APM_ENVIRONMENT}
- APM_SERVICE_NODE_NAME=${APM_SERVICE_NODE_NAME}
- APM_SERVICE_VERSION=${APP_VERSION}
- JMX_PORT=${JMX_PORT}
- JMX_HOSTNAME=${JMX_HOSTNAME}
- SERVER_PORT=${SERVER_PORT}
- DB_URL_INDIC=${DB_URL_INDIC}
- USERNAME_INDIC=${USERNAME_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}
- USERNAME_SIGNAL=${USERNAME_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}
- USERNAME_SCORING=${USERNAME_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_ARCHIVE_SIZE=${ACCEPTED_ARCHIVE_SIZE}
- SIGNALS_ROOT_URI=${SIGNALS_ROOT_URI}
- SIGNALS_TOKEN=${SIGNALS_TOKEN}
- SIGNALS_USER=${SIGNALS_USER}
- MONITORING_INTERVAL=${MONITORING_INTERVAL}
- UPDATE_FLAGS_INTERVAL=${UPDATE_FLAGS_INTERVAL}
- DAYS_TO_UPDATE_FLAGS=${DAYS_TO_UPDATE_FLAGS}

431
pom.xml
View file

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

View file

@ -4,6 +4,7 @@ import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.ConfigurableApplicationContext;
import ru.nbch.credit_tracker.service.MonitoringService; import ru.nbch.credit_tracker.service.MonitoringService;
import ru.nbch.credit_tracker.service.UpdateProcessingPackagesService;
import ru.nbch.credit_tracker.service.UpdatingFlagsService; import ru.nbch.credit_tracker.service.UpdatingFlagsService;
@SpringBootApplication @SpringBootApplication
@ -11,10 +12,12 @@ public class CreditTrackerApplication {
public static void main(String[] args) { public static void main(String[] args) {
ConfigurableApplicationContext ctx = SpringApplication.run(CreditTrackerApplication.class, args); ConfigurableApplicationContext ctx = SpringApplication.run(CreditTrackerApplication.class, args);
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);
monitoringService.run(); monitoringService.run();
updatingFlagsService.run(); updatingFlagsService.run();
updateProcessingPackagesService.run();
} }
} }

View file

@ -0,0 +1,124 @@
package ru.nbch.credit_tracker.builder;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import ru.nbch.credit_tracker.entities.external_request.signals.Person;
import ru.nbch.credit_tracker.service.indic.PersonService;
import ru.nbch.credit_tracker.utils.CTUtil;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
@Slf4j
public class PersonFactoryBuilder {
private final static DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("dd.MM.yyyy");
private final static String DEFAULT_DATE = "01.01.2000";
private final static String IP = "0";
private final static String OKSM = "999";
private final static String CONSENT_PURPOSE = "16";
private final static String REPORT_USER = "АО «НБКИ»";
private final static String LIABILITY = "1";
private final static String CONSENT_PERIOD = "3";
private final static String REPORT_USER_REG_NUM = "1057746710713";
private final static String REPORT_USER_TAX_ID = "7703548386";
private final static String CONSENT_HASH = "hash";
private final static String CONSENT_DIV_CODE = "000-000";
private final static String INQ_PURPOSE = "16";
private final static String ODT = "Иной документ";
private final static String CONSENT_ISSUE_AUTHORITY = "Кем выдан";
private PersonService.PersonData personData;
private Long uid;
public static PersonFactoryBuilder builder() {
return new PersonFactoryBuilder();
}
public PersonFactoryBuilder personData(PersonService.PersonData personData) {
this.personData = personData;
return this;
}
public PersonFactoryBuilder uid(Long uid) {
this.uid = uid;
return this;
}
public Person build() {
if (personData == null) {
return null;
}
PersonService.NameData nameData = personData.getName();
PersonService.PassportData passportData = personData.getPassport();
Person person = new Person();
person.setS(nameData.getLastName());
person.setN(nameData.getFirstName());
person.setM(StringUtils.isNotBlank(nameData.getMiddleName()) ? nameData.getMiddleName() : null);
person.setD(DATE_FORMAT.format(nameData.getBirthDate()));
person.setPn(getPn(passportData.getSerNum(), passportData.getIdNum()));
person.setPd(DATE_FORMAT.format(LocalDate.now())); // Do not use passport iss_date. Signal service could reject the package
person.setDt(getDt(passportData.getIdType(), passportData.getIdType2()));
if (person.getDt() != null && person.getDt().equals("999")) {
person.setOdt(ODT);
person.setConsentOdt(ODT);
}
person.setIp(IP);
person.setUid(uid);
person.setOksm(OKSM);
person.setConsentDate(DATE_FORMAT.format(LocalDate.now()));
person.setConsentPurpose(CONSENT_PURPOSE);
person.setReportUser(REPORT_USER);
person.setLiability(LIABILITY);
person.setConsentPeriod(CONSENT_PERIOD);
person.setAgrDate(DATE_FORMAT.format(LocalDate.now()));
person.setReportUserRegNum(REPORT_USER_REG_NUM);
person.setReportUserTaxID(REPORT_USER_TAX_ID);
person.setConsentHash(CONSENT_HASH);
person.setCs(person.getS());
person.setConsentN(person.getN());
person.setConsentM(person.getM());
person.setConsentBd(person.getD());
person.setConsentPn(person.getPn());
person.setConsentPd(person.getPd());
person.setConsentDivCode(CONSENT_DIV_CODE);
person.setConsentDt(person.getDt());
person.setConsentIssueAuthority(CONSENT_ISSUE_AUTHORITY);
person.setInqPurpose(INQ_PURPOSE);
return person;
}
private String getPn(String serNum, String idNum) {
String normSerNum = CTUtil.normalize(serNum);
String normIdNum = CTUtil.normalize(idNum);
return StringUtils.isNotBlank(normSerNum) ? normSerNum + normIdNum : normIdNum;
}
private String getDt(String idType, String idType2) {
if (StringUtils.isNotBlank(idType2)) {
return idType2;
} else {
return switch (idType) {
case "21" -> "21";
case "22" -> "22.1";
case "09" -> "22.2";
case "26" -> "23";
case "04" -> "24";
case "07" -> "25";
case "14", "03" -> "27";
case "10" -> "31";
case "15" -> "32";
case "13" -> "37";
case "06", "27" -> "28";
case "01", "02" -> "999";
default -> {
log.warn("ID_TYPE2 is blank and no matching found for ID_TYPE {}", idType);
yield "";
}
};
}
}
}

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

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

View file

@ -7,11 +7,11 @@ import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan; import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import 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;
@ -23,26 +23,32 @@ import java.sql.SQLException;
basePackages = "ru.nbch.credit_tracker.mapper.indic", basePackages = "ru.nbch.credit_tracker.mapper.indic",
sqlSessionFactoryRef = "sqlSessionIndic" sqlSessionFactoryRef = "sqlSessionIndic"
) )
public class IndicDataSourceConfig { public class IndicDataSourceConfig extends DatabaseConfig {
@Value("classpath:mybatis/mapper/indic/*.xml") @Value("classpath:mybatis/mapper/indic/*.xml")
private Resource[] resources; private Resource[] resources;
@Bean(name = "indicDataSource") @Bean(name = "indicDataSource")
public DataSource dataSource(CreditTrackerSettings config) throws SQLException { public DataSource dataSource(CreditTrackerSettings config) throws SQLException {
DataSourceBuilder<HikariDataSource> dataSourceBuilder = (DataSourceBuilder<HikariDataSource>) DataSourceBuilder.create(); DataSourceSettings indicSetings = config.getIndicDataSourceSettings();
dataSourceBuilder.driverClassName(config.getIndicDataSourceSettings().driver());
dataSourceBuilder.url(config.getIndicDataSourceSettings().url()); HikariDataSource dataSource = createDataSource(indicSetings);
dataSourceBuilder.username(config.getIndicDataSourceSettings().username()); dataSource.setMaximumPoolSize(indicSetings.maximumPoolSize());
dataSourceBuilder.password(config.getIndicDataSourceSettings().password()); dataSource.setMinimumIdle(indicSetings.minimumIdle());
HikariDataSource dataSource = dataSourceBuilder.build(); 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

@ -7,41 +7,48 @@ import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan; import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import 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;
import java.sql.SQLException; import java.sql.SQLException;
@Slf4j @Slf4j
@Configuration @Configuration
@MapperScan( @MapperScan(
basePackages = "ru.nbch.credit_tracker.mapper.scoring", basePackages = "ru.nbch.credit_tracker.mapper.scoring",
sqlSessionFactoryRef = "sqlSessionScoring" sqlSessionFactoryRef = "sqlSessionScoring"
) )
public class ScoringDataSourceConfig { public class ScoringDataSourceConfig extends DatabaseConfig {
@Value("classpath:mybatis/mapper/scoring/*.xml") @Value("classpath:mybatis/mapper/scoring/*.xml")
private Resource[] resources; private Resource[] resources;
@Bean(name = "scoringDataSource") @Bean(name = "scoringDataSource")
public DataSource dataSource(CreditTrackerSettings config) throws SQLException { public DataSource dataSource(CreditTrackerSettings config) throws SQLException {
DataSourceBuilder<HikariDataSource> dataSourceBuilder = (DataSourceBuilder<HikariDataSource>) DataSourceBuilder.create(); DataSourceSettings scoringSetings = config.getScoringDataSourceSettings();
dataSourceBuilder.driverClassName(config.getScoringDataSourceSettings().driver());
dataSourceBuilder.url(config.getScoringDataSourceSettings().url()); HikariDataSource dataSource = createDataSource(scoringSetings);
dataSourceBuilder.username(config.getScoringDataSourceSettings().username()); dataSource.setMaximumPoolSize(scoringSetings.maximumPoolSize());
dataSourceBuilder.password(config.getScoringDataSourceSettings().password()); dataSource.setMinimumIdle(scoringSetings.minimumIdle());
HikariDataSource dataSource = dataSourceBuilder.build(); 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

@ -7,11 +7,11 @@ import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan; import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import 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;
@ -23,25 +23,31 @@ import java.sql.SQLException;
basePackages = "ru.nbch.credit_tracker.mapper.signal", basePackages = "ru.nbch.credit_tracker.mapper.signal",
sqlSessionFactoryRef = "sqlSessionSignal" sqlSessionFactoryRef = "sqlSessionSignal"
) )
public class SignalDataSourceConfig { public class SignalDataSourceConfig extends DatabaseConfig {
@Value("classpath:mybatis/mapper/signal/*.xml") @Value("classpath:mybatis/mapper/signal/*.xml")
private Resource[] resources; private Resource[] resources;
@Bean(name = "signalDataSource") @Bean(name = "signalDataSource")
public DataSource dataSource(CreditTrackerSettings config) throws SQLException { public DataSource dataSource(CreditTrackerSettings config) throws SQLException {
DataSourceBuilder<HikariDataSource> dataSourceBuilder = (DataSourceBuilder<HikariDataSource>) DataSourceBuilder.create(); DataSourceSettings signalSetings = config.getSignalDataSourceSettings();
dataSourceBuilder.driverClassName(config.getSignalDataSourceSettings().driver());
dataSourceBuilder.url(config.getSignalDataSourceSettings().url()); HikariDataSource dataSource = createDataSource(signalSetings);
dataSourceBuilder.username(config.getSignalDataSourceSettings().username()); dataSource.setMaximumPoolSize(signalSetings.maximumPoolSize());
// dataSourceBuilder.password(config.getSignalDataSourceSettings().password()); TODO uncomment whe password will be set dataSource.setMinimumIdle(signalSetings.minimumIdle());
HikariDataSource dataSource = dataSourceBuilder.build(); 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,27 +1,85 @@
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;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.bind.annotation.RestControllerAdvice;
import ru.nbch.credit_tracker.entities.response.errors.CommonError; import org.springframework.web.multipart.MaxUploadSizeExceededException;
import ru.nbch.credit_tracker.exceptions.SignalErrorException; import ru.nbch.credit_tracker.config.settings.CreditTrackerSettings;
import ru.nbch.credit_tracker.entities.user_response.errors.*;
import ru.nbch.credit_tracker.enums.SignalError;
import ru.nbch.credit_tracker.exceptions.SignalClientException;
import ru.nbch.credit_tracker.exceptions.SignalException;
import ru.nbch.credit_tracker.exceptions.SignalServerException;
import ru.nbch.credit_tracker.utils.error.SimpleMessageResolver;
import java.nio.charset.Charset; import java.nio.charset.Charset;
@Slf4j
@RestControllerAdvice @RestControllerAdvice
public class GlobalExceptionHandler { public class GlobalExceptionHandler {
@ExceptionHandler(value = SignalErrorException.class, produces = MediaType.APPLICATION_XML_VALUE) private final SimpleMessageResolver messageResolver = new SimpleMessageResolver();
public ResponseEntity<CommonError> handleInvalidUser(SignalErrorException ex) { private final String encoding;
public GlobalExceptionHandler(CreditTrackerSettings settings) {
this.encoding = settings.getEncoding();
}
@ExceptionHandler(value = MaxUploadSizeExceededException.class, produces = MediaType.APPLICATION_XML_VALUE)
public ResponseEntity<MonitoringError> handleMaxUploadSizeExceeded(MaxUploadSizeExceededException ex) {
HttpHeaders headers = new HttpHeaders(); HttpHeaders headers = new HttpHeaders();
headers.setContentType(new MediaType("application", "xml", Charset.forName("windows-1251"))); headers.setContentType(new MediaType("application", "xml", Charset.forName(encoding)));
if (ex.isUserError()) {
return new ResponseEntity<>(ex.getError(), headers, HttpStatus.BAD_REQUEST); MonitoringError error = new MonitoringError();
error.setStatus("error");
error.setErrorCode(SignalError.ERROR_009.getCode());
error.setErrorText(SignalError.ERROR_009.getKey());
return new ResponseEntity<>(error, headers, HttpStatus.BAD_REQUEST);
}
@ExceptionHandler(value = SignalException.class, produces = MediaType.APPLICATION_XML_VALUE)
public ResponseEntity<CommonError> handleClientException(SignalException ex) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(new MediaType("application", "xml", Charset.forName(encoding)));
String errorText = messageResolver.resolve(ex.getErrorMessage());
CommonError commonError = getCommonError(ex);
commonError.setStatus("error");
commonError.setErrorCode(ex.getErrorMessage().getSubject().getCode());
commonError.setErrorText(errorText);
if (ex instanceof SignalClientException) {
String packageName = ((SignalClientException) ex).getPackageName();
if(packageName != null ) {
log.warn("Response error: {}. PackageName: {}", errorText, packageName);
} else {
log.warn("Response error: {}", errorText);
}
return new ResponseEntity<>(commonError, headers, HttpStatus.BAD_REQUEST);
} else { } else {
return new ResponseEntity<>(ex.getError(), headers, HttpStatus.INTERNAL_SERVER_ERROR); Exception cause = ((SignalServerException) ex).getCause();
log.error(cause.getMessage(), cause);
return new ResponseEntity<>(commonError, headers, HttpStatus.INTERNAL_SERVER_ERROR);
} }
} }
private static CommonError getCommonError(SignalException ex) {
CommonError commonError;
switch (ex.getErrorFormat()) {
case MonitoringError -> commonError = new MonitoringError();
case MonitoringActivePackagesError -> commonError = new MonitoringActivePackagesError();
case MonitoringAllPackagesError -> commonError = new MonitoringAllPackagesError();
case MonitoringDownloadError -> commonError = new MonitoringDownloadError();
case MonitoringReportListError -> commonError = new MonitoringReportListError();
case MonitoringPackageDeleteError -> commonError = new MonitoringPackageDeleteError();
default -> throw new IllegalStateException("Unsupported error format: " + ex.getErrorFormat());
}
return commonError;
}
} }

View file

@ -2,6 +2,7 @@ package ru.nbch.credit_tracker.config.rest;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.CustomizableThreadFactory;
import org.springframework.web.client.RestTemplate; import org.springframework.web.client.RestTemplate;
import ru.nbch.credit_tracker.config.settings.CreditTrackerSettings; import ru.nbch.credit_tracker.config.settings.CreditTrackerSettings;
@ -21,6 +22,6 @@ public class RestConfig {
@Bean(name = "executorService") @Bean(name = "executorService")
public ExecutorService executorService() { public ExecutorService executorService() {
return Executors.newCachedThreadPool(); return Executors.newCachedThreadPool(new CustomizableThreadFactory("package-registration-"));
} }
} }

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

@ -26,9 +26,11 @@ public class CreditTrackerSettings {
private String signalsToken; private String signalsToken;
private String signalsuser; private String signalsuser;
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;
} }

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

@ -0,0 +1,48 @@
package ru.nbch.credit_tracker.config.xml;
import jakarta.xml.bind.JAXBException;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import ru.nbch.credit_tracker.config.settings.CreditTrackerSettings;
import ru.nbch.credit_tracker.entities.external_response.monitoring_report_file.SgnlReportMonitorigSetupError;
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.success.MonitoringSetupResponse;
import ru.nbch.credit_tracker.service.xml.jaxb.IXmlProcessor;
import ru.nbch.credit_tracker.service.xml.jaxb.XmlProcessorImplAnyClasses;
import ru.nbch.credit_tracker.service.xml.stax.*;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Supplier;
@Configuration
public class XmlConfig {
@Bean
public IXmlProcessor xmlProcessor(CreditTrackerSettings settings) throws JAXBException {
return new XmlProcessorImplAnyClasses(Charset.forName(settings.getEncoding()),
// unmarshalling classes
SgnlOnlineReport.class,
SgnlReportMonitorigSetupError.class,
MonitoringActivePackagesRequest.class,
MonitoringAllPackagesRequest.class,
// marshalling classes
MonitoringError.class,
MonitoringSetupResponse.class);
}
@Bean
public StaxXmlProcessor staxXmlProcessor() {
List<Supplier<StaxReader<?>>> staxReader = new ArrayList<>();
staxReader.add(SgnlReportStatusStaxReader::new);
staxReader.add(MonitoringRequestStaxReader::new);
staxReader.add(OnlineDownloadReportPersonsStaxReader::new);
staxReader.add(MonitoringSignalResponseStaxXmlReader::new);
staxReader.add(MonitoringReportErrorsStaxtReader::new);
return new XmlProcessorStaxImpl(staxReader);
}
}

View file

@ -5,11 +5,13 @@ import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import ru.nbch.credit_tracker.entities.request.active_packages.MonitoringActivePackagesRequest; import ru.nbch.credit_tracker.entities.user_request.active_packages.MonitoringActivePackagesRequest;
import ru.nbch.credit_tracker.entities.response.active_packages.MonitoringActivePackagesResponse; import ru.nbch.credit_tracker.entities.user_request.all_packages.MonitoringAllPackagesRequest;
import ru.nbch.credit_tracker.entities.response.success.MonitoringPackageDeleteResponse; import ru.nbch.credit_tracker.entities.user_response.active_packages.MonitoringActivePackagesResponse;
import ru.nbch.credit_tracker.entities.response.success.MonitoringReportListResponse; import ru.nbch.credit_tracker.entities.user_response.all_packages.MonitoringAllPackagesResponse;
import ru.nbch.credit_tracker.entities.response.success.MonitoringResponse; import ru.nbch.credit_tracker.entities.user_response.success.MonitoringPackageDeleteResponse;
import ru.nbch.credit_tracker.entities.user_response.success.MonitoringReportListResponse;
import ru.nbch.credit_tracker.entities.user_response.success.MonitoringSetupResponse;
import ru.nbch.credit_tracker.facade.SignalFacade; import ru.nbch.credit_tracker.facade.SignalFacade;
@RestController @RestController
@ -23,7 +25,7 @@ public class SignalController {
} }
@PostMapping(value = "/set", produces = MediaType.APPLICATION_XML_VALUE) @PostMapping(value = "/set", produces = MediaType.APPLICATION_XML_VALUE)
public ResponseEntity<MonitoringResponse> setupMonitoring(@RequestParam("file") MultipartFile file) { public ResponseEntity<MonitoringSetupResponse> setupMonitoring(@RequestParam("file") MultipartFile file) {
return signalFacade.setupMonitoring(file); return signalFacade.setupMonitoring(file);
} }
@ -32,6 +34,11 @@ public class SignalController {
return signalFacade.findActivePackages(activePackagesRequest); return signalFacade.findActivePackages(activePackagesRequest);
} }
@PostMapping(value = "/all_packages", consumes = MediaType.APPLICATION_XML_VALUE, produces = MediaType.APPLICATION_XML_VALUE)
public ResponseEntity<MonitoringAllPackagesResponse> findAllPackages(@RequestBody MonitoringAllPackagesRequest allPackagesRequest) {
return signalFacade.findAllPackages(allPackagesRequest);
}
@PostMapping(value = "/delete", params = {"username", "packageName"}, produces = MediaType.APPLICATION_XML_VALUE) @PostMapping(value = "/delete", params = {"username", "packageName"}, produces = MediaType.APPLICATION_XML_VALUE)
public ResponseEntity<MonitoringPackageDeleteResponse> deletePackage(@RequestParam("username") String username, @RequestParam("packageName") String packageName) { public ResponseEntity<MonitoringPackageDeleteResponse> deletePackage(@RequestParam("username") String username, @RequestParam("packageName") String packageName) {
return signalFacade.deletePackage(username, packageName); return signalFacade.deletePackage(username, packageName);

View file

@ -7,9 +7,9 @@ import java.time.LocalDateTime;
@Getter @Getter
@Setter @Setter
public class HitsData { 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

@ -1,9 +1,6 @@
package ru.nbch.credit_tracker.entities.dto; package ru.nbch.credit_tracker.entities.dto;
import lombok.AllArgsConstructor; import lombok.*;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.time.LocalDateTime; import java.time.LocalDateTime;
@ -11,13 +8,15 @@ import java.time.LocalDateTime;
@Setter @Setter
@AllArgsConstructor @AllArgsConstructor
@NoArgsConstructor @NoArgsConstructor
public class SubjectData { @Builder
private Integer id; public class PhoneFidMap {
private Integer packageId; private Long id;
private Long packageId;
private String subjectId; private String subjectId;
private String phone; private String phone;
private Integer fid; private Integer fid;
private Integer flagDebt5000; private Integer flagDebt5000;
private Integer flag90days; private Integer flag90days;
private LocalDateTime calculatedAt; private LocalDateTime calculatedAt;
private String errorText;
} }

View file

@ -1,5 +1,6 @@
package ru.nbch.credit_tracker.entities.dto; package ru.nbch.credit_tracker.entities.dto;
import lombok.Builder;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
@ -7,11 +8,14 @@ import java.time.LocalDateTime;
@Getter @Getter
@Setter @Setter
public class PackageData { @Builder
private Integer id; public class UserPackage {
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 String lastDownloadedReport; private String lastDownloadedReport;
private LocalDateTime monitoringAt;
} }

View file

@ -1,4 +1,4 @@
package ru.nbch.credit_tracker.entities.signal_package; package ru.nbch.credit_tracker.entities.external_request.signals;
import jakarta.xml.bind.annotation.XmlAccessType; import jakarta.xml.bind.annotation.XmlAccessType;
import jakarta.xml.bind.annotation.XmlAccessorType; import jakarta.xml.bind.annotation.XmlAccessorType;

View file

@ -1,4 +1,4 @@
package ru.nbch.credit_tracker.entities.signal_package; package ru.nbch.credit_tracker.entities.external_request.signals;
import jakarta.xml.bind.annotation.XmlAccessType; import jakarta.xml.bind.annotation.XmlAccessType;
import jakarta.xml.bind.annotation.XmlAccessorType; import jakarta.xml.bind.annotation.XmlAccessorType;

View file

@ -1,4 +1,4 @@
package ru.nbch.credit_tracker.entities.signal_package; package ru.nbch.credit_tracker.entities.external_request.signals;
import jakarta.xml.bind.annotation.*; import jakarta.xml.bind.annotation.*;
import lombok.Getter; import lombok.Getter;
@ -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 String 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)
@ -34,6 +34,8 @@ public class Person {
protected String pd; protected String pd;
@XmlAttribute(name = "dt", required = true) @XmlAttribute(name = "dt", required = true)
protected String dt; protected String dt;
@XmlAttribute(name = "odt")
protected String odt;
@XmlAttribute(name = "ip", required = true) @XmlAttribute(name = "ip", required = true)
protected String ip; protected String ip;
@XmlAttribute(name = "consentDate") @XmlAttribute(name = "consentDate")
@ -73,6 +75,10 @@ public class Person {
protected String consentDivCode; protected String consentDivCode;
@XmlAttribute(name = "consentDt") @XmlAttribute(name = "consentDt")
protected String consentDt; protected String consentDt;
@XmlAttribute(name = "consentOdt")
protected String consentOdt;
@XmlAttribute(name = "consentIssueAuthority")
protected String consentIssueAuthority;
@XmlAttribute(name = "inqPurpose") @XmlAttribute(name = "inqPurpose")
protected String inqPurpose; protected String inqPurpose;

View file

@ -1,4 +1,4 @@
package ru.nbch.credit_tracker.entities.signal_package; package ru.nbch.credit_tracker.entities.external_request.signals;
import jakarta.xml.bind.annotation.XmlAccessType; import jakarta.xml.bind.annotation.XmlAccessType;
import jakarta.xml.bind.annotation.XmlAccessorType; import jakarta.xml.bind.annotation.XmlAccessorType;

View file

@ -1,5 +1,5 @@
package ru.nbch.credit_tracker.entities.signal_package; package ru.nbch.credit_tracker.entities.external_request.signals;
import jakarta.xml.bind.annotation.*; import jakarta.xml.bind.annotation.*;
import lombok.Getter; import lombok.Getter;
@ -25,6 +25,8 @@ public class Signals {
protected Conditions conditions; protected Conditions conditions;
@XmlAttribute(name = "online", required = true) @XmlAttribute(name = "online", required = true)
protected String online; protected String online;
@XmlAttribute(name = "processWithErrors", required = true)
protected String processWithErrors;
public Conditions getConditions() { public Conditions getConditions() {
if (conditions == null) { if (conditions == null) {

View file

@ -1,4 +1,4 @@
package ru.nbch.credit_tracker.entities.response.external_errors; package ru.nbch.credit_tracker.entities.external_response.errors;
import jakarta.xml.bind.annotation.XmlAccessType; import jakarta.xml.bind.annotation.XmlAccessType;
import jakarta.xml.bind.annotation.XmlAccessorType; import jakarta.xml.bind.annotation.XmlAccessorType;

View file

@ -1,4 +1,4 @@
package ru.nbch.credit_tracker.entities.response.external_errors; package ru.nbch.credit_tracker.entities.external_response.errors;
import jakarta.xml.bind.annotation.XmlAccessType; import jakarta.xml.bind.annotation.XmlAccessType;
import jakarta.xml.bind.annotation.XmlAccessorType; import jakarta.xml.bind.annotation.XmlAccessorType;

View file

@ -1,4 +1,4 @@
package ru.nbch.credit_tracker.entities.response.external_errors; package ru.nbch.credit_tracker.entities.external_response.errors;
import jakarta.xml.bind.annotation.*; import jakarta.xml.bind.annotation.*;
import lombok.Getter; import lombok.Getter;

View file

@ -0,0 +1,72 @@
package ru.nbch.credit_tracker.entities.external_response.monitoring_report_error;
import jakarta.xml.bind.annotation.*;
import lombok.Getter;
import lombok.Setter;
import java.util.ArrayList;
import java.util.List;
@Setter
@Getter
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
"id",
"version",
"user",
"reportCreated"
})
@XmlRootElement(name = "Errors")
public class MonitoringReportErrors {
@XmlElement(name = "Id")
protected String id;
@XmlElement(name = "Version")
protected String version;
@XmlElement(name = "User")
protected String user;
@XmlElement(name = "ReportCreated")
protected String reportCreated;
@XmlElement(name = "p")
protected List<P> persons;
public List<P> getPersons() {
if (persons == null) {
persons = new ArrayList<>();
}
return this.persons;
}
private P currentPerson;
public void createCurrentPerson() {
currentPerson = new P();
}
public P getCurrentPerson() {
return currentPerson;
}
public void finalizePerson() {
getPersons().add(currentPerson);
}
@Getter
@Setter
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
})
public static class P {
@XmlAttribute(name = "uid")
protected Long uid;
@XmlValue
protected String value;
}
}

View file

@ -0,0 +1,21 @@
package ru.nbch.credit_tracker.entities.external_response.monitoring_report_file;
import jakarta.xml.bind.annotation.XmlAccessType;
import jakarta.xml.bind.annotation.XmlAccessorType;
import jakarta.xml.bind.annotation.XmlElement;
import jakarta.xml.bind.annotation.XmlType;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
"filename"
})
public class ErrorsForNextReport {
@XmlElement(name = "filename", required = true)
protected String filename;
}

View file

@ -0,0 +1,24 @@
package ru.nbch.credit_tracker.entities.external_response.monitoring_report_file;
import jakarta.xml.bind.annotation.XmlAccessType;
import jakarta.xml.bind.annotation.XmlAccessorType;
import jakarta.xml.bind.annotation.XmlElement;
import jakarta.xml.bind.annotation.XmlType;
import lombok.Getter;
import lombok.Setter;
@Setter
@Getter
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
"memberCode",
"packCode"
})
public class InputParams {
@XmlElement(name = "memberCode", required = true)
protected String memberCode;
@XmlElement(name = "packCode", required = true)
protected String packCode;
}

View file

@ -0,0 +1,20 @@
package ru.nbch.credit_tracker.entities.external_response.monitoring_report_file;
import jakarta.xml.bind.annotation.XmlAccessType;
import jakarta.xml.bind.annotation.XmlAccessorType;
import jakarta.xml.bind.annotation.XmlElement;
import jakarta.xml.bind.annotation.XmlType;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
"errorsForNextReport"
})
public class OperationReport {
@XmlElement(name = "ErrorsForNextReport")
protected ErrorsForNextReport errorsForNextReport;
}

View file

@ -0,0 +1,22 @@
package ru.nbch.credit_tracker.entities.external_response.monitoring_report_file;
import jakarta.xml.bind.annotation.*;
import lombok.Getter;
import lombok.Setter;
@Setter
@Getter
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
"inputParams",
"operationReport"
})
@XmlRootElement(name = "SgnlReport")
public class SgnlReportMonitorigSetupError {
@XmlElement(name = "InputParams", required = true)
protected InputParams inputParams;
@XmlElement(name = "OperationReport", required = true)
protected OperationReport operationReport;
}

View file

@ -1,4 +1,4 @@
package ru.nbch.credit_tracker.entities.response.online_download; package ru.nbch.credit_tracker.entities.external_response.online_download;
import jakarta.xml.bind.annotation.*; import jakarta.xml.bind.annotation.*;
import lombok.Getter; import lombok.Getter;
@ -42,4 +42,18 @@ public class Inquiry {
return this.signals; return this.signals;
} }
private Signal currentSignal;
public void createCurrentSignal() {
currentSignal = new Signal();
}
public Signal getCurrentSignal() {
return currentSignal;
}
public void finalizeSignal() {
getSignals().add(currentSignal);
}
} }

View file

@ -1,4 +1,4 @@
package ru.nbch.credit_tracker.entities.response.online_download; package ru.nbch.credit_tracker.entities.external_response.online_download;
import jakarta.xml.bind.annotation.*; import jakarta.xml.bind.annotation.*;
import lombok.Getter; import lombok.Getter;

View file

@ -1,4 +1,4 @@
package ru.nbch.credit_tracker.entities.response.online_download; package ru.nbch.credit_tracker.entities.external_response.online_download;
import jakarta.xml.bind.annotation.*; import jakarta.xml.bind.annotation.*;
import lombok.Getter; import lombok.Getter;
@ -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 String uid; protected Long uid;
@XmlElement(name = "Inquiry", required = true) @XmlElement(name = "Inquiry", required = true)
protected List<Inquiry> inquiries; protected List<Inquiry> inquiries;
@ -27,4 +27,18 @@ public class Person {
} }
return this.inquiries; return this.inquiries;
} }
private Inquiry currentInquiry;
public void createCurrentInquiry() {
currentInquiry = new Inquiry();
}
public Inquiry getCurrentInquiry() {
return currentInquiry;
}
public void finalizeInquiry() {
getInquiries().add(currentInquiry);
}
} }

View file

@ -1,4 +1,4 @@
package ru.nbch.credit_tracker.entities.response.online_download; package ru.nbch.credit_tracker.entities.external_response.online_download;
import jakarta.xml.bind.annotation.XmlAccessType; import jakarta.xml.bind.annotation.XmlAccessType;
import jakarta.xml.bind.annotation.XmlAccessorType; import jakarta.xml.bind.annotation.XmlAccessorType;
@ -24,5 +24,18 @@ public class Persons {
return this.persons; return this.persons;
} }
private Person currentPerson;
public void createCurrentPerson() {
currentPerson = new Person();
}
public Person getCurrentPerson() {
return currentPerson;
}
public void finalizePerson() {
getPersons().add(currentPerson);
}
} }

View file

@ -1,4 +1,4 @@
package ru.nbch.credit_tracker.entities.response.online_download; package ru.nbch.credit_tracker.entities.external_response.online_download;
import jakarta.xml.bind.annotation.XmlAccessType; import jakarta.xml.bind.annotation.XmlAccessType;
import jakarta.xml.bind.annotation.XmlAccessorType; import jakarta.xml.bind.annotation.XmlAccessorType;

View file

@ -1,4 +1,4 @@
package ru.nbch.credit_tracker.entities.response.online_report; package ru.nbch.credit_tracker.entities.external_response.online_report;
import jakarta.xml.bind.annotation.XmlAccessType; import jakarta.xml.bind.annotation.XmlAccessType;
import jakarta.xml.bind.annotation.XmlAccessorType; import jakarta.xml.bind.annotation.XmlAccessorType;

View file

@ -1,4 +1,4 @@
package ru.nbch.credit_tracker.entities.response.online_report; package ru.nbch.credit_tracker.entities.external_response.online_report;
import jakarta.xml.bind.annotation.XmlAccessType; import jakarta.xml.bind.annotation.XmlAccessType;
import jakarta.xml.bind.annotation.XmlAccessorType; import jakarta.xml.bind.annotation.XmlAccessorType;

View file

@ -1,4 +1,4 @@
package ru.nbch.credit_tracker.entities.response.online_report; package ru.nbch.credit_tracker.entities.external_response.online_report;
import jakarta.xml.bind.annotation.XmlAccessType; import jakarta.xml.bind.annotation.XmlAccessType;
import jakarta.xml.bind.annotation.XmlAccessorType; import jakarta.xml.bind.annotation.XmlAccessorType;

View file

@ -1,4 +1,4 @@
package ru.nbch.credit_tracker.entities.response.online_report; package ru.nbch.credit_tracker.entities.external_response.online_report;
import jakarta.xml.bind.annotation.XmlAccessType; import jakarta.xml.bind.annotation.XmlAccessType;
import jakarta.xml.bind.annotation.XmlAccessorType; import jakarta.xml.bind.annotation.XmlAccessorType;

View file

@ -1,4 +1,4 @@
package ru.nbch.credit_tracker.entities.response.online_report; package ru.nbch.credit_tracker.entities.external_response.online_report;
import jakarta.xml.bind.annotation.*; import jakarta.xml.bind.annotation.*;
import lombok.Getter; import lombok.Getter;

View file

@ -1,31 +0,0 @@
package ru.nbch.credit_tracker.entities.request.monitoring_request;
import jakarta.xml.bind.annotation.*;
import lombok.Getter;
import lombok.Setter;
@Setter
@Getter
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
"userId",
"password",
"packageName",
"subjects"
})
@XmlRootElement(name = "MonitoringRequest")
public class MonitoringRequest {
@XmlElement(name = "UserId", required = true)
protected String userId;
@XmlElement(name = "Password", required = true)
protected String password;
@XmlElement(name = "PackageName", required = true)
protected String packageName;
@XmlElement(name = "Subjects", required = true)
protected Subjects subjects;
}

View file

@ -1,4 +1,4 @@
package ru.nbch.credit_tracker.entities.request.active_packages; package ru.nbch.credit_tracker.entities.user_request.active_packages;
import jakarta.xml.bind.annotation.*; import jakarta.xml.bind.annotation.*;
import lombok.Getter; import lombok.Getter;

View file

@ -0,0 +1,22 @@
package ru.nbch.credit_tracker.entities.user_request.all_packages;
import jakarta.xml.bind.annotation.*;
import lombok.Getter;
import lombok.Setter;
@Setter
@Getter
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
"userId",
"password"
})
@XmlRootElement(name = "MonitoringAllPackagesRequest")
public class MonitoringAllPackagesRequest {
@XmlElement(name = "UserId", required = true)
protected String userId;
@XmlElement(name = "Password", required = true)
protected String password;
}

View file

@ -0,0 +1,51 @@
package ru.nbch.credit_tracker.entities.user_request.monitoring_request;
import jakarta.xml.bind.annotation.*;
import lombok.Getter;
import lombok.Setter;
@Setter
@Getter
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
"userId",
"password",
"packageName",
"subjects"
})
@XmlRootElement(name = "MonitoringRequest")
public class MonitoringRequest {
@XmlElement(name = "UserId", required = true)
protected String userId;
@XmlElement(name = "Password", required = true)
protected String password;
@XmlElement(name = "PackageName", required = true)
protected String packageName;
@XmlElement(name = "Subjects", required = true)
protected Subjects subjects;
public Subjects getSubjects() {
if (subjects == null) {
subjects = new Subjects();
}
return subjects;
}
private Subject currentSubject;
public void createCurrentSubject() {
currentSubject = new Subject();
}
public Subject getCurrentSubject() {
return currentSubject;
}
public void finalizeSubject() {
getSubjects().getSubject().add(currentSubject);
}
}

View file

@ -1,5 +1,5 @@
package ru.nbch.credit_tracker.entities.request.monitoring_request; package ru.nbch.credit_tracker.entities.user_request.monitoring_request;
import jakarta.xml.bind.annotation.XmlAccessType; import jakarta.xml.bind.annotation.XmlAccessType;
import jakarta.xml.bind.annotation.XmlAccessorType; import jakarta.xml.bind.annotation.XmlAccessorType;

View file

@ -1,4 +1,4 @@
package ru.nbch.credit_tracker.entities.request.monitoring_request; package ru.nbch.credit_tracker.entities.user_request.monitoring_request;
import jakarta.xml.bind.annotation.XmlAccessType; import jakarta.xml.bind.annotation.XmlAccessType;
import jakarta.xml.bind.annotation.XmlAccessorType; import jakarta.xml.bind.annotation.XmlAccessorType;

View file

@ -1,4 +1,4 @@
package ru.nbch.credit_tracker.entities.response.active_packages; package ru.nbch.credit_tracker.entities.user_response.active_packages;
import jakarta.xml.bind.annotation.*; import jakarta.xml.bind.annotation.*;
import lombok.Getter; import lombok.Getter;

View file

@ -1,4 +1,4 @@
package ru.nbch.credit_tracker.entities.response.active_packages; package ru.nbch.credit_tracker.entities.user_response.active_packages;
import jakarta.xml.bind.annotation.XmlAccessType; import jakarta.xml.bind.annotation.XmlAccessType;
import jakarta.xml.bind.annotation.XmlAccessorType; import jakarta.xml.bind.annotation.XmlAccessorType;

View file

@ -1,4 +1,4 @@
package ru.nbch.credit_tracker.entities.response.active_packages; package ru.nbch.credit_tracker.entities.user_response.active_packages;
import jakarta.xml.bind.annotation.XmlAccessType; import jakarta.xml.bind.annotation.XmlAccessType;
import jakarta.xml.bind.annotation.XmlAccessorType; import jakarta.xml.bind.annotation.XmlAccessorType;

View file

@ -0,0 +1,24 @@
package ru.nbch.credit_tracker.entities.user_response.all_packages;
import jakarta.xml.bind.annotation.*;
import lombok.Getter;
import lombok.Setter;
@Setter
@Getter
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
"status",
"membercode",
"packages"
})
@XmlRootElement(name = "MonitoringAllPackagesResponse")
public class MonitoringAllPackagesResponse {
@XmlElement(name = "Status", required = true)
protected String status;
@XmlElement(name = "Membercode", required = true)
protected String membercode;
@XmlElement(name = "Packages", required = true)
protected PackagesAll packages;
}

View file

@ -0,0 +1,29 @@
package ru.nbch.credit_tracker.entities.user_response.all_packages;
import jakarta.xml.bind.annotation.XmlAccessType;
import jakarta.xml.bind.annotation.XmlAccessorType;
import jakarta.xml.bind.annotation.XmlElement;
import jakarta.xml.bind.annotation.XmlType;
import lombok.Getter;
import lombok.Setter;
@Setter
@Getter
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
"packageName",
"createdAt",
"status",
"deactivatedAt"
})
public class PackageAll {
@XmlElement(name = "PackageName", required = true)
protected String packageName;
@XmlElement(name = "CreatedAt", required = true)
protected String createdAt;
@XmlElement(name = "Status", required = false)
protected String status;
@XmlElement(name = "DeactivatedAt", required = false)
protected String deactivatedAt;
}

View file

@ -0,0 +1,26 @@
package ru.nbch.credit_tracker.entities.user_response.all_packages;
import jakarta.xml.bind.annotation.XmlAccessType;
import jakarta.xml.bind.annotation.XmlAccessorType;
import jakarta.xml.bind.annotation.XmlElement;
import jakarta.xml.bind.annotation.XmlType;
import java.util.ArrayList;
import java.util.List;
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
"packages"
})
public class PackagesAll {
@XmlElement(name = "Package", required = true)
protected List<PackageAll> packages;
public List<PackageAll> getPackages() {
if (packages == null) {
packages = new ArrayList<>();
}
return this.packages;
}
}

View file

@ -1,4 +1,4 @@
package ru.nbch.credit_tracker.entities.response.errors; package ru.nbch.credit_tracker.entities.user_response.errors;
public interface CommonError { public interface CommonError {
void setStatus(String status); void setStatus(String status);

View file

@ -1,4 +1,4 @@
package ru.nbch.credit_tracker.entities.response.errors; package ru.nbch.credit_tracker.entities.user_response.errors;
import jakarta.xml.bind.annotation.*; import jakarta.xml.bind.annotation.*;
import lombok.Getter; import lombok.Getter;

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

@ -1,4 +1,4 @@
package ru.nbch.credit_tracker.entities.response.errors; package ru.nbch.credit_tracker.entities.user_response.errors;
import jakarta.xml.bind.annotation.*; import jakarta.xml.bind.annotation.*;
import lombok.Getter; import lombok.Getter;
@ -17,9 +17,9 @@ public class MonitoringDownloadError implements CommonError {
@XmlElement(name = "Status", required = true) @XmlElement(name = "Status", required = true)
protected String status; protected String status;
@XmlElement(name = "ErrorCode",required = true) @XmlElement(name = "Code",required = true)
protected String errorCode; protected String errorCode;
@XmlElement(name = "ErrorText",required = true) @XmlElement(name = "Message",required = true)
protected String errorText; protected String errorText;
} }

View file

@ -1,4 +1,4 @@
package ru.nbch.credit_tracker.entities.response.errors; package ru.nbch.credit_tracker.entities.user_response.errors;
import jakarta.xml.bind.annotation.*; import jakarta.xml.bind.annotation.*;
import lombok.Getter; import lombok.Getter;

View file

@ -1,4 +1,4 @@
package ru.nbch.credit_tracker.entities.response.errors; package ru.nbch.credit_tracker.entities.user_response.errors;
import jakarta.xml.bind.annotation.*; import jakarta.xml.bind.annotation.*;
import lombok.Getter; import lombok.Getter;

View file

@ -1,4 +1,4 @@
package ru.nbch.credit_tracker.entities.response.errors; package ru.nbch.credit_tracker.entities.user_response.errors;
import jakarta.xml.bind.annotation.*; import jakarta.xml.bind.annotation.*;
import lombok.Getter; import lombok.Getter;
@ -17,9 +17,9 @@ public class MonitoringReportListError implements CommonError {
@XmlElement(name = "Status", required = true) @XmlElement(name = "Status", required = true)
protected String status; protected String status;
@XmlElement(name = "ErrorCode",required = true) @XmlElement(name = "Code",required = true)
protected String errorCode; protected String errorCode;
@XmlElement(name = "ErrorText",required = true) @XmlElement(name = "Message",required = true)
protected String errorText; protected String errorText;
} }

View file

@ -1,4 +1,4 @@
package ru.nbch.credit_tracker.entities.response.monitoring_signal_response; package ru.nbch.credit_tracker.entities.user_response.monitoring_signal_response;
import jakarta.xml.bind.annotation.*; import jakarta.xml.bind.annotation.*;
import lombok.Getter; import lombok.Getter;
@ -17,5 +17,5 @@ public class MonitoringSignalResponse {
@XmlElement(name = "PackageName", required = true) @XmlElement(name = "PackageName", required = true)
protected String packageName; protected String packageName;
@XmlElement(name = "Subjects", required = true) @XmlElement(name = "Subjects", required = true)
protected Subjects subjects; protected Subjects subjects = new Subjects();
} }

View file

@ -1,4 +1,4 @@
package ru.nbch.credit_tracker.entities.response.monitoring_signal_response; package ru.nbch.credit_tracker.entities.user_response.monitoring_signal_response;
import jakarta.xml.bind.annotation.XmlAccessType; import jakarta.xml.bind.annotation.XmlAccessType;
import jakarta.xml.bind.annotation.XmlAccessorType; import jakarta.xml.bind.annotation.XmlAccessorType;

View file

@ -1,4 +1,4 @@
package ru.nbch.credit_tracker.entities.response.monitoring_signal_response; package ru.nbch.credit_tracker.entities.user_response.monitoring_signal_response;
import jakarta.xml.bind.annotation.XmlAccessType; import jakarta.xml.bind.annotation.XmlAccessType;
import jakarta.xml.bind.annotation.XmlAccessorType; import jakarta.xml.bind.annotation.XmlAccessorType;
@ -24,4 +24,17 @@ public class Signals {
return this.signals; return this.signals;
} }
private Signal currentSignal;
public void createCurrentSignal() {
currentSignal = new Signal();
}
public Signal getCurrentSignal() {
return currentSignal;
}
public void finalizeSignal() {
getSignals().add(currentSignal);
}
} }

View file

@ -1,5 +1,5 @@
package ru.nbch.credit_tracker.entities.response.monitoring_signal_response; package ru.nbch.credit_tracker.entities.user_response.monitoring_signal_response;
import jakarta.xml.bind.annotation.XmlAccessType; import jakarta.xml.bind.annotation.XmlAccessType;
import jakarta.xml.bind.annotation.XmlAccessorType; import jakarta.xml.bind.annotation.XmlAccessorType;
@ -30,7 +30,7 @@ public class Subject {
@XmlElement(name = "Flag90_12", required = true) @XmlElement(name = "Flag90_12", required = true)
protected Integer flag9012; protected Integer flag9012;
@XmlElement(name = "Signals", required = true) @XmlElement(name = "Signals", required = true)
protected Signals signals; protected Signals signals = new Signals();
} }

View file

@ -1,4 +1,4 @@
package ru.nbch.credit_tracker.entities.response.monitoring_signal_response; package ru.nbch.credit_tracker.entities.user_response.monitoring_signal_response;
import jakarta.xml.bind.annotation.XmlAccessType; import jakarta.xml.bind.annotation.XmlAccessType;
import jakarta.xml.bind.annotation.XmlAccessorType; import jakarta.xml.bind.annotation.XmlAccessorType;
@ -24,4 +24,18 @@ public class Subjects {
return this.subjects; return this.subjects;
} }
private Subject currentSubject;
public void createCurrentSubject() {
currentSubject = new Subject();
}
public Subject getCurrentSubject() {
return currentSubject;
}
public void finalizeSubject() {
getSubjects().add(currentSubject);
}
} }

View file

@ -1,4 +1,4 @@
package ru.nbch.credit_tracker.entities.response.success; package ru.nbch.credit_tracker.entities.user_response.success;
import jakarta.xml.bind.annotation.*; import jakarta.xml.bind.annotation.*;
import lombok.Getter; import lombok.Getter;

View file

@ -1,4 +1,4 @@
package ru.nbch.credit_tracker.entities.response.success; package ru.nbch.credit_tracker.entities.user_response.success;
import jakarta.xml.bind.annotation.*; import jakarta.xml.bind.annotation.*;
import lombok.Getter; import lombok.Getter;

View file

@ -1,4 +1,4 @@
package ru.nbch.credit_tracker.entities.response.success; package ru.nbch.credit_tracker.entities.user_response.success;
import jakarta.xml.bind.annotation.*; import jakarta.xml.bind.annotation.*;
import lombok.Getter; import lombok.Getter;
@ -11,7 +11,7 @@ import lombok.Setter;
"status", "status",
}) })
@XmlRootElement(name = "MonitoringResponse") @XmlRootElement(name = "MonitoringResponse")
public class MonitoringResponse { public class MonitoringSetupResponse {
@XmlElement(name = "Status", required = true) @XmlElement(name = "Status", required = true)
protected String status = "OK"; protected String status = "OK";

View file

@ -0,0 +1,41 @@
package ru.nbch.credit_tracker.enums;
import java.util.Arrays;
import java.util.Collections;
public class EnumKeyMessage {
private final IEnumKeyCode subject;
/**
* Параметры для подстановки в текст сообщения. Все Serializable.
* args not null.
*/
private Object[] args;
public EnumKeyMessage(IEnumKeyCode subject) {
this.subject = subject;
this.args = Collections.emptyList().toArray(new Object[0]);
}
public EnumKeyMessage(IEnumKeyCode subject, Object... args) {
this.subject = subject;
this.args = args;
}
public IEnumKeyCode getSubject() {
return subject;
}
public Object[] getArgs() {
return args;
}
public void setArgs(Object[] args) {
this.args = args;
}
@Override
public String toString() {
return "EnumKeyMessage{subject=" + subject + ", args: " + Arrays.toString(args) + "}";
}
}

View file

@ -0,0 +1,102 @@
package ru.nbch.credit_tracker.enums;
import java.io.Serializable;
import java.util.Objects;
public interface IEnumKeyCode extends Serializable {
String getKey();
String getCode();
default boolean equalsByKey(String key) {
return key != null && getKey().equalsIgnoreCase(key);
}
/**
* Проверяет что среди данного набора Enum, присутствует элемент с данным id
* id может быть null
*/
static <T extends Enum<T> & IEnumKeyCode> boolean contains(String id, T... enumSet) {
for (T e : enumSet) {
if (Objects.equals(id, e.getKey()))
return true;
}
return false;
}
static <T extends Enum<T> & IEnumKeyCode> boolean containsIgnoreCase(String id, T... enumSet) {
for (T e : enumSet) {
if (e.getKey() != null && e.getKey().equalsIgnoreCase(id)) {
return true;
}
}
return false;
}
static <T extends Enum<T> & IEnumKeyCode> boolean contains(T e, T... enumSet) {
for (T enumEl : enumSet) {
if (enumEl.equals(e))
return true;
}
return false;
}
/**
* Проверяет что среди всех Enum данного класса, присутствует элемент с данным id
* id может быть null
*/
static <T extends Enum<T> & IEnumKeyCode> boolean contains(String key, Class<T> enumClass) {
for (T e : enumClass.getEnumConstants()) {
if (Objects.equals(key, e.getKey()))
return true;
}
return false;
}
/**
* Возвращает Enum по id, если в заданном классе такой определен
* id может быть null
*
* @return Enum если нашел, иначе <tt>null</tt>
*/
static <T extends Enum<T> & IEnumKeyCode> T getEnumByKey(Class<T> enumClass, String key) {
for (T e : enumClass.getEnumConstants()) {
if (Objects.equals(key, e.getKey()))
return e;
}
return null;
}
static <T extends Enum<T> & IEnumKeyCode> T getEnumByKeyOrUndefined(Class<T> enumClass, String key) {
T enumByKey = getEnumByKey(enumClass, key);
return enumByKey == null ? getUndefined(enumClass) : enumByKey;
}
static <T extends Enum<T> & IEnumKeyCode> T getUndefined(Class<T> enumClass) {
T enumByKey = getEnumByKey(enumClass, UNDEFINED_VALUE);
if (enumByKey == null) {
throw new IllegalArgumentException("Undefined value not found in enum " + enumClass);
}
return enumByKey;
}
default boolean theSameAs(Object other) {
if (!(other instanceof IEnumKeyCode otherCasted)) {
return false;
}
return this.getKey().equalsIgnoreCase(otherCasted.getKey());
}
default String getErrorCode() {
String key = getKey();
String[] split = key.split("\\.");
if (split.length < 1) return null;
String s = split[split.length - 1];
if (!s.matches("\\d+")) return null;
return s;
}
static String getKeyOrNull(IEnumKeyCode enumVal) {
return enumVal == null ? null : enumVal.getKey();
}
String UNDEFINED_VALUE = "_Undefined";
}

View file

@ -1,28 +1,28 @@
package ru.nbch.credit_tracker.enums; package ru.nbch.credit_tracker.enums;
import lombok.Getter; public enum SignalError implements IEnumKeyCode {
@Getter
public enum SignalError {
ERROR_001("001", "Ошибка распаковки архива"), ERROR_001("001", "Ошибка распаковки архива"),
ERROR_002("002", "Отсутствует обязательный элемент: "), ERROR_002("002", "Отсутствует обязательный элемент: {}"),
ERROR_003("003", "Элемент ClientId не соответствует формату XXXXXXXXX"), ERROR_003("003", "Элемент ClientId не соответствует формату XXXXXXXXX. Id: {}"),
ERROR_004("004", "Указан неуникальный 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", "На стороне сервиса произошла ошибка, попробуйте повторить запрос позже или сообщите в техническую поддержку"),
ERROR_014("014", "Файл не найден"), ERROR_014("014", "Файл с именем {} не найден "),
ERROR_015("015", "Клиент с указанным membercode не найден"), ERROR_015("015", "У данного пользователя не найдено активных пакетов"),
ERROR_016("016", "Нет активного пакета с именем "), ERROR_016("016", "Нет активного пакета с именем {}"),
ERROR_017("017", "При обработке запроса произошла ошибка. Повторите запрос позже или обратитесь в службу технической поддержки"), // TODO get rid of duplicate? ERROR_017("017", "При обработке запроса произошла ошибка. Повторите запрос позже или обратитесь в службу технической поддержки"), // TODO get rid of duplicate?
ERROR_018("018", "Пакет {} находится в обработке. Дождитесь его установки на мониторинг и повторите попытку позже."),
ERROR_019("019", "У данного пользователя не найдено ни одного пакета"),
ERROR_020("020", "Пакет {} был отклонён при установке на мониторинг. Отчеты не формируются."),
ERROR_021("021", "Обработка пакета {} завершилась с ошибкой, отчёты недоступны. Обратитесь в техническую поддержку."),
ERROR_022("022", "Пакет {} снят с мониторинга, отчёты больше недоступны.");
ERROR_100("100", "Непредвиденная ошибка");
private final String code; private final String code;
private final String message; private final String message;
@ -31,4 +31,16 @@ public enum SignalError {
this.code = code; this.code = code;
this.message = message; this.message = message;
} }
@Override
public String getKey() {
return message;
}
@Override
public String getCode() {
return code;
}
} }

View file

@ -0,0 +1,11 @@
package ru.nbch.credit_tracker.enums;
public enum XmlErrorFormat {
MonitoringActivePackagesError,
MonitoringAllPackagesError,
MonitoringDownloadError,
MonitoringError,
MonitoringPackageDeleteError,
MonitoringReportListError
}

View file

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

View file

@ -1,29 +0,0 @@
package ru.nbch.credit_tracker.exceptions;
import lombok.Getter;
import ru.nbch.credit_tracker.entities.response.errors.CommonError;
import ru.nbch.credit_tracker.enums.SignalError;
@Getter
public class SignalErrorException extends RuntimeException {
private final boolean isUserError;
private final CommonError error;
public SignalErrorException(SignalError signalError, String tag, CommonError error, boolean isUserError) {
this.isUserError = isUserError;
this.error = createErrorMessage(error, signalError, tag);
}
private CommonError createErrorMessage(CommonError errorMessage, SignalError error, String tag) {
errorMessage.setStatus("error");
errorMessage.setErrorCode(error.getCode());
if (tag != null) {
errorMessage.setErrorText(error.getMessage() + " " + tag);
} else {
errorMessage.setErrorText(error.getMessage());
}
return errorMessage;
}
}

View file

@ -0,0 +1,17 @@
package ru.nbch.credit_tracker.exceptions;
import lombok.Getter;
import ru.nbch.credit_tracker.enums.EnumKeyMessage;
import ru.nbch.credit_tracker.enums.XmlErrorFormat;
@Getter
public class SignalException extends RuntimeException {
private final EnumKeyMessage errorMessage;
private final XmlErrorFormat errorFormat;
public SignalException(EnumKeyMessage errorMessage,
XmlErrorFormat errorFormat) {
this.errorMessage = errorMessage;
this.errorFormat = errorFormat;
}
}

View file

@ -0,0 +1,17 @@
package ru.nbch.credit_tracker.exceptions;
import lombok.Getter;
import ru.nbch.credit_tracker.enums.EnumKeyMessage;
import ru.nbch.credit_tracker.enums.XmlErrorFormat;
@Getter
public class SignalServerException extends SignalException {
private final Exception cause;
public SignalServerException(EnumKeyMessage errorMessage, XmlErrorFormat errorFormat, Exception cause) {
super(errorMessage, errorFormat);
this.cause = cause;
}
}

View file

@ -1,145 +1,154 @@
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.request.active_packages.MonitoringActivePackagesRequest; import ru.nbch.credit_tracker.entities.dto.UserPackage;
import ru.nbch.credit_tracker.entities.request.monitoring_request.MonitoringRequest; import ru.nbch.credit_tracker.entities.user_request.active_packages.MonitoringActivePackagesRequest;
import ru.nbch.credit_tracker.entities.request.monitoring_request.Subject; import ru.nbch.credit_tracker.entities.user_request.all_packages.MonitoringAllPackagesRequest;
import ru.nbch.credit_tracker.entities.response.active_packages.MonitoringActivePackagesResponse; import ru.nbch.credit_tracker.entities.user_request.monitoring_request.MonitoringRequest;
import ru.nbch.credit_tracker.entities.response.errors.*; import ru.nbch.credit_tracker.entities.user_response.active_packages.MonitoringActivePackagesResponse;
import ru.nbch.credit_tracker.entities.response.success.MonitoringPackageDeleteResponse; import ru.nbch.credit_tracker.entities.user_response.active_packages.Package;
import ru.nbch.credit_tracker.entities.response.success.MonitoringReportListResponse; import ru.nbch.credit_tracker.entities.user_response.active_packages.Packages;
import ru.nbch.credit_tracker.entities.response.success.MonitoringResponse; import ru.nbch.credit_tracker.entities.user_response.all_packages.MonitoringAllPackagesResponse;
import ru.nbch.credit_tracker.entities.signal_package.Signals; import ru.nbch.credit_tracker.entities.user_response.all_packages.PackageAll;
import ru.nbch.credit_tracker.entities.user_response.all_packages.PackagesAll;
import ru.nbch.credit_tracker.entities.user_response.success.MonitoringPackageDeleteResponse;
import ru.nbch.credit_tracker.entities.user_response.success.MonitoringReportListResponse;
import ru.nbch.credit_tracker.entities.user_response.success.MonitoringSetupResponse;
import ru.nbch.credit_tracker.enums.SignalError; import ru.nbch.credit_tracker.enums.SignalError;
import ru.nbch.credit_tracker.enums.Status; import ru.nbch.credit_tracker.enums.Status;
import ru.nbch.credit_tracker.exceptions.SignalErrorException; import ru.nbch.credit_tracker.enums.XmlErrorFormat;
import ru.nbch.credit_tracker.exceptions.SignalClientException;
import ru.nbch.credit_tracker.service.MonitoringErrorProcesingService;
import ru.nbch.credit_tracker.service.SignalService; import ru.nbch.credit_tracker.service.SignalService;
import ru.nbch.credit_tracker.service.indic.PhoneService;
import ru.nbch.credit_tracker.service.scoring.AuthorizationService; 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.jaxb.IXmlProcessor; import ru.nbch.credit_tracker.service.xml.stax.StaxXmlProcessor;
import ru.nbch.credit_tracker.service.xml.jaxb.XmlUtilService;
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 javax.xml.stream.XMLStreamException;
import java.io.BufferedInputStream;
import java.io.File; import java.io.File;
import java.util.HashSet; import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.LocalDateTime;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream; import java.util.zip.ZipInputStream;
import static ru.nbch.credit_tracker.utils.error.ExceptionUtils.clientExcp;
import static ru.nbch.credit_tracker.utils.error.ExceptionUtils.serverExcp;
@Slf4j @Slf4j
@Service @Service
@EnableAspectJAutoProxy(exposeProxy = true)
public class SignalFacade { public class SignalFacade {
private static final long MAX_XML_FILE_SIZE = 2L * 1024 * 1024 * 1024;
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 XmlUtilService xmlUtilService; private final MonitoringErrorProcesingService monitoringErrorProcesingService;
private final ValidationService validationService;
private final StaxXmlProcessor staxXmlProcessor;
private final ExecutorService executor; private final ExecutorService executor;
private final long maxXmlFileSize;
public SignalFacade(CreditTrackerSettings config, public SignalFacade(CreditTrackerSettings config,
AuthorizationService authorizationService, AuthorizationService authorizationService,
SignalService signalService, SignalService signalService,
SignalRestService signalRestService, SignalRestService signalRestService,
XmlUtilService xmlUtilService, MonitoringErrorProcesingService monitoringErrorProcesingService,
ValidationService validationService,
StaxXmlProcessor staxXmlProcessor,
@Qualifier("executorService") ExecutorService executor) { @Qualifier("executorService") ExecutorService executor) {
this.config = config; this.config = config;
this.authorizationService = authorizationService; this.authorizationService = authorizationService;
this.signalService = signalService; this.signalService = signalService;
this.signalRestService = signalRestService; this.signalRestService = signalRestService;
this.xmlUtilService = xmlUtilService; this.monitoringErrorProcesingService = monitoringErrorProcesingService;
this.validationService = validationService;
this.staxXmlProcessor = staxXmlProcessor;
this.executor = executor; this.executor = executor;
this.maxXmlFileSize = config.getMaxXmlFileSize() * 1024 * 1024;
} }
public ResponseEntity<MonitoringResponse> setupMonitoring(MultipartFile file) { public ResponseEntity<MonitoringSetupResponse> setupMonitoring(MultipartFile file) {
// первичные валидации // первичные валидации
if (file.isEmpty()) { if (file.isEmpty() || !isZipFile(file)) {
throw new SignalErrorException(SignalError.ERROR_001, null, new MonitoringError(), true); throw clientExcp(SignalError.ERROR_001, XmlErrorFormat.MonitoringError);
}
if (!isZipFile(file)) {
throw new SignalErrorException(SignalError.ERROR_001, null, new MonitoringError(), true);
} }
// Валидация Архива на количетсво файлов, размер и расширение // Валидация Архива на количетсво файлов, размер и расширение
try (ZipInputStream zipInputStream = new ZipInputStream(file.getInputStream())) {
int fileCount = 0;
ZipEntry xmlEntry = null;
ZipEntry entry = zipInputStream.getNextEntry();
while (entry != null) {
if (!entry.isDirectory()) {
fileCount++;
if (fileCount > 1) { // Архив должен содержать ровно один XML-файл
throw new SignalErrorException(SignalError.ERROR_001, null, new MonitoringError(), true);
}
if (!entry.getName().toLowerCase().endsWith(".xml")) { // Файл должен быть с расширением xml
throw new SignalErrorException(SignalError.ERROR_001, null, new MonitoringError(), true);
}
xmlEntry = entry;
} else {
throw new SignalErrorException(SignalError.ERROR_001, null, new MonitoringError(), true); // Архив должен содержать ровно один XML-файл. Никаких доп директорий
}
zipInputStream.closeEntry();
entry = zipInputStream.getNextEntry();
}
if (fileCount == 0) { // Архив должен содержать ровно один XML-файл
throw new SignalErrorException(SignalError.ERROR_001, null, new MonitoringError(), true);
}
if (xmlEntry.getSize() > MAX_XML_FILE_SIZE) { // максимально 2 Гб до запаковки
throw new SignalErrorException(SignalError.ERROR_009, null, new MonitoringError(), true);
}
} catch (Exception e) {
if (e instanceof SignalErrorException) {
throw (SignalErrorException) e;
}
log.error(e.getMessage(), e);
throw new SignalErrorException(SignalError.ERROR_100, null, new MonitoringError(), false);
}
// Теперь знаем, что в архиве лежит ровно один файл с раширением xml и он весит меньше 2гб. Можно валидировать сам xml
// Пробуем распарсить xml
MonitoringRequest monitoringRequest; MonitoringRequest monitoringRequest;
Integer packageId; Long packageId;
try (ZipInputStream zipInputStream = new ZipInputStream(file.getInputStream())) { ZipInputStream zipInputStream = null;
String filePath;
try {
zipInputStream = new ZipInputStream(file.getInputStream());
Optional<ZipEntry> xmlFileEntry = ZipUtils.extractValidEntry(zipInputStream);
if (xmlFileEntry.isEmpty()) {
throw clientExcp(SignalError.ERROR_001, XmlErrorFormat.MonitoringError);
}
if (xmlFileEntry.get().getSize() > maxXmlFileSize) {
throw clientExcp(SignalError.ERROR_009, XmlErrorFormat.MonitoringError);
}
zipInputStream.close();
zipInputStream = new ZipInputStream(file.getInputStream());
zipInputStream.getNextEntry(); zipInputStream.getNextEntry();
IXmlProcessor processor = xmlUtilService.createJaxbProcessor(); filePath = writeTmpFile(zipInputStream);
monitoringRequest = processor.read(zipInputStream, MonitoringRequest.class);
} catch (Exception e) { } catch (Exception e) {
if (e instanceof SignalErrorException) { if (e instanceof SignalClientException) {
throw (SignalErrorException) e; throw (SignalClientException) e;
}
throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringError, e);
} finally {
if (zipInputStream != null) {
try {
zipInputStream.close();
} catch (IOException e) {
throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringError, e);
}
} }
log.error(e.getMessage(), e);
throw new SignalErrorException(SignalError.ERROR_100, null, new MonitoringError(), false);
} }
// валидация xml // валидация xml
try { try {
validateMonitoringRequest(monitoringRequest); monitoringRequest = readAndValidateMonitoringRequest(filePath);
} catch (SignalErrorException e) { } catch (Exception e) {
signalService.rejectPackage(monitoringRequest); CTUtil.removeFile(filePath);
throw e; if (e.getCause() instanceof XMLStreamException) {
throw clientExcp(SignalError.ERROR_011, XmlErrorFormat.MonitoringError);
}
if (e instanceof SignalClientException) {
throw (SignalClientException) e;
}
throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringError, e);
} }
try { try {
@ -147,99 +156,101 @@ 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 new SignalErrorException(authResult.getError(), null, new MonitoringError(), true); throw clientExcp(authResult.getError(), monitoringRequest.getPackageName(), XmlErrorFormat.MonitoringError);
} }
// проверка на уникальность пакета // проверка на уникальность пакета
if (signalService.isPackageNotUnique(monitoringRequest)) { if (signalService.isPackageNotUnique(monitoringRequest)) {
signalService.rejectPackage(monitoringRequest); signalService.rejectPackage(monitoringRequest);
throw new SignalErrorException(SignalError.ERROR_007, null, new MonitoringError(), true); throw clientExcp(SignalError.ERROR_007, monitoringRequest.getPackageName(), XmlErrorFormat.MonitoringError);
} }
packageId = signalService.registerPackage(monitoringRequest); packageId = signalService.registerPackage(monitoringRequest);
log.info("Package registered. PackageId {}. Status: {}", packageId, Status.PROCESSING.name());
} catch (Exception e) { } catch (Exception e) {
if (e instanceof SignalErrorException) { CTUtil.removeFile(filePath);
throw (SignalErrorException) e; if (e instanceof SignalClientException) {
throw (SignalClientException) e;
} }
log.error(e.getMessage(), e); throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringError, e);
throw new SignalErrorException(SignalError.ERROR_100, null, new MonitoringError(), false);
} }
// в отдельном потоке запускаем поиск фидов и расчет флагов просрочек // в отдельном потоке запускаем регистрацию субъектов, поиск фидов и расчет флагов просрочек
registerFidsAndDebts(packageId, monitoringRequest.getPackageName()); getCurrentProxy().registerFidsAndDebts(packageId, monitoringRequest.getPackageName(), filePath);
return ResponseEntity.ok().body(new MonitoringResponse()); return ResponseEntity.ok().body(new MonitoringSetupResponse());
} }
private void validateMonitoringRequest(MonitoringRequest request) throws SignalErrorException { private MonitoringRequest readAndValidateMonitoringRequest(String filePath) {
if (request == null) { MonitoringRequest monitoringRequest;
throw new SignalErrorException(SignalError.ERROR_011, null, new MonitoringError(), true); try (BufferedInputStream in = new BufferedInputStream(Files.newInputStream(Path.of(filePath)), 8 * 1024 * 1024)) {
monitoringRequest = staxXmlProcessor.read(in, MonitoringRequest.class,
request -> {
try {
validationService.validateMonitoringRequest(request);
} catch (SignalClientException e) {
if (request != null) {
signalService.rejectPackage(request);
}
request = null;
throw e;
}
request.getSubjects().getSubject().clear();
},
request -> request.getSubjects().getSubject().isEmpty()
,
40000, "/MonitoringRequest/Subjects/Subject");
} catch (IOException e) {
throw new RuntimeException("Error while parsing and validating monitoring request", e);
} }
return monitoringRequest;
validateReruqredTags(request);
validateTagsTypes(request);
} }
private void validateReruqredTags(MonitoringRequest request) throws SignalErrorException { private String writeTmpFile(ZipInputStream zipInputStream) throws IOException {
if (request.getUserId() == null) { String folderPath = config.getLoadDir() + File.separator + "tmp_folder" + File.separator;
throw new SignalErrorException(SignalError.ERROR_002, "UserId", new MonitoringError(), true); String filePath = folderPath + UUID.randomUUID() + "monitoring_request.xml";
} CTUtil.createFolder(folderPath);
if (request.getPackageName() == null) { try (OutputStream out = Files.newOutputStream(Path.of(filePath))) {
throw new SignalErrorException(SignalError.ERROR_002, "PackageName", new MonitoringError(), true);
}
if (request.getPassword() == null) { byte[] buffer = new byte[8 * 1024 * 1024];
throw new SignalErrorException(SignalError.ERROR_002, "Password", new MonitoringError(), true); int bytesRead;
} while ((bytesRead = zipInputStream.read(buffer)) > 0) {
out.write(buffer, 0, bytesRead);
}
if (request.getSubjects() == null) { return filePath;
throw new SignalErrorException(SignalError.ERROR_002, "Subjects", new MonitoringError(), true);
}
if (request.getSubjects().getSubject().isEmpty()) {
throw new SignalErrorException(SignalError.ERROR_002, "Subject", new MonitoringError(), true);
} }
} }
private void validateTagsTypes(MonitoringRequest request) throws SignalErrorException { @SetAndClearMdc(argIndex = 1)
if (!ValidationUtil.validatePackageName(request.getPackageName())) { public void registerFidsAndDebts(Long packageId, String packageName, String filePath) {
throw new SignalErrorException(SignalError.ERROR_008, null, new MonitoringError(), true); Map<String, String> context = MDC.getCopyOfContextMap();
}
Set<String> uniqueIds = new HashSet<>();
for (Subject subject : request.getSubjects().getSubject()) {
if (!ValidationUtil.validatePhone(subject.getClientId())) {
throw new SignalErrorException(SignalError.ERROR_003, null, new MonitoringError(), true);
}
if (!ValidationUtil.validateNPId(subject.getId())) {
throw new SignalErrorException(SignalError.ERROR_010, null, new MonitoringError(), true);
}
// проверка на уникальность Id внутри одного пакета
if (!uniqueIds.add(subject.getId())) {
throw new SignalErrorException(SignalError.ERROR_004, subject.getId(), new MonitoringError(), true);
}
}
}
private void registerFidsAndDebts(Integer packageId, String packageName) {
executor.execute(() -> { executor.execute(() -> {
if (context != null) {
MDC.setContextMap(context);
}
try { try {
List<PhoneService.FidsPhoneData> phoneFidsData = signalService.registerSubjectsFidAndDebts(packageId); signalService.registerSubjects(filePath, packageId);
if (!phoneFidsData.isEmpty()) { CTUtil.removeFile(filePath);
Signals signals = signalService.buildSignals(packageId, phoneFidsData); SignalService.UpdateResult updateResult = signalService.updateSubjects(packageId, null);
// TODO validate xsd? if (updateResult.isNoFidsFound()) {
if (signalRestService.update(signals)) { log.info("No fids found for PackageId: {} PackageName: {}. Package status ON_MONITORING. No registration in signal service.", packageId, packageName);
log.info("Package was successfully registered in signal service. PackageId: {} PackageName: {}", packageId, packageName); signalService.updatePackageStatus(packageId, Status.ON_MONITORING);
signalService.setPackageStatus(packageId, Status.ON_MONITORING); return;
} else { }
log.info("Package registration was rejected by signal service. PackageId: {} PackageName: {}", packageId, packageName); if (signalRestService.setupMonitoring(packageId)) {
signalService.setPackageStatus(packageId, Status.ERROR); LocalDateTime monitoringAt = LocalDateTime.now();
} log.info("Package was successfully registered in signal service. PackageId: {} PackageName: {}, MonitoringAt: {}", packageId, packageName, monitoringAt);
signalService.updatePackageOnMonitoringSuccess(packageId, monitoringAt);
monitoringErrorProcesingService.findErrors(packageId);
} else {
log.info("Package registration was rejected by signal service. PackageId: {} PackageName: {}", packageId, packageName);
signalService.updatePackageStatus(packageId, Status.ERROR);
} }
} catch (Exception e) { } catch (Exception e) {
signalService.setPackageStatus(packageId, Status.ERROR); CTUtil.removeFile(filePath);
signalService.updatePackageStatus(packageId, Status.ERROR);
log.error("Ошибка при поиске фидов или расчете флагов просрочек. Package_id {}", packageId, e); log.error("Ошибка при поиске фидов или расчете флагов просрочек. Package_id {}", packageId, e);
} }
}); });
@ -255,46 +266,111 @@ public class SignalFacade {
try { try {
AuthorizationService.AuthResult authResult = authorizationService.findAuthInfo(activePackagesRequest.getUserId(), activePackagesRequest.getPassword()); AuthorizationService.AuthResult authResult = authorizationService.findAuthInfo(activePackagesRequest.getUserId(), activePackagesRequest.getPassword());
if (!authResult.isSuccess()) { if (!authResult.isSuccess()) {
throw new SignalErrorException(authResult.getError(), null, new MonitoringActivePackagesError(), true); throw clientExcp(authResult.getError(), XmlErrorFormat.MonitoringActivePackagesError);
} }
MonitoringActivePackagesResponse response = signalService.findActivePackages(activePackagesRequest.getUserId()); List<UserPackage> activePackages = signalService.findActivePackages(activePackagesRequest.getUserId());
if (response != null) {
if (!activePackages.isEmpty()) {
MonitoringActivePackagesResponse response = new MonitoringActivePackagesResponse();
response.setStatus("OK");
response.setMembercode(activePackagesRequest.getUserId().substring(0, 6));
Packages packages = new Packages();
activePackages.forEach(data -> {
Package pack = new Package();
pack.setPackageName(data.getPackageName());
pack.setCreatedAt(data.getCreatedAt() != null ? data.getCreatedAt().toString() : null);
packages.getPackages().add(pack);
});
response.setPackages(packages);
return ResponseEntity.ok(response); return ResponseEntity.ok(response);
} else { } else {
throw new SignalErrorException(SignalError.ERROR_015, null, new MonitoringActivePackagesError(), true); throw clientExcp(SignalError.ERROR_015, XmlErrorFormat.MonitoringActivePackagesError);
} }
} catch (Exception e) { } catch (Exception e) {
if (e instanceof SignalErrorException) { if (e instanceof SignalClientException) {
throw (SignalErrorException) e; throw (SignalClientException) e;
} }
log.error(e.getMessage(), e); throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringActivePackagesError, e);
throw new SignalErrorException(SignalError.ERROR_013, null, new MonitoringActivePackagesError(), false); }
}
public ResponseEntity<MonitoringAllPackagesResponse> findAllPackages(MonitoringAllPackagesRequest allPackagesRequest) {
try {
AuthorizationService.AuthResult authResult = authorizationService.findAuthInfo(allPackagesRequest.getUserId(), allPackagesRequest.getPassword());
if (!authResult.isSuccess()) {
throw clientExcp(authResult.getError(), XmlErrorFormat.MonitoringAllPackagesError);
}
List<UserPackage> activePackages = signalService.findAllPackages(allPackagesRequest.getUserId());
if (!activePackages.isEmpty()) {
MonitoringAllPackagesResponse response = new MonitoringAllPackagesResponse();
response.setStatus("OK");
response.setMembercode(allPackagesRequest.getUserId().substring(0, 6));
PackagesAll packages = new PackagesAll();
activePackages.forEach(data -> {
PackageAll pack = new PackageAll();
pack.setPackageName(data.getPackageName());
pack.setCreatedAt(data.getCreatedAt() != null ? data.getCreatedAt().toString() : null);
pack.setStatus(data.getStatus());
if (data.getStatus().equals(Status.DELETED.name())) {
pack.setDeactivatedAt(data.getDeactivatedAt() != null ? data.getDeactivatedAt().toString() : null);
}
packages.getPackages().add(pack);
});
response.setPackages(packages);
return ResponseEntity.ok(response);
} else {
throw clientExcp(SignalError.ERROR_019, XmlErrorFormat.MonitoringAllPackagesError);
}
} catch (Exception e) {
if (e instanceof SignalClientException) {
throw (SignalClientException) e;
}
throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringAllPackagesError, e);
} }
} }
public ResponseEntity<MonitoringPackageDeleteResponse> deletePackage(String username, String packageName) { public ResponseEntity<MonitoringPackageDeleteResponse> deletePackage(String username, String packageName) {
try { try {
if (!signalService.isPackageExists(username, packageName)) { if (!signalService.isPackageExists(username, packageName)) {
throw new SignalErrorException(SignalError.ERROR_016, packageName, new MonitoringPackageDeleteError(), true); throw clientExcp(SignalError.ERROR_016, XmlErrorFormat.MonitoringPackageDeleteError, packageName);
} }
MonitoringPackageDeleteResponse response = signalService.deletePackage(username, packageName);
LocalDateTime deactivatedAt = LocalDateTime.now();
signalService.deletePackage(username, packageName, deactivatedAt);
MonitoringPackageDeleteResponse response = new MonitoringPackageDeleteResponse();
response.setStatus("Deleted");
response.setPackageName(packageName);
response.setDeactivatedAt(deactivatedAt.toString());
signalRestService.deletePackage(username, packageName); signalRestService.deletePackage(username, packageName);
log.info("Package {} deleted", packageName); log.info("Package {} deleted", packageName);
return ResponseEntity.ok(response); return ResponseEntity.ok(response);
} catch (Exception e) { } catch (Exception e) {
if (e instanceof SignalErrorException) { if (e instanceof SignalClientException) {
throw (SignalErrorException) e; throw (SignalClientException) e;
} }
log.error(e.getMessage(), e); throw serverExcp(SignalError.ERROR_017, XmlErrorFormat.MonitoringPackageDeleteError, e);
throw new SignalErrorException(SignalError.ERROR_017, null, new MonitoringPackageDeleteError(), false);
} }
} }
public ResponseEntity<MonitoringReportListResponse> getReportList(String username, String packageName) { public ResponseEntity<MonitoringReportListResponse> getReportList(String username, String packageName) {
try { try {
if (!signalService.isPackageExists(username, packageName)) { if (signalService.isPackagesByStatusExist(username, packageName, Status.PROCESSING)) {
throw new SignalErrorException(SignalError.ERROR_016, packageName, new MonitoringReportListError(), true); throw clientExcp(SignalError.ERROR_018, XmlErrorFormat.MonitoringReportListError, packageName);
}
if (signalService.isPackagesByStatusExist(username, packageName, Status.REJECTED)) {
throw clientExcp(SignalError.ERROR_020, XmlErrorFormat.MonitoringReportListError, packageName);
}
if (signalService.isPackagesByStatusExist(username, packageName, Status.ERROR)) {
throw clientExcp(SignalError.ERROR_021, XmlErrorFormat.MonitoringReportListError, packageName);
}
if (signalService.isPackagesByStatusExist(username, packageName, Status.DELETED)) {
throw clientExcp(SignalError.ERROR_022, XmlErrorFormat.MonitoringReportListError, packageName);
}
if (!signalService.isPackagesByStatusExist(username, packageName, Status.ON_MONITORING)) {
throw clientExcp(SignalError.ERROR_012, XmlErrorFormat.MonitoringReportListError, packageName);
} }
String memberCode = username.substring(0, 6); String memberCode = username.substring(0, 6);
@ -302,42 +378,35 @@ public class SignalFacade {
String dirPath = config.getLoadDir() + File.separator + memberCode; String dirPath = config.getLoadDir() + File.separator + memberCode;
File folder = new File(dirPath); File folder = new File(dirPath);
MonitoringReportListResponse.Reports reports = new MonitoringReportListResponse.Reports();
if (folder.exists() && folder.isDirectory()) { if (folder.exists() && folder.isDirectory()) {
MonitoringReportListResponse.Reports reports = new MonitoringReportListResponse.Reports();
File[] files = folder.listFiles(); File[] files = folder.listFiles();
if (files == null) { if (files != null) {
throw new SignalErrorException(SignalError.ERROR_012, packageName, new MonitoringReportListError(), true); for (File file : files) {
} if (file.isFile() && file.getName().startsWith(filePrefix)) {
for (File file : files) { reports.getReports().add(file.getName());
if (file.isFile() && file.getName().startsWith(filePrefix)) { }
reports.getReports().add(file.getName());
} }
} }
if (reports.getReports().isEmpty()) {
throw new SignalErrorException(SignalError.ERROR_012, packageName, new MonitoringReportListError(), true);
} else {
MonitoringReportListResponse response = new MonitoringReportListResponse();
response.setPackageName(packageName);
response.setReports(reports);
return ResponseEntity.ok(response);
}
} else {
log.error("Пакет {} был найден в бд для memberCode {}, но ни одного архива не было найдено в папке {}", packageName, username, dirPath);
throw new SignalErrorException(SignalError.ERROR_013, packageName, new MonitoringReportListError(), true);
} }
MonitoringReportListResponse response = new MonitoringReportListResponse();
response.setPackageName(packageName);
response.setReports(reports);
return ResponseEntity.ok(response);
} catch (Exception e) { } catch (Exception e) {
if (e instanceof SignalErrorException) { if (e instanceof SignalClientException) {
throw (SignalErrorException) e; throw (SignalClientException) e;
} }
log.error(e.getMessage(), e); throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringReportListError, e);
throw new SignalErrorException(SignalError.ERROR_013, null, new MonitoringReportListError(), false);
} }
} }
public ResponseEntity<Resource> downloadFile(String fileName) { public ResponseEntity<Resource> downloadFile(String fileName) {
try { try {
String memberCode = CTUtil.getMemberCodeFromFileName(fileName); String memberCode = CTUtil.getMemberCodeFromFileName(fileName);
if (memberCode == null) {
throw clientExcp(SignalError.ERROR_014, XmlErrorFormat.MonitoringDownloadError, fileName);
}
String dirPath = config.getLoadDir() + File.separator + memberCode; String dirPath = config.getLoadDir() + File.separator + memberCode;
File folder = new File(dirPath); File folder = new File(dirPath);
@ -347,15 +416,21 @@ public class SignalFacade {
return ResponseEntity.ok().body(new FileUrlResource(file.getPath())); return ResponseEntity.ok().body(new FileUrlResource(file.getPath()));
} }
} else { } else {
throw new SignalErrorException(SignalError.ERROR_014, null, new MonitoringDownloadError(), true); throw clientExcp(SignalError.ERROR_014, XmlErrorFormat.MonitoringDownloadError, fileName);
} }
throw new SignalErrorException(SignalError.ERROR_014, null, new MonitoringDownloadError(), true); throw clientExcp(SignalError.ERROR_014, XmlErrorFormat.MonitoringDownloadError, fileName);
} catch (Exception e) { } catch (Exception e) {
if (e instanceof SignalErrorException) { if (e instanceof SignalClientException) {
throw (SignalErrorException) e; throw (SignalClientException) e;
} }
log.error(e.getMessage(), e); throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringDownloadError, e);
throw new SignalErrorException(SignalError.ERROR_013, null, new MonitoringDownloadError(), false);
} }
} }
private SignalFacade getCurrentProxy() {
if (self == null) {
self = (SignalFacade) AopContext.currentProxy();
}
return self;
}
} }

View file

@ -0,0 +1,14 @@
package ru.nbch.credit_tracker.mapper.signal;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import ru.nbch.credit_tracker.entities.dto.Hit;
import java.util.List;
@Mapper
public interface HitsMapper {
void insertHits(@Param("hits") List<Hit> hits);
}

View file

@ -0,0 +1,25 @@
package ru.nbch.credit_tracker.mapper.signal;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import ru.nbch.credit_tracker.entities.dto.PhoneFidMap;
import ru.nbch.credit_tracker.entities.user_request.monitoring_request.Subject;
import java.util.List;
@Mapper
public interface PhoneFidMapMapper {
void registerSubjects(@Param("subjects") List<Subject> subjects, @Param("packageId") Long packageId);
List<PhoneFidMap> findFullPhoneFidMap(@Param("packageId") Long packageId, @Param("offset") int offset, @Param("limit") int limit);
void updatePhoneFidMaps(@Param("subjects") List<PhoneFidMap> subjects);
void updatePhoneFidMapsErrors(@Param("subjects") List<PhoneFidMap> subjects);
PhoneFidMap findPhoneFidMapById(@Param("id") Long packageId);
boolean hasNonNullFids(@Param("packageId") Long packageId);
boolean hasOldRow(@Param("packageId") Long packageId, @Param("daysCount") Integer daysCount);
}

View file

@ -1,48 +0,0 @@
package ru.nbch.credit_tracker.mapper.signal;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import ru.nbch.credit_tracker.entities.dto.HitsData;
import ru.nbch.credit_tracker.entities.dto.PackageData;
import ru.nbch.credit_tracker.entities.dto.SubjectData;
import ru.nbch.credit_tracker.entities.request.monitoring_request.MonitoringRequest;
import ru.nbch.credit_tracker.entities.request.monitoring_request.Subject;
import java.time.LocalDateTime;
import java.util.List;
@Mapper
public interface SignalMapper {
boolean isPackageNotUnique(@Param("userId") String userId, @Param("packageName") String packageName);
boolean isPackageExists(@Param("userId") String userId, @Param("packageName") String packageName, @Param("status") String status);
void deletePackage(@Param("userId") String userId, @Param("packageName") String packageName, @Param("status") String newStatus, @Param("previousStatus") String previousStatus, @Param("deactivatedAt") LocalDateTime deactivatedAt);
Integer registerPackage(@Param("request") MonitoringRequest request, @Param("creadedAt") LocalDateTime creadedAt, @Param("status") String status);
void registerSubjects(@Param("subjects") List<Subject> subjects, @Param("packageId") Integer packageId);
void insertPackage(@Param("request") MonitoringRequest request, @Param("creadedAt") LocalDateTime creadedAt, @Param("status") String status);
void registerSubjectsFidAndDebts(@Param("id") Integer id, @Param("fid") Integer fid, @Param("flagDebt5000") Integer flagDebt5000, @Param("flag90") Integer flag90, @Param("calculatedAt") LocalDateTime calculatedAt);
void setPackageStatus(@Param("packageId") Integer packageId, @Param("status") String status);
List<SubjectData> findSubjectsData(@Param("packageId") Integer packageId);
List<SubjectData> findFullSubjectsData(@Param("packageId") Integer packageId);
PackageData findPackageData(@Param("id") Integer id);
List<PackageData> findPackageDataList(@Param("userId") String userId, @Param("status") String status);
List<PackageData> findPackagesByStatus(@Param("status") String status);
void setHits(@Param("hits") List<HitsData> hits);
void updateSubjects(@Param("subjects") List<SubjectData> subjects);
void setPackageLastDownloadedReport(@Param("id") Integer id, @Param("fileName") String fileName);
}

View file

@ -0,0 +1,36 @@
package ru.nbch.credit_tracker.mapper.signal;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import ru.nbch.credit_tracker.entities.dto.UserPackage;
import java.time.LocalDateTime;
import java.util.List;
@Mapper
public interface UserPackageMapper {
boolean isPackageNotUnique(@Param("userId") String userId, @Param("packageName") String packageName);
boolean isPackageExists(@Param("userId") String userId, @Param("packageName") String packageName, @Param("status") String status);
boolean hasActivePackages(@Param("userId") String userId, @Param("packageName") String packageName, @Param("statuses") List<String> statuses);
void deletePackage(@Param("userId") String userId, @Param("packageName") String packageName, @Param("status") String newStatus, @Param("previousStatus") String previousStatus, @Param("deactivatedAt") LocalDateTime deactivatedAt);
Long insertPackage(@Param("userPackage") UserPackage userPackage);
void updatePackageStatus(@Param("packageId") Long packageId, @Param("status") String status);
void updatePackageLastDownloadedReport(@Param("id") Long id, @Param("fileName") String fileName);
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> findAllPackagesByStatus(@Param("status") String status);
List<UserPackage> findAllPackagesByUserId(@Param("userId") String userId);
}

View file

@ -0,0 +1,59 @@
package ru.nbch.credit_tracker.service;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.aop.framework.AopContext;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Service;
import ru.nbch.credit_tracker.config.mdc.SetAndClearMdc;
import ru.nbch.credit_tracker.entities.dto.UserPackage;
import ru.nbch.credit_tracker.service.signal.SignalRestService;
import ru.nbch.credit_tracker.utils.CTUtil;
@Slf4j
@Service
@EnableAspectJAutoProxy(exposeProxy = true)
public class MonitoringErrorProcesingService {
private MonitoringErrorProcesingService self;
private final SignalService signalService;
private final SignalRestService signalRestService;
public MonitoringErrorProcesingService(SignalService signalService, SignalRestService signalRestService) {
this.signalService = signalService;
this.signalRestService = signalRestService;
}
public void findErrors(Long packageId) {
UserPackage userPackage = signalService.findPackage(packageId);
getCurrentProxy().processPackage(userPackage, packageId);
}
@SetAndClearMdc
public void processPackage(UserPackage userPackage, Long packageId) {
if (userPackage != null) {
String packCode = userPackage.getUserId().substring(0, 6) + "_" + userPackage.getPackageName();
log.info("Проверка на ошибки при постановке на мониторинг. packCode={}, packageId={}", packCode, packageId);
String fileName = signalRestService.getErrorFileName(packageId, packCode);
if (StringUtils.isNotBlank(fileName)) {
String filePath = signalRestService.loadErrorFile(packageId, fileName);
if (StringUtils.isNotBlank(filePath)) {
try {
signalService.updateSubjectsErrors(filePath, packageId);
} catch (Exception e) {
log.error("Ошибка при записи сообщений об ошибках в сформированных субъектах для мониторинга. Package_id = {}", packageId, e);
}
CTUtil.removeFile(filePath);
}
}
}
}
private MonitoringErrorProcesingService getCurrentProxy() {
if (self == null) {
self = (MonitoringErrorProcesingService) AopContext.currentProxy();
}
return self;
}
}

View file

@ -1,129 +1,213 @@
package ru.nbch.credit_tracker.service; package ru.nbch.credit_tracker.service;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.MDC;
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;
import ru.nbch.credit_tracker.entities.dto.HitsData; import ru.nbch.credit_tracker.entities.dto.Hit;
import ru.nbch.credit_tracker.entities.dto.PackageData; import ru.nbch.credit_tracker.entities.dto.PhoneFidMap;
import ru.nbch.credit_tracker.entities.dto.SubjectData; import ru.nbch.credit_tracker.entities.dto.UserPackage;
import ru.nbch.credit_tracker.entities.response.monitoring_signal_response.*; import ru.nbch.credit_tracker.entities.external_response.online_download.Inquiry;
import ru.nbch.credit_tracker.entities.response.online_download.Inquiry; import ru.nbch.credit_tracker.entities.external_response.online_download.Person;
import ru.nbch.credit_tracker.entities.response.online_download.OnlineDownloadReport; import ru.nbch.credit_tracker.entities.external_response.online_download.Persons;
import ru.nbch.credit_tracker.entities.response.online_download.Person; import ru.nbch.credit_tracker.entities.external_response.online_report.Report;
import ru.nbch.credit_tracker.entities.response.online_report.Report; import ru.nbch.credit_tracker.entities.external_response.online_report.SgnlOnlineReport;
import ru.nbch.credit_tracker.entities.response.online_report.SgnlOnlineReport; import ru.nbch.credit_tracker.entities.user_response.monitoring_signal_response.MonitoringSignalResponse;
import ru.nbch.credit_tracker.entities.user_response.monitoring_signal_response.Signal;
import ru.nbch.credit_tracker.entities.user_response.monitoring_signal_response.Signals;
import ru.nbch.credit_tracker.entities.user_response.monitoring_signal_response.Subject;
import ru.nbch.credit_tracker.service.signal.SignalRestService; import ru.nbch.credit_tracker.service.signal.SignalRestService;
import ru.nbch.credit_tracker.service.xml.jaxb.IXmlProcessor; import ru.nbch.credit_tracker.service.xml.stax.MonitoringSignalResponseStaxXmlWriter;
import ru.nbch.credit_tracker.service.xml.jaxb.XmlUtilService; import ru.nbch.credit_tracker.service.xml.stax.StaxXmlProcessor;
import ru.nbch.credit_tracker.utils.CTUtil; import ru.nbch.credit_tracker.utils.CTUtil;
import java.io.ByteArrayInputStream; import java.io.*;
import java.io.File; import java.nio.file.Files;
import java.io.FileOutputStream; import java.nio.file.Path;
import java.nio.charset.Charset;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.*; import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.zip.GZIPInputStream; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream; import java.util.zip.ZipOutputStream;
/**
* Сервис периодического опроса официального сервиса сигналов на наличие новых отчетов по пакетам находящимся в статусе ON_MONITORING
*/
@Slf4j @Slf4j
@Service @Service
public class MonitoringService { public class MonitoringService {
private final SignalRestService signalRestService; private final SignalRestService signalRestService;
private final SignalService signalService; private final SignalService signalService;
private final XmlUtilService xmlUtilService; private final StaxXmlProcessor staxXmlProcessor;
private final CreditTrackerSettings config; private final CreditTrackerSettings config;
public MonitoringService(SignalRestService signalRestService, public MonitoringService(SignalRestService signalRestService,
SignalService signalService, SignalService signalService,
XmlUtilService xmlUtilService, StaxXmlProcessor staxXmlProcessor,
CreditTrackerSettings config) { CreditTrackerSettings config) {
this.signalRestService = signalRestService; this.signalRestService = signalRestService;
this.signalService = signalService; this.signalService = signalService;
this.xmlUtilService = xmlUtilService; this.staxXmlProcessor = staxXmlProcessor;
this.config = config; this.config = config;
} }
public void run() { public void run() {
ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(); ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(new CustomizableThreadFactory("monitoring-"));
long period = config.getMonitoringInterval() != null ? config.getMonitoringInterval() : 15; long period = config.getMonitoringInterval() != null ? config.getMonitoringInterval() : 15;
scheduler.scheduleAtFixedRate(this::monitor, 0, period, TimeUnit.MINUTES); scheduler.scheduleAtFixedRate(this::monitor, 0, period, TimeUnit.MINUTES);
} }
private void monitor() { private void monitor() {
log.info("Start monitoring"); log.info("Start monitoring");
List<PackageData> packages = signalService.findPackagesOnMonitoring(); List<UserPackage> packages = signalService.findPackagesOnMonitoring();
for (PackageData packageData : packages) { for (UserPackage userPackage : packages) {
log.info("Processing package {}", packageData.getPackageName()); MDC.put("ru.nbch.credit_tracker.log.mdc.key", "[" + userPackage.getPackageName() +"]");
SgnlOnlineReport onlineReports = signalRestService.getReports(packageData.getUserId().substring(0, 6) + "_" + packageData.getPackageName()); processPackage(userPackage);
MDC.remove("ru.nbch.credit_tracker.log.mdc.key");
if (onlineReports == null) {
log.info("No reports found for package {}", packageData.getPackageName());
continue;
}
List<String> fileNames = onlineReports.getOperationReport().getReports().getReports().stream().map(Report::getFilename)
.filter(fileName -> CTUtil.parseDateFromReportFileName(fileName) != null).toList(); // TODO оптимизировать
if (fileNames.isEmpty()) {
log.info("No new report found for package {}", packageData.getPackageName());
continue;
}
Optional<String> newReport = Optional.of(fileNames)
.orElseGet(Collections::emptyList)
.stream().max(Comparator.comparing(CTUtil::parseDateFromReportFileName)); // отфильтровали только те имена, которые парсятся в дату, так что null тут не придет
LocalDateTime lastDownloadedReportDate = CTUtil.parseDateFromReportFileName(packageData.getLastDownloadedReport());
if (packageData.getLastDownloadedReport() != null) {
log.info("Last downloaded report found for package {}. Last Downloaded Report Name: {}", packageData.getPackageName(), packageData.getLastDownloadedReport());
} else {
log.info("Last Downloaded Report Name is empty");
}
OnlineDownloadReport report = null;
if (packageData.getLastDownloadedReport() == null && newReport.isPresent()) {
report = loadReport(newReport.get()); // по этому пакету еще не было загруженных отчетов, продолжаем работу с полученным
} else if (lastDownloadedReportDate != null && newReport.isPresent()) {
LocalDateTime newReportDate = CTUtil.parseDateFromReportFileName(newReport.get());
if (newReportDate.isAfter(lastDownloadedReportDate)) {
report = loadReport(newReport.get()); // полученный отчет новее записанного в базу
}
}
if (report == null) {
log.info("Report was not loaded for package {}", packageData.getPackageName());
continue;
}
MonitoringSignalResponse response = generateResponse(packageData, report.getPersons().getPersons());
String fileName = createResponseFile(packageData, response);
if (fileName != null) {
log.info("New report fileName: {}", fileName);
updateSignalHits(packageData, response, fileName);
signalService.updateLastDownloadedReport(packageData.getId(), newReport.get());
} else {
log.info("Report was not created for package {}", packageData.getPackageName());
}
} }
log.info("Finish monitoring"); log.info("Finish monitoring");
} }
private MonitoringSignalResponse generateResponse(PackageData packageData, List<Person> persons) { private void processPackage(UserPackage userPackage) {
log.info("Processing package");
if (!signalService.hasNonNullfids(userPackage.getId())) {
log.info("Package has no fids. Package will be skipped.");
return;
}
SgnlOnlineReport onlineReports = signalRestService.getReports(userPackage.getUserId().substring(0, 6) + "_" + userPackage.getPackageName());
if (onlineReports == null) {
log.info("No reports found for package");
return;
}
// Формируем список имен файлов, по которым сработали сигналы. Сюда входят только те имена, у которых парсится дата формирования отчета
List<String> fileNames = onlineReports.getOperationReport().getReports().getReports().stream().map(Report::getFilename)
.filter(fileName -> CTUtil.parseDateFromReportFileName(fileName) != null).toList();
if (fileNames.isEmpty()) {
log.info("No new report found for package");
return;
}
LocalDateTime lastDownloadedReportDate = CTUtil.parseDateFromReportFileName(userPackage.getLastDownloadedReport());
if (lastDownloadedReportDate != null) {
// Отбираем только те отчеты, дата которых позже даты последнего загруженного отчета
fileNames = fileNames.stream()
.filter(fileName -> CTUtil.parseDateFromReportFileName(fileName).isAfter(lastDownloadedReportDate)) // отфильтровали только те имена, которые парсятся в дату, так что null тут не придет
.sorted(Comparator.comparing(CTUtil::parseDateFromReportFileName)) // Сортируем по дате
.toList();
} else {
fileNames = fileNames.stream()
.sorted(Comparator.comparing(CTUtil::parseDateFromReportFileName)) // Сортируем по дате
.toList();
}
if (StringUtils.isNotBlank(userPackage.getLastDownloadedReport())) {
log.info("Last downloaded report found for package. Last Downloaded Report Name: {}", userPackage.getLastDownloadedReport());
} else {
log.info("Last Downloaded Report Name is empty");
}
for (String newReport : fileNames) {
try {
String loadedReportPath = null;
if (StringUtils.isBlank(userPackage.getLastDownloadedReport())) {
loadedReportPath = loadReport(userPackage.getId(), newReport); // по этому пакету еще не было загруженных отчетов, продолжаем работу с полученным
} else if (lastDownloadedReportDate != null) {
LocalDateTime newReportDate = CTUtil.parseDateFromReportFileName(newReport);
if (newReportDate.isAfter(lastDownloadedReportDate)) {
loadedReportPath = loadReport(userPackage.getId(), newReport); // полученный отчет новее записанного в базу
}
}
if (loadedReportPath == null) {
log.info("Report was not loaded for package");
continue;
}
String responsePath = generateResponse(userPackage, loadedReportPath);
String fileName = null;
if (responsePath != null) {
fileName = createResponseFile(userPackage, responsePath);
}
if (fileName != null) {
log.info("New report fileName: {}", fileName);
updateSignalHits(userPackage, responsePath, fileName);
signalService.updateLastDownloadedReport(userPackage.getId(), newReport);
} else {
log.info("Report was not created for package");
}
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
}
/**
* Формирует результирующий xml файл для конечного пользователя на основе файла из официального сервиса сигналов
* @return Путь к временному рещультирующему файлу
*/
private String generateResponse(UserPackage userPackage, String loadedReportPath) {
AtomicBoolean generateResponse = new AtomicBoolean(false); // Выставляется в true, если хотя бы один субъект с мониторинга подошел под условие срабатывания сигнала. В этом случае отчет сохраняется
String folderPath = config.getLoadDir() + File.separator + "tmp_folder" + File.separator;
String responsePath = folderPath + UUID.randomUUID() + "_response_output.xml";
CTUtil.createFolder(folderPath);
MonitoringSignalResponse response = new MonitoringSignalResponse(); MonitoringSignalResponse response = new MonitoringSignalResponse();
response.setPackageName(packageData.getPackageName()); // Имя пакета, переданного при установке response.setPackageName(userPackage.getPackageName()); // Имя пакета, переданного при установке
Subjects subjects = new Subjects();
List<SubjectData> subjectDataList = signalService.findFullSubjectsData(packageData.getId()); // Запись в результрующий файл
MonitoringSignalResponseStaxXmlWriter staxXmlWriter = new MonitoringSignalResponseStaxXmlWriter(config.getEncoding());
staxXmlWriter.writeWithStax(responsePath, response,
writer -> {
try (BufferedInputStream in = new BufferedInputStream(Files.newInputStream(Path.of(loadedReportPath)), 8 * 1024 * 1024)) {
staxXmlProcessor.read(in, Persons.class,
personsData -> { // как только считали пачку Persons сразу записали ее в результирующий файл
List<Subject> subjectsBatch = processPersons(personsData.getPersons());
if (!subjectsBatch.isEmpty()) {
generateResponse.set(true);
}
writer.accept(subjectsBatch);
personsData.getPersons().clear(); // чистим список для экономии памяти
},
pesons -> pesons.getPersons().isEmpty()
,
10000,
"/Report/Persons/Person");
} catch (IOException e) {
throw new RuntimeException("Error while parsing nbki downloaded report", e);
}
});
CTUtil.removeFile(loadedReportPath);
if (!generateResponse.get()) { // ни один субъект не подошел под условие, удаляем временный отчет
CTUtil.removeFile(responsePath);
return null;
}
return responsePath;
}
private List<Subject> processPersons(List<Person> persons) {
List<Subject> subjects = new ArrayList<>();
for (Person person : persons) { for (Person person : persons) {
Signals signals = new Signals(); Signals signals = new Signals();
for (Inquiry inquiry : person.getInquiries()) { for (Inquiry inquiry : person.getInquiries()) {
// Отбираем только тех фл, у которых bus_category in (MFO, MKK, MFK) и есть сигналы с кодом 16 // Отбираем только тех фл, у которых bus_category in (MFO, MKK, MFK) и есть сигналы с кодом 16
if (!CTUtil.checkBusCategory(inquiry.getBusCategory()) && inquiry.getSignals().stream().anyMatch(signal -> signal.getN().equals(16))) { if (!CTUtil.checkBusCategory(inquiry.getBusCategory()) || inquiry.getSignals().stream().noneMatch(signal -> signal.getN().equals(16))) {
continue; continue;
} }
Signal signal = new Signal(); Signal signal = new Signal();
@ -135,86 +219,101 @@ public class MonitoringService {
// Если есть хотя бы один заполненный сигнал, значит заполняем остальные данные // Если есть хотя бы один заполненный сигнал, значит заполняем остальные данные
if (!signals.getSignals().isEmpty()) { if (!signals.getSignals().isEmpty()) {
subjectDataList.stream().filter(p -> p.getSubjectId().equals(person.getUid())).findFirst().ifPresent(subjectData -> { PhoneFidMap subjectData = signalService.findPhoneFidMapById(person.getUid());
if (subjectData != null) {
Subject subject = new Subject(); Subject subject = new Subject();
subject.setId(subjectData.getSubjectId()); // Уникальный идентификатор субъекта (равен Id из XML-пакета при установке мониторинга) subject.setId(subjectData.getSubjectId()); // Уникальный идентификатор субъекта (равен Id из XML-пакета при установке мониторинга)
subject.setClientId(subjectData.getPhone()); subject.setClientId(subjectData.getPhone());
subject.setFlagDebt5000(subjectData.getFlagDebt5000()); subject.setFlagDebt5000(subjectData.getFlagDebt5000());
subject.setFlag9012(subjectData.getFlag90days()); subject.setFlag9012(subjectData.getFlag90days());
subject.setSignals(signals); subject.setSignals(signals);
subjects.getSubjects().add(subject); subjects.add(subject);
}); }
} }
} }
response.setSubjects(subjects); return subjects;
return response;
} }
private String createResponseFile(PackageData packageData, MonitoringSignalResponse response) { /**
* Архивирует результирующий xml файл для пользователя
* @return Путь к результрующему файлу. Пример <loadDir>/<member_code>/0001ZZ_MY_BATCH_TEST_0717_04_20250723_101929.zip
*/
private String createResponseFile(UserPackage userPackage, String responsePath) {
try { try {
String fileName = CTUtil.generateResponseReportFileName(packageData.getUserId(), packageData.getPackageName()); String fileName = CTUtil.generateResponseReportFileName(userPackage.getUserId(), userPackage.getPackageName());
String filePath = config.getLoadDir() + File.separator + packageData.getUserId().substring(0, 6) + File.separator; Thread.sleep(1000); // Если отчеты формируются слишком быстро, нужно подождать секунду, тк в имени отчета LocalDateTime до секунд
String folderPath = config.getLoadDir() + File.separator + userPackage.getUserId().substring(0, 6) + File.separator;
File folder = new File(filePath); File folder = new File(folderPath);
if (!folder.exists()) { if (!folder.exists()) {
folder.mkdir(); folder.mkdir();
} }
IXmlProcessor processor = xmlUtilService.createJaxbProcessor(); try (BufferedInputStream in = new BufferedInputStream(Files.newInputStream(Path.of(responsePath)), 8 * 1024 * 1024);
String xmlStr = processor.asString(response, true); BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(folderPath + fileName + ".zip"));
ZipOutputStream zos = new ZipOutputStream(bos)) {
byte[] xmlBytes = xmlStr.getBytes(Charset.forName("windows-1251"));
try (FileOutputStream fos = new FileOutputStream(filePath + fileName + ".zip");
ZipOutputStream zos = new ZipOutputStream(fos)) {
ZipEntry zipEntry = new ZipEntry(fileName + ".xml"); ZipEntry zipEntry = new ZipEntry(fileName + ".xml");
zos.putNextEntry(zipEntry); zos.putNextEntry(zipEntry);
zos.write(xmlBytes); byte[] buffer = new byte[8 * 1024 * 1024];
int bytesRead;
while ((bytesRead = in.read(buffer)) > 0) {
zos.write(buffer, 0, bytesRead);
}
zos.finish();
zos.closeEntry(); zos.closeEntry();
} }
return fileName; return fileName;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} catch (Exception e) { } catch (Exception e) {
log.error("Ошибка при записи отчета пользователя в файл: ", e); log.error("Ошибка при записи отчета пользователя в файл: ", e);
} }
CTUtil.removeFile(responsePath);
return null; return null;
} }
private void updateSignalHits(PackageData packageData, MonitoringSignalResponse response, String reportFileName) { private void updateSignalHits(UserPackage userPackage, String responsePath, String reportFileName) {
log.info("Updating signal hits for {}", packageData.getPackageName()); log.info("Updating signal hits for {}", userPackage.getPackageName());
LocalDateTime responseSentAt = LocalDateTime.now(); LocalDateTime responseSentAt = LocalDateTime.now();
List<HitsData> hits = new ArrayList<>(); // Считываем результирующий файл пачками и обновляем таблицу hits
response.getSubjects().getSubjects().forEach(subject -> { try (BufferedInputStream in = new BufferedInputStream(Files.newInputStream(Path.of(responsePath)), 8 * 1024 * 1024)) {
subject.getSignals().getSignals().forEach(signal -> { staxXmlProcessor.read(in, MonitoringSignalResponse.class,
HitsData hitsData = new HitsData(); response -> { // как только считали пачку Subjects сразу обновлям таблицу hits
hitsData.setPackageId(packageData.getId()); List<Hit> hits = new ArrayList<>();
hitsData.setSubjectId(subject.getId()); response.getSubjects().getSubjects().forEach(subject -> {
hitsData.setSignalType(1); subject.getSignals().getSignals().forEach(signal -> {
hitsData.setResponseSentAt(responseSentAt); Hit hit = new Hit();
hitsData.setReportFileName(reportFileName); hit.setPackageId(userPackage.getId());
hits.add(hitsData); hit.setSubjectId(subject.getId());
}); hit.setSignalType(1);
}); hit.setResponseSentAt(responseSentAt);
signalService.setHits(hits); hit.setReportFileName(reportFileName);
log.info("Signal hits updated for package {}", packageData.getPackageName()); hits.add(hit);
});
if (!hits.isEmpty()) {
signalService.insertHits(hits);
}
});
response.getSubjects().getSubjects().clear(); // чистим список для экономии памяти
},
response -> response.getSubjects().getSubjects().isEmpty()
,
10000,
"/MonitoringSignalResponse/Subjects/Subject");
} catch (IOException e) {
throw new RuntimeException("Error while updating hits", e);
}
CTUtil.removeFile(responsePath); // удалям результирующий xml файл с диска (Он уже заархивирован к этому моменту)
log.info("Signal hits updated for package {}", userPackage.getPackageName());
} }
private OnlineDownloadReport loadReport(String reportName) { public String loadReport(Long packageId, String reportName) {
byte[] archive = signalRestService.downloadReport(reportName); return signalRestService.downloadReport(packageId, reportName);
if (archive == null) {
return null;
}
OnlineDownloadReport report;
try (GZIPInputStream zipInputStream = new GZIPInputStream(new ByteArrayInputStream(archive))) {
IXmlProcessor processor = xmlUtilService.createJaxbProcessor();
report = processor.read(zipInputStream, OnlineDownloadReport.class);
} catch (Exception e) {
log.error(e.getMessage(), e);
return null;
}
return report;
} }
} }

View file

@ -1,149 +1,279 @@
package ru.nbch.credit_tracker.service; package ru.nbch.credit_tracker.service;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import ru.nbch.credit_tracker.builder.PersonFactoryBuilder;
import ru.nbch.credit_tracker.config.settings.CreditTrackerSettings; import ru.nbch.credit_tracker.config.settings.CreditTrackerSettings;
import ru.nbch.credit_tracker.entities.dto.HitsData; import ru.nbch.credit_tracker.entities.dto.Hit;
import ru.nbch.credit_tracker.entities.dto.PackageData; import ru.nbch.credit_tracker.entities.dto.PhoneFidMap;
import ru.nbch.credit_tracker.entities.dto.SubjectData; import ru.nbch.credit_tracker.entities.dto.UserPackage;
import ru.nbch.credit_tracker.entities.request.monitoring_request.MonitoringRequest; import ru.nbch.credit_tracker.entities.external_request.signals.Auth;
import ru.nbch.credit_tracker.entities.request.monitoring_request.Subject; import ru.nbch.credit_tracker.entities.external_request.signals.Person;
import ru.nbch.credit_tracker.entities.response.active_packages.MonitoringActivePackagesResponse; import ru.nbch.credit_tracker.entities.external_request.signals.Signals;
import ru.nbch.credit_tracker.entities.response.active_packages.Package; import ru.nbch.credit_tracker.entities.external_response.monitoring_report_error.MonitoringReportErrors;
import ru.nbch.credit_tracker.entities.response.active_packages.Packages; import ru.nbch.credit_tracker.entities.user_request.monitoring_request.MonitoringRequest;
import ru.nbch.credit_tracker.entities.response.success.MonitoringPackageDeleteResponse;
import ru.nbch.credit_tracker.entities.signal_package.Auth;
import ru.nbch.credit_tracker.entities.signal_package.Person;
import ru.nbch.credit_tracker.entities.signal_package.Signals;
import ru.nbch.credit_tracker.enums.Status; import ru.nbch.credit_tracker.enums.Status;
import ru.nbch.credit_tracker.mapper.signal.SignalMapper;
import ru.nbch.credit_tracker.service.indic.FlagsService; import ru.nbch.credit_tracker.service.indic.FlagsService;
import ru.nbch.credit_tracker.service.indic.PersonService; import ru.nbch.credit_tracker.service.indic.PersonService;
import ru.nbch.credit_tracker.service.indic.PhoneService; import ru.nbch.credit_tracker.service.indic.PhoneService;
import ru.nbch.credit_tracker.service.signal.HitsService;
import ru.nbch.credit_tracker.service.signal.PhoneFidMapService;
import ru.nbch.credit_tracker.service.signal.UserPackageService;
import ru.nbch.credit_tracker.service.xml.stax.SignalsStaxXmlWriter;
import ru.nbch.credit_tracker.service.xml.stax.StaxXmlProcessor;
import ru.nbch.credit_tracker.utils.CTUtil; import ru.nbch.credit_tracker.utils.CTUtil;
import java.text.DateFormat; import java.io.BufferedInputStream;
import java.text.SimpleDateFormat; import java.io.File;
import java.time.LocalDate; import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.ArrayList; import java.util.*;
import java.util.List; import java.util.function.Consumer;
import java.util.Map; import java.util.function.Predicate;
import java.util.Objects;
@Slf4j @Slf4j
@Service @Service
public class SignalService { public class SignalService {
private final static DateFormat DATE_FORMAT = new SimpleDateFormat("dd.MM.yyyy");
private final CreditTrackerSettings config; private final CreditTrackerSettings config;
private final SignalMapper mapper; private final HitsService hitsService;
private final UserPackageService userPackageService;
private final PhoneFidMapService phoneFidMapService;
private final PhoneService phoneService; private final PhoneService phoneService;
private final FlagsService flagsService; private final FlagsService flagsService;
private final PersonService personService; private final PersonService personService;
private final StaxXmlProcessor staxXmlProcessor;
private final int batchSize = 10000; private final int batchSize = 10000;
public SignalService(CreditTrackerSettings config, public SignalService(CreditTrackerSettings config,
SignalMapper mapper, HitsService hitsService,
UserPackageService userPackageService,
PhoneFidMapService phoneFidMapService,
PhoneService phoneService, PhoneService phoneService,
FlagsService flagsService, FlagsService flagsService,
PersonService personService) { PersonService personService,
StaxXmlProcessor staxXmlProcessor) {
this.config = config; this.config = config;
this.mapper = mapper; this.hitsService = hitsService;
this.userPackageService = userPackageService;
this.phoneFidMapService = phoneFidMapService;
this.phoneService = phoneService; this.phoneService = phoneService;
this.flagsService = flagsService; this.flagsService = flagsService;
this.personService = personService; this.personService = personService;
this.staxXmlProcessor = staxXmlProcessor;
} }
public boolean isPackageNotUnique(MonitoringRequest request) { public boolean isPackageNotUnique(MonitoringRequest request) {
String userId = request.getUserId(); return userPackageService.isPackageNotUnique(request.getUserId(), request.getPackageName());
return mapper.isPackageNotUnique(userId.substring(0, 6), request.getPackageName());
} }
public Integer registerPackage(MonitoringRequest request) { public Long registerPackage(MonitoringRequest request) {
Integer packageId = mapper.registerPackage(request, LocalDateTime.now(), Status.PROCESSING.name()); UserPackage userPackage = UserPackage.builder()
List<Subject> subjects = request.getSubjects().getSubject(); .userId(request.getUserId())
for (int i = 0; i < subjects.size(); i += batchSize) { .packageName(request.getPackageName())
int end = Math.min(i + batchSize, subjects.size()); .createdAt(LocalDateTime.now())
mapper.registerSubjects(subjects.subList(i, end), packageId); .status(Status.PROCESSING.name())
.build();
return userPackageService.insertPackage(userPackage);
}
public void registerSubjects(String filePath, Long packageId) {
log.info("Start registering subjects. PackageId: {}", packageId);
try (BufferedInputStream in = new BufferedInputStream(Files.newInputStream(Path.of(filePath)), 8 * 1024 * 1024)) {
staxXmlProcessor.read(in, MonitoringRequest.class,
request -> {
if (!request.getSubjects().getSubject().isEmpty()) {
phoneFidMapService.registerSubjects(request.getSubjects().getSubject(), packageId);
log.trace("Registered subject's batch. Total count: {}. PackageId: {}", request.getSubjects().getSubject().size(), packageId);
request.getSubjects().getSubject().clear();
}
},
request -> request.getSubjects().getSubject().isEmpty()
,
batchSize, "/MonitoringRequest/Subjects/Subject");
} catch (IOException e) {
throw new RuntimeException("Error while parsing monitoring request", e);
} }
return packageId; log.info("Subjects were registered. PackageId: {}", packageId);
} }
public List<PhoneService.FidsPhoneData> registerSubjectsFidAndDebts(Integer packageId) { /**
List<SubjectData> subjects = mapper.findSubjectsData(packageId); * Обрабатывает все PhoneFidMap по packageId, выгружая их пачками и фильтруя по переданному фильтру
return updateFlags(subjects, packageId); */
} public UpdateResult updateSubjects(Long packageId, Predicate<PhoneFidMap> filter) {
boolean updated = false;
boolean noFidsFound = true;
int offset = 0;
List<PhoneFidMap> batch;
do {
batch = phoneFidMapService.findFullPhoneFidMap(packageId, offset, batchSize);
public List<PhoneService.FidsPhoneData> updateFlags(List<SubjectData> subjects, Integer packageId) { List<PhoneFidMap> filteredItems;
List<PhoneService.FidsPhoneData> fidsPhoneDataList = phoneService.findFidsPhoneData(subjects, true); if (filter != null) {
LocalDateTime calculatedAt = LocalDateTime.now(); filteredItems = batch.stream().filter(filter).toList();
for (int i = 0; i < fidsPhoneDataList.size(); i += batchSize) { } else {
int end = Math.min(i + batchSize, fidsPhoneDataList.size()); filteredItems = batch;
List<PhoneService.FidsPhoneData> currentList = fidsPhoneDataList.subList(i, end);
Map<Integer, FlagsService.FlagsData> flagsDataMap = flagsService.findFlags(currentList.stream().map(PhoneService.FidsPhoneData::getFid).filter(Objects::nonNull).toList());
List<SubjectData> subjectToUpdate = new ArrayList<>();
currentList.forEach(data -> {
SubjectData subject;
if (data.getFid() != null) {
FlagsService.FlagsData flagsData = flagsDataMap.get(data.getFid());
subject = new SubjectData(data.getId(), packageId, data.getSubjectId(), data.getPhone(), data.getFid(), flagsData.getFlagDebt5000(), flagsData.getFlag90days(), calculatedAt);
} else {
subject = new SubjectData(data.getId(), packageId, data.getSubjectId(), data.getPhone(), null, null, null, calculatedAt);
}
subjectToUpdate.add(subject);
});
int updateBatchSize = batchSize / 8; // 8 параметров на одну запись для обновления. Максимальное количество параметров ~65000
for (int j = 0; j < subjectToUpdate.size(); j += updateBatchSize) {
int endIndex = Math.min(j + updateBatchSize, subjectToUpdate.size());
mapper.updateSubjects(subjectToUpdate.subList(j, endIndex));
} }
if (!updated && !filteredItems.isEmpty()) {
updated = true;
}
if (!filteredItems.isEmpty()) {
boolean fidsFound = updateFlags(filteredItems, packageId);
if (noFidsFound) {
noFidsFound = !fidsFound;
}
}
offset += batchSize;
} while (!batch.isEmpty());
return new UpdateResult(updated, noFidsFound);
}
/**
* Обрабатывает пачку PhoneFidMap: обновляет фиды и флаги просрочек
* @return fidsFound true - если хотя бы по одному номеру был найден фид, false - в противном случае
*/
public boolean updateFlags(List<PhoneFidMap> subjects, Long packageId) {
log.info("Start calculating fids and flags for subjects batch. PackageId: {}", packageId);
Map<String, Integer> fidsMap = phoneService.findFidsPhoneData(subjects);
LocalDateTime calculatedAt = LocalDateTime.now();
boolean fidsFound = fidsMap.values().stream().anyMatch(Objects::nonNull);
Map<Integer, FlagsService.FlagsData> flagsDataMap = flagsService.findFlags(fidsMap.values().stream().filter(Objects::nonNull).toList());
List<PhoneFidMap> subjectToUpdate = new ArrayList<>();
subjects.forEach(subject -> {
Integer fid = fidsMap.get("9" + subject.getPhone());
if (fid != null) {
FlagsService.FlagsData flagsData = flagsDataMap.get(fid);
subject = PhoneFidMap.builder()
.id(subject.getId())
.packageId(packageId)
.subjectId(subject.getSubjectId())
.phone(subject.getPhone())
.fid(fid)
.flagDebt5000(flagsData.getFlagDebt5000())
.flag90days(flagsData.getFlag90days())
.calculatedAt(calculatedAt)
.build();
} else {
subject = PhoneFidMap.builder()
.id(subject.getId())
.packageId(packageId)
.subjectId(subject.getSubjectId())
.phone(subject.getPhone())
.calculatedAt(calculatedAt)
.build();
}
subjectToUpdate.add(subject);
});
int updateBatchSize = batchSize / 9; // 9 параметров на одну запись для обновления. Максимальное количество параметров ~65000
for (int j = 0; j < subjectToUpdate.size(); j += updateBatchSize) {
int endIndex = Math.min(j + updateBatchSize, subjectToUpdate.size());
phoneFidMapService.updatePhoneFidMaps(subjectToUpdate.subList(j, endIndex));
log.trace("Updated fids and flags for subject's batch from: {} to: {}. Total count: {}. PackageId: {}", j, endIndex, subjectToUpdate.size(), packageId);
} }
return fidsPhoneDataList; log.info("Finish calculating fids and flags for subjects. PackageId: {}. Batch size: {}", packageId, subjects.size());
return fidsFound;
} }
public void rejectPackage(MonitoringRequest request) { public void rejectPackage(MonitoringRequest request) {
mapper.insertPackage(request, LocalDateTime.now(), Status.REJECTED.name()); UserPackage userPackage = UserPackage.builder()
.userId(request.getUserId())
.packageName(request.getPackageName())
.createdAt(LocalDateTime.now())
.status(Status.REJECTED.name())
.build();
userPackageService.insertPackage(userPackage);
} }
public void setPackageStatus(Integer packageId, Status status) { public void updatePackageStatus(Long packageId, Status status) {
mapper.setPackageStatus(packageId, status.name()); userPackageService.updatePackageStatus(packageId, status);
} }
public Signals buildSignals(Integer packageId, List<PhoneService.FidsPhoneData> fids) { public void updatePackageOnMonitoringSuccess(Long packageId, LocalDateTime monitoringAt) {
PackageData packageData = mapper.findPackageData(packageId); userPackageService.updatePackageOnMonitoringSuccess(packageId, monitoringAt);
}
/**
* Формирует файл запроса в официальный сервис сигналов, выгружая паспортные данные пачками и сразу записывая их в файл
*
* @return Путь к файлу запроса к официальному сервису сигналов на постановку на мониторинг
*/
public String writeSignals(Long packageId) {
String folderPath = config.getLoadDir() + File.separator + "tmp_folder" + File.separator;
String filePath = folderPath + UUID.randomUUID() + "_output.xml";
CTUtil.createFolder(folderPath);
Signals signals = buildSignals(packageId);
SignalsStaxXmlWriter signalsStaxXmlWriter = new SignalsStaxXmlWriter(config.getEncoding());
signalsStaxXmlWriter.writeWithStax(filePath,
signals,
writer -> buildPersons(packageId, writer));
return filePath;
}
private Signals buildSignals(Long packageId) {
UserPackage userPackage = userPackageService.findPackage(packageId);
Signals signals = new Signals(); Signals signals = new Signals();
signals.setOnline("2"); // Всегда=2 (только онлайн-сигналы) signals.setOnline("2"); // Всегда=2 (только онлайн-сигналы)
signals.setId(packageData.getUserId().substring(0, 6) + "_" + packageData.getPackageName()); // <MEMBERCODE>_<PackageName> signals.setProcessWithErrors("1"); // Всегда=1 (обработать пакет полностью, по обнаруженным ошибкам (при наличии) сформировать файл отказа, записи без ошибок установить на мониторинг)
signals.setId(userPackage.getUserId().substring(0, 6) + "_" + userPackage.getPackageName()); // <MEMBERCODE>_<PackageName>
Auth auth = new Auth(); Auth auth = new Auth();
auth.setUser(config.getSignalsuser()); auth.setUser(config.getSignalsuser());
signals.setAuth(auth); signals.setAuth(auth);
for (int i = 0; i < fids.size(); i += batchSize) {
int end = Math.min(i + batchSize, fids.size());
List<Person> persons = findPersons(fids.subList(i, end));
if (!persons.isEmpty()) {
signals.getConditions().getPersons().getPerson().addAll(persons);
}
}
return signals; return signals;
} }
private List<Person> findPersons(List<PhoneService.FidsPhoneData> fidsPhoneData) { /**
* Обрабатывает все PhoneFidMap по packageId, выгружая их пачками и фильтруя по переданному фильтру.
* writer записывает пачку сразу в файл
*/
private void buildPersons(Long packageId, Consumer<List<Person>> writer) {
int offset = 0;
List<PhoneFidMap> batch;
do {
batch = phoneFidMapService.findFullPhoneFidMap(packageId, offset, batchSize);
List<Person> persons = findPersons(batch);
if (!persons.isEmpty()) {
writer.accept(persons); // записывает пачку Persons в файл
}
offset += batchSize;
} while (!batch.isEmpty());
}
/**
* Формирует список Person для постановки на мониторинг в официальный сервис сигналов на основе списка PhoneFidMap
*/
private List<Person> findPersons(List<PhoneFidMap> subjects) {
if (subjects.isEmpty()) {
return Collections.emptyList();
}
List<Person> persons = new ArrayList<>(); List<Person> persons = new ArrayList<>();
Map<Integer, PersonService.PersonData> personFidMap = personService.findPersonData(fidsPhoneData.stream() Map<Integer, PersonService.PersonData> personFidMap = personService.findPersonData(subjects.stream()
.map(PhoneService.FidsPhoneData::getFid) .map(PhoneFidMap::getFid)
.filter(Objects::nonNull).toList()); .filter(Objects::nonNull).toList());
fidsPhoneData.forEach(data -> { subjects.forEach(data -> {
if (data.getFid() != null) { if (data.getFid() != null) {
PersonService.PersonData personData = personFidMap.get(data.getFid()); PersonService.PersonData personData = personFidMap.get(data.getFid());
Person person = getPerson(personData, data.getSubjectId()); Person person = PersonFactoryBuilder.builder()
.personData(personData)
.uid(data.getId())
.build();
if (person != null) { if (person != null) {
persons.add(person); persons.add(person);
} }
@ -152,101 +282,85 @@ public class SignalService {
return persons; return persons;
} }
private Person getPerson(PersonService.PersonData personData, String id) { public void updateSubjectsErrors(String filePath, Long packageId) {
if (personData == null) { log.info("Start updating subjects errors. PackageId: {}", packageId);
return null; try (BufferedInputStream in = new BufferedInputStream(Files.newInputStream(Path.of(filePath)), 8 * 1024 * 1024)) {
staxXmlProcessor.read(in, MonitoringReportErrors.class,
request -> {
if (!request.getPersons().isEmpty()) {
List<PhoneFidMap> subjects = request.getPersons().stream().map(p -> {
log.warn("Ошибка при формировании субъекта. package_id = {}, id = {}. Описание: {}", packageId, p.getUid(), p.getValue());
return PhoneFidMap.builder()
.id(p.getUid())
.errorText(p.getValue())
.build();
}).toList();
phoneFidMapService.updatePhoneFidMapsErrors(subjects);
log.trace("Updated subject's batch. Total count: {}. PackageId: {}", request.getPersons().size(), packageId);
request.getPersons().clear();
}
},
request -> request.getPersons().isEmpty()
,
batchSize, "/Errors/p");
} catch (IOException e) {
throw new RuntimeException("Error while parsing MonitoringReportErrors", e);
} }
PersonService.NameData nameData = personData.getName(); log.info("Subject's errors were updated. PackageId: {}", packageId);
PersonService.PassportData passportData = personData.getPassport();
Person person = new Person();
person.setS(nameData.getLastName());
person.setN(nameData.getFirstName());
person.setM(StringUtils.isNotBlank(nameData.getMiddleName()) ? nameData.getMiddleName() : null);
person.setD(DATE_FORMAT.format(CTUtil.localDatetoDate(nameData.getBirthDate())));
person.setPn(getPn(passportData.getSerNum(), passportData.getIdNum()));
person.setPd(passportData.getIssDate() != null ? DATE_FORMAT.format(CTUtil.localDatetoDate(passportData.getIssDate())) : "01.01.2000");
person.setDt(StringUtils.isNotBlank(passportData.getIdType2()) ? passportData.getIdType2() : passportData.getIdType());
person.setIp("0");
person.setUid(id);
person.setOksm("999");
person.setConsentDate(DATE_FORMAT.format(CTUtil.localDatetoDate(LocalDate.now())));
person.setConsentPurpose("16");
person.setReportUser("АО «НБКИ»");
person.setLiability("1");
person.setConsentPeriod("3");
person.setAgrDate(DATE_FORMAT.format(CTUtil.localDatetoDate(LocalDate.now())));
person.setReportUserRegNum("1057746710713");
person.setReportUserTaxID("7703548386");
person.setConsentHash("hash");
person.setCs(person.getS());
person.setConsentN(person.getN());
person.setConsentM(person.getM());
person.setConsentBd(person.getD());
person.setConsentPn(person.getPn());
person.setConsentPd(person.getPd());
person.setConsentDivCode("000-000");
person.setConsentDt(person.getDt());
person.setInqPurpose("16");
return person;
} }
private String getPn(String serNum, String idNum) { public List<UserPackage> findActivePackages(String userId) {
String normSerNum = CTUtil.normalize(serNum); return userPackageService.findUserPackages(userId, Status.ON_MONITORING);
String normIdNum = CTUtil.normalize(idNum);
return StringUtils.isNotBlank(normSerNum) ? normSerNum + normIdNum : normIdNum;
} }
public List<UserPackage> findAllPackages(String userId) {
public MonitoringActivePackagesResponse findActivePackages(String userId) { return userPackageService.findAllPackagesByUserId(userId);
String memberCode = userId.substring(0, 6);
MonitoringActivePackagesResponse activePackages = new MonitoringActivePackagesResponse();
activePackages.setStatus("OK");
activePackages.setMembercode(memberCode);
Packages packages = new Packages();
mapper.findPackageDataList(memberCode, Status.ON_MONITORING.name()).forEach(data -> {
Package pack = new Package();
pack.setPackageName(data.getPackageName());
pack.setCreatedAt(data.getCreatedAt() != null ? data.getCreatedAt().toString() : null);
packages.getPackages().add(pack);
});
activePackages.setPackages(packages);
if (packages.getPackages() == null || packages.getPackages().isEmpty()) {
return null;
}
return activePackages;
} }
public boolean isPackageExists(String username, String packageName) { public boolean isPackageExists(String username, String packageName) {
return mapper.isPackageExists(username.substring(0, 6), packageName, Status.ON_MONITORING.name()); return userPackageService.isPackageExists(username, packageName, Status.ON_MONITORING);
} }
public MonitoringPackageDeleteResponse deletePackage(String username, String packageName) { public boolean isPackagesByStatusExist(String username, String packageName, Status status) {
LocalDateTime deactivatedAt = LocalDateTime.now(); return userPackageService.hasActivePackages(username, packageName, status);
mapper.deletePackage(username.substring(0, 6), packageName, Status.DELETED.name(), Status.ON_MONITORING.name(), deactivatedAt);
MonitoringPackageDeleteResponse deleteResponse = new MonitoringPackageDeleteResponse();
deleteResponse.setStatus("Deleted");
deleteResponse.setPackageName(packageName);
deleteResponse.setDeactivatedAt(deactivatedAt.toString());
return deleteResponse;
} }
public List<PackageData> findPackagesOnMonitoring() { public void deletePackage(String username, String packageName, LocalDateTime deactivatedAt) {
return mapper.findPackagesByStatus(Status.ON_MONITORING.name()); userPackageService.deletePackage(username, packageName, Status.DELETED, Status.ON_MONITORING, deactivatedAt);
} }
public List<SubjectData> findFullSubjectsData(Integer packageId) { public List<UserPackage> findPackagesOnMonitoring() {
return mapper.findFullSubjectsData(packageId); return userPackageService.findAllPackagesByStatus(Status.ON_MONITORING);
} }
public void setHits(List<HitsData> hits) { public List<UserPackage> findPackagesOnProcessing() {
mapper.setHits(hits); return userPackageService.findAllPackagesByStatus(Status.PROCESSING);
} }
public void updateLastDownloadedReport(Integer id, String fileName) { public void insertHits(List<Hit> hits) {
mapper.setPackageLastDownloadedReport(id, fileName); hitsService.insertHits(hits);
}
public void updateLastDownloadedReport(Long id, String fileName) {
userPackageService.updatePackageLastDownloadedReport(id, fileName);
}
public PhoneFidMap findPhoneFidMapById(Long id) {
return phoneFidMapService.findPhoneFidMapById(id);
}
public boolean hasNonNullfids(Long packageId) {
return phoneFidMapService.hasNonNullfids(packageId);
}
public UserPackage findPackage(Long packageId) {
return userPackageService.findPackage(packageId);
}
@Getter
@AllArgsConstructor
public static class UpdateResult {
private boolean updated;
private boolean noFidsFound;
} }
} }

View file

@ -0,0 +1,76 @@
package ru.nbch.credit_tracker.service;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.framework.AopContext;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Service;
import ru.nbch.credit_tracker.config.mdc.SetAndClearMdc;
import ru.nbch.credit_tracker.entities.dto.UserPackage;
import ru.nbch.credit_tracker.enums.Status;
import ru.nbch.credit_tracker.service.signal.SignalRestService;
import java.util.List;
/**
* Сервис для завершения обработки пакетов находящихся в статусе PROCESSING
* Запускается при старте приложения и отрабатывает 1 раз
*/
@Slf4j
@Service
@EnableAspectJAutoProxy(exposeProxy = true)
public class UpdateProcessingPackagesService {
private UpdateProcessingPackagesService self;
private final SignalService signalService;
private final SignalRestService signalRestService;
private final MonitoringErrorProcesingService monitoringErrorProcesingService;
public UpdateProcessingPackagesService(SignalService signalService,
SignalRestService signalRestService,
MonitoringErrorProcesingService monitoringErrorProcesingService) {
this.signalService = signalService;
this.signalRestService = signalRestService;
this.monitoringErrorProcesingService = monitoringErrorProcesingService;
}
public void run() {
log.info("Start updating processing packages");
List<UserPackage> packages = signalService.findPackagesOnProcessing();
for (UserPackage userPackage : packages) {
log.info("Updating flags for Package {}", userPackage.getPackageName());
getCurrentProxy().process(userPackage.getId(), userPackage.getPackageName());
}
log.info("Finish updating processing packages");
}
@SetAndClearMdc(argIndex = 1)
public void process(Long packageId, String packageName) {
try {
SignalService.UpdateResult updateResult = signalService.updateSubjects(packageId, null);
if (updateResult.isNoFidsFound()) {
log.info("No fids found for PackageId: {} . Package status ON_MONITORING. No registration in signal service.", packageId);
signalService.updatePackageStatus(packageId, Status.ON_MONITORING);
return;
}
if (signalRestService.setupMonitoring(packageId)) {
log.info("Package was successfully registered in signal service. PackageId: {}", packageId);
signalService.updatePackageStatus(packageId, Status.ON_MONITORING);
monitoringErrorProcesingService.findErrors(packageId);
} else {
log.info("Package registration was rejected by signal service. PackageId: {}", packageId);
signalService.updatePackageStatus(packageId, Status.ERROR);
}
} catch (Exception e) {
signalService.updatePackageStatus(packageId, Status.ERROR);
log.error("Ошибка при поиске фидов или расчете флагов просрочек. Package_id {}", packageId, e);
}
}
private UpdateProcessingPackagesService getCurrentProxy() {
if (self == null) {
self = (UpdateProcessingPackagesService) AopContext.currentProxy();
}
return self;
}
}

View file

@ -1,73 +1,89 @@
package ru.nbch.credit_tracker.service; package ru.nbch.credit_tracker.service;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import ru.nbch.credit_tracker.config.settings.CreditTrackerSettings;
import ru.nbch.credit_tracker.entities.dto.PackageData;
import ru.nbch.credit_tracker.entities.dto.SubjectData;
import ru.nbch.credit_tracker.entities.signal_package.Signals;
import ru.nbch.credit_tracker.enums.Status;
import ru.nbch.credit_tracker.service.indic.PhoneService;
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 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
* По обновленным пакетам формируется новый запрос постановки на мониторинг в официальный сервис сигналов
*/
@Slf4j @Slf4j
@Service @Service
public class UpdatingFlagsService { public class UpdatingFlagsService {
private final SignalService signalService; private final SignalService signalService;
private final SignalRestService signalRestService; private final SignalRestService signalRestService;
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,
CreditTrackerSettings config) { MonitoringErrorProcesingService monitoringErrorProcesingService,
CreditTrackerSettings config,
PhoneFidMapMapper phoneFidMapMapper) {
this.signalService = signalService; this.signalService = signalService;
this.signalRestService = signalRestService; this.signalRestService = signalRestService;
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(); 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<PackageData> packages = signalService.findPackagesOnMonitoring(); List<UserPackage> packages = signalService.findPackagesOnMonitoring();
for (PackageData packageData : packages) { for (UserPackage userPackage : packages) {
log.info("Updating flags for Package {}", packageData.getPackageName()); MDC.put("ru.nbch.credit_tracker.log.mdc.key", "[" + userPackage.getPackageName() +"]");
List<SubjectData> subjects = signalService.findFullSubjectsData(packageData.getId()) log.info("Check on updating flags for Package {}", userPackage.getPackageName());
.stream().filter(subject -> subject.getCalculatedAt() != null && hasDaysPassed(subject.getCalculatedAt())).toList(); boolean hasOldRow = phoneFidMapMapper.hasOldRow(userPackage.getId(), daysToUpdateFlags.intValue());
updateFlags(packageData.getId(), subjects, packageData.getPackageName()); 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, List<SubjectData> subjects, String packageName) { private void updateFlags(UserPackage userPackage) {
Long packageId = userPackage.getId();
String packageName = userPackage.getPackageName();
try { try {
signalService.setPackageStatus(packageId, Status.PROCESSING); signalService.updatePackageStatus(packageId, Status.PROCESSING);
List<PhoneService.FidsPhoneData> phoneFidsData = signalService.updateFlags(subjects, packageId); SignalService.UpdateResult updateResult = signalService.updateSubjects(packageId, null);
if (!phoneFidsData.isEmpty()) { if (updateResult.isUpdated()) {
Signals signals = signalService.buildSignals(packageId, phoneFidsData); if (signalRestService.setupMonitoring(packageId)) {
if (signalRestService.update(signals)) { LocalDateTime monitoringAt = userPackage.getMonitoringAt() != null ? userPackage.getMonitoringAt() : LocalDateTime.now();
log.info("Flags were updated successfully. PackageId: {} PackageName: {}. Package status changed to ON_MONITORING", packageId, packageName); log.info("Flags were updated successfully. PackageId: {} PackageName: {} MonitoringAt: {}. Package status changed to ON_MONITORING", packageId, packageName, monitoringAt);
signalService.setPackageStatus(packageId, Status.ON_MONITORING); signalService.updatePackageOnMonitoringSuccess(packageId, monitoringAt);
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);
signalService.setPackageStatus(packageId, Status.ERROR); signalService.updatePackageStatus(packageId, Status.ERROR);
} }
} else { } else {
log.info("No flags were updated for package id {}. Package status changed to ON_MONITORING", packageId); log.info("No flags were updated for package id {}. Package status changed to ON_MONITORING", packageId);
signalService.setPackageStatus(packageId, Status.ON_MONITORING); signalService.updatePackageStatus(packageId, Status.ON_MONITORING);
} }
} catch (Exception e) { } catch (Exception e) {
log.error("Ошибка при поиске фидов или перерасчете флагов просрочек. Package_id {}", packageId, e); log.error("Ошибка при поиске фидов или перерасчете флагов просрочек. Package_id {}", packageId, e);

View file

@ -70,23 +70,27 @@ public class PersonService {
} }
private Map<Integer, PassportData> findPassportData(List<Integer> fids) { private Map<Integer, PassportData> findPassportData(List<Integer> fids) {
Map<Integer, PassportData> passportDataMap = new HashMap<>(); Map<Integer, PassportData> resultMap = new HashMap<>();
List<PassportData> passports = mapper.findPassportData(fids); List<PassportData> passports = mapper.findPassportData(fids);
Map<Integer, List<PassportData>> passportsMap = passports.stream()
.collect(Collectors.groupingBy(PassportData::getFid));
fids.forEach(fid -> { fids.forEach(fid -> {
PassportData passport = findPassport(passports.stream() PassportData passport = findPassport(passportsMap.get(fid));
.filter(passportData -> passportData.getFid().equals(fid))
.collect(Collectors.toList()));
if (passport != null) { if (passport != null) {
passportDataMap.put(fid, passport); resultMap.put(fid, passport);
} }
}); });
return passportDataMap; return resultMap;
} }
private PassportData findPassport(List<PassportData> passports) { private PassportData findPassport(List<PassportData> passports) {
if (passports == null || passports.isEmpty()) {
return null;
}
List<PassportData> documents = passports.stream() List<PassportData> documents = passports.stream()
.filter(this::isDUL) .filter(this::isDUL)
.toList(); // берем только ДУЛ .toList(); // берем только ДУЛ
@ -139,7 +143,7 @@ public class PersonService {
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
idType2 = null; idType2 = null;
} }
return (idType != null && idType <= 30) || (idType2 != null && (idType2 >= 1 || idType2 <= 999)); return (idType != null && idType <= 30 && idType != 12) || (idType2 != null && (idType2 >= 1 || idType2 <= 999));
} }
private boolean requiredFieldPresent(NameData nameData, PassportData passportData) { private boolean requiredFieldPresent(NameData nameData, PassportData passportData) {

View file

@ -1,14 +1,16 @@
package ru.nbch.credit_tracker.service.indic; package ru.nbch.credit_tracker.service.indic;
import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import ru.nbch.credit_tracker.entities.dto.SubjectData; import ru.nbch.credit_tracker.entities.dto.PhoneFidMap;
import ru.nbch.credit_tracker.mapper.indic.PhoneMapper; import ru.nbch.credit_tracker.mapper.indic.PhoneMapper;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.*; import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@Service @Service
@ -20,32 +22,13 @@ public class PhoneService {
this.mapper = mapper; this.mapper = mapper;
} }
public List<FidsPhoneData> findFidsPhoneData(List<SubjectData> subjects) { /**
return findFidsPhoneData(subjects, false); * Ищет фиды по номерам телефона
} * @return Map: key - номер телефона; value - fid
*/
public List<FidsPhoneData> findFidsPhoneData(List<SubjectData> subjects, boolean processNullFids) { public Map<String, Integer> findFidsPhoneData(List<PhoneFidMap> subjects) {
List<FidsPhoneData> fidsPhoneDataList = new ArrayList<>(); List<String> phones = subjects.stream().map(subject -> "9" + subject.getPhone()).toList();
int batchSize = 10000; return convertFidsDataToMap(mapper.findFids(phones));
for (int i = 0; i < subjects.size(); i += batchSize) {
int end = Math.min(i + batchSize, subjects.size());
List<SubjectData> currentSubjects = subjects.subList(i, end);
List<String> phones = currentSubjects.stream().map(subject -> "9" + subject.getPhone()).toList();
List<FidsData> fidsData = mapper.findFids(phones);
Map<String, Integer> fidsMap = convertFidsDataToMap(fidsData);
currentSubjects.forEach(subject -> {
String phone = "9" + subject.getPhone();
Integer fid = fidsMap.get(phone);
if (fid != null) {
fidsPhoneDataList.add(new FidsPhoneData(subject.getId(), fid, subject.getPhone(), subject.getSubjectId()));
} else if (processNullFids) {
fidsPhoneDataList.add(new FidsPhoneData(subject.getId(), null, subject.getPhone(), subject.getSubjectId()));
}
});
}
return fidsPhoneDataList;
} }
private Map<String, Integer> convertFidsDataToMap(List<FidsData> fidsData) { private Map<String, Integer> convertFidsDataToMap(List<FidsData> fidsData) {
@ -65,16 +48,6 @@ public class PhoneService {
)); ));
} }
@Getter
@Setter
@AllArgsConstructor
public static class FidsPhoneData {
private Integer id;
private Integer fid;
private String phone;
private String subjectId;
}
@Getter @Getter
@Setter @Setter
public static class FidsData { public static class FidsData {

Some files were not shown because too many files have changed in this diff Show more