Compare commits

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

243 commits

Author SHA1 Message Date
Mikhail Trofimov
005e228bba Get rid of InquiryDateDeserializer; Changed ack mode 2026-07-10 09:52:39 +03:00
Mikhail Trofimov
55700d46aa Fix inaccuracy 2026-07-09 17:00:41 +03:00
Mikhail Trofimov
77f217e0f8 Minor refactoring 2026-07-09 16:51:21 +03:00
Mikhail Trofimov
59b074b2da get rid of setting reject status 2026-07-06 09:56:30 +03:00
Mikhail Trofimov
7004a1b7e3 get rid of external_package_name, FID_CHANGED, PROCESSING_REPEATED, NO_FID 2026-07-06 09:54:30 +03:00
Mikhail Trofimov
0cfe976670 Added ReportNameRegistry to store generated report names for hits writing 2026-07-05 14:15:41 +03:00
Mikhail Trofimov
2d75350f75 get rid of kafka producer bean 2026-07-05 12:57:52 +03:00
Mikhail Trofimov
20ec6871d5 get rid of InnRegnumPair record 2026-07-05 12:22:55 +03:00
Mikhail Trofimov
bcfe0328f0 Fix chache selecting for LP 2026-07-05 11:56:40 +03:00
Mikhail Trofimov
bbc7c64acf Fix bean creating; fix has fid logic (NO_FID status) 2026-07-05 11:40:50 +03:00
Mikhail Trofimov
e115919f1a Async db writing 2026-07-04 19:10:41 +03:00
Mikhail Trofimov
5d09e25e87 LP: Added restore logic 2026-07-04 18:33:48 +03:00
Mikhail Trofimov
d84763b754 Added separate executors 2026-07-04 18:26:36 +03:00
Mikhail Trofimov
2c0525d28d Save last downloaded report name 2026-07-03 17:54:56 +03:00
Mikhail Trofimov
4bd58624ed Added hits inserting 2026-07-03 17:25:54 +03:00
Mikhail Trofimov
1813647170 Added hits field to ddl 2026-07-03 16:40:03 +03:00
Mikhail Trofimov
d3fc6ce7e8 Added enew env for lp batch size saving 2026-07-03 15:59:31 +03:00
Mikhail Trofimov
e6edf52d16 Added env vars for 2 types of pipelines 2026-07-03 15:55:51 +03:00
Mikhail Trofimov
a93ed1ccb4 Fix monitoring process destroy 2026-07-03 15:29:11 +03:00
Mikhail Trofimov
0c977947cd LP: aAdded updating fids for lp; Added abstraction for PipelineOrchestrators 2026-07-03 15:27:24 +03:00
Mikhail Trofimov
9a9ba6b803 LP: added last rejecting date calculating 2026-07-03 09:32:25 +03:00
Mikhail Trofimov
bc78b389e7 NP: get rid of unused kick pipelines;
LP: added lp kick pipelines with data loading
2026-07-02 17:09:39 +03:00
Mikhail Trofimov
012d7085ad LP: added build for fids loading 2026-07-02 16:11:37 +03:00
Mikhail Trofimov
e94df35c76 LP: added LpFidEnricher 2026-07-02 15:37:08 +03:00
Mikhail Trofimov
58e7f9ef4b NP: delete unused data classes 2026-07-02 12:50:45 +03:00
Mikhail Trofimov
44512925f4 NP: get rid of SignalRestService and personal data loading/saving 2026-07-02 12:50:07 +03:00
Mikhail Trofimov
2709eaeb19 LP: added flags update by cron 2026-06-30 16:51:28 +03:00
Mikhail Trofimov
05ae297474 LP: added dummy LpPipelinePackageFabric 2026-06-30 15:43:07 +03:00
Mikhail Trofimov
ef5aaf418a LP: Rename PipelinePackageFabric to NpPipelinePackageFabric 2026-06-30 15:18:13 +03:00
Mikhail Trofimov
3921087946 LP: added subject registration 2026-06-30 14:06:52 +03:00
Mikhail Trofimov
6767a8c204 LP: added package registration 2026-06-30 13:50:25 +03:00
Mikhail Trofimov
15c651debd LP: added package validation 2026-06-30 12:56:53 +03:00
Mikhail Trofimov
50b8eeee89 Increase batch 2026-06-30 12:25:47 +03:00
Mikhail Trofimov
ecbea33a7e Added kafka settings to props 2026-06-29 16:51:14 +03:00
Mikhail Trofimov
d958d73609 Fix lp UserPackageLegalData selecting 2026-06-29 15:55:57 +03:00
Mikhail Trofimov
a3a1debe45 Drain ivents from queue 2026-06-29 15:45:42 +03:00
Mikhail Trofimov
c5ace5e151 Fix Source selecting 2026-06-29 15:21:47 +03:00
Mikhail Trofimov
a1f4b86f99 Added zipping response 2026-06-29 10:55:49 +03:00
Mikhail Trofimov
61f2420ca4 Added writing to xml for lp 2026-06-25 15:16:35 +03:00
Mikhail Trofimov
a7a59c0c10 Added processing for legal persons 2026-06-25 14:48:12 +03:00
Mikhail Trofimov
5982819965 Added writing to xml for np 2026-06-25 11:31:03 +03:00
ialbert
1f142cf2a8 KafkaMonitoringService graceful shutdown 2026-06-24 17:45:47 +03:00
Mikhail Trofimov
c0421f28af minor fix 2026-06-24 11:26:40 +03:00
Mikhail Trofimov
b9973f8cdd Added signals_segment_map table; complete data preparing for natural persons 2026-06-24 11:04:04 +03:00
Mikhail Trofimov
ec93c4b726 Use semaphore instead of AtomicInteger counter 2026-06-23 15:35:35 +03:00
Mikhail Trofimov
d9f23cdff9 Prepare data for writing 2026-06-22 17:21:51 +03:00
Mikhail Trofimov
4633a83a64 Moved RotateSheduler as separate component 2026-06-22 16:22:22 +03:00
Mikhail Trofimov
d2800aa2bf Added writing logic 2026-06-22 15:31:50 +03:00
Mikhail Trofimov
56ac04ede0 Added queue for kafka signals; Added SignalKafkaEventWriterService 2026-06-22 14:08:44 +03:00
Mikhail Trofimov
e3246bda64 Setup logic for listener 2026-06-18 15:54:15 +03:00
Mikhail Trofimov
d3041a48ba Get rid of MonitoringTimerService 2026-06-18 14:56:19 +03:00
Mikhail Trofimov
689dd5cf36 Improve batch loading; Added SourceService 2026-06-15 17:12:12 +03:00
Mikhail Trofimov
f734f9147b Added NBCH datasource for SOURCE table (bus_category searching) 2026-06-15 13:04:14 +03:00
Mikhail Trofimov
c3abd1ef6c Get rid of test module; Moved code to MonitoringService 2026-06-10 12:15:41 +03:00
Mikhail Trofimov
5b6f3ce63c Added code for test load time 2026-06-09 17:42:01 +03:00
Mikhail Trofimov
249b351428 update expectedDbVersion 2026-06-09 15:02:07 +03:00
Mikhail Trofimov
a8a1b7fd53 added v2.22.ddl with new table and updated indexes 2026-06-09 14:53:22 +03:00
Mikhail Trofimov
8b9de539f4 added props for test 2026-06-05 10:14:00 +03:00
Mikhail Trofimov
e65f002672 Improve MonitoringTimerService logic 2026-06-05 10:04:55 +03:00
Mikhail Trofimov
8071209098 test prototype for kafka 2026-06-04 12:32:15 +03:00
Mikhail Trofimov
bd30604225 Remove FlagsServiceTest; Added FlagsEnricherTest with mocked methods 2026-05-07 15:34:28 +03:00
Mikhail Trofimov
8ddf3b6316 Update pipeline-engine version 2026-04-27 10:10:56 +03:00
Mikhail Trofimov
36f5f7d4c3 Delete unused entities 2026-04-23 18:46:37 +03:00
Mikhail Trofimov
45e6447d2e Use pipline engine as library 2026-04-23 17:28:27 +03:00
Mikhail Trofimov
4cc3f6eb3f Rename all *V2 and *2 classes 2026-04-23 16:31:03 +03:00
Mikhail Trofimov
b05b48f800 Get rid of unused mappers and mapper's methods 2026-04-23 15:49:36 +03:00
Mikhail Trofimov
e9092e63db Added Suspicios annotation 2026-04-23 15:29:23 +03:00
Mikhail Trofimov
6ded5b3a49 Get rid of old PersonService and FlagsService 2026-04-23 14:40:37 +03:00
etreschenkov
a3dafedb27 http://jira.mfd.msk:8088/browse/BKI-3575 2026-03-24 12:01:30 +03:00
etreschenkov
aa9a581527 http://jira.mfd.msk:8088/browse/BKI-3575 2026-03-24 12:01:05 +03:00
ialbert
02e20da2c7 upversion 2.21.10 -> 2.21.11 2026-03-18 15:58:52 +03:00
ialbert
3542f9fc59 upversion 2.21.9 -> 2.21.10 2026-03-17 18:05:15 +03:00
ialbert
8a434848f0 http://jira.mfd.msk:8088/browse/BKI-3488 2026-03-17 17:02:40 +03:00
ialbert
9b249b4f4f http://jira.mfd.msk:8088/browse/BKI-3489 2026-03-17 17:01:20 +03:00
ialbert
a287d29d32 http://jira.mfd.msk:8088/browse/BKI-3489 2026-03-17 16:29:37 +03:00
ialbert
9729697f3a http://jira.mfd.msk:8088/browse/BKI-3489 2026-03-16 17:33:53 +03:00
ialbert
a2bb357755 Merge remote-tracking branch 'origin/dev' into dev 2026-03-16 14:45:01 +03:00
ialbert
03328d6ffd http://jira.mfd.msk:8088/browse/BKI-3465 2026-03-16 14:44:45 +03:00
etreschenkov
6c9e30658b http://jira.mfd.msk:8088/browse/BKI-3458 2026-02-24 11:48:17 +03:00
etreschenkov
074375987d http://jira.mfd.msk:8088/browse/BKI-3456 2026-02-19 16:23:53 +03:00
etreschenkov
40fc51264e http://jira.mfd.msk:8088/browse/BKI-3456 2026-02-19 15:55:20 +03:00
etreschenkov
8233617d66 http://jira.mfd.msk:8088/browse/BKI-3456 2026-02-19 15:17:23 +03:00
etreschenkov
5e984bd628 http://jira.mfd.msk:8088/browse/BKI-3241 2026-02-12 16:09:08 +03:00
etreschenkov
06370d4aa7 http://jira.mfd.msk:8088/browse/BKI-3241 2026-02-12 13:44:58 +03:00
Mikhail Trofimov
f9edb2999c Updated version to 2.21.3; Convert start-credit-tracker.sh to lf 2026-02-12 10:20:15 +03:00
Mikhail Trofimov
53c91af26b Updated version to 2.21.2 2026-02-12 09:56:56 +03:00
etreschenkov
ede0b6792f http://jira.mfd.msk:8088/browse/BKI-3241 2026-02-11 17:43:38 +03:00
etreschenkov
5fc51a58c8 http://jira.mfd.msk:8088/browse/BKI-3241 old destination for client's reports 2026-02-11 15:38:52 +03:00
etreschenkov
6c99e6408e http://jira.mfd.msk:8088/browse/BKI-3241 2026-02-11 12:30:20 +03:00
etreschenkov
f9e3af2dd6 fix hit progression bug 2026-02-10 15:18:37 +03:00
etreschenkov
7266400169 fix migration properties 2026-02-10 14:32:06 +03:00
etreschenkov
8faa23ac63 http://jira.mfd.msk:8088/browse/BKI-3241#comment-72722 [db version] 2026-02-10 13:24:54 +03:00
etreschenkov
8df92fde73 fix deadlock 2026-02-09 16:27:56 +03:00
etreschenkov
46ff036c38 fix deadlock 2026-02-09 12:26:36 +03:00
etreschenkov
71843eb27f new app version 2026-02-06 15:47:42 +03:00
etreschenkov
337eb606c3 new app version 2026-02-06 15:46:46 +03:00
etreschenkov
48873104b8 fix conflict on key 2026-02-06 14:49:44 +03:00
etreschenkov
7e02ccb622 fix xmx and xms 2026-02-05 18:12:50 +03:00
etreschenkov
935d680415 http://jira.mfd.msk:8088/browse/BKI-3272 2026-02-05 16:23:13 +03:00
etreschenkov
7f2b1081d3 some fix 2026-02-04 10:07:56 +03:00
etreschenkov
be7876e90f add env to docker-compose 2026-02-03 16:52:23 +03:00
etreschenkov
9278d55955 add env to docker-compose 2026-02-03 11:02:15 +03:00
etreschenkov
6f3662190f http://jira.mfd.msk:8088/browse/BKI-3343 2026-01-29 17:41:30 +03:00
etreschenkov
fc0015a2a9 update build docker image 2026-01-29 14:42:16 +03:00
etreschenkov
fee03921bf update migration script 2026-01-28 13:12:34 +03:00
etreschenkov
1e7dc551c2 http://jira.mfd.msk:8088/browse/BKI-3217 2026-01-28 12:22:43 +03:00
etreschenkov
cdaac1f22e http://jira.mfd.msk:8088/browse/BKI-3272 2026-01-27 17:38:50 +03:00
etreschenkov
18c87beafe http://jira.mfd.msk:8088/browse/BKI-3272 2026-01-27 12:36:38 +03:00
etreschenkov
ab8ef5b90e http://jira.mfd.msk:8088/browse/BKI-3343 2026-01-26 13:28:30 +03:00
etreschenkov
a688510e85 http://jira.mfd.msk:8088/browse/BKI-3241 2026-01-23 12:03:05 +03:00
ialbert
b1fe08fcf6 update flags 2026-01-23 11:34:15 +03:00
ialbert
9d664898ff update flags: days-to-update-flags 2026-01-23 10:48:47 +03:00
etreschenkov
d133ac804d http://jira.mfd.msk:8088/browse/BKI-3241 2026-01-22 17:24:31 +03:00
ialbert
90e16f1c66 Merge remote-tracking branch 'origin/dev' into dev 2026-01-22 13:49:39 +03:00
etreschenkov
e6b3bdfb16 http://jira.mfd.msk:8088/browse/BKI-3241 2026-01-22 13:37:44 +03:00
ialbert
90b8d8ac4e flags new scheduller 2026-01-22 13:18:21 +03:00
etreschenkov
abfb805e4f http://jira.mfd.msk:8088/browse/BKI-3241 2026-01-22 12:15:58 +03:00
etreschenkov
94c88e16bd http://jira.mfd.msk:8088/browse/BKI-3241 2026-01-21 15:19:04 +03:00
etreschenkov
5b5f96309e http://jira.mfd.msk:8088/browse/BKI-3241 [NO_FID status] 2026-01-21 10:44:37 +03:00
etreschenkov
ff5e15418e http://jira.mfd.msk:8088/browse/BKI-3281 2026-01-21 10:10:48 +03:00
etreschenkov
0f2e41611a http://jira.mfd.msk:8088/browse/BKI-3241 2026-01-21 09:00:45 +03:00
etreschenkov
52d526757b http://jira.mfd.msk:8088/browse/BKI-3217 2026-01-20 16:31:31 +03:00
etreschenkov
c32d430483 http://jira.mfd.msk:8088/browse/BKI-3241 2026-01-20 13:36:48 +03:00
etreschenkov
4994af50bd http://jira.mfd.msk:8088/browse/BKI-3241 2026-01-20 10:27:47 +03:00
etreschenkov
a90d76d209 http://jira.mfd.msk:8088/browse/BKI-3241 2026-01-20 10:27:13 +03:00
ialbert
405028d078 рассчет флагов: flag_debt5000 & flag_90_12 2026-01-19 22:04:54 +03:00
etreschenkov
543e492bf2 http://jira.mfd.msk:8088/browse/BKI-3217 2026-01-19 17:54:48 +03:00
etreschenkov
e7cb1526d7 http://jira.mfd.msk:8088/browse/BKI-3308 2026-01-19 17:04:54 +03:00
ialbert
5126b7042e http://jira.mfd.msk:8088/browse/BKI-3281 2026-01-19 16:19:13 +03:00
ialbert
e6844640c2 http://jira.mfd.msk:8088/browse/BKI-3281 2026-01-19 16:09:36 +03:00
etreschenkov
986fa4201b http://jira.mfd.msk:8088/browse/BKI-3217 2026-01-19 13:26:51 +03:00
etreschenkov
ce10dbafcc http://jira.mfd.msk:8088/browse/BKI-3217 2026-01-19 13:20:51 +03:00
etreschenkov
9838eb1541 http://jira.mfd.msk:8088/browse/BKI-3217 2026-01-19 12:35:21 +03:00
etreschenkov
1cc1695b65 http://jira.mfd.msk:8088/browse/BKI-3241 2026-01-19 12:07:09 +03:00
ialbert
23201aeddb Merge remote-tracking branch 'origin/dev' into dev 2026-01-16 17:48:22 +03:00
ialbert
9fce65ea4f update flags 2026-01-16 17:47:41 +03:00
etreschenkov
7513cf91b3 http://jira.mfd.msk:8088/browse/BKI-3217 2026-01-16 15:29:15 +03:00
etreschenkov
477a515244 http://jira.mfd.msk:8088/browse/BKI-3241 2026-01-16 12:37:25 +03:00
etreschenkov
75366d7d54 http://jira.mfd.msk:8088/browse/BKI-3241 2026-01-16 12:28:25 +03:00
etreschenkov
c135bf8481 http://jira.mfd.msk:8088/browse/BKI-3241 [recalc person data] 2026-01-15 19:03:04 +03:00
etreschenkov
e92111273f http://jira.mfd.msk:8088/browse/BKI-3241 // http://jira.mfd.msk:8088/browse/BKI-3337 2026-01-15 12:47:06 +03:00
etreschenkov
5c1d9ccd2a http://jira.mfd.msk:8088/browse/BKI-3217 2026-01-14 19:07:23 +03:00
etreschenkov
35bc835a0d http://jira.mfd.msk:8088/browse/BKI-3217 2026-01-14 18:13:56 +03:00
etreschenkov
6b4d6908d1 http://jira.mfd.msk:8088/browse/BKI-3241 [recalc person data] 2026-01-14 15:55:09 +03:00
etreschenkov
fe87213cf8 http://jira.mfd.msk:8088/browse/BKI-3241 [10; 11] 2026-01-13 15:57:27 +03:00
etreschenkov
e8268be5aa http://jira.mfd.msk:8088/browse/BKI-3217 2026-01-13 13:38:31 +03:00
etreschenkov
62f842ed08 http://jira.mfd.msk:8088/browse/BKI-3299 2026-01-13 13:27:32 +03:00
etreschenkov
8475bb5f37 http://jira.mfd.msk:8088/browse/BKI-3241?focusedCommentId=72178&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#comment-72178 2026-01-13 12:44:56 +03:00
ialbert
995a97a69c http://jira.mfd.msk:8088/browse/BKI-3281 2026-01-12 15:38:59 +03:00
etreschenkov
c7d22ae3d1 http://jira.mfd.msk:8088/browse/BKI-3299 //
http://jira.mfd.msk:8088/browse/BKI-3241
2026-01-12 12:57:15 +03:00
etreschenkov
b79a2d982f fix ddl 2025-12-23 20:24:29 +03:00
etreschenkov
09d7f53143 http://jira.mfd.msk:8088/browse/BKI-3299 2025-12-23 20:09:05 +03:00
etreschenkov
6ad9d5606d установка пакета с учетом кэш таблицы phones_map 2025-12-23 19:32:16 +03:00
etreschenkov
77ec90a92e установка пакета с учетом кэш таблицы phones_map 2025-12-23 18:16:53 +03:00
etreschenkov
d87444dac1 http://jira.mfd.msk:8088/browse/BKI-3241 2025-12-16 17:25:47 +03:00
etreschenkov
a9f9588258 http://jira.mfd.msk:8088/browse/BKI-3308 2025-12-16 16:30:35 +03:00
etreschenkov
3bdb16f8ef http://jira.mfd.msk:8088/browse/BKI-3281 2025-12-11 12:37:13 +03:00
etreschenkov
6d582efa16 http://jira.mfd.msk:8088/browse/BKI-3241 2025-12-05 12:57:37 +03:00
ialbert
51e6bb05c9 pipeline context example 2025-12-03 17:12:25 +03:00
etreschenkov
e331ff403f delete deprecated classes 2025-12-02 19:08:42 +03:00
etreschenkov
f88af385c3 ./ 2025-12-02 15:10:00 +03:00
ialbert
90259217b4 pipeline orchestrator [2] 2025-12-02 14:53:42 +03:00
ialbert
f2f7cfd5a1 pipeline orchestrator 2025-12-02 14:44:01 +03:00
etreschenkov
d78af08405 fix recalc flags 2025-12-02 13:27:21 +03:00
etreschenkov
a2ef593bc9 http://jira.mfd.msk:8088/browse/BKI-3241 2025-12-01 19:20:35 +03:00
etreschenkov
945073948f change property name 2025-12-01 13:03:35 +03:00
ialbert
08f7692899 update flags 2025-11-28 14:39:11 +03:00
ialbert
8000a22dd4 update flags 2025-11-28 14:27:36 +03:00
etreschenkov
a24a3b5975 restore package in ACCEPTER;PROCCESSING statuses 2025-11-28 14:24:54 +03:00
ialbert
3d86558acb update flags 2025-11-28 13:30:08 +03:00
ialbert
d5c6e7e08f updating flags 2025-11-27 21:11:32 +03:00
etreschenkov
d0e2fa4135 restore package in ACCEPTER;PROCCESSING statuses 2025-11-26 23:46:13 +03:00
etreschenkov
5d3b973919 new logic [setup on monitoring; download report] 2025-11-26 20:25:06 +03:00
etreschenkov
d4f689e85a fix pipeline settings 2025-11-26 16:08:17 +03:00
etreschenkov
930a563172 ./ 2025-11-26 14:59:32 +03:00
etreschenkov
c0cd36cd0a ./ 2025-11-25 19:43:11 +03:00
etreschenkov
0f98d8d28d add new tables 2025-11-25 15:59:51 +03:00
ialbert
015c7ca24b . 2025-11-25 15:44:08 +03:00
etreschenkov
c65d88f645 add new stage; status model 2025-11-24 15:32:07 +03:00
etreschenkov
d06ea8334b add time statistic 2025-11-21 21:00:12 +03:00
etreschenkov
7e641162ab remove poison step + delete tmp xml file request after validation 2025-11-21 16:16:48 +03:00
ialbert
9794cc6244 pipeline interrupt [2] 2025-11-21 15:57:58 +03:00
ialbert
69746d2b5e http://jira.mfd.msk:8088/browse/BKI-3281 2025-11-20 18:43:04 +03:00
ialbert
04601b618b версия БД 2025-11-20 16:27:27 +03:00
ialbert
2a58d73e52 Удаление пакета с мониторинга [2] 2025-11-20 14:44:11 +03:00
ialbert
d0f9459ce8 Удаление пакета с мониторинга 2025-11-19 17:47:20 +03:00
etreschenkov
e18e1f3724 ./ 2025-11-18 18:34:27 +03:00
etreschenkov
89f6d58f98 - изменена БД: добавлены новые поля, флаги вынесены в отдельную таблицу. 2025-11-18 15:38:44 +03:00
ialbert
4cfdd75dd9 Merge remote-tracking branch 'origin/dev' into dev 2025-11-18 12:17:53 +03:00
ialbert
28ec19775e handler concurrency & logs 2025-11-18 12:17:38 +03:00
etreschenkov
3ecb5282cb add new tables 2025-11-17 16:44:58 +03:00
etreschenkov
8ce1948b07 add batch size as parameter 2025-11-13 13:37:24 +03:00
ialbert
945694e50d pipeline bugfixes 2025-11-11 16:59:18 +03:00
etreschenkov
395613ae01 flag calc 2025-11-11 16:47:58 +03:00
etreschenkov
53a2ab77a9 flag calc 2025-11-11 14:36:46 +03:00
etreschenkov
aaf91195b1 flag calc 2025-11-11 14:23:12 +03:00
ialbert
bf693c3fbc pipeline blocking bugfix 2025-11-11 14:02:14 +03:00
ialbert
b487fc832f Merge remote-tracking branch 'origin/dev' into dev 2025-11-11 13:37:14 +03:00
ialbert
a67cc4056a . 2025-11-11 13:37:00 +03:00
etreschenkov
6da5991483 flag calc 2025-11-11 13:11:13 +03:00
etreschenkov
0f04ab3e42 flag calc 2025-11-11 13:01:00 +03:00
etreschenkov
4e6ab1a862 flag calc 2025-11-11 12:52:01 +03:00
etreschenkov
7176a49814 flag calc 2025-11-11 12:23:52 +03:00
etreschenkov
881c31becd flag calc 2025-11-11 09:43:38 +03:00
etreschenkov
83a452fed6 flag calc 2025-11-11 09:24:08 +03:00
etreschenkov
00662f9685 flag calc 2025-11-10 19:17:29 +03:00
etreschenkov
f10540e80a implement terminator 2025-11-10 16:51:03 +03:00
ialbert
525de91285 interrupt 2025-11-10 16:37:30 +03:00
etreschenkov
786004e3c8 attempt to remove unused classes 2025-11-10 11:32:12 +03:00
etreschenkov
74084f93c8 attempt to remove unused classes 2025-11-10 00:17:46 +03:00
ialbert
9813fe4053 pipeline futures 2025-11-05 13:58:28 +03:00
ialbert
d24cd03656 pipeline builder refactor 2025-11-01 15:18:58 +03:00
ialbert
8e53f24aeb added pipeline builder
added queue size
commented out merging
stage bugfix
2025-11-01 13:58:10 +03:00
etreschenkov
9ce437a438 FidIdEnricher: rename and chane payload type 2025-11-01 12:30:43 +03:00
etreschenkov
9cb5a16490 some refactoring :] 2025-10-31 13:25:26 +03:00
etreschenkov
f2df1c0183 some refactoring :] 2025-10-30 18:57:03 +03:00
etreschenkov
5d3507e12b some refactoring :/ 2025-10-30 15:04:58 +03:00
ialbert
999ac42f48 find packages method; global exceptions 2025-10-29 17:44:09 +03:00
etreschenkov
e7d5ff82f4 remove useless classes 2025-10-29 14:18:47 +03:00
etreschenkov
bb7606125a ./ 2025-10-16 17:42:03 +03:00
Mikhail Trofimov
76489609c7 Fix 2025-10-16 17:19:04 +03:00
Mikhail Trofimov
386ea4c18e Fix after merging master 2025-10-16 16:45:23 +03:00
Mikhail Trofimov
438956f8b5 Merge branch 'master' into dev
# Conflicts:
#	pom.xml
#	src/main/java/ru/nbch/credit_tracker/entities/dto/PhoneFidMap.java
#	src/main/java/ru/nbch/credit_tracker/mapper/signal/PhoneFidMapMapper.java
#	src/main/java/ru/nbch/credit_tracker/service/SignalService.java
#	src/main/java/ru/nbch/credit_tracker/service/signal/PhoneFidMapService.java
2025-10-16 16:33:01 +03:00
Mikhail Trofimov
1680abbec9 Fix fids finding 2025-10-16 12:28:23 +03:00
Mikhail Trofimov
7fe066c42d Minor fix: put phoneFid to map not only if name found 2025-10-15 18:03:01 +03:00
Mikhail Trofimov
bc34f3f89c Added FlagsEnricher; Added id to SubjectProfile to use it as uid in PersonFactoryBuilderV2 2025-10-12 20:43:25 +03:00
Mikhail Trofimov
e0d96d9246 Minor fix 2025-10-09 16:30:07 +03:00
Mikhail Trofimov
fc830f8eae Fix after merging master into dev 2025-10-09 15:55:02 +03:00
Mikhail Trofimov
c013b616c7 Merge branch 'master' into dev
# Conflicts:
#	credit-tracker.properties
#	pom.xml
#	src/main/java/ru/nbch/credit_tracker/config/datasource/DatabaseConfig.java
#	src/main/java/ru/nbch/credit_tracker/config/datasource/IndicDataSourceConfig.java
#	src/main/java/ru/nbch/credit_tracker/config/datasource/ScoringDataSourceConfig.java
#	src/main/java/ru/nbch/credit_tracker/config/datasource/SignalDataSourceConfig.java
#	src/main/java/ru/nbch/credit_tracker/config/rest/GlobalExceptionHandler.java
#	src/main/java/ru/nbch/credit_tracker/config/rest/filters/HttpLoggingFilter.java
#	src/main/java/ru/nbch/credit_tracker/facade/SignalFacade.java
#	src/main/java/ru/nbch/credit_tracker/utils/ValidationService.java
#	src/main/java/ru/nbch/credit_tracker/utils/error/ExceptionUtils.java
#	src/main/resources/logback.xml
2025-10-09 15:39:53 +03:00
Mikhail Trofimov
b272123802 Fixed XmlMergeCoordinator::mergeBatch 2025-10-09 15:30:36 +03:00
etreschenkov
e5945a1ef2 TaskConsumerV2 with poison pill 2025-10-09 11:42:41 +03:00
etreschenkov
7fc7bacbc8 конвейер очередей, тестовый вариант 2025-10-08 17:59:43 +03:00
etreschenkov
194828d0b7 some refactoring and new Facade classes 2025-10-07 10:44:27 +03:00
Mikhail Trofimov
f4b49c06ed Added processSubjects method to SignalService; Load Name data with fids in PhoneService2 2025-10-06 10:21:37 +03:00
Mikhail Trofimov
fcca92e6a5 Added code for compare 2 algorithms for fids finding 2025-10-02 17:58:29 +03:00
Mikhail Trofimov
c31d515c84 Moved phone jdbc calls to repository; Added RepositoryTimingAspect 2025-10-02 17:35:38 +03:00
Mikhail Trofimov
f95b547a81 Fix mereger function, moved it to PhoneService; Use previous logic for PhoneService2 2025-10-02 17:19:08 +03:00
Mikhail Trofimov
daceff96af Update tests; Used new merger function for PhoneService2 2025-10-02 13:29:38 +03:00
etreschenkov
978b45eda8 fix merging fids and phones 2025-10-02 11:51:48 +03:00
etreschenkov
769fabf79a fix merging fids and phones 2025-10-02 11:50:56 +03:00
Mikhail Trofimov
ae7260d790 http://jira.mfd.msk:8088/browse/BKI-3213 Optimize fids searching 2025-10-02 10:33:36 +03:00
Mikhail Trofimov
ae18c5d568 Merge branch 'query_optimization' into 'dev'
http://jira.mfd.msk:8088/browse/BKI-3213 Optimized flags calculating

