some refactoring and new Facade classes
This commit is contained in:
parent
f4b49c06ed
commit
194828d0b7
17 changed files with 654 additions and 1 deletions
|
|
@ -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<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(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;
|
||||||
|
}
|
||||||
|
}
|
||||||
15
src/main/java/ru/nbch/credit_tracker/config/AppConfig.java
Normal file
15
src/main/java/ru/nbch/credit_tracker/config/AppConfig.java
Normal file
|
|
@ -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<Integer> registeredPackages() {
|
||||||
|
return new ArrayDeque<>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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<PackageSavePayload> savingRequest() {
|
||||||
|
return new LinkedBlockingQueue<>();
|
||||||
|
}
|
||||||
|
@Bean
|
||||||
|
public TaskConsumer<PackageSavePayload> saverTask(PackageSaver packageSaver,
|
||||||
|
BlockingQueue<PackageSavePayload> savingRequest) {
|
||||||
|
return new TaskConsumer<>(packageSaver, savingRequest, Executors.newFixedThreadPool(5));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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<MonitoringSetupResponse> setupMonitoring(@RequestParam("file") MultipartFile file) {
|
||||||
|
signalFacadeV2.setupMonitoringRequest(file);
|
||||||
|
return ResponseEntity.ok().body(new MonitoringSetupResponse());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
108
src/main/java/ru/nbch/credit_tracker/facade/SignalFacadeV2.java
Normal file
108
src/main/java/ru/nbch/credit_tracker/facade/SignalFacadeV2.java
Normal file
|
|
@ -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<PackageSavePayload> savingRequest;
|
||||||
|
public SignalFacadeV2(MonitoringRequestValidator monitoringRequestValidator,
|
||||||
|
CreditTrackerSettings config,
|
||||||
|
SignalService signalService,
|
||||||
|
AuthorizationService authorizationService,
|
||||||
|
BlockingQueue<PackageSavePayload> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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<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 List<MonitoringValidationError> getValidationErrors() {
|
||||||
|
return validationErrors;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addError(MonitoringValidationError error) {
|
||||||
|
this.validationErrors.add(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -73,6 +73,10 @@ public class SignalService {
|
||||||
return userPackageService.isPackageNotUnique(request.getUserId(), request.getPackageName());
|
return userPackageService.isPackageNotUnique(request.getUserId(), request.getPackageName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isPackageNotUnique(String userId, String packageName) {
|
||||||
|
return userPackageService.isPackageNotUnique(userId, packageName);
|
||||||
|
}
|
||||||
|
|
||||||
public Integer registerPackage(MonitoringRequest request) {
|
public Integer registerPackage(MonitoringRequest request) {
|
||||||
UserPackage userPackage = UserPackage.builder()
|
UserPackage userPackage = UserPackage.builder()
|
||||||
.userId(request.getUserId())
|
.userId(request.getUserId())
|
||||||
|
|
@ -83,6 +87,16 @@ public class SignalService {
|
||||||
return userPackageService.insertPackage(userPackage);
|
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) {
|
public void registerSubjects(String filePath, Integer packageId) {
|
||||||
log.info("Start registering subjects. PackageId: {}", packageId);
|
log.info("Start registering subjects. PackageId: {}", packageId);
|
||||||
try (BufferedInputStream in = new BufferedInputStream(Files.newInputStream(Path.of(filePath)), 8 * 1024 * 1024)) {
|
try (BufferedInputStream in = new BufferedInputStream(Files.newInputStream(Path.of(filePath)), 8 * 1024 * 1024)) {
|
||||||
|
|
@ -153,6 +167,7 @@ public class SignalService {
|
||||||
if (fidsMapByBatch.size() + phonesByFidToProcessing.size() <= batchSize) {
|
if (fidsMapByBatch.size() + phonesByFidToProcessing.size() <= batchSize) {
|
||||||
phonesByFidToProcessing.putAll(fidsMapByBatch);
|
phonesByFidToProcessing.putAll(fidsMapByBatch);
|
||||||
} else {
|
} else {
|
||||||
|
//todo execute find name and id -> forming xml
|
||||||
boolean fidsFound = updateFlags(phonesByFidToProcessing, packageId);
|
boolean fidsFound = updateFlags(phonesByFidToProcessing, packageId);
|
||||||
if (noFidsFound) {
|
if (noFidsFound) {
|
||||||
noFidsFound = !fidsFound;
|
noFidsFound = !fidsFound;
|
||||||
|
|
@ -221,6 +236,16 @@ public class SignalService {
|
||||||
userPackageService.insertPackage(userPackage);
|
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) {
|
public void updatePackageStatus(Integer packageId, Status status) {
|
||||||
userPackageService.updatePackageStatus(packageId, status);
|
userPackageService.updatePackageStatus(packageId, status);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
package ru.nbch.credit_tracker.service.task;
|
||||||
|
|
||||||
|
public interface Dispatcher<X> {
|
||||||
|
void handle(X taskPayload);
|
||||||
|
}
|
||||||
|
|
@ -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<PackageSavePayload> {
|
||||||
|
private final StaxXmlProcessor xmlProcessor;
|
||||||
|
private final PhoneFidMapService phoneFidMapService;
|
||||||
|
private final Queue<Integer> registeredPackage;
|
||||||
|
private final static int BATCH_SIZE = 10_000;
|
||||||
|
|
||||||
|
public PackageSaver(StaxXmlProcessor xmlProcessor,
|
||||||
|
PhoneFidMapService phoneFidMapService,
|
||||||
|
Queue<Integer> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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<X extends TaskPayload> implements SmartLifecycle {
|
||||||
|
private final Dispatcher<X> dispatcher;
|
||||||
|
private final BlockingQueue<X> taskQueue;
|
||||||
|
private final ExecutorService worker;
|
||||||
|
private volatile boolean running;
|
||||||
|
|
||||||
|
public TaskConsumer(Dispatcher<X> dispatcher,
|
||||||
|
BlockingQueue<X> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
package ru.nbch.credit_tracker.service.task;
|
||||||
|
|
||||||
|
public interface TaskPayload {
|
||||||
|
}
|
||||||
|
|
@ -104,7 +104,11 @@ public class XmlProcessorStaxImpl implements StaxXmlProcessor {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <Z> Z read(InputStream inputStream, Class<Z> clazz, Consumer<Z> objProcessor, Function<Z, Boolean> emptyBatchCheckFunction, Integer batchSize, String endBatchElementPath) {
|
public <Z> Z read(InputStream inputStream,
|
||||||
|
Class<Z> clazz, Consumer<Z> objProcessor,
|
||||||
|
Function<Z, Boolean> emptyBatchCheckFunction,
|
||||||
|
Integer batchSize,
|
||||||
|
String endBatchElementPath) {
|
||||||
try {
|
try {
|
||||||
XMLStreamReader xmlStreamReader = xmlInputFactory.createXMLStreamReader(inputStream);
|
XMLStreamReader xmlStreamReader = xmlInputFactory.createXMLStreamReader(inputStream);
|
||||||
Supplier<StaxReader<?>> staxReaderSupplier = staxReaders.get(clazz);
|
Supplier<StaxReader<?>> staxReaderSupplier = staxReaders.get(clazz);
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,15 @@
|
||||||
package ru.nbch.credit_tracker.utils;
|
package ru.nbch.credit_tracker.utils;
|
||||||
|
|
||||||
import java.io.IOException;
|
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.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import java.util.zip.ZipInputStream;
|
import java.util.zip.ZipInputStream;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class ZipUtils {
|
public class ZipUtils {
|
||||||
|
|
@ -25,4 +30,27 @@ public class ZipUtils {
|
||||||
}
|
}
|
||||||
return zipEntry;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,47 @@ public class PhoneService2Test {
|
||||||
Assertions.assertEquals("1115", res.get(127).getPhone());
|
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> 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<Integer, PhoneFidMap> 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> 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<Integer, PhoneFidMap> 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<PhoneRepository.FidsData> fidsDataWithEqualSinceDt = new ArrayList<>(
|
private final List<PhoneRepository.FidsData> fidsDataWithEqualSinceDt = new ArrayList<>(
|
||||||
List.of(
|
List.of(
|
||||||
new PhoneRepository.FidsData(123, "91111", LocalDate.of(2025, 9, 10)),
|
new PhoneRepository.FidsData(123, "91111", LocalDate.of(2025, 9, 10)),
|
||||||
|
|
@ -57,6 +98,23 @@ public class PhoneService2Test {
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
private final List<PhoneRepository.FidsData> 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<PhoneRepository.FidsData> 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<PhoneRepository.FidsData> byDbOrder =
|
Comparator<PhoneRepository.FidsData> byDbOrder =
|
||||||
Comparator.comparing(PhoneRepository.FidsData::getPhone, Comparator.nullsFirst(Comparator.naturalOrder())) // pn.number ASC
|
Comparator.comparing(PhoneRepository.FidsData::getPhone, Comparator.nullsFirst(Comparator.naturalOrder())) // pn.number ASC
|
||||||
.thenComparing(PhoneRepository.FidsData::getFileSinceDate, Comparator.nullsLast(Comparator.reverseOrder())) // file_since_dt DESC
|
.thenComparing(PhoneRepository.FidsData::getFileSinceDate, Comparator.nullsLast(Comparator.reverseOrder())) // file_since_dt DESC
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,47 @@ public class PhoneServiceTest {
|
||||||
Assertions.assertEquals("1115", res.get(127).getPhone());
|
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> 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<Integer, PhoneFidMap> 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> 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<Integer, PhoneFidMap> 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<PhoneRepository.FidsDataLong> fidsDataWithEqualSinceDt = new ArrayList<>(
|
private final List<PhoneRepository.FidsDataLong> fidsDataWithEqualSinceDt = new ArrayList<>(
|
||||||
List.of(
|
List.of(
|
||||||
new PhoneRepository.FidsDataLong(123, 91111L, LocalDate.of(2025, 9, 10)),
|
new PhoneRepository.FidsDataLong(123, 91111L, LocalDate.of(2025, 9, 10)),
|
||||||
|
|
@ -57,6 +98,23 @@ public class PhoneServiceTest {
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
private final List<PhoneRepository.FidsDataLong> 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<PhoneRepository.FidsDataLong> 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<PhoneRepository.FidsDataLong> byDbOrder =
|
Comparator<PhoneRepository.FidsDataLong> byDbOrder =
|
||||||
Comparator.comparing(PhoneRepository.FidsDataLong::getPhone, Comparator.nullsFirst(Comparator.naturalOrder())) // pn.number ASC
|
Comparator.comparing(PhoneRepository.FidsDataLong::getPhone, Comparator.nullsFirst(Comparator.naturalOrder())) // pn.number ASC
|
||||||
.thenComparing(PhoneRepository.FidsDataLong::getFileSinceDate, Comparator.nullsLast(Comparator.reverseOrder())) // file_since_dt DESC
|
.thenComparing(PhoneRepository.FidsDataLong::getFileSinceDate, Comparator.nullsLast(Comparator.reverseOrder())) // file_since_dt DESC
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue