diff --git a/pom.xml b/pom.xml index f876eb6..efc39bf 100644 --- a/pom.xml +++ b/pom.xml @@ -27,6 +27,10 @@ org.springframework.boot spring-boot-starter-web + + org.springframework.boot + spring-boot-starter-aop + org.mybatis.spring.boot mybatis-spring-boot-starter diff --git a/src/main/java/ru/nbch/credit_tracker/config/mdc/AopMdcConfig.java b/src/main/java/ru/nbch/credit_tracker/config/mdc/AopMdcConfig.java new file mode 100644 index 0000000..1cfbbf1 --- /dev/null +++ b/src/main/java/ru/nbch/credit_tracker/config/mdc/AopMdcConfig.java @@ -0,0 +1,19 @@ +package ru.nbch.credit_tracker.config.mdc; + +import org.aspectj.lang.annotation.Aspect; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.EnableAspectJAutoProxy; + +@Configuration +@ConditionalOnClass(Aspect.class) +@EnableAspectJAutoProxy(proxyTargetClass = true) +public class AopMdcConfig { + + @Bean + public MdcAspect mdcAspect() { + return new MdcAspect(); + } + +} diff --git a/src/main/java/ru/nbch/credit_tracker/config/mdc/MdcAspect.java b/src/main/java/ru/nbch/credit_tracker/config/mdc/MdcAspect.java new file mode 100644 index 0000000..3bcbddd --- /dev/null +++ b/src/main/java/ru/nbch/credit_tracker/config/mdc/MdcAspect.java @@ -0,0 +1,49 @@ +package ru.nbch.credit_tracker.config.mdc; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.slf4j.MDC; +import ru.nbch.credit_tracker.entities.dto.UserPackage; +import ru.nbch.credit_tracker.entities.user_request.monitoring_request.MonitoringRequest; + +@Slf4j +@Aspect +public class MdcAspect { + + // For methods that set an MDC value (from a method argument) and then clear it at the end + @Around("@annotation(setAndClearMdc)") + public Object setAndClearMdcAdvice(ProceedingJoinPoint pjp, SetAndClearMdc setAndClearMdc) throws Throwable { + Object[] args = pjp.getArgs(); + int index = setAndClearMdc.argIndex(); + Object obj = (args != null && args.length > index) ? args[index] : null; + if (obj instanceof MonitoringRequest request) { + if (request.getPackageName() != null) { + putMdcValue(request.getPackageName(), setAndClearMdc.key()); + } + } else if (obj instanceof UserPackage userPackage) { + if (userPackage.getPackageName() != null) { + putMdcValue(userPackage.getPackageName(), setAndClearMdc.key()); + } + } else if (obj instanceof String packageName) { + putMdcValue(setAndClearMdc.key(), packageName); + } else { + log.debug("no packageName found for for {}", pjp.toShortString()); + } + try { + return pjp.proceed(); + } finally { + MDC.remove(setAndClearMdc.key()); + } + } + + private void putMdcValue(String value, String key) { + try { + MDC.put(key, "[" + value + "] "); + } catch (Throwable e) { + log.error("{}", ExceptionUtils.getStackTrace(e)); + } + } +} diff --git a/src/main/java/ru/nbch/credit_tracker/config/mdc/SetAndClearMdc.java b/src/main/java/ru/nbch/credit_tracker/config/mdc/SetAndClearMdc.java new file mode 100644 index 0000000..8d8a14a --- /dev/null +++ b/src/main/java/ru/nbch/credit_tracker/config/mdc/SetAndClearMdc.java @@ -0,0 +1,15 @@ +package ru.nbch.credit_tracker.config.mdc; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +// Annotation to set an MDC key based on a method argument and clear it after method execution +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface SetAndClearMdc { + String key() default "ru.nbch.credit_tracker.log.mdc.key"; + // The index of the method argument to use for the MDC value (defaults to the first argument) + int argIndex() default 0; +} diff --git a/src/main/java/ru/nbch/credit_tracker/controller/SignalController.java b/src/main/java/ru/nbch/credit_tracker/controller/SignalController.java index cb7037b..401a9da 100644 --- a/src/main/java/ru/nbch/credit_tracker/controller/SignalController.java +++ b/src/main/java/ru/nbch/credit_tracker/controller/SignalController.java @@ -1,5 +1,6 @@ package ru.nbch.credit_tracker.controller; +import lombok.extern.slf4j.Slf4j; import org.springframework.core.io.Resource; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; @@ -14,6 +15,7 @@ import ru.nbch.credit_tracker.entities.user_response.success.MonitoringReportLis import ru.nbch.credit_tracker.entities.user_response.success.MonitoringSetupResponse; import ru.nbch.credit_tracker.facade.SignalFacade; +@Slf4j @RestController @RequestMapping("/api/v1/signal") public class SignalController { @@ -26,31 +28,37 @@ public class SignalController { @PostMapping(value = "/set", produces = MediaType.APPLICATION_XML_VALUE) public ResponseEntity setupMonitoring(@RequestParam("file") MultipartFile file) { + log.info("Processing /api/v1/signal/set"); return signalFacade.setupMonitoring(file); } @PostMapping(value = "/active_packages", consumes = MediaType.APPLICATION_XML_VALUE, produces = MediaType.APPLICATION_XML_VALUE) public ResponseEntity findActivePackages(@RequestBody MonitoringActivePackagesRequest activePackagesRequest) { + log.info("Processing /api/v1/signal/active_packages. UserId: {}", activePackagesRequest.getUserId()); return signalFacade.findActivePackages(activePackagesRequest); } @PostMapping(value = "/all_packages", consumes = MediaType.APPLICATION_XML_VALUE, produces = MediaType.APPLICATION_XML_VALUE) public ResponseEntity findAllPackages(@RequestBody MonitoringAllPackagesRequest allPackagesRequest) { + log.info("Processing /api/v1/signal/all_packages. UserId: {}", allPackagesRequest.getUserId()); return signalFacade.findAllPackages(allPackagesRequest); } @PostMapping(value = "/delete", params = {"username", "packageName"}, produces = MediaType.APPLICATION_XML_VALUE) public ResponseEntity deletePackage(@RequestParam("username") String username, @RequestParam("packageName") String packageName) { + log.info("Processing /api/v1/signal/delete. UserName: {}", username); return signalFacade.deletePackage(username, packageName); } @GetMapping(value = "/report_list", params = {"username", "packageName"}, produces = MediaType.APPLICATION_XML_VALUE) public ResponseEntity getReportList(@RequestParam("username") String username, @RequestParam("packageName") String packageName) { + log.info("Processing /api/v1/signal/report_list. UserName: {}, PackageName: {}", username, packageName); return signalFacade.getReportList(username, packageName); } @GetMapping(value = "/download", params = "file", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) public ResponseEntity downloadFile(@RequestParam("file") String file) { + log.info("Processing /api/v1/signal/download. FileName: {}", file); return signalFacade.downloadFile(file); } diff --git a/src/main/java/ru/nbch/credit_tracker/enums/SignalError.java b/src/main/java/ru/nbch/credit_tracker/enums/SignalError.java index 30f5e4c..ea68795 100644 --- a/src/main/java/ru/nbch/credit_tracker/enums/SignalError.java +++ b/src/main/java/ru/nbch/credit_tracker/enums/SignalError.java @@ -3,13 +3,13 @@ package ru.nbch.credit_tracker.enums; public enum SignalError implements IEnumKeyCode { ERROR_001("001", "Ошибка распаковки архива"), ERROR_002("002", "Отсутствует обязательный элемент: {}"), - ERROR_003("003", "Элемент ClientId не соответствует формату XXXXXXXXX"), + ERROR_003("003", "Элемент ClientId не соответствует формату XXXXXXXXX. Id: {}"), ERROR_005("005", "Неверный логин или пароль"), ERROR_006("006", "Доступ к услуге закрыт, обратитесь к курирующему менеджеру"), ERROR_007("007", "В системе уже существует пакет с таким именем"), ERROR_008("008", "PackageName не соответствует допустимому формату"), ERROR_009("009", "Превышен допустимый размер пакета"), - ERROR_010("010", "Идентификатор субъекта (Id) не соответствует допустимому формату"), + ERROR_010("010", "Идентификатор субъекта (Id) не соответствует допустимому формату: {}"), ERROR_011("011", "Получен невалидный XML"), ERROR_012("012", "Пакет {} не найден"), ERROR_013("013", "На стороне сервиса произошла ошибка, попробуйте повторить запрос позже или сообщите в техническую поддержку"), diff --git a/src/main/java/ru/nbch/credit_tracker/facade/SignalFacade.java b/src/main/java/ru/nbch/credit_tracker/facade/SignalFacade.java index 218a1c8..855692c 100644 --- a/src/main/java/ru/nbch/credit_tracker/facade/SignalFacade.java +++ b/src/main/java/ru/nbch/credit_tracker/facade/SignalFacade.java @@ -7,6 +7,7 @@ import org.springframework.core.io.Resource; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; +import ru.nbch.credit_tracker.config.mdc.SetAndClearMdc; import ru.nbch.credit_tracker.config.settings.CreditTrackerSettings; import ru.nbch.credit_tracker.entities.dto.UserPackage; import ru.nbch.credit_tracker.entities.user_request.active_packages.MonitoringActivePackagesRequest; @@ -33,6 +34,7 @@ import ru.nbch.credit_tracker.service.xml.stax.StaxXmlProcessor; import ru.nbch.credit_tracker.utils.CTUtil; import ru.nbch.credit_tracker.utils.ValidationUtil; import ru.nbch.credit_tracker.utils.ZipUtils; +import ru.nbch.credit_tracker.utils.error.SimpleMessageResolver; import javax.xml.stream.XMLStreamException; import java.io.BufferedInputStream; @@ -61,6 +63,7 @@ public class SignalFacade { private final SignalRestService signalRestService; private final MonitoringErrorProcesingService monitoringErrorProcesingService; private final StaxXmlProcessor staxXmlProcessor; + private final SimpleMessageResolver messageResolver; private final ExecutorService executor; private final long maxXmlFileSize; @@ -81,12 +84,16 @@ public class SignalFacade { this.staxXmlProcessor = staxXmlProcessor; this.executor = executor; this.maxXmlFileSize = config.getMaxXmlFileSize() * 1024 * 1024; + this.messageResolver = new SimpleMessageResolver(); } public ResponseEntity setupMonitoring(MultipartFile file) { // первичные валидации if (file.isEmpty() || !isZipFile(file)) { - throw clientExcp(SignalError.ERROR_001, XmlErrorFormat.MonitoringError); + SignalClientException e = clientExcp(SignalError.ERROR_001, XmlErrorFormat.MonitoringError); + String errorReason = messageResolver.resolve(e.getErrorMessage()); + log.warn("/api/v1/signal/set failed. Reason: {}. File is empty or don't match the type.", errorReason); + throw e; } // Валидация Архива на количетсво файлов, размер и расширение @@ -114,6 +121,8 @@ public class SignalFacade { } } catch (Exception e) { if (e instanceof SignalClientException) { + String errorReason = messageResolver.resolve(((SignalClientException) e).getErrorMessage()); + log.warn("/api/v1/signal/set failed on unzipping archive. Reason: {}", errorReason); throw (SignalClientException) e; } log.error(e.getMessage(), e); @@ -135,9 +144,12 @@ public class SignalFacade { } catch (Exception e) { CTUtil.removeFile(filePath); if (e.getCause() instanceof XMLStreamException) { + log.warn("/api/v1/signal/set failed on reading xml. Reason: {}", e.getMessage()); throw clientExcp(SignalError.ERROR_011, XmlErrorFormat.MonitoringError); } if (e instanceof SignalClientException) { + String errorReason = messageResolver.resolve(((SignalClientException) e).getErrorMessage()); + log.warn("/api/v1/signal/set failed on validation. Reason: {}", errorReason); throw (SignalClientException) e; } log.error(e.getMessage(), e); @@ -159,10 +171,12 @@ public class SignalFacade { } packageId = signalService.registerPackage(monitoringRequest); - log.info("Package registered. PackageId {}. Status: {}", packageId, Status.PROCESSING.name()); + log.info("Package registered. PackageId {}. PackageName: {}. Status: {}", packageId, monitoringRequest.getPackageName(), Status.PROCESSING.name()); } catch (Exception e) { CTUtil.removeFile(filePath); if (e instanceof SignalClientException) { + String errorReason = messageResolver.resolve(((SignalClientException) e).getErrorMessage()); + log.warn("/api/v1/signal/set rejected. PackageName: {}. Reason: {}", monitoringRequest.getPackageName(), errorReason); throw (SignalClientException) e; } log.error(e.getMessage(), e); @@ -172,6 +186,7 @@ public class SignalFacade { // в отдельном потоке запускаем регистрацию субъектов, поиск фидов и расчет флагов просрочек registerFidsAndDebts(packageId, monitoringRequest.getPackageName(), filePath); + log.info("/api/v1/signal/set processed successfully. PackageName: {}", monitoringRequest.getPackageName()); return ResponseEntity.ok().body(new MonitoringSetupResponse()); } @@ -220,6 +235,7 @@ public class SignalFacade { } } + @SetAndClearMdc(argIndex = 1) private void registerFidsAndDebts(Integer packageId, String packageName, String filePath) { executor.execute(() -> { try { @@ -273,12 +289,15 @@ public class SignalFacade { packages.getPackages().add(pack); }); response.setPackages(packages); + log.info("/api/v1/signal/active_packages processed successfully. UserId: {}", activePackagesRequest.getUserId()); return ResponseEntity.ok(response); } else { throw clientExcp(SignalError.ERROR_015, XmlErrorFormat.MonitoringActivePackagesError); } } catch (Exception e) { if (e instanceof SignalClientException) { + String errorReason = messageResolver.resolve(((SignalClientException) e).getErrorMessage()); + log.warn("/api/v1/signal/active_packages failed. UserId: {}. Reason: {}", activePackagesRequest.getUserId(), errorReason); throw (SignalClientException) e; } log.error(e.getMessage(), e); @@ -310,12 +329,15 @@ public class SignalFacade { packages.getPackages().add(pack); }); response.setPackages(packages); + log.info("/api/v1/signal/all_packages processed successfully. UserId: {}", allPackagesRequest.getUserId()); return ResponseEntity.ok(response); } else { throw clientExcp(SignalError.ERROR_019, XmlErrorFormat.MonitoringActivePackagesError); } } catch (Exception e) { if (e instanceof SignalClientException) { + String errorReason = messageResolver.resolve(((SignalClientException) e).getErrorMessage()); + log.warn("/api/v1/signal/all_packages failed. UserId: {}. Reason: {}", allPackagesRequest.getUserId(), errorReason); throw (SignalClientException) e; } log.error(e.getMessage(), e); @@ -339,9 +361,12 @@ public class SignalFacade { signalRestService.deletePackage(username, packageName); log.info("Package {} deleted", packageName); + log.info("/api/v1/signal/delete processed successfully. UserName: {}, PackageName: {}", username, packageName); return ResponseEntity.ok(response); } catch (Exception e) { if (e instanceof SignalClientException) { + String errorReason = messageResolver.resolve(((SignalClientException) e).getErrorMessage()); + log.warn("/api/v1/signal/delete failed. UserName: {}, PackageName: {}, Reason: {}", username, packageName, errorReason); throw (SignalClientException) e; } log.error(e.getMessage(), e); @@ -386,9 +411,12 @@ public class SignalFacade { MonitoringReportListResponse response = new MonitoringReportListResponse(); response.setPackageName(packageName); response.setReports(reports); + log.info("/api/v1/signal/report_list processes successfully. UserName: {}, PackageName: {}", username, packageName); return ResponseEntity.ok(response); } catch (Exception e) { if (e instanceof SignalClientException) { + String errorReason = messageResolver.resolve(((SignalClientException) e).getErrorMessage()); + log.warn("/api/v1/signal/report_list failed. UserName: {}, PackageName: {}. Reason: {}", username, packageName, errorReason); throw (SignalClientException) e; } log.error(e.getMessage(), e); @@ -408,6 +436,7 @@ public class SignalFacade { if (folder.exists() && folder.isDirectory()) { File file = new File(dirPath + File.separator + fileName); if (file.exists() && file.isFile()) { + log.info("/api/v1/signal/download processed successfully. FileName: {}", fileName); return ResponseEntity.ok().body(new FileUrlResource(file.getPath())); } } else { @@ -416,6 +445,8 @@ public class SignalFacade { throw clientExcp(SignalError.ERROR_014, XmlErrorFormat.MonitoringDownloadError, fileName); } catch (Exception e) { if (e instanceof SignalClientException) { + String errorReason = messageResolver.resolve(((SignalClientException) e).getErrorMessage()); + log.warn("/api/v1/signal/download failed. FileName: {}. Reason: {}", fileName, errorReason); throw (SignalClientException) e; } log.error(e.getMessage(), e); diff --git a/src/main/java/ru/nbch/credit_tracker/service/MonitoringErrorProcesingService.java b/src/main/java/ru/nbch/credit_tracker/service/MonitoringErrorProcesingService.java index 1d68f97..00edfbc 100644 --- a/src/main/java/ru/nbch/credit_tracker/service/MonitoringErrorProcesingService.java +++ b/src/main/java/ru/nbch/credit_tracker/service/MonitoringErrorProcesingService.java @@ -3,6 +3,7 @@ package ru.nbch.credit_tracker.service; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; 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; @@ -21,6 +22,11 @@ public class MonitoringErrorProcesingService { public void findErrors(Integer packageId) { UserPackage userPackage = signalService.findPackage(packageId); + processPackage(userPackage, packageId); + } + + @SetAndClearMdc + private void processPackage(UserPackage userPackage, Integer packageId) { if (userPackage != null) { String packCode = userPackage.getUserId().substring(0, 6) + "_" + userPackage.getPackageName(); log.info("Проверка на ошибки при постановке на мониторинг. packCode={}, packageId={}", packCode, packageId); diff --git a/src/main/java/ru/nbch/credit_tracker/service/MonitoringService.java b/src/main/java/ru/nbch/credit_tracker/service/MonitoringService.java index 1f30550..e6598a4 100644 --- a/src/main/java/ru/nbch/credit_tracker/service/MonitoringService.java +++ b/src/main/java/ru/nbch/credit_tracker/service/MonitoringService.java @@ -4,6 +4,7 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.scheduling.concurrent.CustomizableThreadFactory; import org.springframework.stereotype.Service; +import ru.nbch.credit_tracker.config.mdc.SetAndClearMdc; import ru.nbch.credit_tracker.config.settings.CreditTrackerSettings; import ru.nbch.credit_tracker.entities.dto.Hit; import ru.nbch.credit_tracker.entities.dto.PhoneFidMap; @@ -69,83 +70,88 @@ public class MonitoringService { log.info("Start monitoring"); List packages = signalService.findPackagesOnMonitoring(); for (UserPackage userPackage : packages) { - log.info("Processing package {}", userPackage.getPackageName()); - if (!signalService.hasNonNullfids(userPackage.getId())) { - log.info("Package {} has no fids. Package will be skipped.", userPackage.getPackageName()); - continue; - } - SgnlOnlineReport onlineReports = signalRestService.getReports(userPackage.getUserId().substring(0, 6) + "_" + userPackage.getPackageName()); - - if (onlineReports == null) { - log.info("No reports found for package {}", userPackage.getPackageName()); - continue; - } - - // Формируем список имен файлов, по которым сработали сигналы. Сюда входят только те имена, у которых парсится дата формирования отчета - List fileNames = onlineReports.getOperationReport().getReports().getReports().stream().map(Report::getFilename) - .filter(fileName -> CTUtil.parseDateFromReportFileName(fileName) != null).toList(); - - if (fileNames.isEmpty()) { - log.info("No new report found for package {}", userPackage.getPackageName()); - continue; - } - - LocalDateTime lastDownloadedReportDate = CTUtil.parseDateFromReportFileName(userPackage.getLastDownloadedReport()); - - if (lastDownloadedReportDate != null) { - // Отбираем только те отчеты, дата которых позже даты последнего загруженного отчета - fileNames = fileNames.stream() - .filter(fileName -> CTUtil.parseDateFromReportFileName(fileName).isAfter(lastDownloadedReportDate)) // отфильтровали только те имена, которые парсятся в дату, так что null тут не придет - .sorted(Comparator.comparing(CTUtil::parseDateFromReportFileName)) // Сортируем по дате - .toList(); - } else { - fileNames = fileNames.stream() - .sorted(Comparator.comparing(CTUtil::parseDateFromReportFileName)) // Сортируем по дате - .toList(); - } - - if (StringUtils.isNotBlank(userPackage.getLastDownloadedReport())) { - log.info("Last downloaded report found for package {}. Last Downloaded Report Name: {}", userPackage.getPackageName(), userPackage.getLastDownloadedReport()); - } else { - log.info("Last Downloaded Report Name is empty"); - } - - for (String newReport : fileNames) { - try { - String loadedReportPath = null; - if (StringUtils.isBlank(userPackage.getLastDownloadedReport())) { - loadedReportPath = loadReport(userPackage.getId(), newReport); // по этому пакету еще не было загруженных отчетов, продолжаем работу с полученным - } else if (lastDownloadedReportDate != null) { - LocalDateTime newReportDate = CTUtil.parseDateFromReportFileName(newReport); - if (newReportDate.isAfter(lastDownloadedReportDate)) { - loadedReportPath = loadReport(userPackage.getId(), newReport); // полученный отчет новее записанного в базу - } - } - if (loadedReportPath == null) { - log.info("Report was not loaded for package {}", userPackage.getPackageName()); - continue; - } - - String responsePath = generateResponse(userPackage, loadedReportPath); - String fileName = null; - if (responsePath != null) { - fileName = createResponseFile(userPackage, responsePath); - } - if (fileName != null) { - log.info("New report fileName: {}", fileName); - updateSignalHits(userPackage, responsePath, fileName); - signalService.updateLastDownloadedReport(userPackage.getId(), newReport); - } else { - log.info("Report was not created for package {}", userPackage.getPackageName()); - } - } catch (Exception e) { - log.error(e.getMessage(), e); - } - } + processPackage(userPackage); } log.info("Finish monitoring"); } + @SetAndClearMdc + 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 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 Путь к временному рещультирующему файлу @@ -297,7 +303,7 @@ public class MonitoringService { 10000, "/MonitoringSignalResponse/Subjects/Subject"); } catch (IOException e) { - throw new RuntimeException("Error while and updating hits", e); + throw new RuntimeException("Error while updating hits", e); } CTUtil.removeFile(responsePath); // удалям результирующий xml файл с диска (Он уже заархивирован к этому моменту) diff --git a/src/main/java/ru/nbch/credit_tracker/service/SignalService.java b/src/main/java/ru/nbch/credit_tracker/service/SignalService.java index 138a075..aa7d910 100644 --- a/src/main/java/ru/nbch/credit_tracker/service/SignalService.java +++ b/src/main/java/ru/nbch/credit_tracker/service/SignalService.java @@ -285,7 +285,7 @@ public class SignalService { request -> { if (!request.getPersons().isEmpty()) { List subjects = request.getPersons().stream().map(p -> { - log.error("Ошибка при формировании субъекта. package_id = {}, id = {}. Описание: {}", packageId, p.getUid(), p.getValue()); + log.warn("Ошибка при формировании субъекта. package_id = {}, id = {}. Описание: {}", packageId, p.getUid(), p.getValue()); return PhoneFidMap.builder() .id(p.getUid()) .errorText(p.getValue()) diff --git a/src/main/java/ru/nbch/credit_tracker/service/UpdateProcessingPackagesService.java b/src/main/java/ru/nbch/credit_tracker/service/UpdateProcessingPackagesService.java index 2888c82..4c03fc3 100644 --- a/src/main/java/ru/nbch/credit_tracker/service/UpdateProcessingPackagesService.java +++ b/src/main/java/ru/nbch/credit_tracker/service/UpdateProcessingPackagesService.java @@ -2,6 +2,7 @@ package ru.nbch.credit_tracker.service; import lombok.extern.slf4j.Slf4j; 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; @@ -38,6 +39,7 @@ public class UpdateProcessingPackagesService { log.info("Finish updating processing packages"); } + @SetAndClearMdc(argIndex = 1) private void process(Integer packageId, String packageName) { try { SignalService.UpdateResult updateResult = signalService.updateSubjects(packageId, null); @@ -56,7 +58,7 @@ public class UpdateProcessingPackagesService { } } catch (Exception e) { signalService.updatePackageStatus(packageId, Status.ERROR); - log.error("Ошибка при поиске фидов или расчете флагов просрочек. Package_id {}", packageId, e); + log.error("Ошибка при поиске фидов или расчете флагов просрочек. Package_id {}, PackageName: {}", packageId, packageName, e); } } } diff --git a/src/main/java/ru/nbch/credit_tracker/service/UpdatingFlagsService.java b/src/main/java/ru/nbch/credit_tracker/service/UpdatingFlagsService.java index 19549fb..d6a7664 100644 --- a/src/main/java/ru/nbch/credit_tracker/service/UpdatingFlagsService.java +++ b/src/main/java/ru/nbch/credit_tracker/service/UpdatingFlagsService.java @@ -3,6 +3,7 @@ package ru.nbch.credit_tracker.service; import lombok.extern.slf4j.Slf4j; import org.springframework.scheduling.concurrent.CustomizableThreadFactory; import org.springframework.stereotype.Service; +import ru.nbch.credit_tracker.config.mdc.SetAndClearMdc; import ru.nbch.credit_tracker.config.settings.CreditTrackerSettings; import ru.nbch.credit_tracker.entities.dto.PhoneFidMap; import ru.nbch.credit_tracker.entities.dto.UserPackage; @@ -57,6 +58,7 @@ public class UpdatingFlagsService { log.info("Finish updating flags"); } + @SetAndClearMdc(argIndex = 1) private void updateFlags(Integer packageId, String packageName) { try { signalService.updatePackageStatus(packageId, Status.PROCESSING); diff --git a/src/main/java/ru/nbch/credit_tracker/utils/ValidationUtil.java b/src/main/java/ru/nbch/credit_tracker/utils/ValidationUtil.java index d0068b2..240c3a6 100644 --- a/src/main/java/ru/nbch/credit_tracker/utils/ValidationUtil.java +++ b/src/main/java/ru/nbch/credit_tracker/utils/ValidationUtil.java @@ -1,5 +1,7 @@ package ru.nbch.credit_tracker.utils; +import lombok.extern.slf4j.Slf4j; +import ru.nbch.credit_tracker.config.mdc.SetAndClearMdc; import ru.nbch.credit_tracker.entities.user_request.monitoring_request.MonitoringRequest; import ru.nbch.credit_tracker.entities.user_request.monitoring_request.Subject; import ru.nbch.credit_tracker.enums.SignalError; @@ -10,47 +12,58 @@ import java.util.regex.Pattern; import static ru.nbch.credit_tracker.utils.error.ExceptionUtils.clientExcp; +@Slf4j public class ValidationUtil { private static String phoneRegex = "^\\d{9}$"; private static String packageNameRegex = "^[A-Z0-9_]{1,22}$"; private static String npIdRegex = "^\\S{1,36}$"; + @SetAndClearMdc public static void validateMonitoringRequest(MonitoringRequest request) throws SignalClientException { if (request == null) { + log.warn("Ошибка при валидации пакета. Получен невалидный XML"); throw clientExcp(SignalError.ERROR_011, XmlErrorFormat.MonitoringError); } - if (request.getUserId() == null) { - throw clientExcp(SignalError.ERROR_002, XmlErrorFormat.MonitoringError, "UserId"); - } - if (request.getPackageName() == null) { + log.warn("Ошибка при валидации пакета. Отсутствует обязательный элемент: PackageName"); throw clientExcp(SignalError.ERROR_002, XmlErrorFormat.MonitoringError, "PackageName"); } + if (!validatePackageName(request.getPackageName())) { + log.error("Ошибка при валидации пакета. PackageName не соответствует допустимому формату"); + throw clientExcp(SignalError.ERROR_008, XmlErrorFormat.MonitoringError); + } + + if (request.getUserId() == null) { + log.warn("Ошибка при валидации пакета.Отсутствует обязательный элемент: UserId"); + throw clientExcp(SignalError.ERROR_002, XmlErrorFormat.MonitoringError, "UserId"); + } + if (request.getPassword() == null) { + log.warn("Ошибка при валидации пакета.Отсутствует обязательный элемент: Password"); throw clientExcp(SignalError.ERROR_002, XmlErrorFormat.MonitoringError, "Password"); } if (request.getSubjects() == null) { + log.warn("Ошибка при валидации пакета.Отсутствует обязательный элемент: Subjects"); throw clientExcp(SignalError.ERROR_002, XmlErrorFormat.MonitoringError, "Subjects"); } if (request.getSubjects().getSubject().isEmpty()) { + log.warn("Ошибка при валидации пакета.Отсутствует обязательный элемент: Subject"); throw clientExcp(SignalError.ERROR_002, XmlErrorFormat.MonitoringError, "Subject"); } - if (!validatePackageName(request.getPackageName())) { - throw clientExcp(SignalError.ERROR_008, XmlErrorFormat.MonitoringError); - } - for (Subject subject : request.getSubjects().getSubject()) { - if (!validatePhone(subject.getClientId())) { - throw clientExcp(SignalError.ERROR_003, XmlErrorFormat.MonitoringError); - } if (!validateNPId(subject.getId())) { - throw clientExcp(SignalError.ERROR_010, XmlErrorFormat.MonitoringError); + log.warn("Ошибка при валидации пакета.Идентификатор субъекта (Id) не соответствует допустимому формату: {}.", subject.getId()); + throw clientExcp(SignalError.ERROR_010, XmlErrorFormat.MonitoringError, subject.getId()); + } + if (!validatePhone(subject.getClientId())) { + log.warn("Ошибка при валидации пакета.Элемент ClientId не соответствует формату XXXXXXXXX: {}", subject.getClientId()); + throw clientExcp(SignalError.ERROR_003, XmlErrorFormat.MonitoringError, subject.getId()); } } } diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml index 0825d76..e56b9a9 100644 --- a/src/main/resources/logback.xml +++ b/src/main/resources/logback.xml @@ -8,7 +8,7 @@ - %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n + %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} %X{ru.nbch.credit_tracker.log.mdc.key} - %msg%n utf-8 @@ -22,7 +22,7 @@ 100MB - %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n + %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} %X{ru.nbch.credit_tracker.log.mdc.key} - %msg%n utf-8 diff --git a/src/test/java/ru/nbch/credit_tracker/utils/ValidationUtilTest.java b/src/test/java/ru/nbch/credit_tracker/utils/ValidationUtilTest.java index b8146f3..aea414b 100644 --- a/src/test/java/ru/nbch/credit_tracker/utils/ValidationUtilTest.java +++ b/src/test/java/ru/nbch/credit_tracker/utils/ValidationUtilTest.java @@ -69,12 +69,12 @@ public class ValidationUtilTest { Arguments.of( "/monitoring_request/invalid_phone_request.zip", SignalError.ERROR_003, - Collections.emptyList().toArray(new Object[0]) + new Object[]{"abc123"} ), Arguments.of( "/monitoring_request/invalid_id_request.zip", SignalError.ERROR_010, - Collections.emptyList().toArray(new Object[0]) + new Object[]{"#####"} ) ); }