See merge request nbki/credit_tracker!2
2025-09-30 08:03:39 +00:00
Mikhail Trofimov
8e90a7624c http://jira.mfd.msk:8088/browse/BKI-3213 Optimized flags calculating 2025-09-30 08:03:39 +00:00
187 changed files with 8926 additions and 2669 deletions

1
.gitignore vendored
View file

@ -31,3 +31,4 @@ build/
### VS Code ### ### VS Code ###
.vscode/ .vscode/
tmp/

View file

@ -1,4 +1,4 @@
FROM eclipse-temurin:17-jre-alpine FROM eclipse-temurin:21-jre-alpine
MAINTAINER ru.nbch MAINTAINER ru.nbch
VOLUME /opt/app/logs/app VOLUME /opt/app/logs/app
@ -8,7 +8,8 @@ VOLUME /opt/app/reports
WORKDIR /opt/app WORKDIR /opt/app
COPY maven/elastic-apm-agent-1.45.0.jar /opt/app/elastic-apm-agent.jar 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/credit-tracker-main.jar /opt/app/credit-tracker-main.jar
COPY maven/credit-tracker-monitoring.jar /opt/app/credit-tracker-monitoring.jar
COPY maven/start-credit-tracker.sh /opt/app/start-credit-tracker.sh COPY maven/start-credit-tracker.sh /opt/app/start-credit-tracker.sh
COPY maven/credit-tracker.properties /opt/app/credit-tracker.properties COPY maven/credit-tracker.properties /opt/app/credit-tracker.properties
COPY maven/monitoring-request.xsd /opt/app/monitoring-request.xsd COPY maven/monitoring-request.xsd /opt/app/monitoring-request.xsd
@ -20,4 +21,3 @@ RUN apk add --no-cache ca-certificates \
RUN apk add gcompat bash RUN apk add gcompat bash
RUN chmod +x /opt/app/start-credit-tracker.sh RUN chmod +x /opt/app/start-credit-tracker.sh
ENTRYPOINT ["/opt/app/start-credit-tracker.sh"]

View file

