diff --git a/src/main/java/ru/nbch/credit_tracker/component/MonitoringRequestValidator.java b/src/main/java/ru/nbch/credit_tracker/component/MonitoringRequestValidator.java new file mode 100644 index 0000000..e48ba48 --- /dev/null +++ b/src/main/java/ru/nbch/credit_tracker/component/MonitoringRequestValidator.java @@ -0,0 +1,90 @@ +package ru.nbch.credit_tracker.component; + +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.zip.ZipEntry; +import java.util.zip.ZipInputStream; +import lombok.extern.slf4j.Slf4j; +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.xml.stax.StaxXmlProcessor; +import ru.nbch.credit_tracker.utils.ValidationUtil; +import ru.nbch.credit_tracker.utils.ZipUtils; +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 StaxXmlProcessor staxXmlProcessor; + + public MonitoringRequestValidator(CreditTrackerSettings config, + StaxXmlProcessor staxXmlProcessor) { + this.config = config; + 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 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(String filePath) { + log.debug("Validating monitoring request..."); + ValidationResult validationResult = new ValidationResult(); + try (BufferedInputStream in = new BufferedInputStream(Files.newInputStream(Path.of(filePath)), 8 * 1024 * 1024)) { + staxXmlProcessor.read(in, MonitoringRequest.class, + request -> { + try { + ValidationUtil.validateMonitoringRequest(request); + 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()); + } + request.getSubjects().getSubject().clear(); + } catch (SignalException e) { + validationResult.addError(new MonitoringValidationError(-1, "", e)); + } + }, + request -> request.getSubjects().getSubject().isEmpty(), + 40000, "/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; + } +} diff --git a/src/main/java/ru/nbch/credit_tracker/config/AppConfig.java b/src/main/java/ru/nbch/credit_tracker/config/AppConfig.java new file mode 100644 index 0000000..b4f4e0b --- /dev/null +++ b/src/main/java/ru/nbch/credit_tracker/config/AppConfig.java @@ -0,0 +1,15 @@ +package ru.nbch.credit_tracker.config; + +import java.util.ArrayDeque; +import java.util.Queue; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class AppConfig { + + @Bean + public Queue registeredPackages() { + return new ArrayDeque<>(); + } +} diff --git a/src/main/java/ru/nbch/credit_tracker/config/task/ConsumerConfig.java b/src/main/java/ru/nbch/credit_tracker/config/task/ConsumerConfig.java new file mode 100644 index 0000000..4c6141c --- /dev/null +++ b/src/main/java/ru/nbch/credit_tracker/config/task/ConsumerConfig.java @@ -0,0 +1,24 @@ +package ru.nbch.credit_tracker.config.task; + +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.Executors; +import java.util.concurrent.LinkedBlockingQueue; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import ru.nbch.credit_tracker.model.PackageSavePayload; +import ru.nbch.credit_tracker.service.task.PackageSaver; +import ru.nbch.credit_tracker.service.task.TaskConsumer; + +@Configuration +public class ConsumerConfig { + + @Bean + public BlockingQueue savingRequest() { + return new LinkedBlockingQueue<>(); + } + @Bean + public TaskConsumer saverTask(PackageSaver packageSaver, + BlockingQueue savingRequest) { + return new TaskConsumer<>(packageSaver, savingRequest, Executors.newFixedThreadPool(5)); + } +} diff --git a/src/main/java/ru/nbch/credit_tracker/controller/SignalControllerV2.java b/src/main/java/ru/nbch/credit_tracker/controller/SignalControllerV2.java new file mode 100644 index 0000000..2d878db --- /dev/null +++ b/src/main/java/ru/nbch/credit_tracker/controller/SignalControllerV2.java @@ -0,0 +1,30 @@ +package ru.nbch.credit_tracker.controller; + +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; +import ru.nbch.credit_tracker.entities.user_response.success.MonitoringSetupResponse; +import ru.nbch.credit_tracker.facade.SignalFacadeV2; + +@RestController +@RequestMapping("/api/v2/signal") +public class SignalControllerV2 { + + private final SignalFacadeV2 signalFacadeV2; + + public SignalControllerV2(SignalFacadeV2 signalFacadeV2) { + this.signalFacadeV2 = signalFacadeV2; + } + + @PostMapping(value = "/set", produces = MediaType.APPLICATION_XML_VALUE) + public ResponseEntity setupMonitoring(@RequestParam("file") MultipartFile file) { + signalFacadeV2.setupMonitoringRequest(file); + return ResponseEntity.ok().body(new MonitoringSetupResponse()); + } + +} + diff --git a/src/main/java/ru/nbch/credit_tracker/facade/SignalFacadeV2.java b/src/main/java/ru/nbch/credit_tracker/facade/SignalFacadeV2.java new file mode 100644 index 0000000..f4904a9 --- /dev/null +++ b/src/main/java/ru/nbch/credit_tracker/facade/SignalFacadeV2.java @@ -0,0 +1,108 @@ +package ru.nbch.credit_tracker.facade; + +import java.io.File; +import java.io.IOException; +import java.util.concurrent.BlockingQueue; +import javax.xml.stream.XMLStreamException; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.io.FileUtils; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; +import ru.nbch.credit_tracker.component.MonitoringRequestValidator; +import ru.nbch.credit_tracker.config.settings.CreditTrackerSettings; +import ru.nbch.credit_tracker.enums.SignalError; +import ru.nbch.credit_tracker.enums.Status; +import ru.nbch.credit_tracker.enums.XmlErrorFormat; +import ru.nbch.credit_tracker.exceptions.SignalClientException; +import ru.nbch.credit_tracker.model.MonitoringValidationError; +import ru.nbch.credit_tracker.model.PackageSavePayload; +import ru.nbch.credit_tracker.model.ValidationResult; +import ru.nbch.credit_tracker.service.SignalService; +import ru.nbch.credit_tracker.service.scoring.AuthorizationService; +import ru.nbch.credit_tracker.utils.CTUtil; +import ru.nbch.credit_tracker.utils.ZipUtils; +import static ru.nbch.credit_tracker.utils.error.ExceptionUtils.clientExcp; +import static ru.nbch.credit_tracker.utils.error.ExceptionUtils.serverExcp; + +@Service +@Slf4j +public class SignalFacadeV2 { + + private final MonitoringRequestValidator monitoringRequestValidator; + private final CreditTrackerSettings config; + private final SignalService signalService; + private final AuthorizationService authorizationService; + private final BlockingQueue savingRequest; + public SignalFacadeV2(MonitoringRequestValidator monitoringRequestValidator, + CreditTrackerSettings config, + SignalService signalService, + AuthorizationService authorizationService, + BlockingQueue savingRequest) { + this.monitoringRequestValidator = monitoringRequestValidator; + this.config = config; + this.signalService = signalService; + this.authorizationService = authorizationService; + this.savingRequest = savingRequest; + } + + public void setupMonitoringRequest(MultipartFile file) { + log.debug("Start processing monitoring request"); + monitoringRequestValidator.validateZipContent(file); + + log.trace("Saving monitoring request to tmp file..."); + String filePath; + try { + String folderPath = config.getLoadDir() + File.separator + "tmp_folder" + File.separator; + filePath = ZipUtils.writeTmpFile(folderPath, file); + } catch (IOException e) { + throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringError, e); + } + + log.debug("File saved to {}", filePath); + ValidationResult validationResult = null; + try { + validationResult = monitoringRequestValidator.readAndValidateMonitoringRequest(filePath); + if (!validationResult.getValidationErrors().isEmpty()) { + MonitoringValidationError firstError = validationResult.getValidationErrors().get(0); + signalService.rejectPackage(validationResult.getUserId(), validationResult.getPackageName()); + FileUtils.delete(new File(filePath)); + throw firstError.getSignalException(); + } + } catch (RuntimeException e) { + if (e.getCause() instanceof XMLStreamException) { + throw clientExcp(SignalError.ERROR_011, XmlErrorFormat.MonitoringError); + } + throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringError, e); + } catch (IOException e) { + log.warn("Error deleting tmp file", e); + } + + String packageName = validationResult.getPackageName(); + String userId = validationResult.getUserId(); + String password = validationResult.getPassword(); + try { + // проверка авторизации + AuthorizationService.AuthResult authResult = authorizationService.findAuthInfo(userId, password); + if (!authResult.isSuccess()) { + signalService.rejectPackage(userId, packageName); + throw clientExcp(authResult.getError(), XmlErrorFormat.MonitoringError); + } + + // проверка на уникальность пакета + if (signalService.isPackageNotUnique(userId, packageName)) { + signalService.rejectPackage(userId, packageName); + throw clientExcp(SignalError.ERROR_007, XmlErrorFormat.MonitoringError); + } + + Integer packageId = signalService.registerPackage(userId, packageName); + log.info("Package registered. PackageId {}. Status: {}", packageId, Status.PROCESSING.name()); + savingRequest.add(new PackageSavePayload(packageId, filePath)); + } catch (Exception e) { + CTUtil.removeFile(filePath); + if (e instanceof SignalClientException) { + throw (SignalClientException) e; + } + throw serverExcp(SignalError.ERROR_013, XmlErrorFormat.MonitoringError, e); + } + } +} diff --git a/src/main/java/ru/nbch/credit_tracker/model/MonitoringValidationError.java b/src/main/java/ru/nbch/credit_tracker/model/MonitoringValidationError.java new file mode 100644 index 0000000..e975552 --- /dev/null +++ b/src/main/java/ru/nbch/credit_tracker/model/MonitoringValidationError.java @@ -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; + } +} diff --git a/src/main/java/ru/nbch/credit_tracker/model/PackageSavePayload.java b/src/main/java/ru/nbch/credit_tracker/model/PackageSavePayload.java new file mode 100644 index 0000000..79b1f3d --- /dev/null +++ b/src/main/java/ru/nbch/credit_tracker/model/PackageSavePayload.java @@ -0,0 +1,21 @@ +package ru.nbch.credit_tracker.model; + +import ru.nbch.credit_tracker.service.task.TaskPayload; + +public class PackageSavePayload implements TaskPayload { + private final Integer packageId; + private final String filePath; + + public PackageSavePayload(Integer packageId, String filePath) { + this.packageId = packageId; + this.filePath = filePath; + } + + public Integer getPackageId() { + return packageId; + } + + public String getFilePath() { + return filePath; + } +} diff --git a/src/main/java/ru/nbch/credit_tracker/model/ValidationResult.java b/src/main/java/ru/nbch/credit_tracker/model/ValidationResult.java new file mode 100644 index 0000000..19149c5 --- /dev/null +++ b/src/main/java/ru/nbch/credit_tracker/model/ValidationResult.java @@ -0,0 +1,51 @@ +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 List validationErrors = new ArrayList<>(); + + public void setPackageName(String packageName) { + this.packageName = packageName; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public void setValidationErrors(List 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 List getValidationErrors() { + return validationErrors; + } + + public void addError(MonitoringValidationError error) { + this.validationErrors.add(error); + } +} + 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 8eca0bf..1becb6d 100644 --- a/src/main/java/ru/nbch/credit_tracker/service/SignalService.java +++ b/src/main/java/ru/nbch/credit_tracker/service/SignalService.java @@ -73,6 +73,10 @@ public class SignalService { return userPackageService.isPackageNotUnique(request.getUserId(), request.getPackageName()); } + public boolean isPackageNotUnique(String userId, String packageName) { + return userPackageService.isPackageNotUnique(userId, packageName); + } + public Integer registerPackage(MonitoringRequest request) { UserPackage userPackage = UserPackage.builder() .userId(request.getUserId()) @@ -83,6 +87,16 @@ public class SignalService { return userPackageService.insertPackage(userPackage); } + public Integer registerPackage(String userId, String packageName) { + UserPackage userPackage = UserPackage.builder() + .userId(userId) + .packageName(packageName) + .createdAt(LocalDateTime.now()) + .status(Status.PROCESSING.name()) + .build(); + return userPackageService.insertPackage(userPackage); + } + public void registerSubjects(String filePath, Integer packageId) { log.info("Start registering subjects. PackageId: {}", packageId); try (BufferedInputStream in = new BufferedInputStream(Files.newInputStream(Path.of(filePath)), 8 * 1024 * 1024)) { @@ -153,6 +167,7 @@ public class SignalService { if (fidsMapByBatch.size() + phonesByFidToProcessing.size() <= batchSize) { phonesByFidToProcessing.putAll(fidsMapByBatch); } else { + //todo execute find name and id -> forming xml boolean fidsFound = updateFlags(phonesByFidToProcessing, packageId); if (noFidsFound) { noFidsFound = !fidsFound; @@ -221,6 +236,16 @@ public class SignalService { userPackageService.insertPackage(userPackage); } + public void rejectPackage(String userId, String packageName) { + UserPackage userPackage = UserPackage.builder() + .userId(userId) + .packageName(packageName) + .createdAt(LocalDateTime.now()) + .status(Status.REJECTED.name()) + .build(); + userPackageService.insertPackage(userPackage); + } + public void updatePackageStatus(Integer packageId, Status status) { userPackageService.updatePackageStatus(packageId, status); } diff --git a/src/main/java/ru/nbch/credit_tracker/service/task/Dispatcher.java b/src/main/java/ru/nbch/credit_tracker/service/task/Dispatcher.java new file mode 100644 index 0000000..1e46e57 --- /dev/null +++ b/src/main/java/ru/nbch/credit_tracker/service/task/Dispatcher.java @@ -0,0 +1,5 @@ +package ru.nbch.credit_tracker.service.task; + +public interface Dispatcher { + void handle(X taskPayload); +} diff --git a/src/main/java/ru/nbch/credit_tracker/service/task/PackageSaver.java b/src/main/java/ru/nbch/credit_tracker/service/task/PackageSaver.java new file mode 100644 index 0000000..99f6df7 --- /dev/null +++ b/src/main/java/ru/nbch/credit_tracker/service/task/PackageSaver.java @@ -0,0 +1,54 @@ +package ru.nbch.credit_tracker.service.task; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Queue; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import ru.nbch.credit_tracker.entities.user_request.monitoring_request.MonitoringRequest; +import ru.nbch.credit_tracker.model.PackageSavePayload; +import ru.nbch.credit_tracker.service.signal.PhoneFidMapService; +import ru.nbch.credit_tracker.service.xml.stax.StaxXmlProcessor; + +@Service +@Slf4j +public class PackageSaver implements Dispatcher { + private final StaxXmlProcessor xmlProcessor; + private final PhoneFidMapService phoneFidMapService; + private final Queue registeredPackage; + private final static int BATCH_SIZE = 10_000; + + public PackageSaver(StaxXmlProcessor xmlProcessor, + PhoneFidMapService phoneFidMapService, + Queue registeredPackage) { + this.xmlProcessor = xmlProcessor; + this.phoneFidMapService = phoneFidMapService; + this.registeredPackage = registeredPackage; + } + @Override + public void handle(PackageSavePayload taskPayload) { + Integer packageId = taskPayload.getPackageId(); + Path pkgPath = Paths.get(taskPayload.getFilePath()); + log.info("Start registering subjects. PackageId: {}", taskPayload.getPackageId()); + try (BufferedInputStream in = new BufferedInputStream(Files.newInputStream(pkgPath), 8 * 1024 * 1024)) { + xmlProcessor.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() + , + BATCH_SIZE, "/MonitoringRequest/Subjects/Subject"); + } catch (IOException e) { + throw new RuntimeException("Error while parsing monitoring request", e); + } + log.info("Subjects were registered. PackageId: {}", packageId); + registeredPackage.add(packageId); + } +} diff --git a/src/main/java/ru/nbch/credit_tracker/service/task/TaskConsumer.java b/src/main/java/ru/nbch/credit_tracker/service/task/TaskConsumer.java new file mode 100644 index 0000000..c8b57c2 --- /dev/null +++ b/src/main/java/ru/nbch/credit_tracker/service/task/TaskConsumer.java @@ -0,0 +1,48 @@ +package ru.nbch.credit_tracker.service.task; + +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ExecutorService; +import org.springframework.context.SmartLifecycle; + +public class TaskConsumer implements SmartLifecycle { + private final Dispatcher dispatcher; + private final BlockingQueue taskQueue; + private final ExecutorService worker; + private volatile boolean running; + + public TaskConsumer(Dispatcher dispatcher, + BlockingQueue taskQueue, + ExecutorService worker) { + this.dispatcher = dispatcher; + this.taskQueue = taskQueue; + this.worker = worker; + } + + + @Override + public void start() { + running = true; + worker.submit(() -> { + while (running && !Thread.currentThread().isInterrupted()) { + try { + X job = taskQueue.take(); + dispatcher.handle(job); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } catch (Exception e) { + } + } + }); + } + + @Override + public void stop() { + running = false; + worker.shutdownNow(); + } + + @Override + public boolean isRunning() { + return running; + } +} diff --git a/src/main/java/ru/nbch/credit_tracker/service/task/TaskPayload.java b/src/main/java/ru/nbch/credit_tracker/service/task/TaskPayload.java new file mode 100644 index 0000000..0660f52 --- /dev/null +++ b/src/main/java/ru/nbch/credit_tracker/service/task/TaskPayload.java @@ -0,0 +1,4 @@ +package ru.nbch.credit_tracker.service.task; + +public interface TaskPayload { +} diff --git a/src/main/java/ru/nbch/credit_tracker/service/xml/stax/XmlProcessorStaxImpl.java b/src/main/java/ru/nbch/credit_tracker/service/xml/stax/XmlProcessorStaxImpl.java index 052ed62..a39a207 100644 --- a/src/main/java/ru/nbch/credit_tracker/service/xml/stax/XmlProcessorStaxImpl.java +++ b/src/main/java/ru/nbch/credit_tracker/service/xml/stax/XmlProcessorStaxImpl.java @@ -104,7 +104,11 @@ public class XmlProcessorStaxImpl implements StaxXmlProcessor { } @Override - public Z read(InputStream inputStream, Class clazz, Consumer objProcessor, Function emptyBatchCheckFunction, Integer batchSize, String endBatchElementPath) { + public Z read(InputStream inputStream, + Class clazz, Consumer objProcessor, + Function emptyBatchCheckFunction, + Integer batchSize, + String endBatchElementPath) { try { XMLStreamReader xmlStreamReader = xmlInputFactory.createXMLStreamReader(inputStream); Supplier> staxReaderSupplier = staxReaders.get(clazz); diff --git a/src/main/java/ru/nbch/credit_tracker/utils/ZipUtils.java b/src/main/java/ru/nbch/credit_tracker/utils/ZipUtils.java index 4d1438e..8acc2fd 100644 --- a/src/main/java/ru/nbch/credit_tracker/utils/ZipUtils.java +++ b/src/main/java/ru/nbch/credit_tracker/utils/ZipUtils.java @@ -1,10 +1,15 @@ package ru.nbch.credit_tracker.utils; import java.io.IOException; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Optional; +import java.util.UUID; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import lombok.extern.slf4j.Slf4j; +import org.springframework.web.multipart.MultipartFile; @Slf4j public class ZipUtils { @@ -25,4 +30,27 @@ public class ZipUtils { } return zipEntry; } + + public static boolean isZipFile(MultipartFile file) { + return file.getContentType() != null && + (file.getContentType().equals("application/zip") || file.getContentType().equals("application/x-zip-compressed")) || + file.getOriginalFilename() != null && file.getOriginalFilename().toLowerCase().endsWith(".zip"); + } + + public static String writeTmpFile(String path, MultipartFile file) throws IOException { + CTUtil.createFolder(path); + String filePath = path + UUID.randomUUID() + "monitoring_request.xml"; + + try (OutputStream out = Files.newOutputStream(Path.of(filePath))) { + ZipInputStream zipInputStream = new ZipInputStream(file.getInputStream()); + zipInputStream.getNextEntry(); + byte[] buffer = new byte[8 * 1024 * 1024]; + int bytesRead; + while ((bytesRead = zipInputStream.read(buffer)) > 0) { + out.write(buffer, 0, bytesRead); + } + + return filePath; + } + } } diff --git a/src/test/java/ru/nbch/credit_tracker/service/PhoneService2Test.java b/src/test/java/ru/nbch/credit_tracker/service/PhoneService2Test.java index 509324a..87389a4 100644 --- a/src/test/java/ru/nbch/credit_tracker/service/PhoneService2Test.java +++ b/src/test/java/ru/nbch/credit_tracker/service/PhoneService2Test.java @@ -46,6 +46,47 @@ public class PhoneService2Test { Assertions.assertEquals("1115", res.get(127).getPhone()); } + @Test + public void defineFidByPhoneTest2() { + PhoneService2 phoneService = new PhoneService2(phoneRepository); + + fidsDataWithEqualSinceDt.sort(byDbOrder); + when(phoneRepository.findFids(any())).thenReturn(fidsDataWithEqualSinceDt2); + + List phoneFidMap = new ArrayList<>(); + phoneFidMap.add(PhoneFidMap.builder().phone("1111").build()); + phoneFidMap.add(PhoneFidMap.builder().phone("1112").build()); + phoneFidMap.add(PhoneFidMap.builder().phone("1113").build()); + phoneFidMap.add(PhoneFidMap.builder().phone("1114").build()); + phoneFidMap.add(PhoneFidMap.builder().phone("1115").build()); + + Map res = phoneService.defineFidByPhone(phoneFidMap); + Assertions.assertEquals(3, res.size()); + Assertions.assertEquals("1112", res.get(125).getPhone()); + Assertions.assertEquals("1114", res.get(126).getPhone()); + Assertions.assertEquals("1115", res.get(127).getPhone()); + } + + @Test + public void defineFidByPhoneTest3() { + PhoneService2 phoneService = new PhoneService2(phoneRepository); + + fidsDataWithEqualSinceDt.sort(byDbOrder); + when(phoneRepository.findFids(any())).thenReturn(fidsDataWithEqualSinceDt3); + + List phoneFidMap = new ArrayList<>(); + phoneFidMap.add(PhoneFidMap.builder().phone("1111").build()); + phoneFidMap.add(PhoneFidMap.builder().phone("1112").build()); + phoneFidMap.add(PhoneFidMap.builder().phone("1113").build()); + phoneFidMap.add(PhoneFidMap.builder().phone("1114").build()); + phoneFidMap.add(PhoneFidMap.builder().phone("1115").build()); + + Map res = phoneService.defineFidByPhone(phoneFidMap); + Assertions.assertEquals(2, res.size()); + Assertions.assertEquals("1114", res.get(126).getPhone()); + Assertions.assertEquals("1115", res.get(127).getPhone()); + } + private final List fidsDataWithEqualSinceDt = new ArrayList<>( List.of( new PhoneRepository.FidsData(123, "91111", LocalDate.of(2025, 9, 10)), @@ -57,6 +98,23 @@ public class PhoneService2Test { ) ); + private final List fidsDataWithEqualSinceDt2 = new ArrayList<>( + List.of( + new PhoneRepository.FidsData(125, "91112", LocalDate.of(2024, 10, 10)), + new PhoneRepository.FidsData(126, "91114", LocalDate.of(2024, 10, 11)), + new PhoneRepository.FidsData(127, "91115", LocalDate.of(2025, 9, 11)), + new PhoneRepository.FidsData(128, "91115", LocalDate.of(2025, 9, 10)) + ) + ); + + private final List fidsDataWithEqualSinceDt3 = new ArrayList<>( + List.of( + new PhoneRepository.FidsData(126, "91114", LocalDate.of(2024, 10, 11)), + new PhoneRepository.FidsData(127, "91115", LocalDate.of(2025, 9, 11)), + new PhoneRepository.FidsData(128, "91115", LocalDate.of(2025, 9, 10)) + ) + ); + Comparator byDbOrder = Comparator.comparing(PhoneRepository.FidsData::getPhone, Comparator.nullsFirst(Comparator.naturalOrder())) // pn.number ASC .thenComparing(PhoneRepository.FidsData::getFileSinceDate, Comparator.nullsLast(Comparator.reverseOrder())) // file_since_dt DESC diff --git a/src/test/java/ru/nbch/credit_tracker/service/PhoneServiceTest.java b/src/test/java/ru/nbch/credit_tracker/service/PhoneServiceTest.java index 1b9f5fa..af187ce 100644 --- a/src/test/java/ru/nbch/credit_tracker/service/PhoneServiceTest.java +++ b/src/test/java/ru/nbch/credit_tracker/service/PhoneServiceTest.java @@ -46,6 +46,47 @@ public class PhoneServiceTest { Assertions.assertEquals("1115", res.get(127).getPhone()); } + @Test + public void defineFidByPhoneTest2() { + PhoneService phoneService = new PhoneService(phoneRepository); + + fidsDataWithEqualSinceDt.sort(byDbOrder); + when(phoneRepository.findFidsLong(any())).thenReturn(fidsDataWithEqualSinceDt2); + + List phoneFidMap = new ArrayList<>(); + phoneFidMap.add(PhoneFidMap.builder().phone("1111").build()); + phoneFidMap.add(PhoneFidMap.builder().phone("1112").build()); + phoneFidMap.add(PhoneFidMap.builder().phone("1113").build()); + phoneFidMap.add(PhoneFidMap.builder().phone("1114").build()); + phoneFidMap.add(PhoneFidMap.builder().phone("1115").build()); + + Map res = phoneService.defineFidByPhone(phoneFidMap); + Assertions.assertEquals(3, res.size()); + Assertions.assertEquals("1112", res.get(125).getPhone()); + Assertions.assertEquals("1114", res.get(126).getPhone()); + Assertions.assertEquals("1115", res.get(127).getPhone()); + } + + @Test + public void defineFidByPhoneTest3() { + PhoneService phoneService = new PhoneService(phoneRepository); + + fidsDataWithEqualSinceDt.sort(byDbOrder); + when(phoneRepository.findFidsLong(any())).thenReturn(fidsDataWithEqualSinceDt3); + + List phoneFidMap = new ArrayList<>(); + phoneFidMap.add(PhoneFidMap.builder().phone("1111").build()); + phoneFidMap.add(PhoneFidMap.builder().phone("1112").build()); + phoneFidMap.add(PhoneFidMap.builder().phone("1113").build()); + phoneFidMap.add(PhoneFidMap.builder().phone("1114").build()); + phoneFidMap.add(PhoneFidMap.builder().phone("1115").build()); + + Map res = phoneService.defineFidByPhone(phoneFidMap); + Assertions.assertEquals(2, res.size()); + Assertions.assertEquals("1114", res.get(126).getPhone()); + Assertions.assertEquals("1115", res.get(127).getPhone()); + } + private final List fidsDataWithEqualSinceDt = new ArrayList<>( List.of( new PhoneRepository.FidsDataLong(123, 91111L, LocalDate.of(2025, 9, 10)), @@ -57,6 +98,23 @@ public class PhoneServiceTest { ) ); + private final List fidsDataWithEqualSinceDt2 = new ArrayList<>( + List.of( + new PhoneRepository.FidsDataLong(125, 91112L, LocalDate.of(2024, 10, 10)), + new PhoneRepository.FidsDataLong(126, 91114L, LocalDate.of(2024, 10, 11)), + new PhoneRepository.FidsDataLong(127, 91115L, LocalDate.of(2025, 9, 11)), + new PhoneRepository.FidsDataLong(128, 91115L, LocalDate.of(2025, 9, 10)) + ) + ); + + private final List fidsDataWithEqualSinceDt3 = new ArrayList<>( + List.of( + new PhoneRepository.FidsDataLong(126, 91114L, LocalDate.of(2024, 10, 11)), + new PhoneRepository.FidsDataLong(127, 91115L, LocalDate.of(2025, 9, 11)), + new PhoneRepository.FidsDataLong(128, 91115L, LocalDate.of(2025, 9, 10)) + ) + ); + Comparator byDbOrder = Comparator.comparing(PhoneRepository.FidsDataLong::getPhone, Comparator.nullsFirst(Comparator.naturalOrder())) // pn.number ASC .thenComparing(PhoneRepository.FidsDataLong::getFileSinceDate, Comparator.nullsLast(Comparator.reverseOrder())) // file_since_dt DESC