@ -8,10 +8,10 @@ server.tomcat.accesslog.pattern=%h %l %u %t "%r" %s %b
# datasources # datasources
# indic # indic
ct.indic-data-source-settings.driver=com.ibm.db2.jcc.DB2Driver ct.indic-data-source-settings.driver=com.ibm.db2.jcc.DB2Driver
ct.indic-data-source-settings.url=${DB_URL_INDIC:jdbc:db2://10.230.227.101:2668/cprosd22:currentSchema=INDIC;} ct.indic-data-source-settings.url=${DB_URL_INDIC:jdbc:db2://10.230.227.102:2668/cprosd22:currentSchema=INDIC;}
ct.indic-data-source-settings.username=${USERNAME_INDIC:tester} ct.indic-data-source-settings.username=${USERNAME_INDIC:ros9appl}
ct.indic-data-source-settings.password=${PASSWORD_INDIC:tester} ct.indic-data-source-settings.password=${PASSWORD_INDIC:R0s9Appl}
ct.indic-data-source-settings.maximum-pool-size=${MAXIMUM_POOL_SIZE_INDIC:10} ct.indic-data-source-settings.maximum-pool-size=${MAXIMUM_POOL_SIZE_INDIC:30}
ct.indic-data-source-settings.minimum-idle=${MINIMUM_IDLE_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.connection-timeout=${CONNECTION_TIMEOUT_INDIC:10000}
ct.indic-data-source-settings.validation-timeout=${VALIDATION_TIMEOUT_INDIC:5000} ct.indic-data-source-settings.validation-timeout=${VALIDATION_TIMEOUT_INDIC:5000}
@ -22,10 +22,10 @@ ct.indic-data-source-settings.leak-detection-threshold=${LEAK_DETECTION_THRESHOL
# signal # signal
ct.signal-data-source-settings.driver=org.postgresql.Driver ct.signal-data-source-settings.driver=org.postgresql.Driver
ct.signal-data-source-settings.url=${DB_URL_SIGNAL:jdbc:postgresql://10.200.200.133:5432/credit_tracker} ct.signal-data-source-settings.url=${DB_URL_SIGNAL:jdbc:postgresql://10.200.200.133:5432/credit_tracker?currentSchema=test_3}
ct.signal-data-source-settings.username=${USERNAME_SIGNAL:credit_tracker_user} ct.signal-data-source-settings.username=${USERNAME_SIGNAL:credit_tracker_user}
ct.signal-data-source-settings.password=${PASSWORD_SIGNAL:-} ct.signal-data-source-settings.password=${PASSWORD_SIGNAL:-}
ct.signal-data-source-settings.maximum-pool-size=${MAXIMUM_POOL_SIZE_SIGNAL:10} ct.signal-data-source-settings.maximum-pool-size=${MAXIMUM_POOL_SIZE_SIGNAL:40}
ct.signal-data-source-settings.minimum-idle=${MINIMUM_IDLE_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.connection-timeout=${CONNECTION_TIMEOUT_SIGNAL:10000}
ct.signal-data-source-settings.validation-timeout=${VALIDATION_TIMEOUT_SIGNAL:5000} ct.signal-data-source-settings.validation-timeout=${VALIDATION_TIMEOUT_SIGNAL:5000}
@ -39,7 +39,7 @@ ct.scoring-data-source-settings.driver=com.ibm.db2.jcc.DB2Driver
ct.scoring-data-source-settings.url=${DB_URL_SCORING:jdbc:db2://10.230.227.101:2668/cprosd22:currentSchema=TEST_2_INDIC;} ct.scoring-data-source-settings.url=${DB_URL_SCORING:jdbc:db2://10.230.227.101:2668/cprosd22:currentSchema=TEST_2_INDIC;}
ct.scoring-data-source-settings.username=${USERNAME_SCORING:tester} ct.scoring-data-source-settings.username=${USERNAME_SCORING:tester}
ct.scoring-data-source-settings.password=${PASSWORD_SCORING:tester} ct.scoring-data-source-settings.password=${PASSWORD_SCORING:tester}
ct.scoring-data-source-settings.maximum-pool-size=${MAXIMUM_POOL_SIZE_SCORING:10} ct.scoring-data-source-settings.maximum-pool-size=${MAXIMUM_POOL_SIZE_SCORING:30}
ct.scoring-data-source-settings.minimum-idle=${MINIMUM_IDLE_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.connection-timeout=${CONNECTION_TIMEOUT_SCORING:10000}
ct.scoring-data-source-settings.validation-timeout=${VALIDATION_TIMEOUT_SCORING:5000} ct.scoring-data-source-settings.validation-timeout=${VALIDATION_TIMEOUT_SCORING:5000}
@ -48,6 +48,20 @@ 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.keepalive-time=${KEEPALIVE_TIME_SCORING:0}
ct.scoring-data-source-settings.leak-detection-threshold=${LEAK_DETECTION_THRESHOLD_SCORING:0} ct.scoring-data-source-settings.leak-detection-threshold=${LEAK_DETECTION_THRESHOLD_SCORING:0}
# nbch
ct.nbch-data-source-settings.driver=com.ibm.db2.jcc.DB2Driver
ct.nbch-data-source-settings.url=${DB_URL_NBCH:jdbc:db2://10.230.227.102:2668/cprosd22:currentSchema=NBCH;}
ct.nbch-data-source-settings.username=${USERNAME_NBCH:ros9appl}
ct.nbch-data-source-settings.password=${PASSWORD_NBCH:R0s9Appl}
ct.nbch-data-source-settings.maximum-pool-size=${MAXIMUM_POOL_SIZE_NBCH:30}
ct.nbch-data-source-settings.minimum-idle=${MINIMUM_IDLE_NBCH:10}
ct.nbch-data-source-settings.connection-timeout=${CONNECTION_TIMEOUT_NBCH:10000}
ct.nbch-data-source-settings.validation-timeout=${VALIDATION_TIMEOUT_NBCH:5000}
ct.nbch-data-source-settings.idle-timeout=${IDLE_TIMEOUT_NBCH:600000}
ct.nbch-data-source-settings.max-lifetime=${MAX_LIFETIME_NBCH:1800000}
ct.nbch-data-source-settings.keepalive-time=${KEEPALIVE_TIME_NBCH:0}
ct.nbch-data-source-settings.leak-detection-threshold=${LEAK_DETECTION_THRESHOLD_NBCH:0}
ct.max_xml_file_size=${ACCEPTED_XML_SIZE:1024} ct.max_xml_file_size=${ACCEPTED_XML_SIZE:1024}
spring.servlet.multipart.max-file-size=${ACCEPTED_ARCHIVE_SIZE:2048MB} spring.servlet.multipart.max-file-size=${ACCEPTED_ARCHIVE_SIZE:2048MB}
spring.servlet.multipart.max-request-size=${ACCEPTED_ARCHIVE_SIZE:2048MB} spring.servlet.multipart.max-request-size=${ACCEPTED_ARCHIVE_SIZE:2048MB}
@ -56,17 +70,90 @@ spring.servlet.multipart.max-request-size=${ACCEPTED_ARCHIVE_SIZE:2048MB}
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=${SIGNALS_ROOT_URI:http://10.230.227.102:11094/signalRUalfa/}
ct.signals-token=${SIGNALS_TOKEN:gttcHxwcGojoAvZK}
ct.signals-user=${SIGNALS_USER:0001ZZ000001}
ct.load-dir=/opt/app/reports ct.load-dir=/opt/app/reports
#ct.load-dir=D:\\folder_name\\signals #ct.load-dir=D:\\folder_name\\signals
ct.tmp.dir=D:\\folder_name\\signals\\tmp
ct.target.dir=D:\\folder_name\\signals\\tmpt
# min # min
ct.monitoring-interval=${MONITORING_INTERVAL:15} ct.monitoring-interval=${MONITORING_INTERVAL:5}
# days # days
ct.update-flags-interval=${UPDATE_FLAGS_INTERVAL:1} ct.update-flags-cron-expression=${UPDATE_FLAGS_CRON_EXPRESSION:0 25 18 * * *}
ct.new-flags-cron-expression=${NEW_FLAGS_CRON_EXPRESSION:0 25 18 * * *}
ct.days-to-update-flags=${DAYS_TO_UPDATE_FLAGS:30} ct.days-to-update-flags=${DAYS_TO_UPDATE_FLAGS:30}
ct.update-np-fid-cron-expression=${UPDATE_FIDS_CRON_EXPRESSION:0 10 12 * * *}
ct.update-lp-fid-cron-expression=${UPDATE_FIDS_CRON_EXPRESSION:0 10 12 * * *}
ct.days-to-update-np-fids=${DAYS_TO_UPDATE_FIDS:30}
ct.days-to-update-lp-fids=${DAYS_TO_UPDATE_FIDS:30}
#kafka
ct.kafka-consumer-settings.topic=${CONSUMER_TOPIC:my-topic}
ct.kafka-consumer-settings.concurency=${CONSUMER_CONCURENCY:8}
ct.kafka-consumer-settings.bootstrap-servers=${CONSUMER_BOOTSTRAP_SERVERS:localhost:29092}
ct.kafka-consumer-settings.group-id=${CONSUMER_GROUP_ID:credit-tracker}
ct.kafka-consumer-settings.auto-offset-reset=${CONSUMER_AUTO_OFFSET_RESET:latest}
ct.kafka-consumer-settings.max-poll-records=${CONSUMER_MAX_POLL_RECORDS:500}
ct.kafka-consumer-settings.max-poll-interval-ms=${CONSUMER_MAX_POLL_INTERVAL_MS:300000}
ct.db-version=${DB_VERSION:1234}
ct.encoding=windows-1251 ct.encoding=windows-1251
#----------------------------Pipeline settings----------------------------#
#batch size for saving in phone_fid_map
ct.npsaver-subject-batch-size=${NP_SAVER_SUBJECT_BATCH_SIZE:15000}
#batch size for saving in signals_legal_fid_map
ct.lp-saver-subject-batch-size=${LP_SAVER_SUBJECT_BATCH_SIZE:15000}
#limit on select phone_fid_map
ct.np-pipeline.subject-batch-size=${NP_PIPELINE_SUBJECT_BATCH_SIZE:10000}
#limit on select signals_legal_map
ct.lp-pipeline.subject-batch-size=${LP_PIPELINE_SUBJECT_BATCH_SIZE:10000}
#STAGES SETTINGS
#select NAME + FID BY PHONE
ct.np-pipeline.name-enricher-stage-concurrency=${NP_PIPELINE_NAME_ENRICHER_STAGE_CONCURRENCY:20}
ct.np-pipeline.name-enricher-queue-capacity=${NP_PIPELINE_NAME_ENRICHER_QUEUE_CAPACITY:20}
#select FID BY INN+REG_NUM
ct.lp-pipeline.name-enricher-stage-concurrency=${LP_PIPELINE_NAME_ENRICHER_STAGE_CONCURRENCY:20}
ct.lp-pipeline.name-enricher-queue-capacity=${LP_PIPELINE_NAME_ENRICHER_QUEUE_CAPACITY:20}
#Save part of signal xml file and merge; only one thread
ct.np-pipeline.merger-queue-capacity=${NP_PIPELINE_MERGER_QUEUE_CAPACITY:50}
ct.lp-pipeline.merger-queue-capacity=${LP_PIPELINE_MERGER_QUEUE_CAPACITY:50}
#STAGES SETTINGS
#select NAME + FID BY PHONE
ct.np-pipeline.name-updater-stage-concurrency=${NP_PIPELINE_NAME_UPDATER_STAGE_CONCURRENCY:20}
ct.np-pipeline.name-updater-queue-capacity=${NP_PIPELINE_NAME_UPDATER_QUEUE_CAPACITY:20}
#select FID BY INN+REG_NUM
ct.lp-pipeline.name-updater-stage-concurrency=${LP_PIPELINE_NAME_UPDATER_STAGE_CONCURRENCY:20}
ct.lp-pipeline.name-updater-queue-capacity=${LP_PIPELINE_NAME_UPDATER_QUEUE_CAPACITY:20}
#select and calc flags
ct.flag-updater.concurrency=${FLAG_UPDATER_CONCURRENCY:10}
ct.flag-updater.queue-capacity=${FLAG_UPDATER_QUEUE_CAPACITY:100}
#select and calc last rejected date
ct.update-rejected-date-cron-expression=${LP_UPDATE_REJECTED_DATE_CRON_EXPRESSION:0 25 18 * * *}
ct.rejected-date-updater.concurrency=${LP_REJECTED_DATE_UPDATER_CONCURRENCY:10}
ct.rejected-date-updater.queue-capacity=${LP_REJECTED_DATE_UPDATER_QUEUE_CAPACITY:100}
#Backoff settings
ct.np-pipeline.error-attempt-count=${NP_PIPELINE_ERROR_ATTEMPT_COUNT:5}
ct.np-pipeline.error-attempt-delay=${NP_PIPELINE_ERROR_ATTEMPT_DELAY:2S}
ct.np-pipeline.retryable-exceptions=${NP_PIPELINE_RETRYABLE_EXCEPTIONS:org.springframework.dao.DataAccessException,org.springframework.dao.DataIntegrityViolationException}
ct.lp-pipeline.error-attempt-count=${LP_PIPELINE_ERROR_ATTEMPT_COUNT:5}
ct.lp-pipeline.error-attempt-delay=${LP_PIPELINE_ERROR_ATTEMPT_DELAY:2S}
ct.lp-pipeline.retryable-exceptions=${LP_PIPELINE_RETRYABLE_EXCEPTIONS:org.springframework.dao.DataAccessException,org.springframework.dao.DataIntegrityViolationException}
#Pipeline global settings
ct.np-pipeline.global-timeout=${NP_PIPELINE_GLOBAL_TIMEOUT:2H}
ct.np-pipeline.parallel-requests=${NP_PIPELINE_PARALLEL_REQUESTS:3}
ct.lp-pipeline.global-timeout=${LP_PIPELINE_GLOBAL_TIMEOUT:2H}
ct.lp-pipeline.parallel-requests=${LP_PIPELINE_PARALLEL_REQUESTS:3}

View file

@ -1,14 +1,16 @@
#commons #commons
APP_VERSION=1.20 APP_VERSION=2.0
REGISTRY_URL=rh8-vm3.nbki.msk:5000 REGISTRY_URL=10.230.238.56:5000
JMX_PORT= JMX_MAIN_PORT=11001
JMX_MONITORING_PORT=11002
JMX_HOSTNAME= JMX_HOSTNAME=
#volumes #volumes
REPORT_DIR=D:\folder_name\signals REPORT_MAIN_DIR=/opt/credit_tracker_docker/main/reports
LOGS_DIR=D:\folder_name\signals\logs\app REPORT_MONITOR_DIR=/opt/credit_tracker_docker/monitor/reports
JSON_LOGS_DIR=D:\folder_name\signals\logs\json LOGS_DIR=/opt/credit_tracker_docker/logs
ACCESS_LOGS_DIR=D:\folder_name\signals\logs\access JSON_LOGS_DIR=/opt/credit_tracker_docker/logs/json
ACCESS_LOGS_DIR=/opt/credit_tracker_docker/logs/access
#APM settings #APM settings
APM_SERVICE_NAME=credit-tracker APM_SERVICE_NAME=credit-tracker
@ -60,6 +62,28 @@ MAX_LIFETIME_SCORING=1800000
KEEPALIVE_TIME_SCORING=0 KEEPALIVE_TIME_SCORING=0
LEAK_DETECTION_THRESHOLD_SCORING=0 LEAK_DETECTION_THRESHOLD_SCORING=0
# nbch
DB_URL_NBCH=jdbc:db2://10.230.227.102:2668/cprosd22:currentSchema=NBCH;
USERNAME_NBCH=ros9appl
PASSWORD_NBCH=R0s9Appl
MAXIMUM_POOL_SIZE_NBCH=30
MINIMUM_IDLE_NBCH=10
CONNECTION_TIMEOUT_NBCH=10000
VALIDATION_TIMEOUT_NBCH=5000
IDLE_TIMEOUT_NBCH=600000
MAX_LIFETIME_NBCH=1800000
KEEPALIVE_TIME_NBCH=0
LEAK_DETECTION_THRESHOLD_NBCH=0
#kafka consumer
CONSUMER_TOPIC=my-topic
CONSUMER_CONCURENCY=8
CONSUMER_BOOTSTRAP_SERVERS=localhost:29092
CONSUMER_GROUP_ID=credit-tracker
CONSUMER_AUTO_OFFSET_RESET=latest
CONSUMER_MAX_POLL_RECORDS=125
CONSUMER_MAX_POLL_INTERVAL_MS=300000
# размер в mb # размер в mb
ACCEPTED_XML_SIZE=1024 ACCEPTED_XML_SIZE=1024
ACCEPTED_ARCHIVE_SIZE=2048MB ACCEPTED_ARCHIVE_SIZE=2048MB
@ -71,7 +95,46 @@ SIGNALS_USER=0001ZZ000001
# min # min
MONITORING_INTERVAL=15 MONITORING_INTERVAL=15
# days # days
UPDATE_FLAGS_INTERVAL=1
DAYS_TO_UPDATE_FLAGS=30 DAYS_TO_UPDATE_FLAGS=30
UPDATE_FLAGS_CRON_EXPRESSION=0 25 18 * * *
NEW_FLAGS_CRON_EXPRESSION=0 25 18 * * *
UPDATE_FIDS_CRON_EXPRESSION=0 10 12 * * *
DAYS_TO_UPDATE_FIDS=30
# общие настройки
FLAG_UPDATER_CONCURRENCY=10
FLAG_UPDATER_QUEUE_CAPACITY=100
# Физ лица
NP_SAVER_SUBJECT_BATCH_SIZE=15000
NP_PIPELINE_SUBJECT_BATCH_SIZE=10000
NP_PIPELINE_NAME_ENRICHER_STAGE_CONCURRENCY=20
NP_PIPELINE_NAME_ENRICHER_QUEUE_CAPACITY=20
NP_PIPELINE_MERGER_QUEUE_CAPACITY=50
NP_PIPELINE_NAME_UPDATER_STAGE_CONCURRENCY=20
NP_PIPELINE_NAME_UPDATER_QUEUE_CAPACITY=20
NP_PIPELINE_ERROR_ATTEMPT_COUNT=5
NP_PIPELINE_ERROR_ATTEMPT_DELAY=2S
NP_PIPELINE_RETRYABLE_EXCEPTIONS=org.springframework.dao.DataAccessException,org.springframework.dao.DataIntegrityViolationException}
NP_PIPELINE_GLOBAL_TIMEOUT=2H
NP_PIPELINE_PARALLEL_REQUESTS=3
# Юр. Лица
LP_SAVER_SUBJECT_BATCH_SIZE=15000
LP_PIPELINE_SUBJECT_BATCH_SIZE=10000
LP_PIPELINE_NAME_ENRICHER_STAGE_CONCURRENCY=20
LP_PIPELINE_NAME_ENRICHER_QUEUE_CAPACITY=20
LP_PIPELINE_MERGER_QUEUE_CAPACITY=50
LP_PIPELINE_NAME_UPDATER_STAGE_CONCURRENCY=20
LP_PIPELINE_NAME_UPDATER_QUEUE_CAPACITY=20
LP_PIPELINE_ERROR_ATTEMPT_COUNT=5
LP_PIPELINE_ERROR_ATTEMPT_DELAY=2S
LP_PIPELINE_RETRYABLE_EXCEPTIONS=org.springframework.dao.DataAccessException,org.springframework.dao.DataIntegrityViolationException}
LP_PIPELINE_GLOBAL_TIMEOUT=2H
LP_PIPELINE_PARALLEL_REQUESTS=3
LP_UPDATE_REJECTED_DATE_CRON_EXPRESSION=
LP_REJECTED_DATE_UPDATER_CONCURRENCY=10
LP_REJECTED_DATE_UPDATER_QUEUE_CAPACITY=100

View file

@ -6,18 +6,23 @@ services:
volumes: volumes:
- ${LOGS_DIR}:/opt/app/logs/app - ${LOGS_DIR}:/opt/app/logs/app
- ${JSON_LOGS_DIR}:/opt/app/logs/json - ${JSON_LOGS_DIR}:/opt/app/logs/json
- ${REPORT_DIR}:/opt/app/reports - shared_data:/opt/app/reports
- ${REPORT_MAIN_DIR}:/opt/app/reports
- ${ACCESS_LOGS_DIR}:/opt/app/logs/access - ${ACCESS_LOGS_DIR}:/opt/app/logs/access
ports: ports:
- "${HOST_PORT}:${SERVER_PORT}" - "${HOST_PORT}:${SERVER_PORT}"
environment: environment:
- APP_JAR=main
- XMX=4G
- XMS=1G
- TZ=Europe/Moscow - TZ=Europe/Moscow
- APM_SERVICE_NAME=${APM_SERVICE_NAME} - ${JVM_EXTRA_OPTS:-} \
- APM_SERVICE_NAME=${APM_SERVICE_NAME}-${APP_JAR}
- APM_SERVER_URLS=${APM_SERVER_URLS} - APM_SERVER_URLS=${APM_SERVER_URLS}
- APM_ENVIRONMENT=${APM_ENVIRONMENT} - APM_ENVIRONMENT=${APM_ENVIRONMENT}
- APM_SERVICE_NODE_NAME=${APM_SERVICE_NODE_NAME} - APM_SERVICE_NODE_NAME=${APM_SERVICE_NODE_NAME}
- APM_SERVICE_VERSION=${APP_VERSION} - APM_SERVICE_VERSION=${APP_VERSION}
- JMX_PORT=${JMX_PORT} - JMX_PORT=${JMX_MAIN_PORT}
- JMX_HOSTNAME=${JMX_HOSTNAME} - JMX_HOSTNAME=${JMX_HOSTNAME}
- SERVER_PORT=${SERVER_PORT} - SERVER_PORT=${SERVER_PORT}
- DB_URL_INDIC=${DB_URL_INDIC} - DB_URL_INDIC=${DB_URL_INDIC}
@ -53,11 +58,139 @@ services:
- MAX_LIFETIME_SCORING=${MAX_LIFETIME_SCORING} - MAX_LIFETIME_SCORING=${MAX_LIFETIME_SCORING}
- KEEPALIVE_TIME_SCORING=${KEEPALIVE_TIME_SCORING} - KEEPALIVE_TIME_SCORING=${KEEPALIVE_TIME_SCORING}
- LEAK_DETECTION_THRESHOLD_SCORING=${LEAK_DETECTION_THRESHOLD_SCORING} - LEAK_DETECTION_THRESHOLD_SCORING=${LEAK_DETECTION_THRESHOLD_SCORING}
- DB_URL_NBCH=${DB_URL_NBCH}
- USERNAME_NBCH=${USERNAME_NBCH}
- PASSWORD_NBCH=${PASSWORD_NBCH}
- MAXIMUM_POOL_SIZE_NBCH=${MAXIMUM_POOL_SIZE_NBCH}
- MINIMUM_IDLE_NBCH=${MINIMUM_IDLE_NBCH}
- CONNECTION_TIMEOUT_NBCH=${CONNECTION_TIMEOUT_NBCH}
- VALIDATION_TIMEOUT_NBCH=${VALIDATION_TIMEOUT_NBCH}
- IDLE_TIMEOUT_NBCH=${IDLE_TIMEOUT_NBCH}
- MAX_LIFETIME_NBCH=${MAX_LIFETIME_NBCH}
- KEEPALIVE_TIME_NBCH=${KEEPALIVE_TIME_NBCH}
- LEAK_DETECTION_THRESHOLD_NBCH=${LEAK_DETECTION_THRESHOLD_NBCH}
- CONSUMER_TOPIC=${CONSUMER_TOPIC}
- CONSUMER_CONCURENCY=${CONSUMER_CONCURENCY}
- CONSUMER_BOOTSTRAP_SERVERS=${CONSUMER_BOOTSTRAP_SERVERS}
- CONSUMER_GROUP_ID=${CONSUMER_GROUP_ID}
- CONSUMER_AUTO_OFFSET_RESET=${CONSUMER_AUTO_OFFSET_RESET}
- CONSUMER_MAX_POLL_RECORDS=${CONSUMER_MAX_POLL_RECORDS}
- CONSUMER_MAX_POLL_INTERVAL_MS=${CONSUMER_MAX_POLL_INTERVAL_MS}
- ACCEPTED_XML_SIZE=${ACCEPTED_XML_SIZE} - ACCEPTED_XML_SIZE=${ACCEPTED_XML_SIZE}
- ACCEPTED_ARCHIVE_SIZE=${ACCEPTED_ARCHIVE_SIZE} - ACCEPTED_ARCHIVE_SIZE=${ACCEPTED_ARCHIVE_SIZE}
- SIGNALS_ROOT_URI=${SIGNALS_ROOT_URI} - SIGNALS_ROOT_URI=${SIGNALS_ROOT_URI}
- SIGNALS_TOKEN=${SIGNALS_TOKEN} - SIGNALS_TOKEN=${SIGNALS_TOKEN}
- SIGNALS_USER=${SIGNALS_USER} - SIGNALS_USER=${SIGNALS_USER}
- MONITORING_INTERVAL=${MONITORING_INTERVAL} - MONITORING_INTERVAL=${MONITORING_INTERVAL}
- UPDATE_FLAGS_INTERVAL=${UPDATE_FLAGS_INTERVAL} - UPDATE_FLAGS_CRON_EXPRESSION=${UPDATE_FLAGS_CRON_EXPRESSION}
- NEW_FLAGS_CRON_EXPRESSION=${NEW_FLAGS_CRON_EXPRESSION}
- UPDATE_FIDS_CRON_EXPRESSION=${UPDATE_FIDS_CRON_EXPRESSION}
- DAYS_TO_UPDATE_FIDS=${DAYS_TO_UPDATE_FIDS}
- DAYS_TO_UPDATE_FLAGS=${DAYS_TO_UPDATE_FLAGS} - DAYS_TO_UPDATE_FLAGS=${DAYS_TO_UPDATE_FLAGS}
- FLAG_UPDATER_CONCURRENCY=${FLAG_UPDATER_CONCURRENCY}
- FLAG_UPDATER_QUEUE_CAPACITY=${FLAG_UPDATER_QUEUE_CAPACITY}
- NP_SAVER_SUBJECT_BATCH_SIZE=${NP_SAVER_SUBJECT_BATCH_SIZE}
- NP_PIPELINE_SUBJECT_BATCH_SIZE=${NP_PIPELINE_SUBJECT_BATCH_SIZE}
- NP_PIPELINE_NAME_ENRICHER_STAGE_CONCURRENCY=${NP_PIPELINE_NAME_ENRICHER_STAGE_CONCURRENCY}
- NP_PIPELINE_NAME_ENRICHER_QUEUE_CAPACITY=${NP_PIPELINE_NAME_ENRICHER_QUEUE_CAPACITY}
- NP_PIPELINE_MERGER_QUEUE_CAPACITY=${NP_PIPELINE_MERGER_QUEUE_CAPACITY}
- NP_PIPELINE_NAME_UPDATER_STAGE_CONCURRENCY=${NP_PIPELINE_NAME_UPDATER_STAGE_CONCURRENCY}
- NP_PIPELINE_NAME_UPDATER_QUEUE_CAPACITY=${NP_PIPELINE_NAME_UPDATER_QUEUE_CAPACITY}
- NP_PIPELINE_ERROR_ATTEMPT_COUNT=${NP_PIPELINE_ERROR_ATTEMPT_COUNT}
- NP_PIPELINE_ERROR_ATTEMPT_DELAY=${NP_PIPELINE_ERROR_ATTEMPT_DELAY}
- NP_PIPELINE_RETRYABLE_EXCEPTIONS=${NP_PIPELINE_RETRYABLE_EXCEPTIONS}
- NP_PIPELINE_GLOBAL_TIMEOUT=${NP_PIPELINE_GLOBAL_TIMEOUT}
- NP_PIPELINE_PARALLEL_REQUESTS=${NP_PIPELINE_PARALLEL_REQUESTS}
- LP_SAVER_SUBJECT_BATCH_SIZE=${LP_SAVER_SUBJECT_BATCH_SIZE}
- LP_PIPELINE_SUBJECT_BATCH_SIZE=${LP_PIPELINE_SUBJECT_BATCH_SIZE}
- LP_PIPELINE_NAME_ENRICHER_STAGE_CONCURRENCY=${LP_PIPELINE_NAME_ENRICHER_STAGE_CONCURRENCY}
- LP_PIPELINE_NAME_ENRICHER_QUEUE_CAPACITY=${LP_PIPELINE_NAME_ENRICHER_QUEUE_CAPACITY}
- LP_PIPELINE_MERGER_QUEUE_CAPACITY=${LP_PIPELINE_MERGER_QUEUE_CAPACITY}
- LP_PIPELINE_NAME_UPDATER_STAGE_CONCURRENCY=${LP_PIPELINE_NAME_UPDATER_STAGE_CONCURRENCY}
- LP_PIPELINE_NAME_UPDATER_QUEUE_CAPACITY=${LP_PIPELINE_NAME_UPDATER_QUEUE_CAPACITY}
- LP_PIPELINE_ERROR_ATTEMPT_COUNT=${LP_PIPELINE_ERROR_ATTEMPT_COUNT}
- LP_PIPELINE_ERROR_ATTEMPT_DELAY=${LP_PIPELINE_ERROR_ATTEMPT_DELAY}
- LP_PIPELINE_RETRYABLE_EXCEPTIONS=${LP_PIPELINE_RETRYABLE_EXCEPTIONS}
- LP_PIPELINE_GLOBAL_TIMEOUT=${LP_PIPELINE_GLOBAL_TIMEOUT}
- LP_PIPELINE_PARALLEL_REQUESTS=${LP_PIPELINE_PARALLEL_REQUESTS}
command: ["/opt/app/start-credit-tracker.sh"]
credit-tracker-monitoring:
image: ${REGISTRY_URL}/credit-tracker:${APP_VERSION}
container_name: credit-tracker-monitoring
restart: always
volumes:
- shared_data:/opt/app/reports
- ${LOGS_DIR}:/opt/app/logs/app
- ${JSON_LOGS_DIR}:/opt/app/logs/json
environment:
- APP_JAR=monitoring
- TZ=Europe/Moscow
- XMX=4G
- XMS=1G
- APM_SERVICE_NAME=${APM_SERVICE_NAME}-${APP_JAR}
- 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_MONITORING_PORT}
- JMX_HOSTNAME=${JMX_HOSTNAME}
- 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}
- DB_URL_NBCH=${DB_URL_NBCH}
- USERNAME_NBCH=${USERNAME_NBCH}
- PASSWORD_NBCH=${PASSWORD_NBCH}
- MAXIMUM_POOL_SIZE_NBCH=${MAXIMUM_POOL_SIZE_NBCH}
- MINIMUM_IDLE_NBCH=${MINIMUM_IDLE_NBCH}
- CONNECTION_TIMEOUT_NBCH=${CONNECTION_TIMEOUT_NBCH}
- VALIDATION_TIMEOUT_NBCH=${VALIDATION_TIMEOUT_NBCH}
- IDLE_TIMEOUT_NBCH=${IDLE_TIMEOUT_NBCH}
- MAX_LIFETIME_NBCH=${MAX_LIFETIME_NBCH}
- KEEPALIVE_TIME_NBCH=${KEEPALIVE_TIME_NBCH}
- LEAK_DETECTION_THRESHOLD_NBCH=${LEAK_DETECTION_THRESHOLD_NBCH}
- CONSUMER_TOPIC=${CONSUMER_TOPIC}
- CONSUMER_CONCURENCY=${CONSUMER_CONCURENCY}
- CONSUMER_BOOTSTRAP_SERVERS=${CONSUMER_BOOTSTRAP_SERVERS}
- CONSUMER_GROUP_ID=${CONSUMER_GROUP_ID}
- CONSUMER_AUTO_OFFSET_RESET=${CONSUMER_AUTO_OFFSET_RESET}
- CONSUMER_MAX_POLL_RECORDS=${CONSUMER_MAX_POLL_RECORDS}
- CONSUMER_MAX_POLL_INTERVAL_MS=${CONSUMER_MAX_POLL_INTERVAL_MS}
- SIGNALS_ROOT_URI=${SIGNALS_ROOT_URI}
- SIGNALS_TOKEN=${SIGNALS_TOKEN}
- SIGNALS_USER=${SIGNALS_USER}
- MONITORING_INTERVAL=${MONITORING_INTERVAL}
command: ["/opt/app/start-credit-tracker.sh"]
volumes:
shared_data:

106
pom.xml
View file

@ -10,16 +10,18 @@
</parent> </parent>
<groupId>ru.nbch</groupId> <groupId>ru.nbch</groupId>
<artifactId>credit-tracker</artifactId> <artifactId>credit-tracker</artifactId>
<version>1.20</version> <version>2.21.12</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>21</java.version>
<git.info.prefix>git_info</git.info.prefix> <git.info.prefix>git_info</git.info.prefix>
<git.info.filename>git.properties</git.info.filename> <git.info.filename>git.properties</git.info.filename>
<registry.url>10.230.238.56:5000</registry.url> <registry.url>10.230.238.56:5000</registry.url>
<!-- <registry.url>rh8-vm3.nbki.msk:5000</registry.url>--> <jar.main.name>main</jar.main.name>
<jar.monitoring.name>monitoring</jar.monitoring.name>
<!-- <registry.url>rh8-vm3.nbki.msk:5000</registry.url>-->
</properties> </properties>
<dependencies> <dependencies>
@ -36,11 +38,25 @@
<artifactId>mybatis-spring-boot-starter</artifactId> <artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.4</version> <version>3.0.4</version>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
<version>3.3.15</version>
</dependency>
<dependency>
<groupId>ru.nbch</groupId>
<artifactId>pipeline-engine</artifactId>
<version>1.2</version>
</dependency>
<dependency> <dependency>
<groupId>org.postgresql</groupId> <groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId> <artifactId>postgresql</artifactId>
<scope>runtime</scope> <scope>runtime</scope>
</dependency> </dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
<dependency> <dependency>
<groupId>com.ibm.db2</groupId> <groupId>com.ibm.db2</groupId>
<artifactId>jcc</artifactId> <artifactId>jcc</artifactId>
@ -164,7 +180,13 @@
<files> <files>
<file> <file>
<source> <source>
${project.build.directory}/${project.build.finalName}.jar ${project.build.directory}/${project.build.finalName}-${jar.main.name}.jar
</source>
<outputDirectory>.</outputDirectory>
</file>
<file>
<source>
${project.build.directory}/${project.build.finalName}-${jar.monitoring.name}.jar
</source> </source>
<outputDirectory>.</outputDirectory> <outputDirectory>.</outputDirectory>
</file> </file>
@ -229,14 +251,74 @@
<plugin> <plugin>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId> <artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes> <executions>
<exclude> <execution>
<groupId>org.projectlombok</groupId> <id>repackage</id>
<artifactId>lombok</artifactId> <goals>
</exclude> <goal>repackage</goal>
</excludes> </goals>
</configuration> <configuration>
<skip>true</skip>
</configuration>
</execution>
<execution>
<id>build-common</id>
<phase>package</phase>
<goals>
<goal>repackage</goal>
</goals>
<configuration>
<mainClass>ru.nbch.credit_tracker.service.mode.common.CTMainApplication</mainClass>
<classifier>${jar.main.name}</classifier>
<attach>true</attach>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</execution>
<execution>
<id>build-monitoring</id>
<phase>package</phase>
<goals>
<goal>repackage</goal>
</goals>
<configuration>
<mainClass>ru.nbch.credit_tracker.service.mode.monitoring.CTMonitoringApplication</mainClass>
<classifier>${jar.monitoring.name}</classifier>
<attach>true</attach>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</execution>
<execution>
<id>build-migration</id>
<phase>package</phase>
<goals>
<goal>repackage</goal>
</goals>
<configuration>
<mainClass>ru.nbch.credit_tracker.service.mode.migration.CTMigrationApplication</mainClass>
<classifier>migration</classifier>
<attach>true</attach>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</execution>
</executions>
</plugin> </plugin>
<plugin> <plugin>
<groupId>pl.project13.maven</groupId> <groupId>pl.project13.maven</groupId>

View file

@ -1,23 +0,0 @@
package ru.nbch.credit_tracker;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import ru.nbch.credit_tracker.service.MonitoringService;
import ru.nbch.credit_tracker.service.UpdateProcessingPackagesService;
import ru.nbch.credit_tracker.service.UpdatingFlagsService;
@SpringBootApplication
public class CreditTrackerApplication {
public static void main(String[] args) {
ConfigurableApplicationContext ctx = SpringApplication.run(CreditTrackerApplication.class, args);
UpdateProcessingPackagesService updateProcessingPackagesService = ctx.getBean(UpdateProcessingPackagesService.class);
MonitoringService monitoringService = ctx.getBean(MonitoringService.class);
UpdatingFlagsService updatingFlagsService = ctx.getBean(UpdatingFlagsService.class);
updateProcessingPackagesService.run();
monitoringService.run();
updatingFlagsService.run();
}
}

View file

@ -1,124 +0,0 @@
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,109 @@
package ru.nbch.credit_tracker.component;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import ru.nbch.credit_tracker.config.settings.CreditTrackerSettings;
import ru.nbch.credit_tracker.entities.user_request.monitoring_request.MonitoringRequest;
import ru.nbch.credit_tracker.enums.SignalError;
import ru.nbch.credit_tracker.enums.XmlErrorFormat;
import ru.nbch.credit_tracker.exceptions.SignalException;
import ru.nbch.credit_tracker.model.MonitoringValidationError;
import ru.nbch.credit_tracker.model.ValidationResult;
import ru.nbch.credit_tracker.service.ValidationService;
import ru.nbch.credit_tracker.service.xml.stax.StaxXmlProcessor;
import ru.nbch.credit_tracker.utils.ZipUtils;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import static ru.nbch.credit_tracker.utils.error.ExceptionUtils.clientExcp;
import static ru.nbch.credit_tracker.utils.error.ExceptionUtils.serverExcp;
@Component
@Slf4j
public class MonitoringRequestValidator {
private final CreditTrackerSettings config;
private final ValidationService validationService;
private final StaxXmlProcessor staxXmlProcessor;
public MonitoringRequestValidator(CreditTrackerSettings config,
ValidationService validationService,
StaxXmlProcessor staxXmlProcessor) {
this.config = config;
this.validationService = validationService;
this.staxXmlProcessor = staxXmlProcessor;
}
public void validateZipContent(MultipartFile file) {
log.trace("Validating zip content");
if (file.isEmpty() || !ZipUtils.isZipFile(file)) {
throw clientExcp(SignalError.ERROR_001, XmlErrorFormat.MonitoringError);
}
// Валидация Архива на количетсво файлов, размер и расширение
try (ZipInputStream zipInputStream = new ZipInputStream(file.getInputStream())) {
Optional<ZipEntry> xmlFileEntry = ZipUtils.extractValidEntry(zipInputStream);
if (xmlFileEntry.isEmpty()) {
throw clientExcp(SignalError.ERROR_001, XmlErrorFormat.MonitoringError);
}
long maxXmlFileSize = config.getMaxXmlFileSize() * 1024 * 1024;
if (xmlFileEntry.get().getSize() > maxXmlFileSize) {
throw clientExcp(SignalError.ERROR_009, XmlErrorFormat.MonitoringError);
}
} catch (IOException e) {
throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringError, e);
}
log.trace("Validated zip content ok!");
}
public ValidationResult readAndValidateMonitoringRequest(Path filePath) {
log.debug("Validating monitoring request...");
ValidationResult validationResult = new ValidationResult();
AtomicBoolean subjectWasMeet = new AtomicBoolean(false);
try (BufferedInputStream in = new BufferedInputStream(Files.newInputStream(filePath), 8 * 1024 * 1024)) {
staxXmlProcessor.read(in, MonitoringRequest.class,
request -> {
try {
subjectWasMeet.compareAndSet(false, true);
validationService.validateMonitoringRequest(request);
} catch (SignalException e) {
validationResult.addError(new MonitoringValidationError(-1, "", e));
} finally {
if (request.getPackageName() != null && validationResult.getPackageName() == null) {
validationResult.setPackageName(request.getPackageName());
}
if (request.getUserId() != null && validationResult.getUserId() == null) {
validationResult.setUserId(request.getUserId());
}
if (request.getPassword() != null && validationResult.getPassword() == null) {
validationResult.setPassword(request.getPassword());
}
if (StringUtils.isBlank(request.getSubjectType()) && validationResult.getSubjectType() == null) {
validationResult.setSubjectType(1);
} else if (StringUtils.isNotBlank(request.getSubjectType()) && validationResult.getSubjectType() == null) {
validationResult.setSubjectType(2);
}
if (request.getSignalTypes() != null && !request.getSignalTypes().isEmpty()
&& validationResult.getSignalTypes().isEmpty()) {
validationResult.setSignalTypes(request.getSignalTypes());
}
request.getSubjects().getSubject().clear();
}
},
request -> subjectWasMeet.get() && request.getSubjects().getSubject().isEmpty(),
9010, "/MonitoringRequest/Subjects/Subject");
} catch (IOException e) {
throw new RuntimeException("Error while parsing and validating monitoring request", e);
}
log.debug("Validating monitoring request {}!", validationResult.getValidationErrors().isEmpty() ? "ok" : "fail");
return validationResult;
}
}

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 RepositoryTimingAspect {
@Around("within(@org.springframework.stereotype.Repository *)")
public Object timeMapper(ProceedingJoinPoint pjp) throws Throwable {
long start = System.nanoTime();
try {
return pjp.proceed();
} finally {
long tookMs = (System.nanoTime() - start) / 1_000_000;
String method = pjp.getSignature().toShortString();
if (tookMs > 1000) {
log.warn("Slow jdbc template call {} took {} ms", method, tookMs);
} else {
log.debug("Jdbc template call {} took {} ms", method, tookMs);
}
}
}
}

View file

@ -1,10 +1,12 @@
package ru.nbch.credit_tracker.utils.error; package ru.nbch.credit_tracker.component;
import org.springframework.stereotype.Component;
import ru.nbch.credit_tracker.enums.EnumKeyMessage; import ru.nbch.credit_tracker.enums.EnumKeyMessage;
import java.util.Arrays; import java.util.Arrays;
@Component
public class SimpleMessageResolver { public class SimpleMessageResolver {
public String resolve(EnumKeyMessage errorMessage) { public String resolve(EnumKeyMessage errorMessage) {
if (errorMessage == null) return "null"; if (errorMessage == null) return "null";

View file

@ -0,0 +1,28 @@
package ru.nbch.credit_tracker.component.comparator;
import ru.nbch.credit_tracker.entities.app.UserPackage;
import ru.nbch.credit_tracker.enums.Status;
import java.util.Comparator;
import java.util.function.ToIntFunction;
public class DeletePackageStatusComparator {
public static Comparator<UserPackage> INSTANCE = Comparator
.comparingInt((ToIntFunction<UserPackage>) userPackage -> statusToInt(userPackage.getStatus()))
.thenComparingLong(UserPackage::getId);
public static int statusToInt(String str) {
try {
Status status = Status.valueOf(str);
return switch (status) {
case ON_MONITORING -> 1;
case DELETED -> 0;
default -> -1;
};
} catch (Throwable e) {
return -1;
}
}
}

View file

@ -0,0 +1,36 @@
package ru.nbch.credit_tracker.component.comparator;
import ru.nbch.credit_tracker.entities.app.UserPackage;
import ru.nbch.credit_tracker.enums.Status;
import java.util.Comparator;
import java.util.EnumSet;
import java.util.function.ToIntFunction;
public class ReportListPackageStatusComparator {
public static final EnumSet<Status> reportedStatuses = EnumSet.of(
Status.ON_MONITORING
);
public static Comparator<UserPackage> INSTANCE = Comparator
.comparingInt((ToIntFunction<UserPackage>) userPackage -> statusToInt(userPackage.getStatus()))
.thenComparingLong(UserPackage::getId);
/**
* статусы не входящие в {@code reportedStatuses}
* кинут ошибку в <code>ru.nbch.credit_tracker.facade.SignalFacadeV3#getReportList</code>
*/
public static int statusToInt(String str) {
try {
Status status = Status.valueOf(str);
return switch (status) {
case ON_MONITORING -> 1;
default -> -1;
};
} catch (Throwable e) {
return -1;
}
}
}

View file

@ -0,0 +1,20 @@
package ru.nbch.credit_tracker.config;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
@Bean
public ObjectMapper entityToJsonMapper() {
return new ObjectMapper()
.registerModule(new JavaTimeModule())
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
.setSerializationInclusion(JsonInclude.Include.NON_NULL);
}
}

View file

@ -4,6 +4,8 @@ import com.zaxxer.hikari.HikariDataSource;
import org.springframework.boot.jdbc.DataSourceBuilder; import org.springframework.boot.jdbc.DataSourceBuilder;
import ru.nbch.credit_tracker.config.settings.DataSourceSettings; import ru.nbch.credit_tracker.config.settings.DataSourceSettings;
import javax.sql.DataSource;
public class DatabaseConfig { public class DatabaseConfig {
protected HikariDataSource createDataSource(DataSourceSettings settings) { protected HikariDataSource createDataSource(DataSourceSettings settings) {

View file

@ -10,6 +10,7 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import ru.nbch.credit_tracker.config.settings.CreditTrackerSettings; import ru.nbch.credit_tracker.config.settings.CreditTrackerSettings;
import ru.nbch.credit_tracker.config.settings.DataSourceSettings; import ru.nbch.credit_tracker.config.settings.DataSourceSettings;
@ -41,6 +42,7 @@ public class IndicDataSourceConfig extends DatabaseConfig {
dataSource.setMaxLifetime(indicSetings.maxLifetime()); dataSource.setMaxLifetime(indicSetings.maxLifetime());
dataSource.setKeepaliveTime(indicSetings.keepaliveTime()); dataSource.setKeepaliveTime(indicSetings.keepaliveTime());
dataSource.setLeakDetectionThreshold(indicSetings.leakDetectionThreshold()); dataSource.setLeakDetectionThreshold(indicSetings.leakDetectionThreshold());
dataSource.setAutoCommit(false);
try (Connection connection = dataSource.getConnection()) { try (Connection connection = dataSource.getConnection()) {
connection.isValid(3); connection.isValid(3);
@ -60,5 +62,10 @@ public class IndicDataSourceConfig extends DatabaseConfig {
sqlSessionFactoryBean.setTypeHandlersPackage("ru.nbch.credit_tracker.config.datasource.handlers"); sqlSessionFactoryBean.setTypeHandlersPackage("ru.nbch.credit_tracker.config.datasource.handlers");
return sqlSessionFactoryBean.getObject(); return sqlSessionFactoryBean.getObject();
} }
@Bean(name = "jdbcTemplateIndic")
public NamedParameterJdbcTemplate getNamedParameterJdbcTemplate(@Qualifier("indicDataSource") DataSource dataSource) {
return new NamedParameterJdbcTemplate(dataSource);
}
} }

View file

@ -0,0 +1,62 @@
package ru.nbch.credit_tracker.config.datasource;
import com.zaxxer.hikari.HikariDataSource;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import ru.nbch.credit_tracker.config.settings.CreditTrackerSettings;
import ru.nbch.credit_tracker.config.settings.DataSourceSettings;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
@Slf4j
@Configuration
@MapperScan(
basePackages = "ru.nbch.credit_tracker.mapper.nbch",
sqlSessionFactoryRef = "sqlSessionNbch"
)
public class NbchDataSourceConfig extends DatabaseConfig {
@Value("classpath:mybatis/mapper/nbch/*.xml")
private Resource[] resources;
@Bean(name = "nbchDataSource")
public DataSource dataSource(CreditTrackerSettings config) throws SQLException {
DataSourceSettings signalSetings = config.getNbchDataSourceSettings();
HikariDataSource dataSource = createDataSource(signalSetings);
dataSource.setMaximumPoolSize(signalSetings.maximumPoolSize());
dataSource.setMinimumIdle(signalSetings.minimumIdle());
dataSource.setConnectionTimeout(signalSetings.connectionTimeout());
dataSource.setValidationTimeout(signalSetings.validationTimeout());
dataSource.setIdleTimeout(signalSetings.idleTimeout());
dataSource.setMaxLifetime(signalSetings.maxLifetime());
dataSource.setKeepaliveTime(signalSetings.keepaliveTime());
dataSource.setLeakDetectionThreshold(signalSetings.leakDetectionThreshold());
try (Connection connection = dataSource.getConnection()) {
connection.isValid(3);
} catch (SQLException e) {
log.error("Unable to connect to NBCH url={}", signalSetings.url(), e);
throw e;
}
log.info("Connected to NBCH: url={}", signalSetings.url());
return dataSource;
}
@Bean(name = "sqlSessionNbch")
public SqlSessionFactory sqlSessionFactory(@Qualifier("nbchDataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
sqlSessionFactoryBean.setMapperLocations(resources);
sqlSessionFactoryBean.setTypeHandlersPackage("ru.nbch.credit_tracker.config.datasource.handlers");
return sqlSessionFactoryBean.getObject();
}
}

View file

@ -0,0 +1,41 @@
package ru.nbch.credit_tracker.config.datasource.handlers;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedTypes;
@MappedTypes(Long.class)
public class LongTypeHandler extends BaseTypeHandler<Long> {
@Override
public void setNonNullParameter(
PreparedStatement ps, int i, Long parameter, JdbcType jdbcType
) throws SQLException {
ps.setString(i, parameter.toString());
}
@Override
public Long getNullableResult(ResultSet rs, String columnName) throws SQLException {
return parse(rs.getString(columnName));
}
@Override
public Long getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
return parse(rs.getString(columnIndex));
}
@Override
public Long getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
return parse(cs.getString(columnIndex));
}
private Long parse(String value) {
if (value == null || value.isBlank()) {
return null;
}
return Long.valueOf(value);
}
}

View file

@ -0,0 +1,53 @@
package ru.nbch.credit_tracker.config.datasource.handlers;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.UUID;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedTypes;
import org.apache.ibatis.type.TypeHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@MappedTypes({UUID.class})
public class UUIDTypeHandler implements TypeHandler<UUID> {
private static final Logger log = LoggerFactory.getLogger(UUIDTypeHandler.class);
@Override
public void setParameter(PreparedStatement ps, int i, UUID parameter, JdbcType jdbcType) throws SQLException {
if (parameter == null) {
ps.setObject(i, null, Types.OTHER);
} else {
ps.setObject(i, parameter.toString(), Types.OTHER);
}
}
@Override
public UUID getResult(ResultSet rs, String columnName) throws SQLException {
return toUUID(rs.getString(columnName));
}
@Override
public UUID getResult(ResultSet rs, int columnIndex) throws SQLException {
return toUUID(rs.getString(columnIndex));
}
@Override
public UUID getResult(CallableStatement cs, int columnIndex) throws SQLException {
return toUUID(cs.getString(columnIndex));
}
private static UUID toUUID(String val) {
if (val != null && !val.isEmpty() && !val.isBlank()) {
try {
return UUID.fromString(val);
} catch (IllegalArgumentException e) {
log.warn("Bad UUID found: {}", val);
}
}
return null;
}
}

View file

@ -0,0 +1,75 @@
package ru.nbch.credit_tracker.config.kafka;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.apache.kafka.clients.consumer.ConsumerConfig;
import org.apache.kafka.common.serialization.StringDeserializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.kafka.annotation.EnableKafka;
import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory;
import org.springframework.kafka.core.ConsumerFactory;
import org.springframework.kafka.core.DefaultKafkaConsumerFactory;
import org.springframework.kafka.listener.ContainerProperties;
import org.springframework.kafka.support.serializer.JsonDeserializer;
import ru.nbch.credit_tracker.config.settings.CreditTrackerSettings;
import ru.nbch.credit_tracker.config.settings.KafkaConsumerSettings;
import ru.nbch.credit_tracker.entities.external_request.kafka.PackageMessage;
import ru.nbch.credit_tracker.model.SignalKafkaEvent;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
@EnableKafka
@Configuration
public class KafkaConfig {
@Bean("signalKafkaEventQueue")
public BlockingQueue<SignalKafkaEvent> signalKafkaEventQueue(CreditTrackerSettings settings) {
return new LinkedBlockingQueue<>();
}
@Bean
public ConcurrentKafkaListenerContainerFactory<String, PackageMessage> kafkaListenerContainerFactory(ConsumerFactory<String, PackageMessage> consumerFactory) {
ConcurrentKafkaListenerContainerFactory<String, PackageMessage> factory =
new ConcurrentKafkaListenerContainerFactory<>();
factory.setConsumerFactory(consumerFactory);
factory.setBatchListener(true);
factory.getContainerProperties().setAckMode(ContainerProperties.AckMode.MANUAL_IMMEDIATE);
return factory;
}
@Bean
//todo надо проверить конфигурацию внимательно
public ConsumerFactory<String, PackageMessage> consumerFactory(CreditTrackerSettings settings) {
KafkaConsumerSettings kafkaSettings = settings.getKafkaConsumerSettings();
Map<String, Object> props = new HashMap<>();
props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaSettings.bootstrapServers());
props.put(ConsumerConfig.GROUP_ID_CONFIG, kafkaSettings.groupId());
props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, kafkaSettings.autoOffsetReset());
props.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, kafkaSettings.maxPollRecords());
props.put(ConsumerConfig.MAX_POLL_INTERVAL_MS_CONFIG, kafkaSettings.maxPollIntervalMs());
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JavaTimeModule());
JsonDeserializer<PackageMessage> jsonDeserializer = new JsonDeserializer<>(
PackageMessage.class,
objectMapper);
jsonDeserializer.addTrustedPackages("*");
jsonDeserializer.setUseTypeHeaders(false);
props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "false");
return new DefaultKafkaConsumerFactory<>(
props,
new StringDeserializer(),
jsonDeserializer
);
}
}

View file

@ -0,0 +1,14 @@
package ru.nbch.credit_tracker.config.mdc;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import ru.nbch.credit_tracker.utils.LogUtil;
// Annotation to only clear the MDC key at method exit
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ClearMdc {
String key() default LogUtil.mdcKey;
}

View file

@ -1,18 +1,27 @@
package ru.nbch.credit_tracker.config.mdc; package ru.nbch.credit_tracker.config.mdc;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Aspect;
import org.slf4j.MDC; import ru.nbch.credit_tracker.entities.app.UserPackage;
import ru.nbch.credit_tracker.entities.dto.UserPackage;
import ru.nbch.credit_tracker.entities.user_request.monitoring_request.MonitoringRequest; import ru.nbch.credit_tracker.entities.user_request.monitoring_request.MonitoringRequest;
import ru.nbch.credit_tracker.utils.LogUtil;
@Slf4j @Slf4j
@Aspect @Aspect
public class MdcAspect { public class MdcAspect {
// For methods that only require clearing the MDC at the end
@Around("@annotation(clearMdc)")
public Object clearMdcAdvice(ProceedingJoinPoint pjp, ClearMdc clearMdc) throws Throwable {
try {
return pjp.proceed();
} finally {
LogUtil.removeMdcLogKey(clearMdc.key());
}
}
// For methods that set an MDC value (from a method argument) and then clear it at the end // For methods that set an MDC value (from a method argument) and then clear it at the end
@Around("@annotation(setAndClearMdc)") @Around("@annotation(setAndClearMdc)")
public Object setAndClearMdcAdvice(ProceedingJoinPoint pjp, SetAndClearMdc setAndClearMdc) throws Throwable { public Object setAndClearMdcAdvice(ProceedingJoinPoint pjp, SetAndClearMdc setAndClearMdc) throws Throwable {
@ -20,30 +29,18 @@ public class MdcAspect {
int index = setAndClearMdc.argIndex(); int index = setAndClearMdc.argIndex();
Object obj = (args != null && args.length > index) ? args[index] : null; Object obj = (args != null && args.length > index) ? args[index] : null;
if (obj instanceof MonitoringRequest request) { if (obj instanceof MonitoringRequest request) {
if (request.getPackageName() != null) { LogUtil.addMdcLogKey(setAndClearMdc.key(), request.getPackageName());
putMdcValue(setAndClearMdc.key(), request.getPackageName());
}
} else if (obj instanceof UserPackage userPackage) { } else if (obj instanceof UserPackage userPackage) {
if (userPackage.getPackageName() != null) { LogUtil.addMdcLogKey(setAndClearMdc.key(), userPackage.getPackageName());
putMdcValue(setAndClearMdc.key(), userPackage.getPackageName());
}
} else if (obj instanceof String packageName) { } else if (obj instanceof String packageName) {
putMdcValue(setAndClearMdc.key(), packageName); LogUtil.addMdcLogKey(setAndClearMdc.key(), packageName);
} else { } else {
log.debug("no packageName found for for {}", pjp.toShortString()); log.debug("no packageName found for for {}", pjp.toShortString());
} }
try { try {
return pjp.proceed(); return pjp.proceed();
} finally { } finally {
MDC.remove(setAndClearMdc.key()); LogUtil.removeMdcLogKey(setAndClearMdc.key());
}
}
private void putMdcValue(String key, String value) {
try {
MDC.put(key, "[" + value + "] ");
} catch (Throwable e) {
log.error("{}", ExceptionUtils.getStackTrace(e));
} }
} }
} }

View file

@ -4,12 +4,13 @@ import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
import ru.nbch.credit_tracker.utils.LogUtil;
// Annotation to set an MDC key based on a method argument and clear it after method execution // Annotation to set an MDC key based on a method argument and clear it after method execution
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD) @Target(ElementType.METHOD)
public @interface SetAndClearMdc { public @interface SetAndClearMdc {
String key() default "ru.nbch.credit_tracker.log.mdc.key"; String key() default LogUtil.mdcKey;
// The index of the method argument to use for the MDC value (defaults to the first argument) // The index of the method argument to use for the MDC value (defaults to the first argument)
int argIndex() default 0; int argIndex() default 0;
} }

View file

@ -1,5 +1,6 @@
package ru.nbch.credit_tracker.config.rest; package ru.nbch.credit_tracker.config.rest;
import java.nio.charset.Charset;
import lombok.extern.slf4j.Slf4j; 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;
@ -8,24 +9,32 @@ 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 org.springframework.web.multipart.MaxUploadSizeExceededException; import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.multipart.MultipartException;
import ru.nbch.credit_tracker.component.SimpleMessageResolver;
import ru.nbch.credit_tracker.config.settings.CreditTrackerSettings; import ru.nbch.credit_tracker.config.settings.CreditTrackerSettings;
import ru.nbch.credit_tracker.entities.user_response.errors.*; import ru.nbch.credit_tracker.entities.user_response.errors.CommonError;
import ru.nbch.credit_tracker.entities.user_response.errors.MonitoringActivePackagesError;
import ru.nbch.credit_tracker.entities.user_response.errors.MonitoringAllPackagesError;
import ru.nbch.credit_tracker.entities.user_response.errors.MonitoringDownloadError;
import ru.nbch.credit_tracker.entities.user_response.errors.MonitoringError;
import ru.nbch.credit_tracker.entities.user_response.errors.MonitoringPackageDeleteError;
import ru.nbch.credit_tracker.entities.user_response.errors.MonitoringReportListError;
import ru.nbch.credit_tracker.enums.SignalError; import ru.nbch.credit_tracker.enums.SignalError;
import ru.nbch.credit_tracker.exceptions.SignalClientException; import ru.nbch.credit_tracker.exceptions.SignalClientException;
import ru.nbch.credit_tracker.exceptions.SignalException; import ru.nbch.credit_tracker.exceptions.SignalException;
import ru.nbch.credit_tracker.exceptions.SignalServerException; import ru.nbch.credit_tracker.exceptions.SignalServerException;
import ru.nbch.credit_tracker.utils.error.SimpleMessageResolver; import ru.nbch.credit_tracker.utils.error.ExceptionUtils;
import java.nio.charset.Charset;
@Slf4j @Slf4j
@RestControllerAdvice @RestControllerAdvice
public class GlobalExceptionHandler { public class GlobalExceptionHandler {
private final SimpleMessageResolver messageResolver = new SimpleMessageResolver(); private final SimpleMessageResolver messageResolver;
private final String encoding; private final String encoding;
public GlobalExceptionHandler(CreditTrackerSettings settings) { public GlobalExceptionHandler(SimpleMessageResolver messageResolver,
CreditTrackerSettings settings) {
this.messageResolver = messageResolver;
this.encoding = settings.getEncoding(); this.encoding = settings.getEncoding();
} }
@ -42,34 +51,47 @@ public class GlobalExceptionHandler {
return new ResponseEntity<>(error, headers, HttpStatus.BAD_REQUEST); return new ResponseEntity<>(error, headers, HttpStatus.BAD_REQUEST);
} }
@ExceptionHandler(value = SignalException.class, produces = MediaType.APPLICATION_XML_VALUE) @ExceptionHandler(value = MultipartException.class, produces = MediaType.APPLICATION_XML_VALUE)
public ResponseEntity<CommonError> handleClientException(SignalException ex) { public ResponseEntity<CommonError> handleSpecialException(MultipartException ex) {
log.warn("Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception " +
"[Request processing failed: {}", ExceptionUtils.getStackTrace(ex));
HttpHeaders headers = new HttpHeaders(); HttpHeaders headers = new HttpHeaders();
headers.setContentType(new MediaType("application", "xml", Charset.forName(encoding))); headers.setContentType(new MediaType("application", "xml", Charset.forName(encoding)));
String errorText = messageResolver.resolve(ex.getErrorMessage()); MonitoringError error = new MonitoringError();
error.setStatus("error");
CommonError commonError = getCommonError(ex); error.setErrorCode(SignalError.ERROR_028.getCode());
commonError.setStatus("error"); error.setErrorText(SignalError.ERROR_028.getKey());
commonError.setErrorCode(ex.getErrorMessage().getSubject().getCode()); return new ResponseEntity<>(error, headers, HttpStatus.BAD_REQUEST);
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 {
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) { @ExceptionHandler(value = SignalClientException.class, produces = MediaType.APPLICATION_XML_VALUE)
public ResponseEntity<CommonError> handleClientException(SignalClientException ex) {
CommonError err = getCommonError(ex);
String packageName = ex.getPackageName();
if(packageName != null ) {
log.warn("Response error: {}. PackageName: {}", err.getErrorText(), packageName);
} else {
log.warn("Response error: {}", err.getErrorText());
}
return new ResponseEntity<>(err, xmlHeaders(), HttpStatus.BAD_REQUEST);
}
@ExceptionHandler(value = SignalServerException.class, produces = MediaType.APPLICATION_XML_VALUE)
public ResponseEntity<CommonError> handleServerException(SignalServerException ex) {
CommonError commonError = getCommonError(ex);
Exception cause = ex.getCause();
log.error(cause.getMessage(), cause);
return new ResponseEntity<>(commonError, xmlHeaders(), HttpStatus.INTERNAL_SERVER_ERROR);
}
private HttpHeaders xmlHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(new MediaType("application", "xml", Charset.forName(encoding)));
return headers;
}
private CommonError getCommonError(SignalException ex) {
CommonError commonError; CommonError commonError;
switch (ex.getErrorFormat()) { switch (ex.getErrorFormat()) {
case MonitoringError -> commonError = new MonitoringError(); case MonitoringError -> commonError = new MonitoringError();
@ -80,6 +102,10 @@ public class GlobalExceptionHandler {
case MonitoringPackageDeleteError -> commonError = new MonitoringPackageDeleteError(); case MonitoringPackageDeleteError -> commonError = new MonitoringPackageDeleteError();
default -> throw new IllegalStateException("Unsupported error format: " + ex.getErrorFormat()); default -> throw new IllegalStateException("Unsupported error format: " + ex.getErrorFormat());
} }
commonError.setStatus("error");
commonError.setErrorCode(ex.getErrorMessage().getSubject().getCode());
String errorText = messageResolver.resolve(ex.getErrorMessage());
commonError.setErrorText(errorText);
return commonError; return commonError;
} }
} }

View file

@ -5,9 +5,11 @@ import org.springframework.http.client.ClientHttpResponse;
import org.springframework.web.client.DefaultResponseErrorHandler; import org.springframework.web.client.DefaultResponseErrorHandler;
import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.HttpServerErrorException; import org.springframework.web.client.HttpServerErrorException;
import ru.nbch.credit_tracker.refactoring.Suspicios;
import java.io.IOException; import java.io.IOException;
@Suspicios("Не используется больше")
@Slf4j @Slf4j
public class ResponseErrorHandler extends DefaultResponseErrorHandler { public class ResponseErrorHandler extends DefaultResponseErrorHandler {
@Override @Override

View file

@ -3,8 +3,6 @@ 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.scheduling.concurrent.CustomizableThreadFactory;
import org.springframework.web.client.RestTemplate;
import ru.nbch.credit_tracker.config.settings.CreditTrackerSettings;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
@ -12,16 +10,9 @@ import java.util.concurrent.Executors;
@Configuration @Configuration
public class RestConfig { public class RestConfig {
@Bean(name = "restTemplate")
public RestTemplate restTemplate(CreditTrackerSettings config) {
RestTemplate restTemplate = new RestTemplate();
restTemplate.setUriTemplateHandler(new RootUriTemplateHandler(config.getSignalsRootUri()));
restTemplate.setErrorHandler(new ResponseErrorHandler());
return restTemplate;
}
@Bean(name = "executorService") @Bean(name = "executorService")
public ExecutorService executorService() { public ExecutorService executorService() {
return Executors.newCachedThreadPool(new CustomizableThreadFactory("package-registration-")); return Executors.newCachedThreadPool(new CustomizableThreadFactory("package-registration-"));
} }
} }

View file

@ -1,10 +1,12 @@
package ru.nbch.credit_tracker.config.rest; package ru.nbch.credit_tracker.config.rest;
import org.springframework.web.util.DefaultUriBuilderFactory; import org.springframework.web.util.DefaultUriBuilderFactory;
import ru.nbch.credit_tracker.refactoring.Suspicios;
import java.net.URI; import java.net.URI;
import java.util.Map; import java.util.Map;
@Suspicios("Не используется больше")
public class RootUriTemplateHandler extends DefaultUriBuilderFactory { public class RootUriTemplateHandler extends DefaultUriBuilderFactory {
private final String rootUri; private final String rootUri;

View file

@ -20,17 +20,28 @@ public class CreditTrackerSettings {
@NestedConfigurationProperty @NestedConfigurationProperty
private DataSourceSettings scoringDataSourceSettings; private DataSourceSettings scoringDataSourceSettings;
@NestedConfigurationProperty @NestedConfigurationProperty
private DataSourceSettings nbchDataSourceSettings;
@NestedConfigurationProperty
private XsdValidation xsdValidation; private XsdValidation xsdValidation;
@NestedConfigurationProperty
private String signalsRootUri; private PipelineSettings npPipeline;
private String signalsToken; @NestedConfigurationProperty
private String signalsuser; private PipelineSettings lpPipeline;
@NestedConfigurationProperty
private FlagUpdaterSettings flagUpdater;
@NestedConfigurationProperty
private RejectedDateUpdaterSettings rejectedDateUpdater;
@NestedConfigurationProperty
private KafkaConsumerSettings kafkaConsumerSettings;
private Long maxXmlFileSize; private Long maxXmlFileSize;
private String loadDir; private String loadDir;
private Long monitoringInterval; private Long monitoringInterval;
private Long updateFlagsInterval; private String updateFlagsCronExpression;
private Long daysToUpdateFlags; private Integer daysToUpdateFlags;
private Long daysToUpdateNpFids;
private Long daysToUpdateLpFids;
private String encoding; private String encoding;
private Integer npSaverSubjectBatchSize;
private Integer lpSaverSubjectBatchSize;
} }

View file

@ -0,0 +1,9 @@
package ru.nbch.credit_tracker.config.settings;
import org.springframework.boot.context.properties.bind.DefaultValue;
public record FlagUpdaterSettings(
@DefaultValue("10") Integer concurrency,
@DefaultValue("100") Integer queueCapacity
) {
}

View file

@ -0,0 +1,11 @@
package ru.nbch.credit_tracker.config.settings;
public record KafkaConsumerSettings(
String topic,
String concurency,
String bootstrapServers,
String groupId,
String autoOffsetReset,
Integer maxPollRecords,
Integer maxPollIntervalMs
) {}

View file

@ -0,0 +1,24 @@
package ru.nbch.credit_tracker.config.settings;
import org.springframework.boot.context.properties.bind.DefaultValue;
import java.time.Duration;
import java.util.List;
//X это количество потоков Save
//тогда: Name должно быть ~3x, Fid должно быть ~2x чтобы они поспевали друг за другом?
public record PipelineSettings(
@DefaultValue("5000") Integer subjectBatchSize,
@DefaultValue("15") Integer nameEnricherStageConcurrency,
@DefaultValue("20") Integer nameEnricherQueueCapacity,
@DefaultValue("15") Integer nameUpdaterStageConcurrency,
@DefaultValue("20") Integer nameUpdaterQueueCapacity,
@DefaultValue("50") Integer mergerQueueCapacity,
@DefaultValue("5") Integer errorAttemptCount,
@DefaultValue("2s") Duration errorAttemptDelay,
@DefaultValue("5m") Duration globalTimeout,
@DefaultValue("3") Integer parallelRequests,
@DefaultValue("org.springframework.dao.DataAccessException")
List<Class<? extends Throwable>> retryableExceptions
) {
}

View file

@ -0,0 +1,9 @@
package ru.nbch.credit_tracker.config.settings;
import org.springframework.boot.context.properties.bind.DefaultValue;
public record RejectedDateUpdaterSettings(
@DefaultValue("10") Integer concurrency,
@DefaultValue("100") Integer queueCapacity
) {
}

View file

@ -0,0 +1,50 @@
package ru.nbch.credit_tracker.config.task;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import ru.nbch.credit_tracker.config.settings.CreditTrackerSettings;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@Configuration
public class ExecutorsConfig {
@Bean(name = "npPipelineExecutorService",
destroyMethod = "shutdown")
public ExecutorService npPipelineExecutorService() {
return Executors.newFixedThreadPool(15, r -> {
Thread t = new Thread(r);
t.setName("np-pipe-starter-%d".formatted(t.threadId()));
return t;
});
}
@Bean(name = "lpPipelineExecutorService",
destroyMethod = "shutdown")
public ExecutorService lpPipelineExecutorService() {
return Executors.newFixedThreadPool(15, r -> {
Thread t = new Thread(r);
t.setName("lp-pipe-starter-%d".formatted(t.threadId()));
return t;
});
}
@Bean("npSaverExecutorService")
public ExecutorService npSaverExecutorService(CreditTrackerSettings creditTrackerSettings) {
return Executors.newFixedThreadPool(creditTrackerSettings.getNpPipeline().parallelRequests(), r -> {
Thread t = new Thread(r);
t.setName("np-subject-saver-%d".formatted(t.threadId()));
return t;
});
}
@Bean("lpSaverExecutorService")
public ExecutorService lpSaverExecutorService(CreditTrackerSettings creditTrackerSettings) {
return Executors.newFixedThreadPool(creditTrackerSettings.getLpPipeline().parallelRequests(), r -> {
Thread t = new Thread(r);
t.setName("lp-subject-saver-%d".formatted(t.threadId()));
return t;
});
}
}

View file

@ -0,0 +1,77 @@
package ru.nbch.credit_tracker.config.task;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import ru.nbch.credit_tracker.config.settings.CreditTrackerSettings;
import ru.nbch.credit_tracker.mapper.signal.LegalsMapMapper;
import ru.nbch.credit_tracker.mapper.signal.PhonesMapMapper;
import ru.nbch.credit_tracker.mapper.signal.UserPackageMapper;
import ru.nbch.credit_tracker.model.impl.EmptyTerminatePayload;
import ru.nbch.credit_tracker.model.impl.SignalPayload;
import ru.nbch.credit_tracker.model.impl.SubjectPayload;
import ru.nbch.credit_tracker.model.impl.VoidPayload;
import ru.nbch.credit_tracker.service.signal.PackageRecordsService;
import ru.nbch.credit_tracker.service.task.SetupParam;
import ru.nbch.credit_tracker.service.task.impl.*;
import ru.nbch.pipeline.engine.core.PipelineBackoff;
import ru.nbch.pipeline.engine.core.setup.IPipeLineStarter;
import ru.nbch.pipeline.engine.core.setup.IPipeLineTerminator;
import java.util.function.Function;
import java.util.function.Supplier;
@Configuration
public class PipelineStarterConfig {
@Bean
public Function<SetupParam, IPipeLineStarter<SubjectPayload>> pipelineStarterFactory(CreditTrackerSettings creditTrackerSettings,
UserPackageMapper userPackageMapper,
PackageRecordsService packageRecordsService,
PhonesMapMapper phonesMapMapper,
LegalsMapMapper legalsMapMapper) {
return (setupParam) -> {
if (setupParam.subjectType() == 1) {
return switch (setupParam.mode()) {
case DEFAULT, RESEND ->
new NpKickPipeLineDefaultImpl(setupParam, userPackageMapper, packageRecordsService, creditTrackerSettings);
case RECALC_FID ->
new NpKickRecalcPipeLine(setupParam, userPackageMapper, phonesMapMapper, packageRecordsService, creditTrackerSettings);
};
} else {
return switch (setupParam.mode()) {
case DEFAULT, RESEND ->
new LpKickPipelineDefaultImpl(setupParam, userPackageMapper, packageRecordsService, creditTrackerSettings);
case RECALC_FID ->
new LpKickRecalcPipeLine(setupParam, userPackageMapper, legalsMapMapper, packageRecordsService, creditTrackerSettings);
};
}
};
}
@Bean
public Supplier<IPipeLineTerminator<SubjectPayload, SignalPayload>> pipelineTerminatorFactory(CreditTrackerSettings creditTrackerSettings) {
return () -> new SetupMonitoringPipelineTerminator(creditTrackerSettings);
}
@Bean
public Supplier<IPipeLineTerminator<VoidPayload, EmptyTerminatePayload>> noOpTerminatorFactory() {
return NoOpTerminator::new;
}
@Bean
public PipelineBackoff npPipelineBackoff(CreditTrackerSettings creditTrackerSettings) {
return new PipelineBackoff(
creditTrackerSettings.getNpPipeline().errorAttemptCount(),
creditTrackerSettings.getNpPipeline().errorAttemptDelay(),
creditTrackerSettings.getNpPipeline().retryableExceptions()
);
}
@Bean
public PipelineBackoff lpPipelineBackoff(CreditTrackerSettings creditTrackerSettings) {
return new PipelineBackoff(
creditTrackerSettings.getLpPipeline().errorAttemptCount(),
creditTrackerSettings.getLpPipeline().errorAttemptDelay(),
creditTrackerSettings.getLpPipeline().retryableExceptions()
);
}
}

View file

@ -0,0 +1,54 @@
package ru.nbch.credit_tracker.config.task;
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.model.impl.SubjectPayload;
import ru.nbch.credit_tracker.model.impl.VoidPayload;
@Configuration
public class StageConfig {
// NP
@Bean("npFidEnricherQueueFactory")
public StageQueueFactory<SubjectPayload> npFidEnricherQueueFactory(CreditTrackerSettings creditTrackerSettings) {
return new StageQueueFactory<>(creditTrackerSettings.getNpPipeline().nameEnricherQueueCapacity());
}
@Bean("npFidUpdaterQueueFactoryUpdater")
public StageQueueFactory<SubjectPayload> npFidUpdaterQueueFactoryUpdater(CreditTrackerSettings creditTrackerSettings) {
return new StageQueueFactory<>(creditTrackerSettings.getNpPipeline().nameUpdaterQueueCapacity());
}
@Bean("npNoOpQueueFactory")
public StageQueueFactory<VoidPayload> npNoOpQueueFactory(CreditTrackerSettings creditTrackerSettings) {
return new StageQueueFactory<>(creditTrackerSettings.getNpPipeline().mergerQueueCapacity());
}
@Bean("npFinishSetupQueueFactory")
public StageQueueFactory<SubjectPayload> npFinishSetupQueueFactory(CreditTrackerSettings creditTrackerSettings) {
return new StageQueueFactory<>(creditTrackerSettings.getNpPipeline().mergerQueueCapacity());
}
// LP
@Bean("lpFidEnricherQueueFactory")
public StageQueueFactory<SubjectPayload> lpFidEnricherQueueFactory(CreditTrackerSettings creditTrackerSettings) {
return new StageQueueFactory<>(creditTrackerSettings.getLpPipeline().nameEnricherQueueCapacity());
}
@Bean("lpFidUpdaterQueueFactory")
public StageQueueFactory<SubjectPayload> lpFidUpdaterQueueFactory(CreditTrackerSettings creditTrackerSettings) {
return new StageQueueFactory<>(creditTrackerSettings.getLpPipeline().nameUpdaterQueueCapacity());
}
@Bean("lpNoOpQueueFactory")
public StageQueueFactory<VoidPayload> lpNoOpQueueFactory(CreditTrackerSettings creditTrackerSettings) {
return new StageQueueFactory<>(creditTrackerSettings.getLpPipeline().mergerQueueCapacity());
}
@Bean("lpFinishSetupQueueFactory")
public StageQueueFactory<SubjectPayload> lpFinishSetupQueueFactory(CreditTrackerSettings creditTrackerSettings) {
return new StageQueueFactory<>(creditTrackerSettings.getLpPipeline().mergerQueueCapacity());
}
}

View file

@ -0,0 +1,34 @@
package ru.nbch.credit_tracker.config.task;
import org.springframework.beans.factory.FactoryBean;
import ru.nbch.pipeline.engine.core.StagePayload;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
public class StageQueueFactory<T extends StagePayload> implements FactoryBean<BlockingQueue<T>> {
private final int capacity;
public StageQueueFactory(int capacity) {
this.capacity = capacity;
}
public StageQueueFactory() {
this(10);
}
@Override
public BlockingQueue<T> getObject() throws Exception {
return new ArrayBlockingQueue<>(capacity);
}
@Override
public Class<?> getObjectType() {
return BlockingQueue.class;
}
@Override
public boolean isSingleton() {
return false;
}
}

View file

@ -1,6 +1,10 @@
package ru.nbch.credit_tracker.config.xml; package ru.nbch.credit_tracker.config.xml;
import jakarta.xml.bind.JAXBException; import jakarta.xml.bind.JAXBException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Supplier;
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 ru.nbch.credit_tracker.config.settings.CreditTrackerSettings; import ru.nbch.credit_tracker.config.settings.CreditTrackerSettings;
@ -12,12 +16,15 @@ import ru.nbch.credit_tracker.entities.user_response.errors.MonitoringError;
import ru.nbch.credit_tracker.entities.user_response.success.MonitoringSetupResponse; import ru.nbch.credit_tracker.entities.user_response.success.MonitoringSetupResponse;
import ru.nbch.credit_tracker.service.xml.jaxb.IXmlProcessor; import ru.nbch.credit_tracker.service.xml.jaxb.IXmlProcessor;
import ru.nbch.credit_tracker.service.xml.jaxb.XmlProcessorImplAnyClasses; import ru.nbch.credit_tracker.service.xml.jaxb.XmlProcessorImplAnyClasses;
import ru.nbch.credit_tracker.service.xml.stax.*; import ru.nbch.credit_tracker.service.xml.stax.MonitoringReportErrorsStaxtReader;
import ru.nbch.credit_tracker.service.xml.stax.MonitoringRequestStaxReader;
import java.nio.charset.Charset; import ru.nbch.credit_tracker.service.xml.stax.MonitoringSignalResponseStaxXmlReader;
import java.util.ArrayList; import ru.nbch.credit_tracker.service.xml.stax.OnlineDownloadReportPersonsStaxReader;
import java.util.List; import ru.nbch.credit_tracker.service.xml.stax.SgnlReportDeletedStaxReader;
import java.util.function.Supplier; import ru.nbch.credit_tracker.service.xml.stax.SgnlReportStatusStaxReader;
import ru.nbch.credit_tracker.service.xml.stax.StaxReader;
import ru.nbch.credit_tracker.service.xml.stax.StaxXmlProcessor;
import ru.nbch.credit_tracker.service.xml.stax.XmlProcessorStaxImpl;
@Configuration @Configuration
public class XmlConfig { public class XmlConfig {
@ -39,6 +46,7 @@ public class XmlConfig {
public StaxXmlProcessor staxXmlProcessor() { public StaxXmlProcessor staxXmlProcessor() {
List<Supplier<StaxReader<?>>> staxReader = new ArrayList<>(); List<Supplier<StaxReader<?>>> staxReader = new ArrayList<>();
staxReader.add(SgnlReportStatusStaxReader::new); staxReader.add(SgnlReportStatusStaxReader::new);
staxReader.add(SgnlReportDeletedStaxReader::new);
staxReader.add(MonitoringRequestStaxReader::new); staxReader.add(MonitoringRequestStaxReader::new);
staxReader.add(OnlineDownloadReportPersonsStaxReader::new); staxReader.add(OnlineDownloadReportPersonsStaxReader::new);
staxReader.add(MonitoringSignalResponseStaxXmlReader::new); staxReader.add(MonitoringSignalResponseStaxXmlReader::new);

View file

@ -5,6 +5,7 @@ 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.config.mdc.ClearMdc;
import ru.nbch.credit_tracker.entities.user_request.active_packages.MonitoringActivePackagesRequest; import ru.nbch.credit_tracker.entities.user_request.active_packages.MonitoringActivePackagesRequest;
import ru.nbch.credit_tracker.entities.user_request.all_packages.MonitoringAllPackagesRequest; import ru.nbch.credit_tracker.entities.user_request.all_packages.MonitoringAllPackagesRequest;
import ru.nbch.credit_tracker.entities.user_response.active_packages.MonitoringActivePackagesResponse; import ru.nbch.credit_tracker.entities.user_response.active_packages.MonitoringActivePackagesResponse;
@ -12,8 +13,13 @@ import ru.nbch.credit_tracker.entities.user_response.all_packages.MonitoringAllP
import ru.nbch.credit_tracker.entities.user_response.success.MonitoringPackageDeleteResponse; import ru.nbch.credit_tracker.entities.user_response.success.MonitoringPackageDeleteResponse;
import ru.nbch.credit_tracker.entities.user_response.success.MonitoringReportListResponse; 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.entities.user_response.success.MonitoringSetupResponse;
import ru.nbch.credit_tracker.enums.SignalError;
import ru.nbch.credit_tracker.enums.XmlErrorFormat;
import ru.nbch.credit_tracker.exceptions.SignalException;
import ru.nbch.credit_tracker.facade.SignalFacade; import ru.nbch.credit_tracker.facade.SignalFacade;
import static ru.nbch.credit_tracker.utils.error.ExceptionUtils.serverExcp;
@RestController @RestController
@RequestMapping("/api/v1/signal") @RequestMapping("/api/v1/signal")
public class SignalController { public class SignalController {
@ -25,23 +31,41 @@ public class SignalController {
} }
@PostMapping(value = "/set", produces = MediaType.APPLICATION_XML_VALUE) @PostMapping(value = "/set", produces = MediaType.APPLICATION_XML_VALUE)
@ClearMdc
public ResponseEntity<MonitoringSetupResponse> setupMonitoring(@RequestParam("file") MultipartFile file) { public ResponseEntity<MonitoringSetupResponse> setupMonitoring(@RequestParam("file") MultipartFile file) {
return signalFacade.setupMonitoring(file); signalFacade.setupMonitoringRequest(file);
return ResponseEntity.ok().body(new MonitoringSetupResponse());
} }
@PostMapping(value = "/active_packages", consumes = MediaType.APPLICATION_XML_VALUE, produces = MediaType.APPLICATION_XML_VALUE) @PostMapping(value = "/active_packages", consumes = MediaType.APPLICATION_XML_VALUE, produces = MediaType.APPLICATION_XML_VALUE)
public ResponseEntity<MonitoringActivePackagesResponse> findActivePackages(@RequestBody MonitoringActivePackagesRequest activePackagesRequest) { public ResponseEntity<MonitoringActivePackagesResponse> findActivePackages(@RequestBody MonitoringActivePackagesRequest activePackagesRequest) {
return signalFacade.findActivePackages(activePackagesRequest); try {
return signalFacade.findActivePackages(activePackagesRequest);
} catch (Exception e) {
throwOrWrap(SignalError.ERROR_013, XmlErrorFormat.MonitoringActivePackagesError, e);
return null;
}
} }
@PostMapping(value = "/all_packages", consumes = MediaType.APPLICATION_XML_VALUE, produces = MediaType.APPLICATION_XML_VALUE) @PostMapping(value = "/all_packages", consumes = MediaType.APPLICATION_XML_VALUE, produces = MediaType.APPLICATION_XML_VALUE)
public ResponseEntity<MonitoringAllPackagesResponse> findAllPackages(@RequestBody MonitoringAllPackagesRequest allPackagesRequest) { public ResponseEntity<MonitoringAllPackagesResponse> findAllPackages(@RequestBody MonitoringAllPackagesRequest allPackagesRequest) {
return signalFacade.findAllPackages(allPackagesRequest); try {
return signalFacade.findAllPackages(allPackagesRequest);
} catch (Exception e) {
throwOrWrap(SignalError.ERROR_013, XmlErrorFormat.MonitoringAllPackagesError, e);
return null;
}
} }
@PostMapping(value = "/delete", params = {"username", "packageName"}, produces = MediaType.APPLICATION_XML_VALUE) @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); try {
return signalFacade.deletePackage(username, packageName);
} catch (Exception e) {
throwOrWrap(SignalError.ERROR_017, XmlErrorFormat.MonitoringPackageDeleteError, e);
return null;
}
} }
@GetMapping(value = "/report_list", params = {"username", "packageName"}, produces = MediaType.APPLICATION_XML_VALUE) @GetMapping(value = "/report_list", params = {"username", "packageName"}, produces = MediaType.APPLICATION_XML_VALUE)
@ -54,5 +78,11 @@ public class SignalController {
return signalFacade.downloadFile(file); return signalFacade.downloadFile(file);
} }
private static void throwOrWrap(SignalError error, XmlErrorFormat specific, Exception ex) {
if (ex instanceof SignalException) {
throw (SignalException) ex;
}
throw serverExcp(error, specific, ex);
}
} }

View file

@ -0,0 +1,10 @@
package ru.nbch.credit_tracker.entities.app;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class AppVersion {
private String dbVersion;
}

View file

@ -0,0 +1,29 @@
package ru.nbch.credit_tracker.entities.app;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import java.time.LocalDateTime;
@Getter
@Setter
@Builder
public class Hit {
private Long id;
private Long phone;
private Long packageId;
private String subjectId;
private Integer signalType;
private LocalDateTime responseSentAt;
private String reportFileName;
private Long inn;
private Long regNum;
private String busCategory;
private LocalDateTime inqDt;
private String inqP;
private Integer inqT;
private Long inqA;
private Integer segment;
private LocalDateTime recordedAt;
}

View file

@ -0,0 +1,22 @@
package ru.nbch.credit_tracker.entities.app;
import lombok.*;
import java.time.LocalDateTime;
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class LegalsMap {
private Long id;
private Long inn;
private Long regNum;
private Integer fid;
private LocalDateTime searchedAt;
private Integer flag90days;
private LocalDateTime calculatedAt;
private LocalDateTime lastRejectedDate;
}

View file

@ -0,0 +1,20 @@
package ru.nbch.credit_tracker.entities.app;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class PackagesRecordsMap {
private Long id;
private Long packageId;
private String subjectId;
private Long phone_l;
private String errorText;
}

View file

@ -1,4 +1,4 @@
package ru.nbch.credit_tracker.entities.dto; package ru.nbch.credit_tracker.entities.app;
import lombok.*; import lombok.*;
@ -9,14 +9,12 @@ import java.time.LocalDateTime;
@AllArgsConstructor @AllArgsConstructor
@NoArgsConstructor @NoArgsConstructor
@Builder @Builder
public class PhoneFidMap { public class PhonesMap {
private Long id; private Long id;
private Long packageId;
private String subjectId;
private String phone;
private Integer fid; private Integer fid;
private Long phone;
private Integer flagDebt5000; private Integer flagDebt5000;
private Integer flag90days; private Integer flag90days;
private LocalDateTime searchedAt;
private LocalDateTime calculatedAt; private LocalDateTime calculatedAt;
private String errorText;
} }

View file

@ -0,0 +1,15 @@
package ru.nbch.credit_tracker.entities.app;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class SegmentMap {
private String memberCode;
private Integer segment;
}

View file

@ -0,0 +1,16 @@
package ru.nbch.credit_tracker.entities.app;
import lombok.*;
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class SignalLegalFidMap {
private Long id;
private Long packageId;
private String subjectId;
private Long inn;
private Long regNum;
}

View file

@ -1,21 +1,25 @@
package ru.nbch.credit_tracker.entities.dto; package ru.nbch.credit_tracker.entities.app;
import lombok.Builder; import lombok.*;
import lombok.Getter;
import lombok.Setter;
import java.time.LocalDateTime; import java.time.LocalDateTime;
@Getter @Getter
@Setter @Setter
@Builder @Builder
@NoArgsConstructor
@AllArgsConstructor
public class UserPackage { public class UserPackage {
private Long id; private Long id;
private String userId; private String userId;
private String packageName; private String packageName;
private Integer subjectType;
private String signalTypes;
private LocalDateTime createdAt; private LocalDateTime createdAt;
private String status; private String status;
private LocalDateTime deactivatedAt; private LocalDateTime deactivatedAt;
private String lastDownloadedReport; private String lastDownloadedReport;
private LocalDateTime monitoringAt; private LocalDateTime monitoringAt;
private String rejectStatus;
private String externalPackageName;
} }

View file

@ -1,17 +0,0 @@
package ru.nbch.credit_tracker.entities.dto;
import lombok.Getter;
import lombok.Setter;
import java.time.LocalDateTime;
@Getter
@Setter
public class Hit {
private Long id;
private Long packageId;
private String subjectId;
private Integer signalType;
private LocalDateTime responseSentAt;
private String reportFileName;
}

View file

@ -0,0 +1,36 @@
package ru.nbch.credit_tracker.entities.external_request.kafka;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
/**
* Needed only for test purposes (kafka message generator)
*/
public class InquiryDateSerializer extends JsonSerializer<LocalDateTime> {
@Override
public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
if (value == null) {
gen.writeNull();
return;
}
List<Integer> dateParts = Arrays.asList(
value.getYear(),
value.getMonthValue(),
value.getDayOfMonth(),
value.getHour(),
value.getMinute(),
value.getSecond(),
value.getNano()
);
gen.writeObject(dateParts);
}
}

View file

@ -0,0 +1,39 @@
package ru.nbch.credit_tracker.entities.external_request.kafka;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.time.LocalDateTime;
import java.util.List;
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
@JsonIgnoreProperties(ignoreUnknown = true)
public class PackageMessage {
private Integer fid;
private String memberCode;
@JsonFormat(shape = JsonFormat.Shape.ARRAY)
private LocalDateTime inquiryDate;
private String inquiryPurpose;
private Long inquiryAmount;
private String billingCode;
private Integer inquiryType;
private List<Person> persons;
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
public static class Person {
private String lastName;
private String firstName;
private String middleName;
private List<Integer> birthDate;
}
}

View file

@ -0,0 +1,18 @@
package ru.nbch.credit_tracker.entities.external_response.list_package;
import lombok.Getter;
import lombok.Setter;
@Setter
@Getter
public class SgnlReportDeleted {
private Integer deleteFlag;
public Integer getDeleteFlag() {
return deleteFlag;
}
public void setDeleteFlag(Integer deleteFlag) {
this.deleteFlag = deleteFlag;
}
}

View file

@ -0,0 +1,24 @@
package ru.nbch.credit_tracker.entities.indic;
import java.time.LocalDate;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Id {
private Integer fid;
private String number;
private String serNum;
private String idNum;
private LocalDate issDate;
private String idType;
private String idType2;
private LocalDate fileSinceDate;
}

View file

@ -0,0 +1,17 @@
package ru.nbch.credit_tracker.entities.indic;
import lombok.*;
import java.time.LocalDate;
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Name {
private Integer fid;
private String number;
private LocalDate fileSinceDate;
private LocalDate fileSinceDatePn;
}

View file

@ -0,0 +1,11 @@
package ru.nbch.credit_tracker.entities.indic;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class PaymentsFlag {
private Integer fid;
private Integer flag;
}

View file

@ -0,0 +1,13 @@
package ru.nbch.credit_tracker.entities.indic;
import lombok.Getter;
import lombok.Setter;
import java.time.LocalDate;
@Getter
@Setter
public class RuApplication {
private Integer fid;
private LocalDate rejectedDate;
}

View file

@ -0,0 +1,16 @@
package ru.nbch.credit_tracker.entities.nbch;
import lombok.*;
import java.time.LocalDate;
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Source {
private String memberCode;
private String category;
private LocalDate agrDate;
}

View file

@ -5,6 +5,9 @@ import jakarta.xml.bind.annotation.*;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import java.util.ArrayList;
import java.util.List;
@Setter @Setter
@Getter @Getter
@ -13,6 +16,7 @@ import lombok.Setter;
"userId", "userId",
"password", "password",
"packageName", "packageName",
"subjectType",
"subjects" "subjects"
}) })
@XmlRootElement(name = "MonitoringRequest") @XmlRootElement(name = "MonitoringRequest")
@ -24,8 +28,11 @@ public class MonitoringRequest {
protected String password; protected String password;
@XmlElement(name = "PackageName", required = true) @XmlElement(name = "PackageName", required = true)
protected String packageName; protected String packageName;
@XmlElement(name = "SubjectType", required = false)
protected String subjectType;
@XmlElement(name = "Subjects", required = true) @XmlElement(name = "Subjects", required = true)
protected Subjects subjects; protected Subjects subjects;
protected List<Integer> signalTypes = null;
public Subjects getSubjects() { public Subjects getSubjects() {
if (subjects == null) { if (subjects == null) {
@ -40,6 +47,10 @@ public class MonitoringRequest {
currentSubject = new Subject(); currentSubject = new Subject();
} }
public void createSignalTypes() {
signalTypes = new ArrayList<>();
}
public Subject getCurrentSubject() { public Subject getCurrentSubject() {
return currentSubject; return currentSubject;
} }

View file

@ -14,7 +14,9 @@ import lombok.Setter;
@XmlAccessorType(XmlAccessType.FIELD) @XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "SubjectType", propOrder = { @XmlType(name = "SubjectType", propOrder = {
"id", "id",
"clientId" "clientId",
"inn",
"regNum"
}) })
public class Subject { public class Subject {
@ -24,5 +26,10 @@ public class Subject {
@XmlElement(name = "ClientId", required = true) @XmlElement(name = "ClientId", required = true)
protected String clientId; protected String clientId;
@XmlElement(name = "Inn", required = true)
protected String inn;
@XmlElement(name = "RegNum", required = true)
protected String regNum;
} }

View file

@ -4,4 +4,5 @@ public interface CommonError {
void setStatus(String status); void setStatus(String status);
void setErrorCode(String errorCode); void setErrorCode(String errorCode);
void setErrorText(String errorText); void setErrorText(String errorText);
String getErrorText();
} }

View file

@ -0,0 +1,7 @@
package ru.nbch.credit_tracker.enums;
public enum EventType {
WRITE,
ROTATE,
STOP // используется для остановки
}

View file

@ -0,0 +1,7 @@
package ru.nbch.credit_tracker.enums;
public enum PoisonType {
NONE,
NORMALLY,
EXCEPTIONALLY
}

View file

@ -0,0 +1,17 @@
package ru.nbch.credit_tracker.enums;
public enum RejectedStatus {
OK("0"),
ERROR("1"),;
private final String key;
RejectedStatus(String key) {
this.key = key;
}
public String getKey() {
return key;
}
}

View file

@ -21,7 +21,17 @@ public enum SignalError implements IEnumKeyCode {
ERROR_019("019", "У данного пользователя не найдено ни одного пакета"), ERROR_019("019", "У данного пользователя не найдено ни одного пакета"),
ERROR_020("020", "Пакет {} был отклонён при установке на мониторинг. Отчеты не формируются."), ERROR_020("020", "Пакет {} был отклонён при установке на мониторинг. Отчеты не формируются."),
ERROR_021("021", "Обработка пакета {} завершилась с ошибкой, отчёты недоступны. Обратитесь в техническую поддержку."), ERROR_021("021", "Обработка пакета {} завершилась с ошибкой, отчёты недоступны. Обратитесь в техническую поддержку."),
ERROR_022("022", "Пакет {} снят с мониторинга, отчёты больше недоступны."); ERROR_022("022", "Пакет {} снят с мониторинга, отчёты больше недоступны."),
ERROR_023("023", "Пакет {} находится в обработке, удаление невозможно. Дождитесь его установки на мониторинг и повторите попытку."),
ERROR_024("024", "Пакет {} отклонен или при его установке произошла ошибка. Удаление невозможно."),
ERROR_025("025", "Пакет {} уже был удален ранее, удаление невозможно."),
ERROR_026("026", "Пакет {} обновляется. Удаление невозможно. Дождитесь завершения обновления и повторите попытку."),
ERROR_027("027", "Недопустимое значение в элементе SignalTypes"),
ERROR_028("028", "Некорректный формат запроса: в теле ожидается ZIP-архив"),
ERROR_029("029", "Элемент Inn не соответствует формату XXXXXXXXX. Id: {}"),
ERROR_030("030", "Элемент RegNum не соответствует формату XXXXXXXXXXXXX. Id: {}"),
ERROR_031("031", "Состав Subject не соответствует типу пакета"),
;
private final String code; private final String code;

View file

@ -4,6 +4,7 @@ public enum Status {
ON_MONITORING, // пакет стоит на мониторинге в оф. сервисе ON_MONITORING, // пакет стоит на мониторинге в оф. сервисе
REJECTED, // отклонен на стороне НБКИ REJECTED, // отклонен на стороне НБКИ
PROCESSING, // идет поиск телефона и расчет флагов по пакету PROCESSING, // идет поиск телефона и расчет флагов по пакету
ACCEPTED,
DELETED, // пакет удален DELETED, // пакет удален
ERROR; // произошла ошибка при постановке пакета на мониторинг в оф. сервис ERROR; // произошла ошибка при постановке пакета на мониторинг в оф. сервис
} }

View file

@ -1,21 +1,18 @@
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.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.component.MonitoringRequestValidator;
import ru.nbch.credit_tracker.component.comparator.DeletePackageStatusComparator;
import ru.nbch.credit_tracker.component.comparator.ReportListPackageStatusComparator;
import ru.nbch.credit_tracker.config.settings.CreditTrackerSettings; import ru.nbch.credit_tracker.config.settings.CreditTrackerSettings;
import ru.nbch.credit_tracker.entities.dto.UserPackage; import ru.nbch.credit_tracker.entities.app.UserPackage;
import ru.nbch.credit_tracker.entities.user_request.active_packages.MonitoringActivePackagesRequest; import ru.nbch.credit_tracker.entities.user_request.active_packages.MonitoringActivePackagesRequest;
import ru.nbch.credit_tracker.entities.user_request.all_packages.MonitoringAllPackagesRequest; import ru.nbch.credit_tracker.entities.user_request.all_packages.MonitoringAllPackagesRequest;
import ru.nbch.credit_tracker.entities.user_request.monitoring_request.MonitoringRequest;
import ru.nbch.credit_tracker.entities.user_response.active_packages.MonitoringActivePackagesResponse; import ru.nbch.credit_tracker.entities.user_response.active_packages.MonitoringActivePackagesResponse;
import ru.nbch.credit_tracker.entities.user_response.active_packages.Package; import ru.nbch.credit_tracker.entities.user_response.active_packages.Package;
import ru.nbch.credit_tracker.entities.user_response.active_packages.Packages; import ru.nbch.credit_tracker.entities.user_response.active_packages.Packages;
@ -24,352 +21,281 @@ 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.all_packages.PackagesAll;
import ru.nbch.credit_tracker.entities.user_response.success.MonitoringPackageDeleteResponse; 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.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.enums.XmlErrorFormat; import ru.nbch.credit_tracker.enums.XmlErrorFormat;
import ru.nbch.credit_tracker.exceptions.SignalClientException; import ru.nbch.credit_tracker.exceptions.SignalClientException;
import ru.nbch.credit_tracker.service.MonitoringErrorProcesingService; import ru.nbch.credit_tracker.model.MonitoringValidationError;
import ru.nbch.credit_tracker.service.SignalService; import ru.nbch.credit_tracker.model.ValidationResult;
import ru.nbch.credit_tracker.service.UserPackageService;
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.task.*;
import ru.nbch.credit_tracker.service.xml.stax.StaxXmlProcessor; import ru.nbch.credit_tracker.service.task.impl.PipelineMode;
import ru.nbch.credit_tracker.utils.CTUtil; import ru.nbch.credit_tracker.service.task.impl.Saver;
import ru.nbch.credit_tracker.utils.ValidationService; import ru.nbch.credit_tracker.utils.CtFileUtils;
import ru.nbch.credit_tracker.utils.EnumUtil;
import ru.nbch.credit_tracker.utils.LogUtil;
import ru.nbch.credit_tracker.utils.ZipUtils; import ru.nbch.credit_tracker.utils.ZipUtils;
import ru.nbch.pipeline.engine.core.Pipeline;
import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamException;
import java.io.BufferedInputStream;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.regex.Matcher;
import java.util.Optional; import java.util.regex.Pattern;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import static ru.nbch.credit_tracker.utils.error.ExceptionUtils.clientExcp; import static ru.nbch.credit_tracker.utils.error.ExceptionUtils.clientExcp;
import static ru.nbch.credit_tracker.utils.error.ExceptionUtils.serverExcp; import static ru.nbch.credit_tracker.utils.error.ExceptionUtils.serverExcp;
@Slf4j
@Service @Service
@EnableAspectJAutoProxy(exposeProxy = true) @Slf4j
public class SignalFacade { public class SignalFacade {
private SignalFacade self; private final MonitoringRequestValidator monitoringRequestValidator;
private final CreditTrackerSettings config; private final CreditTrackerSettings config;
private final UserPackageService userPackageService;
private final AuthorizationService authorizationService; private final AuthorizationService authorizationService;
private final SignalService signalService; private final NpPipelinePackageFabric npPipelinePackageFabric;
private final SignalRestService signalRestService; private final LpPipelinePackageFabric lpPipelinePackageFabric;
private final MonitoringErrorProcesingService monitoringErrorProcesingService; private final Saver saver;
private final ValidationService validationService; private final NpPipelinePackageOrchestrator pipelineOrchestrator;
private final StaxXmlProcessor staxXmlProcessor; private final LpPipelinePackageOrchestrator lpPipelinePackageOrchestrator;
private final Pattern DOWNLOAD_FILE_PATTERN = Pattern.compile("^(?<member>[A-Za-z0-9]{6})_(?<package>[A-Za-z0-9_]+)_(?<ts>\\d{4}\\d{2}\\d{2}_\\d{2}\\d{2}\\d{2})(?:\\.xml)?\\.zip$");
private final ExecutorService executor; public SignalFacade(MonitoringRequestValidator monitoringRequestValidator,
private final long maxXmlFileSize; CreditTrackerSettings config,
UserPackageService userPackageService,
public SignalFacade(CreditTrackerSettings config,
AuthorizationService authorizationService, AuthorizationService authorizationService,
SignalService signalService, NpPipelinePackageFabric npPipelinePackageManager,
SignalRestService signalRestService, LpPipelinePackageFabric lpPipelinePackageFabric,
MonitoringErrorProcesingService monitoringErrorProcesingService, Saver saver, NpPipelinePackageOrchestrator pipelineOrchestrator,
ValidationService validationService, LpPipelinePackageOrchestrator lpPipelinePackageOrchestrator) {
StaxXmlProcessor staxXmlProcessor, this.monitoringRequestValidator = monitoringRequestValidator;
@Qualifier("executorService") ExecutorService executor) {
this.config = config; this.config = config;
this.userPackageService = userPackageService;
this.authorizationService = authorizationService; this.authorizationService = authorizationService;
this.signalService = signalService; this.npPipelinePackageFabric = npPipelinePackageManager;
this.signalRestService = signalRestService; this.lpPipelinePackageFabric = lpPipelinePackageFabric;
this.monitoringErrorProcesingService = monitoringErrorProcesingService; this.saver = saver;
this.validationService = validationService; this.pipelineOrchestrator = pipelineOrchestrator;
this.staxXmlProcessor = staxXmlProcessor; this.lpPipelinePackageOrchestrator = lpPipelinePackageOrchestrator;
this.executor = executor;
this.maxXmlFileSize = config.getMaxXmlFileSize() * 1024 * 1024;
} }
public ResponseEntity<MonitoringSetupResponse> setupMonitoring(MultipartFile file) { public void setupMonitoringRequest(MultipartFile file) {
// первичные валидации log.debug("Start processing monitoring request");
if (file.isEmpty() || !isZipFile(file)) { monitoringRequestValidator.validateZipContent(file);
throw clientExcp(SignalError.ERROR_001, XmlErrorFormat.MonitoringError);
}
// Валидация Архива на количетсво файлов, размер и расширение log.trace("Saving monitoring request to tmp file...");
MonitoringRequest monitoringRequest; Path tmpReqFilePath;
Long packageId;
ZipInputStream zipInputStream = null;
String filePath;
try { try {
zipInputStream = new ZipInputStream(file.getInputStream()); String folderPath = config.getLoadDir() + File.separator + "tmp" + File.separator;
Optional<ZipEntry> xmlFileEntry = ZipUtils.extractValidEntry(zipInputStream); tmpReqFilePath = ZipUtils.unzipAndGetFirstEntry(folderPath, file);
if (xmlFileEntry.isEmpty()) { } catch (IOException e) {
throw clientExcp(SignalError.ERROR_001, XmlErrorFormat.MonitoringError);
}
if (xmlFileEntry.get().getSize() > maxXmlFileSize) {
throw clientExcp(SignalError.ERROR_009, XmlErrorFormat.MonitoringError);
}
zipInputStream.close();
zipInputStream = new ZipInputStream(file.getInputStream());
zipInputStream.getNextEntry();
filePath = writeTmpFile(zipInputStream);
} catch (Exception e) {
if (e instanceof SignalClientException) {
throw (SignalClientException) e;
}
throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringError, e); 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.debug("File saved to {}", tmpReqFilePath);
// валидация xml ValidationResult validationResult = null;
try { try {
monitoringRequest = readAndValidateMonitoringRequest(filePath); validationResult = monitoringRequestValidator.readAndValidateMonitoringRequest(tmpReqFilePath);
} catch (Exception e) { if (!validationResult.getValidationErrors().isEmpty()) {
CTUtil.removeFile(filePath); MonitoringValidationError firstError = validationResult.getValidationErrors().getFirst();
userPackageService.rejectPackage(validationResult.getUserId(), validationResult.getPackageName(), validationResult.getSubjectType(), validationResult.getSignalTypes());
Files.delete(tmpReqFilePath);
throw firstError.getSignalException();
}
} catch (RuntimeException e) {
if (e.getCause() instanceof XMLStreamException) { if (e.getCause() instanceof XMLStreamException) {
throw clientExcp(SignalError.ERROR_011, XmlErrorFormat.MonitoringError); throw clientExcp(SignalError.ERROR_011, XmlErrorFormat.MonitoringError);
} }
if (e instanceof SignalClientException) { if (e instanceof SignalClientException) {
throw (SignalClientException) e; throw e;
} }
throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringError, e); throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringError, e);
} catch (IOException e) {
log.warn("Error deleting tmp file", e);
} }
String packageName = validationResult.getPackageName();
Integer subjectType = validationResult.getSubjectType();
String userId = validationResult.getUserId();
String password = validationResult.getPassword();
List<Integer> signalTypes = validationResult.getSignalTypes();
Path parsedFilePath = tmpReqFilePath.resolveSibling(Path.of(userId.substring(0, 6) + "_" + packageName + ".xml"));
try {
Files.move(tmpReqFilePath, parsedFilePath, StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
throw new RuntimeException(e);
}
try { try {
// проверка авторизации // проверка авторизации
AuthorizationService.AuthResult authResult = authorizationService.findAuthInfo(monitoringRequest.getUserId(), monitoringRequest.getPassword()); AuthorizationService.AuthResult authResult = authorizationService.findAuthInfo(userId, password);
if (!authResult.isSuccess()) { if (!authResult.isSuccess()) {
signalService.rejectPackage(monitoringRequest); userPackageService.rejectPackage(userId, packageName, subjectType, signalTypes);
throw clientExcp(authResult.getError(), monitoringRequest.getPackageName(), XmlErrorFormat.MonitoringError); throw clientExcp(authResult.getError(), XmlErrorFormat.MonitoringError);
} }
// проверка на уникальность пакета // проверка на уникальность пакета
if (signalService.isPackageNotUnique(monitoringRequest)) { if (userPackageService.isPackageNotUnique(userId, packageName)) {
signalService.rejectPackage(monitoringRequest); userPackageService.rejectPackage(userId, packageName, subjectType, signalTypes);
throw clientExcp(SignalError.ERROR_007, monitoringRequest.getPackageName(), XmlErrorFormat.MonitoringError); throw clientExcp(SignalError.ERROR_007, XmlErrorFormat.MonitoringError);
} }
packageId = signalService.registerPackage(monitoringRequest); Long packageId = userPackageService.registerPackage(userId, packageName, subjectType, signalTypes);
log.info("Package registered. PackageId {}. Status: {}", packageId, Status.PROCESSING.name()); LogUtil.addMdcLogKey("%s %d".formatted(packageName, packageId));
log.info("Package registered. PackageId {}. Status: {}", packageId, Status.ACCEPTED.name());
if (subjectType == 1) {
Pipeline pipeline = npPipelinePackageFabric.build(new SetupParam(packageId, 1, PipelineMode.DEFAULT));
pipelineOrchestrator.startOrDelay(() -> {
saver.readAndSaveNpPackage(packageId, parsedFilePath);
CtFileUtils.deleteQuietly(parsedFilePath);
}, pipeline, packageId);
} else {
Pipeline pipeline = lpPipelinePackageFabric.build(new SetupParam(packageId, 2, PipelineMode.DEFAULT));
lpPipelinePackageOrchestrator.startOrDelay(() -> {
saver.readAndSaveLpPackage(packageId, parsedFilePath);
CtFileUtils.deleteQuietly(parsedFilePath);
}, pipeline, packageId);
}
} catch (Exception e) { } catch (Exception e) {
CTUtil.removeFile(filePath); try {
Files.delete(parsedFilePath);
} catch (IOException ex) {
throw new RuntimeException(ex);
}
if (e instanceof SignalClientException) { if (e instanceof SignalClientException) {
throw (SignalClientException) e; throw (SignalClientException) e;
} }
throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringError, e); throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringError, e);
} }
// в отдельном потоке запускаем регистрацию субъектов, поиск фидов и расчет флагов просрочек
getCurrentProxy().registerFidsAndDebts(packageId, monitoringRequest.getPackageName(), filePath);
return ResponseEntity.ok().body(new MonitoringSetupResponse());
} }
private MonitoringRequest readAndValidateMonitoringRequest(String filePath) { public ResponseEntity<MonitoringActivePackagesResponse> findActivePackages(MonitoringActivePackagesRequest activePackagesRequest) throws Exception {
MonitoringRequest monitoringRequest; AuthorizationService.AuthResult authResult = authorizationService.findAuthInfo(activePackagesRequest.getUserId(), activePackagesRequest.getPassword());
try (BufferedInputStream in = new BufferedInputStream(Files.newInputStream(Path.of(filePath)), 8 * 1024 * 1024)) { if (!authResult.isSuccess()) {
monitoringRequest = staxXmlProcessor.read(in, MonitoringRequest.class, throw clientExcp(authResult.getError(), XmlErrorFormat.MonitoringActivePackagesError);
request -> {
try {
validationService.validateMonitoringRequest(request);
} catch (SignalClientException e) {
if (request != null) {
signalService.rejectPackage(request);
}
request = null;
throw e;
}
request.getSubjects().getSubject().clear();
},
request -> request.getSubjects().getSubject().isEmpty()
,
40000, "/MonitoringRequest/Subjects/Subject");
} catch (IOException e) {
throw new RuntimeException("Error while parsing and validating monitoring request", e);
} }
return monitoringRequest;
}
private String writeTmpFile(ZipInputStream zipInputStream) throws IOException { List<UserPackage> activePackages = userPackageService.findActivePackages(activePackagesRequest.getUserId());
String folderPath = config.getLoadDir() + File.separator + "tmp_folder" + File.separator; if (activePackages.isEmpty()) {
String filePath = folderPath + UUID.randomUUID() + "monitoring_request.xml"; throw clientExcp(SignalError.ERROR_015, XmlErrorFormat.MonitoringActivePackagesError);
CTUtil.createFolder(folderPath);
try (OutputStream out = Files.newOutputStream(Path.of(filePath))) {
byte[] buffer = new byte[8 * 1024 * 1024];
int bytesRead;
while ((bytesRead = zipInputStream.read(buffer)) > 0) {
out.write(buffer, 0, bytesRead);
}
return filePath;
} }
}
@SetAndClearMdc(argIndex = 1) MonitoringActivePackagesResponse response = new MonitoringActivePackagesResponse();
public void registerFidsAndDebts(Long packageId, String packageName, String filePath) { response.setStatus("OK");
Map<String, String> context = MDC.getCopyOfContextMap(); response.setMembercode(activePackagesRequest.getUserId().substring(0, 6));
executor.execute(() -> { Packages packages = new Packages();
if (context != null) { activePackages.forEach(data -> {
MDC.setContextMap(context); Package pack = new Package();
} pack.setPackageName(data.getPackageName());
try { pack.setCreatedAt(data.getCreatedAt() != null ? data.getCreatedAt().toString() : null);
signalService.registerSubjects(filePath, packageId); packages.getPackages().add(pack);
CTUtil.removeFile(filePath);
SignalService.UpdateResult updateResult = signalService.updateSubjects(packageId, null);
if (updateResult.isNoFidsFound()) {
log.info("No fids found for PackageId: {} PackageName: {}. Package status ON_MONITORING. No registration in signal service.", packageId, packageName);
signalService.updatePackageStatus(packageId, Status.ON_MONITORING);
return;
}
if (signalRestService.setupMonitoring(packageId)) {
LocalDateTime monitoringAt = LocalDateTime.now();
log.info("Package was successfully registered in signal service. PackageId: {} PackageName: {}, MonitoringAt: {}", packageId, packageName, monitoringAt);
signalService.updatePackageOnMonitoringSuccess(packageId, monitoringAt);
monitoringErrorProcesingService.findErrors(packageId);
} else {
log.info("Package registration was rejected by signal service. PackageId: {} PackageName: {}", packageId, packageName);
signalService.updatePackageStatus(packageId, Status.ERROR);
}
} catch (Exception e) {
CTUtil.removeFile(filePath);
signalService.updatePackageStatus(packageId, Status.ERROR);
log.error("Ошибка при поиске фидов или расчете флагов просрочек. Package_id {}", packageId, e);
}
}); });
response.setPackages(packages);
return ResponseEntity.ok(response);
} }
private boolean isZipFile(MultipartFile file) { public ResponseEntity<MonitoringAllPackagesResponse> findAllPackages(MonitoringAllPackagesRequest allPackagesRequest) throws Exception {
return file.getContentType() != null && AuthorizationService.AuthResult authResult = authorizationService.findAuthInfo(allPackagesRequest.getUserId(), allPackagesRequest.getPassword());
(file.getContentType().equals("application/zip") || file.getContentType().equals("application/x-zip-compressed")) || if (!authResult.isSuccess()) {
file.getOriginalFilename() != null && file.getOriginalFilename().toLowerCase().endsWith(".zip"); throw clientExcp(authResult.getError(), XmlErrorFormat.MonitoringAllPackagesError);
}
public ResponseEntity<MonitoringActivePackagesResponse> findActivePackages(MonitoringActivePackagesRequest activePackagesRequest) {
try {
AuthorizationService.AuthResult authResult = authorizationService.findAuthInfo(activePackagesRequest.getUserId(), activePackagesRequest.getPassword());
if (!authResult.isSuccess()) {
throw clientExcp(authResult.getError(), XmlErrorFormat.MonitoringActivePackagesError);
}
List<UserPackage> activePackages = signalService.findActivePackages(activePackagesRequest.getUserId());
if (!activePackages.isEmpty()) {
MonitoringActivePackagesResponse response = new MonitoringActivePackagesResponse();
response.setStatus("OK");
response.setMembercode(activePackagesRequest.getUserId().substring(0, 6));
Packages packages = new Packages();
activePackages.forEach(data -> {
Package pack = new Package();
pack.setPackageName(data.getPackageName());
pack.setCreatedAt(data.getCreatedAt() != null ? data.getCreatedAt().toString() : null);
packages.getPackages().add(pack);
});
response.setPackages(packages);
return ResponseEntity.ok(response);
} else {
throw clientExcp(SignalError.ERROR_015, XmlErrorFormat.MonitoringActivePackagesError);
}
} catch (Exception e) {
if (e instanceof SignalClientException) {
throw (SignalClientException) e;
}
throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringActivePackagesError, e);
} }
}
public ResponseEntity<MonitoringAllPackagesResponse> findAllPackages(MonitoringAllPackagesRequest allPackagesRequest) { List<UserPackage> activePackages = userPackageService.findAllPackages(allPackagesRequest.getUserId());
try { if (activePackages.isEmpty()) {
AuthorizationService.AuthResult authResult = authorizationService.findAuthInfo(allPackagesRequest.getUserId(), allPackagesRequest.getPassword()); throw clientExcp(SignalError.ERROR_019, XmlErrorFormat.MonitoringAllPackagesError);
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);
} }
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);
Status packageStatus = EnumUtil.getEnum(Status.class, data.getStatus());
Status statusForClient = packageStatus == Status.ACCEPTED ? Status.PROCESSING : packageStatus;
assert statusForClient != null;
pack.setStatus(statusForClient.name());
if (packageStatus == Status.DELETED) {
pack.setDeactivatedAt(data.getDeactivatedAt() != null ? data.getDeactivatedAt().toString() : null);
}
packages.getPackages().add(pack);
});
response.setPackages(packages);
return ResponseEntity.ok(response);
} }
public ResponseEntity<MonitoringPackageDeleteResponse> deletePackage(String username, String packageName) { public ResponseEntity<MonitoringPackageDeleteResponse> deletePackage(String username, String packageName) {
try { UserPackage packageToDelete = userPackageService.findByMemberCodeAndPackageName(username, packageName)
if (!signalService.isPackageExists(username, packageName)) { .stream()
throw clientExcp(SignalError.ERROR_016, XmlErrorFormat.MonitoringPackageDeleteError, packageName); .max(DeletePackageStatusComparator.INSTANCE)
} .orElseThrow(()
-> clientExcp(SignalError.ERROR_016, XmlErrorFormat.MonitoringPackageDeleteError, packageName));
LocalDateTime deactivatedAt = LocalDateTime.now(); Status status = EnumUtil.getEnum(Status.class, packageToDelete.getStatus());
signalService.deletePackage(username, packageName, deactivatedAt); if (Status.ON_MONITORING != status) {
throw clientExcp(deleteStatusError(status), XmlErrorFormat.MonitoringPackageDeleteError, packageName);
MonitoringPackageDeleteResponse response = new MonitoringPackageDeleteResponse();
response.setStatus("Deleted");
response.setPackageName(packageName);
response.setDeactivatedAt(deactivatedAt.toString());
signalRestService.deletePackage(username, packageName);
log.info("Package {} deleted", packageName);
return ResponseEntity.ok(response);
} catch (Exception e) {
if (e instanceof SignalClientException) {
throw (SignalClientException) e;
}
throw serverExcp(SignalError.ERROR_017, XmlErrorFormat.MonitoringPackageDeleteError, e);
} }
LocalDateTime deactivatedAt = LocalDateTime.now();
userPackageService.deletePackageById(packageToDelete.getId(), deactivatedAt);
MonitoringPackageDeleteResponse response = new MonitoringPackageDeleteResponse();
response.setStatus("Deleted");
response.setPackageName(packageName);
response.setDeactivatedAt(deactivatedAt.toString());
log.info("Package {} deleted", packageName);
return ResponseEntity.ok(response);
}
private SignalError deleteStatusError(Status status) {
if (status == null) return SignalError.ERROR_017;
return switch (status) {
case PROCESSING, ACCEPTED -> SignalError.ERROR_023;
case REJECTED, ERROR -> SignalError.ERROR_024;
case DELETED -> SignalError.ERROR_025;
default -> throw new IllegalStateException("Unexpected value: " + status);
};
} }
public ResponseEntity<MonitoringReportListResponse> getReportList(String username, String packageName) { public ResponseEntity<MonitoringReportListResponse> getReportList(String username, String packageName) {
try { try {
if (signalService.isPackagesByStatusExist(username, packageName, Status.PROCESSING)) { UserPackage userPackage = userPackageService
.findByMemberCodeAndPackageName(username, packageName)
.stream()
.max(ReportListPackageStatusComparator.INSTANCE)
.orElse(null);
if (userPackage == null) {
throw clientExcp(SignalError.ERROR_012, XmlErrorFormat.MonitoringReportListError, packageName);
}
Status status = EnumUtil.getEnum(Status.class, userPackage.getStatus());
if (status == null) {
throw clientExcp(SignalError.ERROR_017, XmlErrorFormat.MonitoringReportListError);
}
if (Arrays.asList(Status.PROCESSING, Status.ACCEPTED).contains(status)) {
throw clientExcp(SignalError.ERROR_018, XmlErrorFormat.MonitoringReportListError, packageName); throw clientExcp(SignalError.ERROR_018, XmlErrorFormat.MonitoringReportListError, packageName);
} }
if (signalService.isPackagesByStatusExist(username, packageName, Status.REJECTED)) { if (status.equals(Status.REJECTED)) {
throw clientExcp(SignalError.ERROR_020, XmlErrorFormat.MonitoringReportListError, packageName); throw clientExcp(SignalError.ERROR_020, XmlErrorFormat.MonitoringReportListError, packageName);
} }
if (signalService.isPackagesByStatusExist(username, packageName, Status.ERROR)) { if (status.equals(Status.ERROR)) {
throw clientExcp(SignalError.ERROR_021, XmlErrorFormat.MonitoringReportListError, packageName); throw clientExcp(SignalError.ERROR_021, XmlErrorFormat.MonitoringReportListError, packageName);
} }
if (signalService.isPackagesByStatusExist(username, packageName, Status.DELETED)) { if (status.equals(Status.DELETED)) {
throw clientExcp(SignalError.ERROR_022, XmlErrorFormat.MonitoringReportListError, packageName); throw clientExcp(SignalError.ERROR_022, XmlErrorFormat.MonitoringReportListError, packageName);
} }
if (!signalService.isPackagesByStatusExist(username, packageName, Status.ON_MONITORING)) { if (!ReportListPackageStatusComparator.reportedStatuses.contains(status)) {
throw clientExcp(SignalError.ERROR_012, XmlErrorFormat.MonitoringReportListError, packageName); throw clientExcp(SignalError.ERROR_012, XmlErrorFormat.MonitoringReportListError, packageName);
} }
@ -377,6 +303,7 @@ public class SignalFacade {
String filePrefix = (memberCode + "_" + packageName).toUpperCase(); String filePrefix = (memberCode + "_" + packageName).toUpperCase();
String dirPath = config.getLoadDir() + File.separator + memberCode; String dirPath = config.getLoadDir() + File.separator + memberCode;
File folder = new File(dirPath); File folder = new File(dirPath);
log.debug("searching for reports from: {}", folder.getAbsolutePath());
MonitoringReportListResponse.Reports reports = new MonitoringReportListResponse.Reports(); MonitoringReportListResponse.Reports reports = new MonitoringReportListResponse.Reports();
if (folder.exists() && folder.isDirectory()) { if (folder.exists() && folder.isDirectory()) {
@ -403,15 +330,21 @@ public class SignalFacade {
public ResponseEntity<Resource> downloadFile(String fileName) { public ResponseEntity<Resource> downloadFile(String fileName) {
try { try {
String memberCode = CTUtil.getMemberCodeFromFileName(fileName); Matcher matcher = DOWNLOAD_FILE_PATTERN.matcher(fileName);
String memberCode = null;
if (matcher.matches()) {
memberCode = matcher.group("member");
}
if (memberCode == null) { if (memberCode == null) {
throw clientExcp(SignalError.ERROR_014, XmlErrorFormat.MonitoringDownloadError, fileName); throw clientExcp(SignalError.ERROR_014, XmlErrorFormat.MonitoringDownloadError, fileName);
} }
String dirPath = config.getLoadDir() + File.separator + memberCode; Path reportRootPath = Path.of(config.getLoadDir(), memberCode);
File folder = new File(dirPath); File folder = reportRootPath.toFile();
if (folder.exists() && folder.isDirectory()) { if (folder.exists() && folder.isDirectory()) {
File file = new File(dirPath + File.separator + fileName); Path filePath = reportRootPath.resolve(fileName);
File file = filePath.toFile();
if (file.exists() && file.isFile()) { if (file.exists() && file.isFile()) {
return ResponseEntity.ok().body(new FileUrlResource(file.getPath())); return ResponseEntity.ok().body(new FileUrlResource(file.getPath()));
} }
@ -426,11 +359,4 @@ public class SignalFacade {
throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringDownloadError, e); throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringDownloadError, e);
} }
} }
private SignalFacade getCurrentProxy() {
if (self == null) {
self = (SignalFacade) AopContext.currentProxy();
}
return self;
}
} }

View file

@ -1,19 +0,0 @@
package ru.nbch.credit_tracker.mapper.indic;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import ru.nbch.credit_tracker.service.indic.FlagsService;
import java.time.LocalDate;
import java.util.List;
@Mapper
public interface FlagsMapper {
List<FlagsService.FlagFidData> findFlagsDebt5000(@Param("fids") List<Integer> fids);
List<FlagsService.FlagFidData> findFlag90ByPastDueArrear(@Param("fids") List<Integer> fids, @Param("reportedDate") LocalDate reportedDate);
List<FlagsService.FlagFidData> findFlag90ByRuPaymt(@Param("fids") List<Integer> fids, @Param("reportedDate") LocalDate reportedDate);
}

View file

@ -0,0 +1,22 @@
package ru.nbch.credit_tracker.mapper.indic;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import ru.nbch.credit_tracker.entities.indic.PaymentsFlag;
import ru.nbch.credit_tracker.entities.indic.RuApplication;
import java.time.LocalDate;
import java.util.List;
@Mapper
public interface IndicFlagsMapper {
List<PaymentsFlag> findFlagsDebt5000(@Param("fids") List<Integer> fids);
List<PaymentsFlag> findFlag90ByPastDueArrear(@Param("fids") List<Integer> fids, @Param("reportedDate") LocalDate reportedDate);
List<PaymentsFlag> findFlag90ByRuPaymt(@Param("fids") List<Integer> fids, @Param("reportedDate") LocalDate reportedDate);
List<PaymentsFlag> findFlag90ByRuAllArrears(@Param("fids") List<Integer> fids, @Param("reportedDate") LocalDate reportedDate);
List<RuApplication> findLastRejecteDates(@Param("fids") List<Integer> fids);
}

View file

@ -2,14 +2,17 @@ package ru.nbch.credit_tracker.mapper.indic;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
import ru.nbch.credit_tracker.service.indic.PersonService; import ru.nbch.credit_tracker.entities.indic.Name;
import ru.nbch.credit_tracker.model.FidIdData;
import java.util.List; import java.util.List;
@Mapper @Mapper
public interface PersonMapper { public interface PersonMapper {
List<PersonService.NameData> findNameData(@Param("fids") List<Integer> fids); List<Name> findNameData(@Param("phones") List<String> phones);
List<PersonService.PassportData> findPassportData(@Param("fids") List<Integer> fids); List<FidIdData> findFidsByInn(@Param("inns") List<String> inns);
List<FidIdData> findFidsByRegNum(@Param("regNums") List<String> regNums);
} }

View file

@ -1,12 +0,0 @@
package ru.nbch.credit_tracker.mapper.indic;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import ru.nbch.credit_tracker.service.indic.PhoneService;
import java.util.List;
@Mapper
public interface PhoneMapper {
List<PhoneService.FidsData> findFids(@Param("phones") List<String> phones);
}

View file

@ -0,0 +1,16 @@
package ru.nbch.credit_tracker.mapper.nbch;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import ru.nbch.credit_tracker.entities.nbch.Source;
import java.util.List;
@Mapper
public interface SourceMapper {
List<Source> findBusCategories(@Param("memberCodes") List<String> memberCodes);
String findBusCategory(@Param("memberCode") String memberCode);
}

View file

@ -0,0 +1,9 @@
package ru.nbch.credit_tracker.mapper.signal;
import org.apache.ibatis.annotations.Mapper;
import ru.nbch.credit_tracker.entities.app.AppVersion;
@Mapper
public interface AppVersionMapper {
AppVersion getAppVersion();
}

View file

@ -2,7 +2,7 @@ package ru.nbch.credit_tracker.mapper.signal;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
import ru.nbch.credit_tracker.entities.dto.Hit; import ru.nbch.credit_tracker.entities.app.Hit;
import java.util.List; import java.util.List;

View file

@ -0,0 +1,19 @@
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.app.SignalLegalFidMap;
import java.util.List;
@Mapper
public interface LegalsFidMapMapper {
List<Long> registerJson(@Param("json") String json);
List<SignalLegalFidMap> findFullLegalFidMap(@Param("packageId") Long packageId,
@Param("limit") int limit,
@Param("offset") int offset);
Long deleteByPackageId(@Param("packageId") Long id);
}

View file

@ -0,0 +1,43 @@
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.app.LegalsMap;
import java.util.List;
@Mapper
public interface LegalsMapMapper {
void registerJson(@Param("json") String json);
List<LegalsMap> findByFids(@Param("fids") List<Integer> fids);
void updateFlagCachedData(@Param("json") String json);
void updateLastRejectedDateCache(@Param("json") String json);
List<LegalsMap> findEligibleForFlagsUpdate(@Param("lastId") Long lastId,
@Param("interval") Integer interval,
@Param("batchSize") Integer batchSize);
List<LegalsMap> findEligibleForRejectedDatesToUpdate(@Param("lastId") Long lastId,
@Param("batchSize") Integer batchSize);
List<LegalsMap> findEligibleForFlagsInsert(@Param("lastId") Long lastId,
@Param("batchSize") Integer batchSize);
List<LegalsMap> searchedAtLessThan(@Param("daysCount") Integer daysCount,
@Param("batchSize") int batchSize,
@Param("lastId") Long lastId);
List<LegalsMap> findCachedByInnAndRegNum(@Param("json") String json);
void updateSearchedAt(@Param("json") String json);
void resetFidAndFlagsByInnAndRegNum(@Param("json") String json);
void updateFidCachedData(@Param("json") String json);
void deleteUnusedMaps();
}

View file

@ -0,0 +1,31 @@
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.app.PackagesRecordsMap;
import ru.nbch.credit_tracker.model.UserPackageLegalData;
import ru.nbch.credit_tracker.model.UserPackagePhoneData;
import java.util.List;
@Mapper
public interface PackagesRecordsMapper {
List<Long> registerJson(@Param("json") String json);
List<PackagesRecordsMap> findFullPhoneFidMap(@Param("packageId") Long packageId,
@Param("limit") int limit,
@Param("offset") int offset);
List<UserPackagePhoneData> selectUserPackagePhoneData(@Param("json") String json);
List<UserPackageLegalData> selectUserPackageLegalData(@Param("json") String json);
Long deleteByPackageId(@Param("packageId") Long id);
PackagesRecordsMap findById(Long id);
List<PackagesRecordsMap> findByPhone(@Param("phone") Long phone);
List<PackagesRecordsMap> findByPhones(@Param("phones") List<Long> phones);
}

View file

@ -1,24 +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.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);
}

View file

@ -0,0 +1,42 @@
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.app.PhonesMap;
import java.util.List;
@Mapper
public interface PhonesMapMapper {
List<PhonesMap> getCachedData(@Param("phones") List<Long> phones);
List<PhonesMap> searchedAtLessThan(@Param("daysCount") Integer daysCount,
@Param("batchSize") int batchSize,
@Param("lastId") Long lastId);
PhonesMap findByPhone(@Param("phone") Long phone);
List<PhonesMap> findEligibleForFlagsUpdate(@Param("lastId") Long lastId,
@Param("interval") Integer interval,
@Param("batchSize") Integer batchSize);
List<PhonesMap> findEligibleForFlagsInsert(@Param("lastId") Long lastId,
@Param("batchSize") Integer batchSize);
void updateNameCachedData(@Param("json") String json);
void updateFlagCachedData(@Param("json") String json);
void resetFidAndPD(@Param("json") String json);
void updateSearchedAt(@Param("json") String json);
void registerJson(@Param("json") String json);
void deleteUnusedPhones();
List<PhonesMap> findByFid(@Param("fid") Integer fid);
List<PhonesMap> findByFids(@Param("fids") List<Integer> fids);
}

View file

@ -0,0 +1,13 @@
package ru.nbch.credit_tracker.mapper.signal;
import org.apache.ibatis.annotations.Mapper;
import ru.nbch.credit_tracker.entities.app.SegmentMap;
import java.util.List;
@Mapper
public interface SegmentMapMapper {
List<SegmentMap> findAllSegmentMaps();
}

View file

@ -2,7 +2,7 @@ package ru.nbch.credit_tracker.mapper.signal;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
import ru.nbch.credit_tracker.entities.dto.UserPackage; import ru.nbch.credit_tracker.entities.app.UserPackage;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.List; import java.util.List;
@ -10,27 +10,36 @@ import java.util.List;
@Mapper @Mapper
public interface UserPackageMapper { public interface UserPackageMapper {
boolean isPackageNotUnique(@Param("userId") String userId, @Param("packageName") String packageName); 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 deletePackageById(@Param("id") Long id,
@Param("status") String newStatus,
boolean hasActivePackages(@Param("userId") String userId, @Param("packageName") String packageName, @Param("statuses") List<String> statuses); @Param("deactivatedAt") LocalDateTime deactivatedAt);
void deletePackage(@Param("userId") String userId, @Param("packageName") String packageName, @Param("status") String newStatus, @Param("previousStatus") String previousStatus, @Param("deactivatedAt") LocalDateTime deactivatedAt);
Long insertPackage(@Param("userPackage") UserPackage userPackage); Long insertPackage(@Param("userPackage") UserPackage userPackage);
void updatePackageStatus(@Param("packageId") Long packageId, @Param("status") String status); 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);
void updatePackageOnMonitoringSuccess(@Param("id") Long id, @Param("monitoringAt") LocalDateTime monitoringAt);
UserPackage findPackage(@Param("id") Long id); UserPackage findPackage(@Param("id") Long id);
List<UserPackage> findUserPackages(@Param("userId") String userId, @Param("statuses") List<String> status); List<UserPackage> findUserPackages(@Param("userId") String userId,
@Param("statuses") List<String> status);
List<UserPackage> findAllPackagesByStatus(@Param("status") String status); List<UserPackage> findAllPackagesByStatus(@Param("statuses") String... statuses);
List<UserPackage> findAllPackagesByUserId(@Param("userId") String userId); List<UserPackage> findAllPackagesByUserId(@Param("userId") String userId);
UserPackage findByPackageName(@Param("packageName") String packageName);
List<UserPackage> findOnMonitoringPackages();
List<UserPackage> findByMemberCodeAndPackageName(@Param("memberCode") String memberCode,
@Param("packageName") String packageName);
void updatePackageLastDownloadedReport(@Param("json") String json);
} }

View file

@ -0,0 +1,11 @@
package ru.nbch.credit_tracker.model;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class FidIdData {
private Integer fid;
private String idNum;
}

View file

@ -0,0 +1,20 @@
package ru.nbch.credit_tracker.model;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import java.time.LocalDate;
@Getter
@Setter
@Builder
public class FlagData {
private Long phone;
private Long inn;
private Long regNum;
private Integer flag_debt5000;
private Integer flag_90_12;
private LocalDate lastRejectedDate;
}

View file

@ -0,0 +1,13 @@
package ru.nbch.credit_tracker.model;
public interface IUserPackageData {
Long getPackageId();
String getUserId();
String getSignalTypes();
String getSubjectId();
String getPackageName();
}

View file

@ -0,0 +1,30 @@
package ru.nbch.credit_tracker.model;
import ru.nbch.credit_tracker.exceptions.SignalException;
public class MonitoringValidationError {
private final int line;
private final String message;
private final SignalException signalException;
public MonitoringValidationError(int line,
String message,
SignalException signalException) {
this.line = line;
this.message = message;
this.signalException = signalException;
}
public int getLine() {
return line;
}
public String getMessage() {
return message;
}
public SignalException getSignalException() {
return signalException;
}
}

View file

@ -0,0 +1,32 @@
package ru.nbch.credit_tracker.model;
import lombok.*;
import ru.nbch.credit_tracker.entities.external_request.kafka.PackageMessage;
import ru.nbch.credit_tracker.enums.EventType;
import java.time.LocalDateTime;
import java.util.Map;
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class SignalKafkaEvent {
private EventType eventType;
private Map<String, String> reportNames;
private UserPackagePhoneData userPackagePhoneData;
private UserPackageLegalData userPackageLegalData;
private PackageMessage packageMessage;
private String subjectId;
private Integer flagDebt5000;
private Integer flag90days;
private String signalType;
private String busCategory;
private Integer segment;
private LocalDateTime lastRejectedDate;
private boolean natural;
}

View file

@ -0,0 +1,23 @@
package ru.nbch.credit_tracker.model;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@Builder
public class SubjectProfile {
// common field
private String identifier;
private Long mapId;
private Integer fid;
private FlagData flagData;
// np fields
private Long phone;
// lp fields
private Long inn;
private Long regNum;
}

View file

@ -0,0 +1,19 @@
package ru.nbch.credit_tracker.model;
import lombok.*;
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class UserPackageLegalData implements IUserPackageData {
private Long packageId;
private String signalTypes;
private String subjectId;
private String userId;
private String packageName;
private Long inn;
private Long regNum;
}

View file

@ -0,0 +1,17 @@
package ru.nbch.credit_tracker.model;
import lombok.*;
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class UserPackagePhoneData implements IUserPackageData {
private Long packageId;
private String signalTypes;
private Long phone;
private String subjectId;
private String userId;
private String packageName;
}

View file

@ -0,0 +1,69 @@
package ru.nbch.credit_tracker.model;
import java.util.ArrayList;
import java.util.List;
public class ValidationResult {
private String packageName;
private String userId;
private String password;
private Integer subjectType;
private List<Integer> signalTypes = new ArrayList<>();
private List<MonitoringValidationError> validationErrors = new ArrayList<>();
public void setPackageName(String packageName) {
this.packageName = packageName;
}
public void setUserId(String userId) {
this.userId = userId;
}
public void setValidationErrors(List<MonitoringValidationError> validationErrors) {
this.validationErrors = validationErrors;
}
public ValidationResult() {
}
public String getPackageName() {
return packageName;
}
public String getUserId() {
return userId;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Integer getSubjectType() {
return subjectType;
}
public void setSubjectType(Integer subjectType) {
this.subjectType = subjectType;
}
public List<Integer> getSignalTypes() {
return signalTypes;
}
public void setSignalTypes(List<Integer> signalTypes) {
this.signalTypes = signalTypes;
}
public List<MonitoringValidationError> getValidationErrors() {
return validationErrors;
}
public void addError(MonitoringValidationError error) {
this.validationErrors.add(error);
}
}

View file

@ -0,0 +1,6 @@
package ru.nbch.credit_tracker.model.impl;
import ru.nbch.pipeline.engine.core.TerminatePayload;
public class EmptyTerminatePayload implements TerminatePayload {
}

View file

@ -0,0 +1,8 @@
package ru.nbch.credit_tracker.model.impl;
public enum PackageVariable {
packageId,
cacheMisses,
cacheHits,
mode
}

View file

@ -0,0 +1,38 @@
package ru.nbch.credit_tracker.model.impl;
import lombok.Getter;
import ru.nbch.credit_tracker.enums.PoisonType;
import ru.nbch.pipeline.engine.core.StagePayload;
import java.nio.file.Path;
@Getter
public class PartFilePayload implements StagePayload {
private final Path partPath;
private final Long packageId;
private final String membercode;
private final String packageName;
private final PoisonType poison;
public static final PartFilePayload POISON = new PartFilePayload(PoisonType.NORMALLY);
public PartFilePayload(PoisonType poison) {
this.poison = poison;
this.membercode = null;
this.packageName = null;
this.partPath = null;
this.packageId = null;
}
public PartFilePayload(Path partPath, Long packageId, String membercode, String packageName) {
this.partPath = partPath;
this.packageId = packageId;
this.membercode = membercode;
this.packageName = packageName;
this.poison = PoisonType.NONE;
}
@Override
public boolean isPoison() {
return poison == PoisonType.NORMALLY;
}
}

View file

@ -0,0 +1,6 @@
package ru.nbch.credit_tracker.model.impl;
import ru.nbch.pipeline.engine.core.TerminatePayload;
public record SignalPayload(boolean hasFids) implements TerminatePayload {
}

View file

@ -0,0 +1,76 @@
package ru.nbch.credit_tracker.model.impl;
import ru.nbch.credit_tracker.enums.PoisonType;
import ru.nbch.credit_tracker.model.SubjectProfile;
import ru.nbch.pipeline.engine.core.PipelineContext;
import ru.nbch.pipeline.engine.core.StagePayload;
import java.util.List;
public class SubjectPayload implements StagePayload {
private final Long packageId;
private final String memberCode;
private final String packageName;
private final List<SubjectProfile> subjects;
private PipelineContext<PackageVariable> ctx;
public static final SubjectPayload POISON = new SubjectPayload(null, PoisonType.NORMALLY);
private final PoisonType poison;
private boolean hasFids = false;
public SubjectPayload(Long packageId, PoisonType poison) {
this.memberCode = null;
this.packageName = null;
this.packageId = packageId;
this.subjects = null;
this.poison = poison;
}
public SubjectPayload(Long packageId, String memberCode, String packageName, List<SubjectProfile> subjects) {
this.packageId = packageId;
this.memberCode = memberCode;
this.packageName = packageName;
this.subjects = subjects;
this.poison = PoisonType.NONE;
}
@Override
public void setContext(PipelineContext<?> context) {
this.ctx = (PipelineContext<PackageVariable>) context;
}
@Override
public PipelineContext<?> getContext() {
return this.ctx;
}
public Long getPackageId() {
return packageId;
}
public List<SubjectProfile> getSubjects() {
return subjects;
}
public String getMemberCode() {
return memberCode;
}
public String getPackageName() {
return packageName;
}
public boolean isHasFids() {
return hasFids;
}
public void setHasFids(boolean hasFids) {
this.hasFids = hasFids;
}
@Override
public boolean isPoison() {
return poison == PoisonType.NORMALLY;
}
}

View file

@ -0,0 +1,24 @@
package ru.nbch.credit_tracker.model.impl;
import lombok.Getter;
import ru.nbch.credit_tracker.enums.PoisonType;
import ru.nbch.pipeline.engine.core.StagePayload;
@Getter
public class VoidPayload implements StagePayload {
private final PoisonType poison;
public static final VoidPayload POISON = new VoidPayload(PoisonType.NORMALLY);
public VoidPayload(PoisonType poison) {
this.poison = poison;
}
public VoidPayload() {
this.poison = PoisonType.NONE;
}
@Override
public boolean isPoison() {
return poison == PoisonType.NORMALLY;
}
}

View file

@ -0,0 +1,5 @@
package ru.nbch.credit_tracker.refactoring;
public @interface Suspicios {
String value();
}

View file

@ -0,0 +1,62 @@
package ru.nbch.credit_tracker.service;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import ru.nbch.credit_tracker.config.settings.CreditTrackerSettings;
import ru.nbch.credit_tracker.entities.app.AppVersion;
import ru.nbch.credit_tracker.mapper.signal.AppVersionMapper;
@Service
public class DbVersionValidationService implements InitializingBean {
private final Logger log = LoggerFactory.getLogger(getClass());
private final AppVersionMapper appVersionMapper;
private final CreditTrackerSettings settings;
private final String expectedDbVersion = "2.22";
@Autowired
public DbVersionValidationService(AppVersionMapper appVersionMapper, CreditTrackerSettings settings) {
this.appVersionMapper = appVersionMapper;
this.settings = settings;
}
@Override
public void afterPropertiesSet() throws Exception {
String[] expectedVersions = expectedDbVersion.split("\\.");
String expectedMajor = expectedVersions[0];
String expectedMinor = expectedVersions[1];
if (StringUtils.isEmpty(expectedMajor) || StringUtils.isEmpty(expectedMinor)) {
log.error("expected database version is not specified in the settings.");
System.exit(1);
}
log.info("expected DB version: {}", expectedDbVersion);
AppVersion appVersion = appVersionMapper.getAppVersion();
String[] actualVersions = appVersion.getDbVersion().split("\\.");
String actualMajor = actualVersions[0];
String actualMinor = actualVersions[1];
if (StringUtils.isEmpty(actualMajor) || StringUtils.isEmpty(actualMinor)) {
log.error("couldn't load version from APP_VERSION database table.");
System.exit(1);
}
log.info("actual DB version: {}", appVersion.getDbVersion());
if (!expectedMajor.equals(actualMajor)) {
log.error("expected database version does not match actual database version");
System.exit(1);
}
if (Integer.parseInt(expectedMinor) < Integer.parseInt(actualMinor)) {
log.error("expected database version does not match actual database version");
System.exit(1);
}
log.info("Database version validation complete: {}", appVersion.getDbVersion());
}
}

View file

@ -1,59 +0,0 @@
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,319 +0,0 @@
package ru.nbch.credit_tracker.service;
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 ru.nbch.credit_tracker.config.settings.CreditTrackerSettings;
import ru.nbch.credit_tracker.entities.dto.Hit;
import ru.nbch.credit_tracker.entities.dto.PhoneFidMap;
import ru.nbch.credit_tracker.entities.dto.UserPackage;
import ru.nbch.credit_tracker.entities.external_response.online_download.Inquiry;
import ru.nbch.credit_tracker.entities.external_response.online_download.Person;
import ru.nbch.credit_tracker.entities.external_response.online_download.Persons;
import ru.nbch.credit_tracker.entities.external_response.online_report.Report;
import ru.nbch.credit_tracker.entities.external_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.xml.stax.MonitoringSignalResponseStaxXmlWriter;
import ru.nbch.credit_tracker.service.xml.stax.StaxXmlProcessor;
import ru.nbch.credit_tracker.utils.CTUtil;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.LocalDateTime;
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.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
/**
* Сервис периодического опроса официального сервиса сигналов на наличие новых отчетов по пакетам находящимся в статусе ON_MONITORING
*/
@Slf4j
@Service
public class MonitoringService {
private final SignalRestService signalRestService;
private final SignalService signalService;
private final StaxXmlProcessor staxXmlProcessor;
private final CreditTrackerSettings config;
public MonitoringService(SignalRestService signalRestService,
SignalService signalService,
StaxXmlProcessor staxXmlProcessor,
CreditTrackerSettings config) {
this.signalRestService = signalRestService;
this.signalService = signalService;
this.staxXmlProcessor = staxXmlProcessor;
this.config = config;
}
public void run() {
ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(new CustomizableThreadFactory("monitoring-"));
long period = config.getMonitoringInterval() != null ? config.getMonitoringInterval() : 15;
scheduler.scheduleAtFixedRate(this::monitor, 0, period, TimeUnit.MINUTES);
}
private void monitor() {
log.info("Start monitoring");
List<UserPackage> packages = signalService.findPackagesOnMonitoring();
for (UserPackage userPackage : packages) {
MDC.put("ru.nbch.credit_tracker.log.mdc.key", "[" + userPackage.getPackageName() +"]");
processPackage(userPackage);
MDC.remove("ru.nbch.credit_tracker.log.mdc.key");
}
log.info("Finish monitoring");
}
private void processPackage(UserPackage userPackage) {
log.info("Processing package");
if (!signalService.hasNonNullfids(userPackage.getId())) {
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();
response.setPackageName(userPackage.getPackageName()); // Имя пакета, переданного при установке
// Запись в результрующий файл
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) {
Signals signals = new Signals();
for (Inquiry inquiry : person.getInquiries()) {
// Отбираем только тех фл, у которых bus_category in (MFO, MKK, MFK) и есть сигналы с кодом 16
if (!CTUtil.checkBusCategory(inquiry.getBusCategory()) || inquiry.getSignals().stream().noneMatch(signal -> signal.getN().equals(16))) {
continue;
}
Signal signal = new Signal();
signal.setSignalType(1); // Код типа сигнала по внутреннему справочнику. В текущей версии: 1 = сигнал 16 от MFO/MKK/MFK
signal.setInquiryDate(inquiry.getDate()); // Дата и время запроса КИ
signal.setInquiryPurpose(inquiry.getInqP()); // Цель запроса КИ
signals.getSignals().add(signal);
}
// Если есть хотя бы один заполненный сигнал, значит заполняем остальные данные
if (!signals.getSignals().isEmpty()) {
PhoneFidMap subjectData = signalService.findPhoneFidMapById(person.getUid());
if (subjectData != null) {
Subject subject = new Subject();
subject.setId(subjectData.getSubjectId()); // Уникальный идентификатор субъекта (равен Id из XML-пакета при установке мониторинга)
subject.setClientId(subjectData.getPhone());
subject.setFlagDebt5000(subjectData.getFlagDebt5000());
subject.setFlag9012(subjectData.getFlag90days());
subject.setSignals(signals);
subjects.add(subject);
}
}
}
return subjects;
}
/**
* Архивирует результирующий xml файл для пользователя
* @return Путь к результрующему файлу. Пример <loadDir>/<member_code>/0001ZZ_MY_BATCH_TEST_0717_04_20250723_101929.zip
*/
private String createResponseFile(UserPackage userPackage, String responsePath) {
try {
String fileName = CTUtil.generateResponseReportFileName(userPackage.getUserId(), userPackage.getPackageName());
Thread.sleep(1000); // Если отчеты формируются слишком быстро, нужно подождать секунду, тк в имени отчета LocalDateTime до секунд
String folderPath = config.getLoadDir() + File.separator + userPackage.getUserId().substring(0, 6) + File.separator;
File folder = new File(folderPath);
if (!folder.exists()) {
folder.mkdir();
}
try (BufferedInputStream in = new BufferedInputStream(Files.newInputStream(Path.of(responsePath)), 8 * 1024 * 1024);
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(folderPath + fileName + ".zip"));
ZipOutputStream zos = new ZipOutputStream(bos)) {
ZipEntry zipEntry = new ZipEntry(fileName + ".xml");
zos.putNextEntry(zipEntry);
byte[] buffer = new byte[8 * 1024 * 1024];
int bytesRead;
while ((bytesRead = in.read(buffer)) > 0) {
zos.write(buffer, 0, bytesRead);
}
zos.finish();
zos.closeEntry();
}
return fileName;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} catch (Exception e) {
log.error("Ошибка при записи отчета пользователя в файл: ", e);
}
CTUtil.removeFile(responsePath);
return null;
}
private void updateSignalHits(UserPackage userPackage, String responsePath, String reportFileName) {
log.info("Updating signal hits for {}", userPackage.getPackageName());
LocalDateTime responseSentAt = LocalDateTime.now();
// Считываем результирующий файл пачками и обновляем таблицу hits
try (BufferedInputStream in = new BufferedInputStream(Files.newInputStream(Path.of(responsePath)), 8 * 1024 * 1024)) {
staxXmlProcessor.read(in, MonitoringSignalResponse.class,
response -> { // как только считали пачку Subjects сразу обновлям таблицу hits
List<Hit> hits = new ArrayList<>();
response.getSubjects().getSubjects().forEach(subject -> {
subject.getSignals().getSignals().forEach(signal -> {
Hit hit = new Hit();
hit.setPackageId(userPackage.getId());
hit.setSubjectId(subject.getId());
hit.setSignalType(1);
hit.setResponseSentAt(responseSentAt);
hit.setReportFileName(reportFileName);
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());
}
public String loadReport(Long packageId, String reportName) {
return signalRestService.downloadReport(packageId, reportName);
}
}

View file

@ -1,366 +0,0 @@
package ru.nbch.credit_tracker.service;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import ru.nbch.credit_tracker.builder.PersonFactoryBuilder;
import ru.nbch.credit_tracker.config.settings.CreditTrackerSettings;
import ru.nbch.credit_tracker.entities.dto.Hit;
import ru.nbch.credit_tracker.entities.dto.PhoneFidMap;
import ru.nbch.credit_tracker.entities.dto.UserPackage;
import ru.nbch.credit_tracker.entities.external_request.signals.Auth;
import ru.nbch.credit_tracker.entities.external_request.signals.Person;
import ru.nbch.credit_tracker.entities.external_request.signals.Signals;
import ru.nbch.credit_tracker.entities.external_response.monitoring_report_error.MonitoringReportErrors;
import ru.nbch.credit_tracker.entities.user_request.monitoring_request.MonitoringRequest;
import ru.nbch.credit_tracker.enums.Status;
import ru.nbch.credit_tracker.service.indic.FlagsService;
import ru.nbch.credit_tracker.service.indic.PersonService;
import ru.nbch.credit_tracker.service.indic.PhoneService;
import ru.nbch.credit_tracker.service.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 java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Predicate;
@Slf4j
@Service
public class SignalService {
private final CreditTrackerSettings config;
private final HitsService hitsService;
private final UserPackageService userPackageService;
private final PhoneFidMapService phoneFidMapService;
private final PhoneService phoneService;
private final FlagsService flagsService;
private final PersonService personService;
private final StaxXmlProcessor staxXmlProcessor;
private final int batchSize = 10000;
public SignalService(CreditTrackerSettings config,
HitsService hitsService,
UserPackageService userPackageService,
PhoneFidMapService phoneFidMapService,
PhoneService phoneService,
FlagsService flagsService,
PersonService personService,
StaxXmlProcessor staxXmlProcessor) {
this.config = config;
this.hitsService = hitsService;
this.userPackageService = userPackageService;
this.phoneFidMapService = phoneFidMapService;
this.phoneService = phoneService;
this.flagsService = flagsService;
this.personService = personService;
this.staxXmlProcessor = staxXmlProcessor;
}
public boolean isPackageNotUnique(MonitoringRequest request) {
return userPackageService.isPackageNotUnique(request.getUserId(), request.getPackageName());
}
public Long registerPackage(MonitoringRequest request) {
UserPackage userPackage = UserPackage.builder()
.userId(request.getUserId())
.packageName(request.getPackageName())
.createdAt(LocalDateTime.now())
.status(Status.PROCESSING.name())
.build();
return userPackageService.insertPackage(userPackage);
}
public void registerSubjects(String filePath, Long packageId) {
log.info("Start registering subjects. PackageId: {}", packageId);
try (BufferedInputStream in = new BufferedInputStream(Files.newInputStream(Path.of(filePath)), 8 * 1024 * 1024)) {
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);
}
log.info("Subjects were registered. PackageId: {}", packageId);
}
/**
* Обрабатывает все PhoneFidMap по 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);
List<PhoneFidMap> filteredItems;
if (filter != null) {
filteredItems = batch.stream().filter(filter).toList();
} else {
filteredItems = batch;
}
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);
}
log.info("Finish calculating fids and flags for subjects. PackageId: {}. Batch size: {}", packageId, subjects.size());
return fidsFound;
}
public void rejectPackage(MonitoringRequest request) {
UserPackage userPackage = UserPackage.builder()
.userId(request.getUserId())
.packageName(request.getPackageName())
.createdAt(LocalDateTime.now())
.status(Status.REJECTED.name())
.build();
userPackageService.insertPackage(userPackage);
}
public void updatePackageStatus(Long packageId, Status status) {
userPackageService.updatePackageStatus(packageId, status);
}
public void updatePackageOnMonitoringSuccess(Long packageId, LocalDateTime monitoringAt) {
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.setOnline("2"); // Всегда=2 (только онлайн-сигналы)
signals.setProcessWithErrors("1"); // Всегда=1 (обработать пакет полностью, по обнаруженным ошибкам (при наличии) сформировать файл отказа, записи без ошибок установить на мониторинг)
signals.setId(userPackage.getUserId().substring(0, 6) + "_" + userPackage.getPackageName()); // <MEMBERCODE>_<PackageName>
Auth auth = new Auth();
auth.setUser(config.getSignalsuser());
signals.setAuth(auth);
return signals;
}
/**
* Обрабатывает все 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<>();
Map<Integer, PersonService.PersonData> personFidMap = personService.findPersonData(subjects.stream()
.map(PhoneFidMap::getFid)
.filter(Objects::nonNull).toList());
subjects.forEach(data -> {
if (data.getFid() != null) {
PersonService.PersonData personData = personFidMap.get(data.getFid());
Person person = PersonFactoryBuilder.builder()
.personData(personData)
.uid(data.getId())
.build();
if (person != null) {
persons.add(person);
}
}
});
return persons;
}
public void updateSubjectsErrors(String filePath, Long packageId) {
log.info("Start updating subjects errors. PackageId: {}", packageId);
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);
}
log.info("Subject's errors were updated. PackageId: {}", packageId);
}
public List<UserPackage> findActivePackages(String userId) {
return userPackageService.findUserPackages(userId, Status.ON_MONITORING);
}
public List<UserPackage> findAllPackages(String userId) {
return userPackageService.findAllPackagesByUserId(userId);
}
public boolean isPackageExists(String username, String packageName) {
return userPackageService.isPackageExists(username, packageName, Status.ON_MONITORING);
}
public boolean isPackagesByStatusExist(String username, String packageName, Status status) {
return userPackageService.hasActivePackages(username, packageName, status);
}
public void deletePackage(String username, String packageName, LocalDateTime deactivatedAt) {
userPackageService.deletePackage(username, packageName, Status.DELETED, Status.ON_MONITORING, deactivatedAt);
}
public List<UserPackage> findPackagesOnMonitoring() {
return userPackageService.findAllPackagesByStatus(Status.ON_MONITORING);
}
public List<UserPackage> findPackagesOnProcessing() {
return userPackageService.findAllPackagesByStatus(Status.PROCESSING);
}
public void insertHits(List<Hit> hits) {
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

@ -1,76 +0,0 @@
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,94 +0,0 @@
package ru.nbch.credit_tracker.service;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.scheduling.concurrent.CustomizableThreadFactory;
import org.springframework.stereotype.Service;
import ru.nbch.credit_tracker.config.settings.CreditTrackerSettings;
import ru.nbch.credit_tracker.entities.dto.PhoneFidMap;
import ru.nbch.credit_tracker.entities.dto.UserPackage;
import ru.nbch.credit_tracker.enums.Status;
import ru.nbch.credit_tracker.service.signal.SignalRestService;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
/**
* Сервис периодического обновления фидов и флагов просрочек по пакетам находящимся в статусе ON_MONITORING
* По обновленным пакетам формируется новый запрос постановки на мониторинг в официальный сервис сигналов
*/
@Slf4j
@Service
public class UpdatingFlagsService {
private final SignalService signalService;
private final SignalRestService signalRestService;
private final MonitoringErrorProcesingService monitoringErrorProcesingService;
private final Long daysToUpdateFlags;
private final Long updateFlagsInterval;
public UpdatingFlagsService(SignalService signalService,
SignalRestService signalRestService,
MonitoringErrorProcesingService monitoringErrorProcesingService,
CreditTrackerSettings config) {
this.signalService = signalService;
this.signalRestService = signalRestService;
this.monitoringErrorProcesingService = monitoringErrorProcesingService;
this.daysToUpdateFlags = config.getDaysToUpdateFlags() != null ? config.getDaysToUpdateFlags() : 1;
this.updateFlagsInterval = config.getUpdateFlagsInterval() != null ? config.getUpdateFlagsInterval() : 1;
}
public void run() {
ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(new CustomizableThreadFactory("updating-flags-"));
scheduler.scheduleAtFixedRate(this::update, 0, updateFlagsInterval, TimeUnit.DAYS);
}
private void update() {
log.info("Start updating flags");
List<UserPackage> packages = signalService.findPackagesOnMonitoring();
for (UserPackage userPackage : packages) {
log.info("Updating flags for Package {}", userPackage.getPackageName());
MDC.put("ru.nbch.credit_tracker.log.mdc.key", "[" + userPackage.getPackageName() +"]");
updateFlags(userPackage);
MDC.remove("ru.nbch.credit_tracker.log.mdc.key");
}
log.info("Finish updating flags");
}
private void updateFlags(UserPackage userPackage) {
Long packageId = userPackage.getId();
String packageName = userPackage.getPackageName();
try {
signalService.updatePackageStatus(packageId, Status.PROCESSING);
Predicate<PhoneFidMap> filter = subject -> subject.getCalculatedAt() != null && hasDaysPassed(subject.getCalculatedAt());
SignalService.UpdateResult updateResult = signalService.updateSubjects(packageId, filter);
if (updateResult.isUpdated()) {
if (signalRestService.setupMonitoring(packageId)) {
LocalDateTime monitoringAt = userPackage.getMonitoringAt() != null ? userPackage.getMonitoringAt() : LocalDateTime.now();
log.info("Flags were updated successfully. PackageId: {} PackageName: {} MonitoringAt: {}. Package status changed to ON_MONITORING", packageId, packageName, monitoringAt);
signalService.updatePackageOnMonitoringSuccess(packageId, monitoringAt);
monitoringErrorProcesingService.findErrors(packageId);
} else {
log.info("Updated package registration was rejected by signal service. PackageId: {} PackageName: {}", packageId, packageName);
signalService.updatePackageStatus(packageId, Status.ERROR);
}
} else {
log.info("No flags were updated for package id {}. Package status changed to ON_MONITORING", packageId);
signalService.updatePackageStatus(packageId, Status.ON_MONITORING);
}
} catch (Exception e) {
log.error("Ошибка при поиске фидов или перерасчете флагов просрочек. Package_id {}", packageId, e);
}
}
private boolean hasDaysPassed(LocalDateTime pastDateTime) {
LocalDateTime now = LocalDateTime.now();
long daysPassed = ChronoUnit.DAYS.between(pastDateTime, now);
return daysPassed >= daysToUpdateFlags;
}
}

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