From af52f7df8d84e0d11c5cffcf04c6d625a6a9d412 Mon Sep 17 00:00:00 2001 From: Eduard Tihomirov Date: Thu, 28 Aug 2025 14:15:18 +0300 Subject: [PATCH 01/45] SUPPORT-9339: add sign verify --- backend/pom.xml | 4 + .../ervu/client/fileupload/WebDavClient.java | 27 +- .../EmployeeInfoFileUploadController.java | 4 +- .../model/fileupload/DownloadResponse.java | 2 +- .../fileupload/EmployeeInfoKafkaMessage.java | 19 +- .../java/ervu/model/fileupload/FileInfo.java | 8 + .../EmployeeInfoFileUploadService.java | 359 +++++-- .../EmployeeInfoKafkaMessageService.java | 30 +- .../ervu/audit/model/AuditUploadEvent.java | 14 +- .../ervu/audit/service/AuditService.java | 2 +- .../audit/service/impl/BaseAuditService.java | 4 +- .../security/esia/model/MchdInfoModel.java | 439 +++++++++ .../model/VerifyDocumentSignResponse.java | 35 + .../src/resources/css/components-lkrp.css | 2 +- .../ervu/component/ErvuFileUpload.html | 2 +- .../component/fileupload/ErvuFileUpload.ts | 53 +- pom.xml | 5 + .../Личный кабинет юр лица.page | 923 +++++++++++++++--- 18 files changed, 1662 insertions(+), 270 deletions(-) create mode 100644 backend/src/main/java/ru/micord/ervu/security/esia/model/MchdInfoModel.java create mode 100644 backend/src/main/java/ru/micord/ervu/security/esia/model/VerifyDocumentSignResponse.java diff --git a/backend/pom.xml b/backend/pom.xml index 1c99cb0a..d1592c8b 100644 --- a/backend/pom.xml +++ b/backend/pom.xml @@ -202,6 +202,10 @@ org.apache.tika tika-core + + org.apache.httpcomponents + httpmime + ${project.parent.artifactId} diff --git a/backend/src/main/java/ervu/client/fileupload/WebDavClient.java b/backend/src/main/java/ervu/client/fileupload/WebDavClient.java index 12721d12..2250c1bf 100644 --- a/backend/src/main/java/ervu/client/fileupload/WebDavClient.java +++ b/backend/src/main/java/ervu/client/fileupload/WebDavClient.java @@ -13,10 +13,7 @@ import java.net.http.HttpResponse; import java.nio.charset.StandardCharsets; import java.time.Duration; import java.time.ZonedDateTime; -import java.util.Arrays; -import java.util.Comparator; -import java.util.List; -import java.util.UUID; +import java.util.*; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; @@ -89,12 +86,22 @@ public class WebDavClient { } @Retryable(value = {IOException.class}, backoff = @Backoff(delayExpression = "${webdav.retry.delay:500}")) - public String uploadFile(MultipartFile multipartFile) { - String fileName = getNewFilename(multipartFile.getOriginalFilename()); + public Map uploadFiles(Map files) { + String fileCatalog = UUID.randomUUID() + "/"; Sardine sardine = initClient(username, password); try { - return putAndGetUrl(multipartFile.getBytes(), fileName, sardine); + Map result = new HashMap<>(); + for (Map.Entry entry : files.entrySet()) { + String key = entry.getKey(); + MultipartFile file = entry.getValue(); + + if (file != null) { + String fileName = fileCatalog + file.getOriginalFilename(); + result.put(key, putAndGetUrl(file.getBytes(), fileName, sardine)); + } + } + return result; } catch (IOException e) { throw new WebDavException("Failed to put file into WebDAV", e); @@ -109,12 +116,6 @@ public class WebDavClient { } } - private String getNewFilename(String filename) { - int lastIndexOf = filename.lastIndexOf("."); - String fileExtension = lastIndexOf == -1 ? "" : filename.substring(lastIndexOf); - return UUID.randomUUID() + fileExtension; - } - public String putAndGetUrl(byte[] fileBytes, String fileName, Sardine client) throws IOException { if (badServersCache.size() == urls.length) { return null; diff --git a/backend/src/main/java/ervu/controller/EmployeeInfoFileUploadController.java b/backend/src/main/java/ervu/controller/EmployeeInfoFileUploadController.java index 204f7429..219d8807 100644 --- a/backend/src/main/java/ervu/controller/EmployeeInfoFileUploadController.java +++ b/backend/src/main/java/ervu/controller/EmployeeInfoFileUploadController.java @@ -26,13 +26,15 @@ public class EmployeeInfoFileUploadController { @RequestMapping(value = "/employee/document", method = RequestMethod.POST) public ResponseEntity saveEmployeeInformationFile( @RequestParam("file") MultipartFile multipartFile, + @RequestParam("signFile") MultipartFile signFile, + @RequestParam(value = "mchdFile", required = false) MultipartFile mchdFile, @RequestHeader("X-Employee-Info-File-Form-Type") String formType, @RequestHeader("Client-Time-Zone") String clientTimeZone) { String offset = ZonedDateTime.now(TimeZone.getTimeZone(clientTimeZone).toZoneId()) .getOffset().getId(); - if (this.fileUploadService.saveEmployeeInformationFile(multipartFile, formType, offset)) { + if (this.fileUploadService.saveEmployeeInformationFiles(multipartFile, signFile, mchdFile, formType, offset)) { return ResponseEntity.ok("File successfully uploaded."); } diff --git a/backend/src/main/java/ervu/model/fileupload/DownloadResponse.java b/backend/src/main/java/ervu/model/fileupload/DownloadResponse.java index 883f749b..297e6399 100644 --- a/backend/src/main/java/ervu/model/fileupload/DownloadResponse.java +++ b/backend/src/main/java/ervu/model/fileupload/DownloadResponse.java @@ -3,5 +3,5 @@ package ervu.model.fileupload; /** * @author r.latypov */ -public record DownloadResponse(UploadOrgInfo orgInfo, FileInfo fileInfo) { +public record DownloadResponse(UploadOrgInfo orgInfo, FileInfo[] filesInfo) { } diff --git a/backend/src/main/java/ervu/model/fileupload/EmployeeInfoKafkaMessage.java b/backend/src/main/java/ervu/model/fileupload/EmployeeInfoKafkaMessage.java index a6f1e08f..a3d27410 100644 --- a/backend/src/main/java/ervu/model/fileupload/EmployeeInfoKafkaMessage.java +++ b/backend/src/main/java/ervu/model/fileupload/EmployeeInfoKafkaMessage.java @@ -1,5 +1,6 @@ package ervu.model.fileupload; +import java.util.Arrays; import java.util.Objects; /** @@ -7,19 +8,19 @@ import java.util.Objects; */ public class EmployeeInfoKafkaMessage { private final UploadOrgInfo orgInfo; - private final FileInfo fileInfo; + private final FileInfo[] filesInfo; - public EmployeeInfoKafkaMessage(UploadOrgInfo orgInfo, FileInfo fileInfo) { + public EmployeeInfoKafkaMessage(UploadOrgInfo orgInfo, FileInfo[] filesInfo) { this.orgInfo = orgInfo; - this.fileInfo = fileInfo; + this.filesInfo = filesInfo; } public UploadOrgInfo getOrgInfo() { return orgInfo; } - public FileInfo getFileInfo() { - return fileInfo; + public FileInfo[] getFilesInfo() { + return filesInfo; } @Override @@ -27,19 +28,21 @@ public class EmployeeInfoKafkaMessage { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; EmployeeInfoKafkaMessage that = (EmployeeInfoKafkaMessage) o; - return Objects.equals(orgInfo, that.orgInfo) && Objects.equals(fileInfo, that.fileInfo); + return Objects.equals(orgInfo, that.orgInfo) && Arrays.equals(filesInfo, that.filesInfo); } @Override public int hashCode() { - return Objects.hash(orgInfo, fileInfo); + int result = Objects.hash(orgInfo); + result = 31 * result + Arrays.hashCode(filesInfo); + return result; } @Override public String toString() { return "KafkaMessage{" + "uploadOrgInfo=" + orgInfo + - ", fileInfo=" + fileInfo + + ", fileInfo=" + Arrays.toString(filesInfo) + '}'; } } diff --git a/backend/src/main/java/ervu/model/fileupload/FileInfo.java b/backend/src/main/java/ervu/model/fileupload/FileInfo.java index 5141e433..1603db83 100644 --- a/backend/src/main/java/ervu/model/fileupload/FileInfo.java +++ b/backend/src/main/java/ervu/model/fileupload/FileInfo.java @@ -69,6 +69,14 @@ public class FileInfo { return fileStatus; } + public void setFileStatus(FileStatus fileStatus) { + this.fileStatus = fileStatus; + } + + public void setFileUrl(String fileUrl) { + this.fileUrl = fileUrl; + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java b/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java index c9eb916e..d21a7371 100644 --- a/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java +++ b/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java @@ -1,21 +1,29 @@ package ervu.service.fileupload; import java.io.IOException; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; import java.nio.charset.StandardCharsets; import java.sql.Timestamp; +import java.time.Duration; import java.time.LocalDateTime; -import java.util.Arrays; -import java.util.Locale; -import java.util.UUID; +import java.util.*; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; import ervu.client.fileupload.WebDavClient; -import ervu.model.fileupload.DownloadResponse; -import ervu.model.fileupload.EmployeeInfoFileFormType; -import ervu.model.fileupload.EmployeeInfoKafkaMessage; -import ervu.model.fileupload.FileInfo; -import ervu.model.fileupload.FileStatus; +import ervu.model.fileupload.*; +import org.apache.http.HttpEntity; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.mime.MultipartEntityBuilder; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.util.EntityUtils; import org.apache.kafka.clients.producer.ProducerRecord; import org.apache.tika.Tika; import org.apache.tika.mime.MediaType; @@ -32,8 +40,12 @@ import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; import ru.micord.ervu.audit.service.AuditService; import ru.micord.ervu.exception.JsonParsingException; +import ru.micord.ervu.security.esia.config.EsiaConfig; +import ru.micord.ervu.security.esia.exception.EsiaException; import ru.micord.ervu.security.esia.model.EmployeeModel; +import ru.micord.ervu.security.esia.model.MchdInfoModel; import ru.micord.ervu.security.esia.model.PersonModel; +import ru.micord.ervu.security.esia.model.VerifyDocumentSignResponse; import ru.micord.ervu.security.esia.service.UlDataService; import ru.micord.ervu.security.esia.EsiaAuthInfoStore; import ru.micord.ervu.security.webbpm.jwt.UserIdsPair; @@ -59,6 +71,8 @@ public class EmployeeInfoFileUploadService { private final InteractionService interactionService; private final UlDataService ulDataService; private final AuditService auditService; + private final ObjectMapper objectMapper; + private final EsiaConfig esiaConfig; @Value("${av.kafka.message.topic.name}") private String kafkaTopicName; @@ -69,24 +83,35 @@ public class EmployeeInfoFileUploadService { @Qualifier("avTemplate") KafkaTemplate kafkaTemplate, InteractionService interactionService, UlDataService ulDataService, - AuditService auditService) { + AuditService auditService, + ObjectMapper objectMapper, + EsiaConfig esiaConfig) { this.webDavClient = webDavClient; this.employeeInfoKafkaMessageService = employeeInfoKafkaMessageService; this.kafkaTemplate = kafkaTemplate; this.interactionService = interactionService; this.ulDataService = ulDataService; this.auditService = auditService; + this.objectMapper = objectMapper; + this.esiaConfig = esiaConfig; } - public boolean saveEmployeeInformationFile(MultipartFile multipartFile, String formType, - String offset) { + public boolean saveEmployeeInformationFiles(MultipartFile multipartFile, + MultipartFile signFile, MultipartFile mchdFile, String formType, + String offset) { UserIdsPair userIdsPair = SecurityUtil.getUserIdsPair(); + LocalDateTime now = LocalDateTime.now(); + String departureDateTime = DateUtils.convertToString(now); - if (userIdsPair == null || !isValid(multipartFile)) { + if (userIdsPair == null || !isValidCsvWithSig(multipartFile, signFile)) { return false; } String fileId = UUID.randomUUID().toString(); + String signFileId = UUID.randomUUID().toString(); String fileName = multipartFile.getOriginalFilename(); + long fileSize = multipartFile.getSize(); + String signFileName = signFile.getOriginalFilename(); + long signFileSize = signFile.getSize(); EmployeeInfoFileFormType employeeInfoFileFormType = EmployeeInfoFileFormType.valueOf( formType); String esiaUserId = userIdsPair.getEsiaUserId(); @@ -94,79 +119,207 @@ public class EmployeeInfoFileUploadService { String accessToken = EsiaAuthInfoStore.getAccessToken(esiaUserId); EmployeeModel employeeModel = ulDataService.getEmployeeModel(accessToken); PersonModel personModel = employeeModel.getPerson(); - - String fileUploadUrl = this.webDavClient.uploadFile(multipartFile); + EmployeeModel chiefModel = ulDataService.getChiefEmployeeModel(accessToken); + VerifyDocumentSignResponse verifyDocumentSignResponse = validateSign(multipartFile, signFile); FileStatus fileStatus = new FileStatus(); - fileStatus.setStatus(fileUploadUrl == null ? "Невозможно проверить файл ЛК РП" : "Загрузка"); - LocalDateTime now = LocalDateTime.now(); - interactionService.setStatus(fileId, fileStatus.getStatus(), fileName, - employeeInfoFileFormType.getFilePatternCode(), Timestamp.valueOf(now), - convertToFio(personModel.getFirstName(), personModel.getMiddleName(), - personModel.getLastName() - ), - ervuId + String fio = convertToFio(personModel.getFirstName(), personModel.getMiddleName(), + personModel.getLastName() ); - - long fileSize = multipartFile.getSize(); - String departureDateTime = DateUtils.convertToString(now); - EmployeeInfoKafkaMessage kafkaMessage = employeeInfoKafkaMessageService.getKafkaMessage( - fileId, - fileUploadUrl, - fileName, - employeeInfoFileFormType, - departureDateTime, - accessToken, - offset, - fileStatus, - ervuId, - esiaUserId, - personModel, - fileSize + UploadOrgInfo uploadOrgInfo = employeeInfoKafkaMessageService.getOrgInfo(accessToken, ervuId, + esiaUserId, personModel ); - - if (fileUploadUrl != null) { - fileStatus.setCode(FILE_UPLOADED.getCode()); - fileStatus.setDescription("Файл принят до проверки на вирусы"); - String jsonMessage = getJsonKafkaMessage(kafkaMessage); - return sendMessage(jsonMessage); + FileInfo fileInfo = employeeInfoKafkaMessageService.getFileInfo(fileId, null, fileName, + employeeInfoFileFormType, departureDateTime, offset, null, fileSize + ); + FileInfo signFileInfo = employeeInfoKafkaMessageService.getFileInfo(signFileId, null, + signFileName, employeeInfoFileFormType, departureDateTime, offset, null, signFileSize + ); + if (verifyDocumentSignResponse.getSignVerifyResult() != null) { + fileStatus.setStatus("Некорректная ЭП"); + fileInfo.setFileStatus(fileStatus); + signFileInfo.setFileStatus(fileStatus); + interactionService.setStatus(fileId, fileStatus.getStatus(), fileName, + employeeInfoFileFormType.getFilePatternCode(), Timestamp.valueOf(now), + fio, ervuId + ); + auditService.processUploadEvent(uploadOrgInfo, new FileInfo[] { + fileInfo, signFileInfo + }); + //Заменить на свою ошибку + throw new RuntimeException(verifyDocumentSignResponse.getSignVerifyResult()); + } + String signerInfo = verifyDocumentSignResponse.getSignerInfo(); + Map signerInfoMap = parseKeyValuePairs(signerInfo); + String chiefMiddleName = chiefModel.getPerson().getMiddleName(); + String chiefLastName = chiefModel.getPerson().getLastName(); + String chiefFirstName = chiefModel.getPerson().getFirstName(); + if (signerInfoMap.get("SN").equalsIgnoreCase(chiefLastName) && signerInfoMap.get("G") + .equalsIgnoreCase(chiefFirstName + " " + chiefMiddleName) && signerInfoMap.get("O") + .equalsIgnoreCase(uploadOrgInfo.getOrgName())) { + Map hashMap = this.webDavClient.uploadFiles(Map.of( + "fileUrl", multipartFile, + "signUrl", signFile + )); + String fileUploadUrl = hashMap.get("fileUrl"); + String signFileUploadUrl = hashMap.get("signFile"); + fileStatus.setStatus(fileUploadUrl == null || signFileUploadUrl == null + ? "Невозможно проверить файл ЛК РП" + : "Загрузка"); + interactionService.setStatus(fileId, fileStatus.getStatus(), fileName, + employeeInfoFileFormType.getFilePatternCode(), Timestamp.valueOf(now), + convertToFio(personModel.getFirstName(), personModel.getMiddleName(), + personModel.getLastName() + ), + ervuId + ); + fileInfo.setFileStatus(fileStatus); + signFileInfo.setFileStatus(fileStatus); + if (fileUploadUrl == null || signFileUploadUrl == null) { + LOGGER.error("Failed to upload files: {}, {}", fileName, signFileName); + fileStatus.setCode(FILE_NOT_CHECKED.getCode()); + fileStatus.setDescription( + "Невозможно проверить файл по причине недоступности или ошибки в работе антивируса"); + auditService.processUploadEvent(uploadOrgInfo, new FileInfo[] {fileInfo, signFileInfo}); + return false; + } + else { + fileInfo.setFileUrl(fileUploadUrl); + signFileInfo.setFileUrl(signFileUploadUrl); + EmployeeInfoKafkaMessage kafkaMessage = employeeInfoKafkaMessageService.getKafkaMessage( + uploadOrgInfo, new FileInfo[] { + fileInfo, signFileInfo + }); + fileStatus.setCode(FILE_UPLOADED.getCode()); + fileStatus.setDescription("Файл принят до проверки на вирусы"); + String jsonMessage = getJsonKafkaMessage(kafkaMessage); + return sendMessage(jsonMessage); + } } else { - LOGGER.error("Failed to upload file: {}", fileName); - fileStatus.setCode(FILE_NOT_CHECKED.getCode()); - fileStatus.setDescription("Невозможно проверить файл по причине недоступности или ошибки в работе антивируса"); - auditService.processUploadEvent(kafkaMessage.getOrgInfo(), kafkaMessage.getFileInfo()); - return false; + if (mchdFile == null) { + fileStatus.setStatus("Некорректная МЧД"); + interactionService.setStatus(fileId, fileStatus.getStatus(), fileName, + employeeInfoFileFormType.getFilePatternCode(), Timestamp.valueOf(now), + convertToFio(personModel.getFirstName(), personModel.getMiddleName(), + personModel.getLastName() + ), + ervuId + ); + auditService.processUploadEvent(uploadOrgInfo, new FileInfo[] { + fileInfo, signFileInfo + }); + //Заменить на свою ошибку + throw new RuntimeException(); + } + String mchdFileId = UUID.randomUUID().toString(); + String mchdFileName = mchdFile.getOriginalFilename(); + long mchdFileSize = mchdFile.getSize(); + FileInfo mchdFileInfo = employeeInfoKafkaMessageService.getFileInfo(mchdFileId, null, + mchdFileName, employeeInfoFileFormType, departureDateTime, offset, fileStatus, + mchdFileSize + ); + if (validateMchd(mchdFile, accessToken)) { + Map hashMap = this.webDavClient.uploadFiles(Map.of( + "fileUrl", multipartFile, + "signUrl", signFile, + "mchdFile", mchdFile + )); + String fileUploadUrl = hashMap.get("fileUrl"); + String signFileUploadUrl = hashMap.get("signFile"); + String mchdFileUploadUrl = hashMap.get("mchdFile"); + fileStatus.setStatus( + fileUploadUrl == null || signFileUploadUrl == null || mchdFileUploadUrl == null + ? "Невозможно проверить файл ЛК РП" + : "Загрузка"); + interactionService.setStatus(fileId, fileStatus.getStatus(), fileName, + employeeInfoFileFormType.getFilePatternCode(), Timestamp.valueOf(now), + convertToFio(personModel.getFirstName(), personModel.getMiddleName(), + personModel.getLastName() + ), + ervuId + ); + fileInfo.setFileStatus(fileStatus); + signFileInfo.setFileStatus(fileStatus); + if (fileUploadUrl == null || signFileUploadUrl == null || mchdFileUploadUrl == null) { + LOGGER.error("Failed to upload files: {}, {}, {}", fileName, signFileName, + mchdFileName + ); + fileStatus.setCode(FILE_NOT_CHECKED.getCode()); + fileStatus.setDescription( + "Невозможно проверить файл по причине недоступности или ошибки в работе антивируса"); + auditService.processUploadEvent(uploadOrgInfo, + new FileInfo[] {fileInfo, signFileInfo, mchdFileInfo} + ); + return false; + } + else { + fileInfo.setFileUrl(fileUploadUrl); + signFileInfo.setFileUrl(signFileUploadUrl); + mchdFileInfo.setFileUrl(mchdFileUploadUrl); + EmployeeInfoKafkaMessage kafkaMessage = employeeInfoKafkaMessageService.getKafkaMessage( + uploadOrgInfo, new FileInfo[] { + fileInfo, signFileInfo, mchdFileInfo + }); + fileStatus.setCode(FILE_UPLOADED.getCode()); + fileStatus.setDescription("Файл принят до проверки на вирусы"); + String jsonMessage = getJsonKafkaMessage(kafkaMessage); + return sendMessage(jsonMessage); + } + } + else { + fileStatus.setStatus("Некорректная МЧД"); + interactionService.setStatus(fileId, fileStatus.getStatus(), fileName, + employeeInfoFileFormType.getFilePatternCode(), Timestamp.valueOf(now), + convertToFio(personModel.getFirstName(), personModel.getMiddleName(), + personModel.getLastName() + ), + ervuId + ); + auditService.processUploadEvent(uploadOrgInfo, new FileInfo[] { + fileInfo, signFileInfo, mchdFileInfo + }); + //Заменить на свою ошибку + throw new RuntimeException(); + } } } - private boolean isValid(MultipartFile multipartFile) { - - if (multipartFile == null || multipartFile.getOriginalFilename() == null) { + private boolean isValidCsvWithSig(MultipartFile file, MultipartFile signFile) { + if (file == null || signFile == null || file.getOriginalFilename() == null + || signFile.getOriginalFilename() == null) { return false; } - String fileName = multipartFile.getOriginalFilename(); + Tika tika = new Tika(); + MimeTypes defaultMimeTypes = MimeTypes.getDefaultMimeTypes(); + try { - String contentType = new Tika().detect(multipartFile.getBytes()); - MimeTypes defaultMimeTypes = MimeTypes.getDefaultMimeTypes(); - MimeType mimeType = defaultMimeTypes.forName(contentType); - boolean isCsv = mimeType.getType().equals(MediaType.TEXT_PLAIN) - && fileName.toLowerCase(Locale.getDefault()).endsWith(".csv"); + String fileName = file.getOriginalFilename().toLowerCase(Locale.getDefault()); + String signFileName = signFile.getOriginalFilename().toLowerCase(Locale.getDefault()); + + String fileContentType = tika.detect(file.getBytes()); + String signContentType = tika.detect(signFile.getBytes()); + + MimeType fileMimeType = defaultMimeTypes.forName(fileContentType); + MimeType signMimeType = defaultMimeTypes.forName(signContentType); + + boolean isCsv = + MediaType.TEXT_PLAIN.equals(fileMimeType.getType()) && fileName.endsWith(".csv"); + boolean isSig = + "application/pkcs7-signature".equals(signMimeType.toString()) && signFileName.endsWith( + ".sig"); if (!isCsv) { - LOGGER.info("Trying to upload file={} with wrong mime type={}", - fileName, mimeType - ); + LOGGER.info("Invalid main file: name={}, mimeType={}", fileName, fileMimeType); } - return isCsv; + if (!isSig) { + LOGGER.info("Invalid signature file: name={}, mimeType={}", signFileName, signMimeType); + } + + return isCsv && isSig; } - catch (MimeTypeException e) { - LOGGER.error( - "Couldn't get mime type from bytes for file=" + fileName, e); - return false; - } - catch (IOException e) { - LOGGER.error("Error while checking file type, file=" + fileName, - e + catch (MimeTypeException | IOException e) { + LOGGER.error("Failed to process files: {}, {}", file.getOriginalFilename(), + signFile.getOriginalFilename(), e ); return false; } @@ -206,14 +359,14 @@ public class EmployeeInfoFileUploadService { try { DownloadResponse downloadResponse = mapper.readValue(kafkaMessage, DownloadResponse.class); - FileInfo fileInfo = downloadResponse.fileInfo(); + FileInfo fileInfo = downloadResponse.filesInfo()[0]; String statusCode = fileInfo.getFileStatus().getCode(); if (Arrays.asList(FILE_INFECTED.getCode(), FILE_CLEAN.getCode()).contains(statusCode)) { interactionService.delete(fileInfo.getFileId(), downloadResponse.orgInfo().getOrgId()); } else if (statusCode.equals(FILE_NOT_CHECKED.getCode())) { - auditService.processUploadEvent(downloadResponse.orgInfo(), downloadResponse.fileInfo()); + auditService.processUploadEvent(downloadResponse.orgInfo(), downloadResponse.filesInfo()); interactionService.updateStatus(fileInfo.getFileId(), fileInfo.getFileStatus().getStatus(), downloadResponse.orgInfo().getOrgId() ); @@ -223,4 +376,66 @@ public class EmployeeInfoFileUploadService { throw new JsonParsingException(String.format("Fail get json from: %s", kafkaMessage), e); } } + + private VerifyDocumentSignResponse validateSign(MultipartFile file, MultipartFile signFile) { + try (CloseableHttpClient httpClient = HttpClients.createDefault()) { + HttpPost upload = new HttpPost(esiaConfig.getSignUrl()); + HttpEntity multipart = MultipartEntityBuilder.create() + .addBinaryBody("data", file.getBytes(), ContentType.APPLICATION_OCTET_STREAM, + file.getOriginalFilename() + ) + .addBinaryBody("sign", signFile.getBytes(), ContentType.APPLICATION_OCTET_STREAM, + signFile.getOriginalFilename() + ) + .build(); + upload.setEntity(multipart); + try (CloseableHttpResponse response = httpClient.execute(upload)) { + int statusCode = response.getStatusLine().getStatusCode(); + String body = EntityUtils.toString(response.getEntity()); + if (statusCode != 200) { + throw new RuntimeException(statusCode + " " + body); + } + return objectMapper.readValue(body, VerifyDocumentSignResponse.class); + } + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + + private boolean validateMchd(MultipartFile mchdFile, String accessToken) { + try { + String mchdGuid = getMchdGuid(mchdFile); + MchdInfoModel mchdInfoModel = ulDataService.getMchdInfo(mchdGuid, accessToken); + mchdInfoModel. + + } + catch (Exception e) { + throw new EsiaException(e); + } + } + + private Map parseKeyValuePairs(String input) { + Map map = new HashMap<>(); + String[] pairs = input.split(","); + + for (String pair : pairs) { + String[] keyValue = pair.split("=", 2); + if (keyValue.length == 2) { + map.put(keyValue[0].trim(), keyValue[1].trim()); + } + } + return map; + } + + private String getMchdGuid(MultipartFile mcnhdFile) { + try { + return "asdasdasd"; + + } + catch (Exception e) { + throw new EsiaException(e); + } + } + } diff --git a/backend/src/main/java/ervu/service/fileupload/EmployeeInfoKafkaMessageService.java b/backend/src/main/java/ervu/service/fileupload/EmployeeInfoKafkaMessageService.java index d8414f2b..90413bab 100644 --- a/backend/src/main/java/ervu/service/fileupload/EmployeeInfoKafkaMessageService.java +++ b/backend/src/main/java/ervu/service/fileupload/EmployeeInfoKafkaMessageService.java @@ -29,20 +29,26 @@ public class EmployeeInfoKafkaMessageService { PersonModel personModel, long fileSize) { return new EmployeeInfoKafkaMessage( getOrgInfo(accessToken, ervuId, prnOid, personModel), - getFileInfo( - fileId, - fileUrl, - fileName, - formType, - departureDateTime, - offset, - fileStatus, - fileSize - ) + new FileInfo[] { + getFileInfo( + fileId, + fileUrl, + fileName, + formType, + departureDateTime, + offset, + fileStatus, + fileSize + ), + } ); } - private FileInfo getFileInfo(String fileId, String fileUrl, String fileName, + public EmployeeInfoKafkaMessage getKafkaMessage(UploadOrgInfo orgInfo, FileInfo[] filesInfo) { + return new EmployeeInfoKafkaMessage(orgInfo, filesInfo); + } + + public FileInfo getFileInfo(String fileId, String fileUrl, String fileName, EmployeeInfoFileFormType formType, String departureDateTime, String offset, FileStatus fileStatus, long fileSize) { return new FileInfo( @@ -58,7 +64,7 @@ public class EmployeeInfoKafkaMessageService { ); } - private UploadOrgInfo getOrgInfo(String accessToken, String ervuId, String prnOid, PersonModel personModel) { + public UploadOrgInfo getOrgInfo(String accessToken, String ervuId, String prnOid, PersonModel personModel) { OrganizationModel organizationModel = ulDataService.getOrganizationModel(accessToken); SenderInfo senderInfo = new SenderInfo(); senderInfo.setFirstName(personModel.getFirstName()); diff --git a/backend/src/main/java/ru/micord/ervu/audit/model/AuditUploadEvent.java b/backend/src/main/java/ru/micord/ervu/audit/model/AuditUploadEvent.java index 65270471..12cca209 100644 --- a/backend/src/main/java/ru/micord/ervu/audit/model/AuditUploadEvent.java +++ b/backend/src/main/java/ru/micord/ervu/audit/model/AuditUploadEvent.java @@ -10,11 +10,11 @@ import ervu.model.fileupload.UploadOrgInfo; public class AuditUploadEvent { private UploadOrgInfo orgInfo; - private FileInfo fileInfo; + private FileInfo[] filesInfo; - public AuditUploadEvent(UploadOrgInfo orgInfo, FileInfo fileInfo) { + public AuditUploadEvent(UploadOrgInfo orgInfo, FileInfo[] filesInfo) { this.orgInfo = orgInfo; - this.fileInfo = fileInfo; + this.filesInfo = filesInfo; } public UploadOrgInfo getOrgInfo() { @@ -25,11 +25,11 @@ public class AuditUploadEvent { this.orgInfo = orgInfo; } - public FileInfo getFileInfo() { - return fileInfo; + public FileInfo[] getFilesInfo() { + return filesInfo; } - public void setFileInfo(FileInfo fileInfo) { - this.fileInfo = fileInfo; + public void setFilesInfo(FileInfo[] filesInfo) { + this.filesInfo = filesInfo; } } diff --git a/backend/src/main/java/ru/micord/ervu/audit/service/AuditService.java b/backend/src/main/java/ru/micord/ervu/audit/service/AuditService.java index d9f9ea8d..c189f19a 100644 --- a/backend/src/main/java/ru/micord/ervu/audit/service/AuditService.java +++ b/backend/src/main/java/ru/micord/ervu/audit/service/AuditService.java @@ -16,7 +16,7 @@ public interface AuditService { void processAuthEvent(HttpServletRequest request, OrgInfo orgInfo, String prnOid, String status, String eventType); - void processUploadEvent(UploadOrgInfo uploadOrgInfo, FileInfo fileInfo); + void processUploadEvent(UploadOrgInfo uploadOrgInfo, FileInfo[] filesInfo); void processDownloadEvent(HttpServletRequest request, long fileSize, String fileName, int formatRegistry, String status, String s3FileUrl); diff --git a/backend/src/main/java/ru/micord/ervu/audit/service/impl/BaseAuditService.java b/backend/src/main/java/ru/micord/ervu/audit/service/impl/BaseAuditService.java index c9777870..14a5a2cc 100644 --- a/backend/src/main/java/ru/micord/ervu/audit/service/impl/BaseAuditService.java +++ b/backend/src/main/java/ru/micord/ervu/audit/service/impl/BaseAuditService.java @@ -110,10 +110,10 @@ public class BaseAuditService implements AuditService { } @Override - public void processUploadEvent(UploadOrgInfo orgInfo, FileInfo fileInfo) { + public void processUploadEvent(UploadOrgInfo orgInfo, FileInfo[] filesInfo) { AuditUploadEvent auditUploadEvent = new AuditUploadEvent( orgInfo, - fileInfo + filesInfo ); String message = convertToMessage(auditUploadEvent); diff --git a/backend/src/main/java/ru/micord/ervu/security/esia/model/MchdInfoModel.java b/backend/src/main/java/ru/micord/ervu/security/esia/model/MchdInfoModel.java new file mode 100644 index 00000000..662dccba --- /dev/null +++ b/backend/src/main/java/ru/micord/ervu/security/esia/model/MchdInfoModel.java @@ -0,0 +1,439 @@ +package ru.micord.ervu.security.esia.model; + +import java.io.Serializable; +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + + +/** + * @author Eduard Tihomirov + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class MchdInfoModel implements Serializable { + private static final long serialVersionUID = 1L; + + private List stateFacts; + private String guid; + private String number; + private String status; + private String issuedOn; + private String expiredOn; + + private Principals principals; + private Agents agents; + private Systems systems; + private Revocations revocations; + + public List getStateFacts() { + return stateFacts; + } + + public void setStateFacts(List stateFacts) { + this.stateFacts = stateFacts; + } + + public String getGuid() { + return guid; + } + + public void setGuid(String guid) { + this.guid = guid; + } + + public String getNumber() { + return number; + } + + public void setNumber(String number) { + this.number = number; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public String getIssuedOn() { + return issuedOn; + } + + public void setIssuedOn(String issuedOn) { + this.issuedOn = issuedOn; + } + + public String getExpiredOn() { + return expiredOn; + } + + public void setExpiredOn(String expiredOn) { + this.expiredOn = expiredOn; + } + + public Principals getPrincipals() { + return principals; + } + + public void setPrincipals(Principals principals) { + this.principals = principals; + } + + public Agents getAgents() { + return agents; + } + + public void setAgents(Agents agents) { + this.agents = agents; + } + + public Systems getSystems() { + return systems; + } + + public void setSystems(Systems systems) { + this.systems = systems; + } + + public Revocations getRevocations() { + return revocations; + } + + public void setRevocations(Revocations revocations) { + this.revocations = revocations; + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static class Principals implements Serializable { + private List stateFacts; + private int size; + private List elements; + + public List getStateFacts() { + return stateFacts; + } + + public void setStateFacts(List stateFacts) { + this.stateFacts = stateFacts; + } + + public int getSize() { + return size; + } + + public void setSize(int size) { + this.size = size; + } + + public List getElements() { + return elements; + } + + public void setElements(List elements) { + this.elements = elements; + } + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static class PrincipalElement implements Serializable { + private List stateFacts; + private Organization organization; + + public List getStateFacts() { + return stateFacts; + } + + public void setStateFacts(List stateFacts) { + this.stateFacts = stateFacts; + } + + public Organization getOrganization() { + return organization; + } + + public void setOrganization(Organization organization) { + this.organization = organization; + } + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static class Agents implements Serializable { + private List stateFacts; + private int size; + private List elements; + + public List getStateFacts() { + return stateFacts; + } + + public void setStateFacts(List stateFacts) { + this.stateFacts = stateFacts; + } + + public int getSize() { + return size; + } + + public void setSize(int size) { + this.size = size; + } + + public List getElements() { + return elements; + } + + public void setElements(List elements) { + this.elements = elements; + } + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static class AgentElement implements Serializable { + private List stateFacts; + private Person person; + + public List getStateFacts() { + return stateFacts; + } + + public void setStateFacts(List stateFacts) { + this.stateFacts = stateFacts; + } + + public Person getPerson() { + return person; + } + + public void setPerson(Person person) { + this.person = person; + } + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static class Systems implements Serializable { + private List stateFacts; + private int size; + private List elements; + + public List getStateFacts() { + return stateFacts; + } + + public void setStateFacts(List stateFacts) { + this.stateFacts = stateFacts; + } + + public int getSize() { + return size; + } + + public void setSize(int size) { + this.size = size; + } + + public List getElements() { + return elements; + } + + public void setElements(List elements) { + this.elements = elements; + } + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static class SystemElement implements Serializable { + private String nsiId; + private String name; + + public String getNsiId() { + return nsiId; + } + + public void setNsiId(String nsiId) { + this.nsiId = nsiId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static class Revocations implements Serializable { + private List stateFacts; + private int size; + private List elements; + + public List getStateFacts() { + return stateFacts; + } + + public void setStateFacts(List stateFacts) { + this.stateFacts = stateFacts; + } + + public int getSize() { + return size; + } + + public void setSize(int size) { + this.size = size; + } + + public List getElements() { + return elements; + } + + public void setElements(List elements) { + this.elements = elements; + } + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static class RevocationElement implements Serializable { + private String number; + private String poaGuid; + private String revokedOn; + private Principals principals; + private Agents agents; + private Signatory signatory; + private Systems systems; + + public String getNumber() { + return number; + } + + public void setNumber(String number) { + this.number = number; + } + + public String getPoaGuid() { + return poaGuid; + } + + public void setPoaGuid(String poaGuid) { + this.poaGuid = poaGuid; + } + + public String getRevokedOn() { + return revokedOn; + } + + public void setRevokedOn(String revokedOn) { + this.revokedOn = revokedOn; + } + + public Principals getPrincipals() { + return principals; + } + + public void setPrincipals(Principals principals) { + this.principals = principals; + } + + public Agents getAgents() { + return agents; + } + + public void setAgents(Agents agents) { + this.agents = agents; + } + + public Signatory getSignatory() { + return signatory; + } + + public void setSignatory(Signatory signatory) { + this.signatory = signatory; + } + + public Systems getSystems() { + return systems; + } + + public void setSystems(Systems systems) { + this.systems = systems; + } + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static class Organization implements Serializable { + private String ogrn; + private String name; + private String inn; + private String kpp; + + public String getOgrn() { + return ogrn; + } + + public void setOgrn(String ogrn) { + this.ogrn = ogrn; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getInn() { + return inn; + } + + public void setInn(String inn) { + this.inn = inn; + } + + public String getKpp() { + return kpp; + } + + public void setKpp(String kpp) { + this.kpp = kpp; + } + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static class Person implements Serializable { + private String snils; + + public String getSnils() { + return snils; + } + + public void setSnils(String snils) { + this.snils = snils; + } + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static class Signatory implements Serializable { + private List stateFacts; + private Person person; + + public List getStateFacts() { + return stateFacts; + } + + public void setStateFacts(List stateFacts) { + this.stateFacts = stateFacts; + } + + public Person getPerson() { + return person; + } + + public void setPerson(Person person) { + this.person = person; + } + } +} diff --git a/backend/src/main/java/ru/micord/ervu/security/esia/model/VerifyDocumentSignResponse.java b/backend/src/main/java/ru/micord/ervu/security/esia/model/VerifyDocumentSignResponse.java new file mode 100644 index 00000000..9485a74c --- /dev/null +++ b/backend/src/main/java/ru/micord/ervu/security/esia/model/VerifyDocumentSignResponse.java @@ -0,0 +1,35 @@ +package ru.micord.ervu.security.esia.model; + +import java.io.Serializable; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * @author Eduard Tihomirov + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class VerifyDocumentSignResponse implements Serializable { + + private static final long serialVersionUID = 1L; + @JsonProperty("error_code") + private String errorCode; + @JsonProperty("signer_subject") + private String signerInfo; + + public String getSignVerifyResult() { + return errorCode; + } + + public void setSignVerifyResult(String signVerifyResult) { + this.errorCode = signVerifyResult; + } + + public String getSignerInfo() { + return signerInfo; + } + + public void setSignerInfo(String signerInfo) { + this.signerInfo = signerInfo; + } +} \ No newline at end of file diff --git a/frontend/src/resources/css/components-lkrp.css b/frontend/src/resources/css/components-lkrp.css index dbbb3748..9c84f7de 100644 --- a/frontend/src/resources/css/components-lkrp.css +++ b/frontend/src/resources/css/components-lkrp.css @@ -855,7 +855,7 @@ overflow: hidden; padding-left: 56px; } -.webbpm.ervu_lkrp_ul .modal.show ervu-file-upload .selected-file .selected-file-name::before { +.webbpm.ervu_lkrp_ul .modal.show ervu-file-upload .selected-file .icon-csv::before { position: absolute; content: url(../img/svg/file-csv.svg); top: -2px; diff --git a/frontend/src/resources/template/ervu/component/ErvuFileUpload.html b/frontend/src/resources/template/ervu/component/ErvuFileUpload.html index 1dc7cd7c..663ae556 100644 --- a/frontend/src/resources/template/ervu/component/ErvuFileUpload.html +++ b/frontend/src/resources/template/ervu/component/ErvuFileUpload.html @@ -18,7 +18,7 @@ hidden>
- {{item?.file?.name}} + {{item?.file?.name}} {{item?.file?.size/1024/1024 | number: '.2'}} MB
diff --git a/frontend/src/ts/ervu/component/fileupload/ErvuFileUpload.ts b/frontend/src/ts/ervu/component/fileupload/ErvuFileUpload.ts index 4ed140be..b79f69f9 100644 --- a/frontend/src/ts/ervu/component/fileupload/ErvuFileUpload.ts +++ b/frontend/src/ts/ervu/component/fileupload/ErvuFileUpload.ts @@ -5,7 +5,7 @@ import { Event, MessagesService, UnsupportedOperationError, - AppConfigService + AppConfigService, ObjectRef } from "@webbpm/base-package"; import {ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef} from "@angular/core"; import {FileItem, FileUploader} from "ng2-file-upload"; @@ -23,6 +23,10 @@ import {TokenConstants} from "../../../modules/security/TokenConstants"; export class ErvuFileUpload extends InputControl { private static readonly BACKEND_URL: string = "backend.context"; + @ObjectRef() + public signFileUploadRef: ErvuFileUpload; + @ObjectRef() + public mchdFileUploadRef: ErvuFileUpload; @NotNull("true") public selectFileFieldText: string; @NotNull("true") @@ -137,24 +141,32 @@ export class ErvuFileUpload extends InputControl { private setUploaderMethods() { this.uploader.onBeforeUploadItem = (fileItem: FileItem) => { + const additionalParams: any = { + signFile: this.signFileUploadRef.uploader.queue[0]._file + }; + + if (this.mchdFileUploadRef && this.mchdFileUploadRef.uploader.queue.length > 0) { + additionalParams.mchdFile = this.mchdFileUploadRef.uploader.queue[0]._file; + } //refresh headers - this.uploader.setOptions({ - headers: [ - { - name: "X-Employee-Info-File-Form-Type", - value: EmployeeInfoFileFormType[this.formType] - }, - { - name: "Client-Time-Zone", - value: Intl.DateTimeFormat().resolvedOptions().timeZone - }, - { - name: TokenConstants.CSRF_HEADER_NAME, - value: this.cookieService.get(TokenConstants.CSRF_TOKEN_NAME) - } - ] - }); + this.uploader.setOptions({ + headers: [ + { + name: "X-Employee-Info-File-Form-Type", + value: EmployeeInfoFileFormType[this.formType] + }, + { + name: "Client-Time-Zone", + value: Intl.DateTimeFormat().resolvedOptions().timeZone + }, + { + name: TokenConstants.CSRF_HEADER_NAME, + value: this.cookieService.get(TokenConstants.CSRF_TOKEN_NAME) + } + ], + additionalParameter: additionalParams + }); this.fileUploadStartEvent.trigger(); this.isDropZoneVisible = false; this.isFilesListVisible = false; @@ -221,6 +233,13 @@ export class ErvuFileUpload extends InputControl { return undefined; } + getFileIconClass(): string { + if (this.extensionFilter && this.extensionFilter.length > 0) { + return `icon-${this.extensionFilter[0].toLowerCase()}`; + } + return 'icon-default'; + } + subscribeToModelChange() { //empty because there is no ngModel here } diff --git a/pom.xml b/pom.xml index f83faf03..7f7dec9d 100644 --- a/pom.xml +++ b/pom.xml @@ -300,6 +300,11 @@ sardine 5.12 + + org.apache.httpcomponents + httpmime + 4.5.14 + diff --git a/resources/src/main/resources/business-model/Личный кабинет юр лица.page b/resources/src/main/resources/business-model/Личный кабинет юр лица.page index 2df73da3..4e016a8e 100644 --- a/resources/src/main/resources/business-model/Личный кабинет юр лица.page +++ b/resources/src/main/resources/business-model/Личный кабинет юр лица.page @@ -1241,7 +1241,7 @@ initialValue - "Перед выбором файла убедитесь, что все данные в файле введены корректно" + "Перед выбором файла убедитесь, что все данные в файле введены корректно. \u003cbr\u003eФайл подписан усиленной квалифицированной электронной подписью." @@ -1359,38 +1359,7 @@ 529140ee-17b6-44e9-8f2d-a097b4bc044d Текст false - false - - - - cssClasses - - - - "subtitle" - - - - - - - - - - - initialValue - - "Сведения о приеме на работу (увольнении), зачислении в образовательную организацию (отчислении)" - - - - - - - - - false - + true 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 @@ -1477,6 +1446,523 @@ +false + + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + 7ea732e4-71c7-4325-8569-462acbea89b8 + Вертикальный контейнер + true + true + + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + 7ea732e4-71c7-4325-8569-462acbea89b8 + Вертикальный контейнер + true + false + + + + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + 529140ee-17b6-44e9-8f2d-a097b4bc044d + Текст + false + false + + + + cssClasses + + + + "subtitle" + + + + + + + + + + + initialValue + + "Сведения о приеме на работу (увольнении), зачислении в образовательную организацию (отчислении)" + + + + + + + + +false + + + + 5694e7c5-bbb5-4d23-be6c-7ad71b8ad38c + 7d338f47-6d12-4040-ba18-f31f520dce8d + FileUploadV2 + false + false + false + + + + collectible + + false + + + + cssClasses + + + + "btn-main" + + + + + + extensionFilter + + + + "csv" + + + + + + + formType + + "FORM_9" + + + + maxFileSizeMb + + 5.0 + + + + maxFilesToUpload + + 1.0 + + + + mchdFileUploadRef + + {"objectId":"1dc25461-a60e-456b-93cb-cc0f28dc347b","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + removeFileButtonName + + "Удалить" + + + + selectFileButtonName + + "Выбрать" + + + + selectFileFieldText + + "Перетащите файл или выберите на компьютере" + + + + signFileUploadRef + + {"objectId":"409c0323-011e-4416-a631-f8f852299e1f","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + 4333be98-584a-4d30-9427-4d4f7a8b7f7e + Текст + false + false + + + + cssClasses + + + + "mute" + + + + + + initialValue + + "Поддерживаемый формат файла - csv" + + + + + + + + +false + + + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + b8373653-133f-43cd-a8c2-ba83f232d379 + Вертикальный контейнер + true + false + + + + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + bc8596fe-1e46-4cf9-b306-43498f61909c + Текст + false + false + + + + cssClasses + + + + "subtitle" + + + + + + + + + + + initialValue + + "Ваша открепленная усиленная квалифицированная электронная подпись в формате sig" + + + + + + + + +false + + + + 5694e7c5-bbb5-4d23-be6c-7ad71b8ad38c + 409c0323-011e-4416-a631-f8f852299e1f + FileUploadV2 + false + false + + + + collectible + + false + + + + cssClasses + + + + "btn-main" + + + + + + extensionFilter + + + + + "sig" + + + + + + formType + + "FORM_9" + + + + maxFileSizeMb + + 5.0 + + + + maxFilesToUpload + + 1.0 + + + + removeFileButtonName + + "Удалить" + + + + selectFileButtonName + + "Выбрать" + + + + selectFileFieldText + + "Перетащите файл или выберите на компьютере" + + + + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + 7f70dc30-8116-47da-ad8d-01cec26a60d7 + Текст + false + false + + + + cssClasses + + + + "mute" + + + + + + initialValue + + "Поддерживаемый формат файла - sig" + + + + + + + + +false + + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + c4d501b1-7565-45c3-8722-d624126f6cfb + Вертикальный контейнер + true + true + + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + c4d501b1-7565-45c3-8722-d624126f6cfb + Вертикальный контейнер + true + false + + + + + + + 8b755f7b-e52b-4800-830a-f01467cd5cbb + e958f2ee-e112-4bef-9c8a-40e2f8278ca9 + Check box + false + false + + + + + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + f0794dfe-f0e9-446b-8029-c4ff9640f650 + Текст + false + false + + + + cssClasses + + + + "subtitle" + + + + + + + + + + + initialValue + + "Внимание: если файл подписан не руководителем организации, рекомендуется приложить МЧД" + + + + label + + null + + + + + + + + +false + + + + 5694e7c5-bbb5-4d23-be6c-7ad71b8ad38c + 1dc25461-a60e-456b-93cb-cc0f28dc347b + FileUploadV2 + false + false + false + + + + collectible + + false + + + + cssClasses + + + + "btn-main" + + + + + + extensionFilter + + + + + "xml" + + + + + + formType + + "FORM_9" + + + + maxFileSizeMb + + 5.0 + + + + maxFilesToUpload + + 1.0 + + + + removeFileButtonName + + "Удалить" + + + + selectFileButtonName + + "Выбрать" + + + + selectFileFieldText + + "Перетащите файл или выберите на компьютере" + + + + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + 9b446462-16e5-4241-b409-0287dea92b3a + Текст + false + false + + + + cssClasses + + + + "mute" + + + + + + initialValue + + "Поддерживаемый формат файла - xml" + + + + + + + + false @@ -1493,73 +1979,35 @@ 7d338f47-6d12-4040-ba18-f31f520dce8d FileUploadV2 false - false - - - - collectible - - false - - - - cssClasses - - - - "btn-main" - - - - - - extensionFilter - - - - "csv" - - - - - - formType - - "FORM_9" - - - - maxFileSizeMb - - 5.0 - - - - maxFilesToUpload - - 1.0 - - - - removeFileButtonName - - "Удалить" - - - - selectFileButtonName - - "Выбрать" - - - - selectFileFieldText - - "Перетащите файл или выберите на компьютере" - - - - + true + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + 4333be98-584a-4d30-9427-4d4f7a8b7f7e + Текст + false + true + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + bc8596fe-1e46-4cf9-b306-43498f61909c + Текст + false + true + + + 5694e7c5-bbb5-4d23-be6c-7ad71b8ad38c + 409c0323-011e-4416-a631-f8f852299e1f + FileUploadV2 + false + true + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + 7f70dc30-8116-47da-ad8d-01cec26a60d7 + Текст + false + true 98594cec-0a9b-4cef-af09-e1b71cb2ad9e @@ -1596,6 +2044,24 @@ + + + + + behavior + + {"objectId":"409c0323-011e-4416-a631-f8f852299e1f","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + propertyName + + "fileAddedEvent" + + + + + @@ -1605,6 +2071,108 @@ conditions + + + + + _isGroupSelected + + false + + + + one + + + + conditionFirstPart + + + + objectValue + + + + behavior + + {"objectId":"7d338f47-6d12-4040-ba18-f31f520dce8d","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + method + + "getValue" + + + + + + + + + + operation + + "IS_NOT_EMPTY" + + + + + + + + + + + + + _isGroupSelected + + false + + + + one + + + + conditionFirstPart + + + + objectValue + + + + behavior + + {"objectId":"409c0323-011e-4416-a631-f8f852299e1f","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + method + + "getValue" + + + + + + + + + + operation + + "IS_NOT_EMPTY" + + + + + + + + @@ -1696,6 +2264,24 @@ + + + + + behavior + + {"objectId":"409c0323-011e-4416-a631-f8f852299e1f","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + propertyName + + "fileDeletedEvent" + + + + + @@ -1705,6 +2291,108 @@ conditions + + + + + _isGroupSelected + + false + + + + one + + + + conditionFirstPart + + + + objectValue + + + + behavior + + {"objectId":"7d338f47-6d12-4040-ba18-f31f520dce8d","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + method + + "getValue" + + + + + + + + + + operation + + "IS_EMPTY" + + + + + + + + + + + + + _isGroupSelected + + false + + + + one + + + + conditionFirstPart + + + + objectValue + + + + behavior + + {"objectId":"409c0323-011e-4416-a631-f8f852299e1f","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + method + + "getValue" + + + + + + + + + + operation + + "IS_EMPTY" + + + + + + + + @@ -1712,7 +2400,7 @@ logicalOperation - null + "OR" @@ -1850,39 +2538,6 @@ - - ba24d307-0b91-4299-ba82-9d0b52384ff2 - 4333be98-584a-4d30-9427-4d4f7a8b7f7e - Текст - false - false - - - - cssClasses - - - - "mute" - - - - - - initialValue - - "Поддерживаемый формат файла - csv" - - - - - - - - - false - - fd7e47b9-dce1-4d14-9f3a-580c79f59579 9d068751-8bbe-4fa0-b4b5-fa8e395fa019 From 22ffb8a86678189a1f5ec996f8ef4ac0c91b67af Mon Sep 17 00:00:00 2001 From: Eduard Tihomirov Date: Thu, 4 Sep 2025 10:19:01 +0300 Subject: [PATCH 02/45] SUPPORT-9339: Add sign verify --- ...vKafkaConfig.java => FileKafkaConfig.java} | 14 +- .../main/java/ervu/enums/FileStatusCode.java | 1 + .../java/ervu/model/fileupload/FileInfo.java | 12 +- .../EmployeeInfoFileUploadService.java | 559 +++++++++++------- .../EmployeeInfoKafkaMessageService.java | 10 +- .../ervu/kafka/ReplyingKafkaConfig.java | 16 +- .../security/esia/model/MchdInfoModel.java | 369 ++---------- .../security/esia/service/UlDataService.java | 2 + .../esia/service/UlDataServiceImpl.java | 28 +- .../common_errors_messages.properties | 10 +- config/standalone/dev/standalone.xml | 2 +- 11 files changed, 467 insertions(+), 556 deletions(-) rename backend/src/main/java/ervu/{AvKafkaConfig.java => FileKafkaConfig.java} (90%) diff --git a/backend/src/main/java/ervu/AvKafkaConfig.java b/backend/src/main/java/ervu/FileKafkaConfig.java similarity index 90% rename from backend/src/main/java/ervu/AvKafkaConfig.java rename to backend/src/main/java/ervu/FileKafkaConfig.java index 09d4ad30..c5c31c46 100644 --- a/backend/src/main/java/ervu/AvKafkaConfig.java +++ b/backend/src/main/java/ervu/FileKafkaConfig.java @@ -23,7 +23,7 @@ import org.springframework.kafka.core.ProducerFactory; * @author Alexandr Shalaginov */ @Configuration -public class AvKafkaConfig { +public class FileKafkaConfig { @Value("${kafka.hosts}") private String kafkaUrl; @Value("${kafka.auth_sec_proto}") @@ -38,7 +38,7 @@ public class AvKafkaConfig { private String saslMechanism; @Bean - public ProducerFactory avProducerFactory() { + public ProducerFactory fileProducerFactory() { return new DefaultKafkaProducerFactory<>(producerConfigs()); } @@ -58,7 +58,7 @@ public class AvKafkaConfig { } @Bean - public ConsumerFactory avConsumerFactory() { + public ConsumerFactory fileConsumerFactory() { return new DefaultKafkaConsumerFactory<>(consumerConfigs()); } @@ -77,16 +77,16 @@ public class AvKafkaConfig { return props; } - @Bean("avContainerFactory") + @Bean("fileContainerFactory") public ConcurrentKafkaListenerContainerFactory kafkaListenerContainerFactory() { ConcurrentKafkaListenerContainerFactory factory = new ConcurrentKafkaListenerContainerFactory<>(); - factory.setConsumerFactory(avConsumerFactory()); + factory.setConsumerFactory(fileConsumerFactory()); return factory; } - @Bean("avTemplate") + @Bean("fileTemplate") public KafkaTemplate kafkaTemplate() { - return new KafkaTemplate<>(avProducerFactory()); + return new KafkaTemplate<>(fileProducerFactory()); } } diff --git a/backend/src/main/java/ervu/enums/FileStatusCode.java b/backend/src/main/java/ervu/enums/FileStatusCode.java index 9fa3f241..f357e7f1 100644 --- a/backend/src/main/java/ervu/enums/FileStatusCode.java +++ b/backend/src/main/java/ervu/enums/FileStatusCode.java @@ -7,6 +7,7 @@ public enum FileStatusCode { FILE_UPLOADED("01"), FILE_INFECTED("02"), FILE_CLEAN("03"), + FILE_ACCEPTED("04"), FILE_NOT_CHECKED("11"); private final String code; diff --git a/backend/src/main/java/ervu/model/fileupload/FileInfo.java b/backend/src/main/java/ervu/model/fileupload/FileInfo.java index 1603db83..4119ad1f 100644 --- a/backend/src/main/java/ervu/model/fileupload/FileInfo.java +++ b/backend/src/main/java/ervu/model/fileupload/FileInfo.java @@ -15,13 +15,14 @@ public class FileInfo { private String departureDateTime; private String timeZone; private FileStatus fileStatus; + private String type; public FileInfo() { } public FileInfo(String fileId, String fileUrl, String fileName, String filePatternCode, String filePatternName, String fileSize, String departureDateTime, String timeZone, - FileStatus fileStatus) { + FileStatus fileStatus, String type) { this.fileId = fileId; this.fileUrl = fileUrl; this.fileName = fileName; @@ -31,6 +32,7 @@ public class FileInfo { this.departureDateTime = departureDateTime; this.timeZone = timeZone; this.fileStatus = fileStatus; + this.type = type; } public String getFileId() { @@ -69,6 +71,14 @@ public class FileInfo { return fileStatus; } + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + public void setFileStatus(FileStatus fileStatus) { this.fileStatus = fileStatus; } diff --git a/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java b/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java index d21a7371..2642f22b 100644 --- a/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java +++ b/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java @@ -11,9 +11,12 @@ import java.time.Duration; import java.time.LocalDateTime; import java.util.*; +import javax.xml.parsers.DocumentBuilderFactory; + import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.gson.Gson; import ervu.client.fileupload.WebDavClient; import ervu.model.fileupload.*; import org.apache.http.HttpEntity; @@ -34,12 +37,18 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.support.MessageSourceAccessor; import org.springframework.kafka.annotation.KafkaListener; import org.springframework.kafka.core.KafkaTemplate; +import org.springframework.kafka.support.Acknowledgment; +import org.springframework.messaging.handler.annotation.Header; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; +import org.w3c.dom.Document; +import org.w3c.dom.Node; import ru.micord.ervu.audit.service.AuditService; import ru.micord.ervu.exception.JsonParsingException; +import ru.micord.ervu.kafka.service.ReplyingKafkaService; import ru.micord.ervu.security.esia.config.EsiaConfig; import ru.micord.ervu.security.esia.exception.EsiaException; import ru.micord.ervu.security.esia.model.EmployeeModel; @@ -53,6 +62,10 @@ import ru.micord.ervu.security.webbpm.jwt.util.SecurityUtil; import ru.micord.ervu.service.InteractionService; import ru.micord.ervu.util.DateUtils; +import ru.cg.webbpm.modules.core.runtime.api.LocalizedException; +import ru.cg.webbpm.modules.core.runtime.api.MessageBundleUtils; + +import static ervu.enums.FileStatusCode.FILE_ACCEPTED; import static ervu.enums.FileStatusCode.FILE_CLEAN; import static ervu.enums.FileStatusCode.FILE_INFECTED; import static ervu.enums.FileStatusCode.FILE_NOT_CHECKED; @@ -65,8 +78,12 @@ import static ru.micord.ervu.util.StringUtils.convertToFio; @Service public class EmployeeInfoFileUploadService { private static final Logger LOGGER = LoggerFactory.getLogger(EmployeeInfoFileUploadService.class); + private static final String DOCUMENT = "DOCUMENT"; + private static final MessageSourceAccessor MESSAGE_SOURCE = MessageBundleUtils.createAccessor( + "messages/common_errors_messages"); private final WebDavClient webDavClient; private final EmployeeInfoKafkaMessageService employeeInfoKafkaMessageService; + private final ReplyingKafkaService replyingKafkaService; private final KafkaTemplate kafkaTemplate; private final InteractionService interactionService; private final UlDataService ulDataService; @@ -74,31 +91,43 @@ public class EmployeeInfoFileUploadService { private final ObjectMapper objectMapper; private final EsiaConfig esiaConfig; - @Value("${av.kafka.message.topic.name}") - private String kafkaTopicName; + @Value("${kafka.clear.s3}") + private String kafkaClearS3Topic; + + @Value("${kafka.download.request}") + private String kafkaDownloadRequestTopic; + + @Value("${av.kafka.download.request}") + private String kafkaRequestTopic; + + @Value("${av.kafka.download.response}") + private String kafkaResponseTopic; public EmployeeInfoFileUploadService( WebDavClient webDavClient, EmployeeInfoKafkaMessageService employeeInfoKafkaMessageService, - @Qualifier("avTemplate") KafkaTemplate kafkaTemplate, + ReplyingKafkaService replyingKafkaService, InteractionService interactionService, UlDataService ulDataService, AuditService auditService, ObjectMapper objectMapper, - EsiaConfig esiaConfig) { + EsiaConfig esiaConfig, + @Qualifier("fileTemplate") KafkaTemplate kafkaTemplate) { this.webDavClient = webDavClient; this.employeeInfoKafkaMessageService = employeeInfoKafkaMessageService; - this.kafkaTemplate = kafkaTemplate; + this.replyingKafkaService = replyingKafkaService; this.interactionService = interactionService; this.ulDataService = ulDataService; this.auditService = auditService; this.objectMapper = objectMapper; this.esiaConfig = esiaConfig; + this.kafkaTemplate = kafkaTemplate; } public boolean saveEmployeeInformationFiles(MultipartFile multipartFile, MultipartFile signFile, MultipartFile mchdFile, String formType, String offset) { + UserIdsPair userIdsPair = SecurityUtil.getUserIdsPair(); LocalDateTime now = LocalDateTime.now(); String departureDateTime = DateUtils.convertToString(now); @@ -106,182 +135,245 @@ public class EmployeeInfoFileUploadService { if (userIdsPair == null || !isValidCsvWithSig(multipartFile, signFile)) { return false; } - String fileId = UUID.randomUUID().toString(); - String signFileId = UUID.randomUUID().toString(); - String fileName = multipartFile.getOriginalFilename(); - long fileSize = multipartFile.getSize(); - String signFileName = signFile.getOriginalFilename(); - long signFileSize = signFile.getSize(); - EmployeeInfoFileFormType employeeInfoFileFormType = EmployeeInfoFileFormType.valueOf( - formType); String esiaUserId = userIdsPair.getEsiaUserId(); String ervuId = userIdsPair.getErvuId(); String accessToken = EsiaAuthInfoStore.getAccessToken(esiaUserId); + EmployeeModel employeeModel = ulDataService.getEmployeeModel(accessToken); PersonModel personModel = employeeModel.getPerson(); EmployeeModel chiefModel = ulDataService.getChiefEmployeeModel(accessToken); - VerifyDocumentSignResponse verifyDocumentSignResponse = validateSign(multipartFile, signFile); - FileStatus fileStatus = new FileStatus(); + String fio = convertToFio(personModel.getFirstName(), personModel.getMiddleName(), personModel.getLastName() ); + UploadOrgInfo uploadOrgInfo = employeeInfoKafkaMessageService.getOrgInfo(accessToken, ervuId, esiaUserId, personModel ); - FileInfo fileInfo = employeeInfoKafkaMessageService.getFileInfo(fileId, null, fileName, - employeeInfoFileFormType, departureDateTime, offset, null, fileSize + + EmployeeInfoFileFormType employeeInfoFileFormType = EmployeeInfoFileFormType.valueOf(formType); + + FileInfo fileInfo = createFileInfo(multipartFile, employeeInfoFileFormType, + departureDateTime, offset, DOCUMENT ); - FileInfo signFileInfo = employeeInfoKafkaMessageService.getFileInfo(signFileId, null, - signFileName, employeeInfoFileFormType, departureDateTime, offset, null, signFileSize + FileInfo signFileInfo = createFileInfo(signFile, employeeInfoFileFormType, + departureDateTime, offset, "SIGNATURE" ); - if (verifyDocumentSignResponse.getSignVerifyResult() != null) { - fileStatus.setStatus("Некорректная ЭП"); - fileInfo.setFileStatus(fileStatus); - signFileInfo.setFileStatus(fileStatus); - interactionService.setStatus(fileId, fileStatus.getStatus(), fileName, - employeeInfoFileFormType.getFilePatternCode(), Timestamp.valueOf(now), - fio, ervuId - ); - auditService.processUploadEvent(uploadOrgInfo, new FileInfo[] { - fileInfo, signFileInfo - }); - //Заменить на свою ошибку - throw new RuntimeException(verifyDocumentSignResponse.getSignVerifyResult()); + FileInfo mchdFileInfo = mchdFile != null ? + createFileInfo(mchdFile, employeeInfoFileFormType, departureDateTime, + offset, "MCHD" + ) : null; + + FileStatus fileStatus = new FileStatus(); + Map uploadResults = uploadFiles(multipartFile, signFile, mchdFile); + + String fileUploadUrl = uploadResults.get("fileUrl"); + String signFileUploadUrl = uploadResults.get("signFile"); + String mchdFileUploadUrl = uploadResults.get("mchdFile"); + + boolean uploadSuccess = checkUploadSuccess(fileUploadUrl, signFileUploadUrl, mchdFileUploadUrl, + mchdFile != null + ); + + fileStatus.setStatus(uploadSuccess ? "Загрузка" : "Невозможно проверить файл ЛК РП"); + interactionService.setStatus(fileInfo.getFileId(), fileStatus.getStatus(), + multipartFile.getOriginalFilename(), employeeInfoFileFormType.getFilePatternCode(), + Timestamp.valueOf(now), fio, ervuId + ); + + fileInfo.setFileStatus(fileStatus); + signFileInfo.setFileStatus(fileStatus); + if (mchdFileInfo != null) { + mchdFileInfo.setFileStatus(fileStatus); } + + if (!uploadSuccess) { + handleUploadFailure(fileStatus, uploadOrgInfo, fileInfo, signFileInfo, mchdFileInfo, + multipartFile.getOriginalFilename(), signFile.getOriginalFilename(), + mchdFile != null ? mchdFile.getOriginalFilename() : null + ); + return false; + } + + fileInfo.setFileUrl(fileUploadUrl); + signFileInfo.setFileUrl(signFileUploadUrl); + if (mchdFileInfo != null) { + mchdFileInfo.setFileUrl(mchdFileUploadUrl); + } + + FileInfo[] fileInfos = mchdFileInfo != null ? + new FileInfo[] {fileInfo, signFileInfo, mchdFileInfo} : + new FileInfo[] {fileInfo, signFileInfo}; + + String response = sendKafkaMessage(uploadOrgInfo, fileInfos, fileStatus); + + DownloadResponse downloadResponse; + try { + downloadResponse = processMessageFromAv(response); + } + catch (JsonProcessingException e) { + fileStatus.setCode(FILE_NOT_CHECKED.getCode()); + fileStatus.setStatus("Невозможно проверить файл ЛК РП"); + interactionService.updateStatus(fileInfo.getFileId(), fileInfo.getFileStatus().getStatus(), + uploadOrgInfo.getOrgId() + ); + auditService.processUploadEvent(uploadOrgInfo, fileInfos); + throw new RuntimeException(e); + } + + VerifyDocumentSignResponse verifyDocumentSignResponse; + try { + verifyDocumentSignResponse = validateSign(multipartFile, signFile); + } + catch (Exception e) { + fileStatus.setCode(FILE_NOT_CHECKED.getCode()); + fileStatus.setStatus("Некорректная ЭП"); + Arrays.stream(downloadResponse.filesInfo()) + .forEach(fileInfo1 -> fileInfo1.setFileStatus(fileStatus)); + + interactionService.updateStatus(fileInfo.getFileId(), fileInfo.getFileStatus().getStatus(), + uploadOrgInfo.getOrgId() + ); + + auditService.processUploadEvent(uploadOrgInfo, new FileInfo[] {fileInfo, signFileInfo}); + clearS3(response); + throw e; + } + + return validateSignerAndMchd(verifyDocumentSignResponse, chiefModel, uploadOrgInfo, + mchdFile, accessToken, fileStatus, fileInfo, signFileInfo, mchdFileInfo, + now, fio, ervuId, employeeInfoFileFormType, response + ); + } + + private FileInfo createFileInfo(MultipartFile file, EmployeeInfoFileFormType formType, + String departureDateTime, String offset, String fileType) { + + String fileId = UUID.randomUUID().toString(); + return employeeInfoKafkaMessageService.getFileInfo(fileId, null, file.getOriginalFilename(), + formType, departureDateTime, offset, null, file.getSize(), fileType + ); + } + + private Map uploadFiles(MultipartFile multipartFile, MultipartFile signFile, + MultipartFile mchdFile) { + + Map filesToUpload = new HashMap<>(); + filesToUpload.put("fileUrl", multipartFile); + filesToUpload.put("signFile", signFile); + + if (mchdFile != null) { + filesToUpload.put("mchdFile", mchdFile); + } + + return this.webDavClient.uploadFiles(filesToUpload); + } + + private boolean checkUploadSuccess(String fileUploadUrl, String signFileUploadUrl, + String mchdFileUploadUrl, boolean hasMchdFile) { + + if (hasMchdFile) { + return fileUploadUrl != null && signFileUploadUrl != null && mchdFileUploadUrl != null; + } + else { + return fileUploadUrl != null && signFileUploadUrl != null; + } + } + + private void handleUploadFailure(FileStatus fileStatus, UploadOrgInfo uploadOrgInfo, + FileInfo fileInfo, FileInfo signFileInfo, FileInfo mchdFileInfo, + String fileName, String signFileName, String mchdFileName) { + + LOGGER.error("Failed to upload files: {}, {}, {}", fileName, signFileName, mchdFileName); + fileStatus.setCode(FILE_NOT_CHECKED.getCode()); + fileStatus.setDescription( + "Невозможно проверить файл по причине недоступности или ошибки в работе антивируса"); + + FileInfo[] fileInfos = mchdFileInfo != null ? + new FileInfo[] {fileInfo, signFileInfo, mchdFileInfo} : + new FileInfo[] {fileInfo, signFileInfo}; + + auditService.processUploadEvent(uploadOrgInfo, fileInfos); + } + + private String sendKafkaMessage(UploadOrgInfo uploadOrgInfo, FileInfo[] fileInfos, + FileStatus fileStatus) { + + EmployeeInfoKafkaMessage kafkaMessage = employeeInfoKafkaMessageService.getKafkaMessage( + uploadOrgInfo, fileInfos); + + fileStatus.setCode(FILE_UPLOADED.getCode()); + fileStatus.setDescription("Файл принят до проверки на вирусы"); + + String jsonMessage = getJsonKafkaMessage(kafkaMessage); + return replyingKafkaService.sendMessageAndGetReply(kafkaRequestTopic, + kafkaResponseTopic, jsonMessage + ); + } + + private boolean validateSignerAndMchd(VerifyDocumentSignResponse verifyDocumentSignResponse, + EmployeeModel chiefModel, UploadOrgInfo uploadOrgInfo, MultipartFile mchdFile, + String accessToken, FileStatus fileStatus, FileInfo fileInfo, FileInfo signFileInfo, + FileInfo mchdFileInfo, LocalDateTime now, String fio, String ervuId, + EmployeeInfoFileFormType formType, String response) { + String signerInfo = verifyDocumentSignResponse.getSignerInfo(); Map signerInfoMap = parseKeyValuePairs(signerInfo); + String chiefMiddleName = chiefModel.getPerson().getMiddleName(); String chiefLastName = chiefModel.getPerson().getLastName(); String chiefFirstName = chiefModel.getPerson().getFirstName(); - if (signerInfoMap.get("SN").equalsIgnoreCase(chiefLastName) && signerInfoMap.get("G") - .equalsIgnoreCase(chiefFirstName + " " + chiefMiddleName) && signerInfoMap.get("O") - .equalsIgnoreCase(uploadOrgInfo.getOrgName())) { - Map hashMap = this.webDavClient.uploadFiles(Map.of( - "fileUrl", multipartFile, - "signUrl", signFile - )); - String fileUploadUrl = hashMap.get("fileUrl"); - String signFileUploadUrl = hashMap.get("signFile"); - fileStatus.setStatus(fileUploadUrl == null || signFileUploadUrl == null - ? "Невозможно проверить файл ЛК РП" - : "Загрузка"); - interactionService.setStatus(fileId, fileStatus.getStatus(), fileName, - employeeInfoFileFormType.getFilePatternCode(), Timestamp.valueOf(now), - convertToFio(personModel.getFirstName(), personModel.getMiddleName(), - personModel.getLastName() - ), - ervuId - ); - fileInfo.setFileStatus(fileStatus); - signFileInfo.setFileStatus(fileStatus); - if (fileUploadUrl == null || signFileUploadUrl == null) { - LOGGER.error("Failed to upload files: {}, {}", fileName, signFileName); - fileStatus.setCode(FILE_NOT_CHECKED.getCode()); - fileStatus.setDescription( - "Невозможно проверить файл по причине недоступности или ошибки в работе антивируса"); - auditService.processUploadEvent(uploadOrgInfo, new FileInfo[] {fileInfo, signFileInfo}); - return false; - } - else { - fileInfo.setFileUrl(fileUploadUrl); - signFileInfo.setFileUrl(signFileUploadUrl); - EmployeeInfoKafkaMessage kafkaMessage = employeeInfoKafkaMessageService.getKafkaMessage( - uploadOrgInfo, new FileInfo[] { - fileInfo, signFileInfo - }); - fileStatus.setCode(FILE_UPLOADED.getCode()); - fileStatus.setDescription("Файл принят до проверки на вирусы"); - String jsonMessage = getJsonKafkaMessage(kafkaMessage); - return sendMessage(jsonMessage); - } + + boolean isSignerValid = signerInfoMap.get("SN").equalsIgnoreCase(chiefLastName) && + signerInfoMap.get("G") + .equalsIgnoreCase(chiefFirstName + " " + chiefMiddleName) && + signerInfoMap.get("O").equalsIgnoreCase(uploadOrgInfo.getOrgName()); + + if (isSignerValid) { + sendMessage(response); + return true; } - else { - if (mchdFile == null) { - fileStatus.setStatus("Некорректная МЧД"); - interactionService.setStatus(fileId, fileStatus.getStatus(), fileName, - employeeInfoFileFormType.getFilePatternCode(), Timestamp.valueOf(now), - convertToFio(personModel.getFirstName(), personModel.getMiddleName(), - personModel.getLastName() - ), - ervuId - ); - auditService.processUploadEvent(uploadOrgInfo, new FileInfo[] { - fileInfo, signFileInfo - }); - //Заменить на свою ошибку - throw new RuntimeException(); - } - String mchdFileId = UUID.randomUUID().toString(); - String mchdFileName = mchdFile.getOriginalFilename(); - long mchdFileSize = mchdFile.getSize(); - FileInfo mchdFileInfo = employeeInfoKafkaMessageService.getFileInfo(mchdFileId, null, - mchdFileName, employeeInfoFileFormType, departureDateTime, offset, fileStatus, - mchdFileSize + + if (mchdFile == null) { + handleMchdValidationError(fileStatus, uploadOrgInfo, fileInfo, signFileInfo, null, + now, fio, ervuId, formType, response ); - if (validateMchd(mchdFile, accessToken)) { - Map hashMap = this.webDavClient.uploadFiles(Map.of( - "fileUrl", multipartFile, - "signUrl", signFile, - "mchdFile", mchdFile - )); - String fileUploadUrl = hashMap.get("fileUrl"); - String signFileUploadUrl = hashMap.get("signFile"); - String mchdFileUploadUrl = hashMap.get("mchdFile"); - fileStatus.setStatus( - fileUploadUrl == null || signFileUploadUrl == null || mchdFileUploadUrl == null - ? "Невозможно проверить файл ЛК РП" - : "Загрузка"); - interactionService.setStatus(fileId, fileStatus.getStatus(), fileName, - employeeInfoFileFormType.getFilePatternCode(), Timestamp.valueOf(now), - convertToFio(personModel.getFirstName(), personModel.getMiddleName(), - personModel.getLastName() - ), - ervuId - ); - fileInfo.setFileStatus(fileStatus); - signFileInfo.setFileStatus(fileStatus); - if (fileUploadUrl == null || signFileUploadUrl == null || mchdFileUploadUrl == null) { - LOGGER.error("Failed to upload files: {}, {}, {}", fileName, signFileName, - mchdFileName - ); - fileStatus.setCode(FILE_NOT_CHECKED.getCode()); - fileStatus.setDescription( - "Невозможно проверить файл по причине недоступности или ошибки в работе антивируса"); - auditService.processUploadEvent(uploadOrgInfo, - new FileInfo[] {fileInfo, signFileInfo, mchdFileInfo} - ); - return false; - } - else { - fileInfo.setFileUrl(fileUploadUrl); - signFileInfo.setFileUrl(signFileUploadUrl); - mchdFileInfo.setFileUrl(mchdFileUploadUrl); - EmployeeInfoKafkaMessage kafkaMessage = employeeInfoKafkaMessageService.getKafkaMessage( - uploadOrgInfo, new FileInfo[] { - fileInfo, signFileInfo, mchdFileInfo - }); - fileStatus.setCode(FILE_UPLOADED.getCode()); - fileStatus.setDescription("Файл принят до проверки на вирусы"); - String jsonMessage = getJsonKafkaMessage(kafkaMessage); - return sendMessage(jsonMessage); - } - } - else { - fileStatus.setStatus("Некорректная МЧД"); - interactionService.setStatus(fileId, fileStatus.getStatus(), fileName, - employeeInfoFileFormType.getFilePatternCode(), Timestamp.valueOf(now), - convertToFio(personModel.getFirstName(), personModel.getMiddleName(), - personModel.getLastName() - ), - ervuId - ); - auditService.processUploadEvent(uploadOrgInfo, new FileInfo[] { - fileInfo, signFileInfo, mchdFileInfo - }); - //Заменить на свою ошибку - throw new RuntimeException(); - } + throw new LocalizedException("mchd_null", MESSAGE_SOURCE); } + + try { + validateMchd(mchdFile, accessToken, signerInfoMap.get("SN") + " " + signerInfoMap.get("G"), + chiefFirstName, chiefLastName, chiefMiddleName + ); + sendMessage(response); + return true; + } + catch (Exception e) { + handleMchdValidationError(fileStatus, uploadOrgInfo, fileInfo, signFileInfo, mchdFileInfo, + now, fio, ervuId, formType, response + ); + throw e; + } + } + + private void handleMchdValidationError(FileStatus fileStatus, UploadOrgInfo uploadOrgInfo, + FileInfo fileInfo, FileInfo signFileInfo, FileInfo mchdFileInfo, LocalDateTime now, + String fio, String ervuId, EmployeeInfoFileFormType formType, String response) { + + fileStatus.setCode(FILE_NOT_CHECKED.getCode()); + fileStatus.setStatus("Некорректная МЧД"); + + interactionService.setStatus(fileInfo.getFileId(), fileStatus.getStatus(), + fileInfo.getFileName(), formType.getFilePatternCode(), Timestamp.valueOf(now), + fio, ervuId + ); + + FileInfo[] fileInfos = mchdFileInfo != null ? + new FileInfo[] {fileInfo, signFileInfo, mchdFileInfo} : + new FileInfo[] {fileInfo, signFileInfo}; + + auditService.processUploadEvent(uploadOrgInfo, fileInfos); + clearS3(response); } private boolean isValidCsvWithSig(MultipartFile file, MultipartFile signFile) { @@ -325,22 +417,6 @@ public class EmployeeInfoFileUploadService { } } - private boolean sendMessage(String message) { - ProducerRecord record = new ProducerRecord<>(this.kafkaTopicName, message); - record.headers() - .add("messageId", UUID.randomUUID().toString().getBytes(StandardCharsets.UTF_8)); - - try { - this.kafkaTemplate.send(record).get(); - LOGGER.debug("Successfully sent record: {}", record); - return true; - } - catch (Exception exception) { - LOGGER.error("Failed to send message", exception); - return false; - } - } - private String getJsonKafkaMessage(EmployeeInfoKafkaMessage employeeInfoKafkaMessage) { ObjectMapper mapper = new ObjectMapper(); try { @@ -352,25 +428,22 @@ public class EmployeeInfoFileUploadService { } } - @KafkaListener(id = "${av.kafka.group.id}", topics = "${av.kafka.download.response}", - containerFactory = "avContainerFactory") + @KafkaListener(id = "${file.kafka.group.id}", topics = "${kafka.download.response}", + containerFactory = "fileContainerFactory") public void listenKafka(String kafkaMessage) { ObjectMapper mapper = new ObjectMapper(); try { DownloadResponse downloadResponse = mapper.readValue(kafkaMessage, DownloadResponse.class); - FileInfo fileInfo = downloadResponse.filesInfo()[0]; + FileInfo fileInfo = Arrays.stream(downloadResponse.filesInfo()) + .filter(fileInfo1 -> fileInfo1.getType().equals(DOCUMENT)) + .findFirst() + .get(); String statusCode = fileInfo.getFileStatus().getCode(); - if (Arrays.asList(FILE_INFECTED.getCode(), FILE_CLEAN.getCode()).contains(statusCode)) { + if (FILE_ACCEPTED.getCode().equals(statusCode)) { interactionService.delete(fileInfo.getFileId(), downloadResponse.orgInfo().getOrgId()); } - else if (statusCode.equals(FILE_NOT_CHECKED.getCode())) { - auditService.processUploadEvent(downloadResponse.orgInfo(), downloadResponse.filesInfo()); - interactionService.updateStatus(fileInfo.getFileId(), fileInfo.getFileStatus().getStatus(), - downloadResponse.orgInfo().getOrgId() - ); - } } catch (JsonProcessingException e) { throw new JsonParsingException(String.format("Fail get json from: %s", kafkaMessage), e); @@ -392,26 +465,61 @@ public class EmployeeInfoFileUploadService { try (CloseableHttpResponse response = httpClient.execute(upload)) { int statusCode = response.getStatusLine().getStatusCode(); String body = EntityUtils.toString(response.getEntity()); + if (statusCode == 401) { + throw new LocalizedException("file_sign_validate", MESSAGE_SOURCE); + } + String errorCode = objectMapper.readTree(body) + .get("error_code") + .asText(); + if (errorCode.equals("CERT_TRUST_REVOCATION_STATUS_UNKNOWN")) { + throw new LocalizedException("crl_certificate_expired", MESSAGE_SOURCE); + } if (statusCode != 200) { - throw new RuntimeException(statusCode + " " + body); + throw new RuntimeException("Unknown error in verify module. Error code " + errorCode); } return objectMapper.readValue(body, VerifyDocumentSignResponse.class); } } - catch (Exception e) { - throw new RuntimeException(e); + catch (IOException e) { + throw new RuntimeException("Failed to process sign module response ", e); } } - private boolean validateMchd(MultipartFile mchdFile, String accessToken) { + private void validateMchd(MultipartFile mchdFile, String accessToken, String agentFio, + String chiefFirstName, String chiefLastName, String chiefMiddleName) { + String mchdGuid; try { - String mchdGuid = getMchdGuid(mchdFile); - MchdInfoModel mchdInfoModel = ulDataService.getMchdInfo(mchdGuid, accessToken); - mchdInfoModel. - + mchdGuid = getMchdGuid(mchdFile); } catch (Exception e) { - throw new EsiaException(e); + throw new LocalizedException("mchd_cant_parse", MESSAGE_SOURCE); + } + MchdInfoModel mchdInfoModel = ulDataService.getMchdInfoModel(mchdGuid, accessToken); + if (!mchdInfoModel.getStatus().equals("A")) { + throw new LocalizedException("mchd_expired", MESSAGE_SOURCE); + } + boolean validAgent = mchdInfoModel.getAgents() + .getElements() + .stream() + .anyMatch(agent1 -> agentFio.equalsIgnoreCase( + agent1.getPerson().getLastName() + " " + agent1.getPerson().getFirstName() + " " + + agent1.getPerson().getMiddleName())); + if (!validAgent) { + throw new LocalizedException("mchd_validate_agent", MESSAGE_SOURCE); + } + while (mchdInfoModel.getParentGuid() != null) { + mchdInfoModel = ulDataService.getMchdInfoModel(mchdInfoModel.getParentGuid(), accessToken); + if (!mchdInfoModel.getStatus().equals("A")) { + throw new LocalizedException("mchd_tree_expired", MESSAGE_SOURCE); + } + } + MchdInfoModel.Element principal = mchdInfoModel.getPrincipals().getElements().get(0); + MchdInfoModel.Person chief = principal.getOrganization().getChief(); + boolean principalFioEquals = chief.getFirstName().equalsIgnoreCase(chiefFirstName) && + chief.getLastName().equalsIgnoreCase(chiefLastName) && + chief.getMiddleName().equalsIgnoreCase(chiefMiddleName); + if (!principalFioEquals) { + throw new LocalizedException("mchd_validate_principal", MESSAGE_SOURCE); } } @@ -428,14 +536,67 @@ public class EmployeeInfoFileUploadService { return map; } - private String getMchdGuid(MultipartFile mcnhdFile) { - try { - return "asdasdasd"; - + private String getMchdGuid(MultipartFile mchdFile) throws Exception { + Document doc = DocumentBuilderFactory + .newInstance() + .newDocumentBuilder() + .parse(mchdFile.getInputStream()); + doc.getDocumentElement().normalize(); + Node node = doc.getElementsByTagNameNS("*", "СвДов").item(0); + if (node != null && node.getAttributes().getNamedItem("НомДовер") != null) { + return node.getAttributes().getNamedItem("НомДовер").getNodeValue(); } - catch (Exception e) { - throw new EsiaException(e); + return null; + } + + private boolean sendMessage(String message) { + ProducerRecord record = new ProducerRecord<>(this.kafkaDownloadRequestTopic, message); + record.headers() + .add("messageId", UUID.randomUUID().toString().getBytes(StandardCharsets.UTF_8)); + + try { + this.kafkaTemplate.send(record).get(); + LOGGER.debug("Successfully sent record: {}", record); + return true; + } + catch (Exception exception) { + LOGGER.error("Failed to send message", exception); + return false; } } + private void clearS3(String message) { + ProducerRecord record = new ProducerRecord<>(this.kafkaClearS3Topic, message); + record.headers() + .add("messageId", UUID.randomUUID().toString().getBytes(StandardCharsets.UTF_8)); + try { + this.kafkaTemplate.send(record).get(); + } + catch (Exception exception) { + LOGGER.error("Failed to clear s3", exception); + } + + } + + private DownloadResponse processMessageFromAv(String response) throws JsonProcessingException { + DownloadResponse downloadResponse = objectMapper.readValue(response, DownloadResponse.class); + FileInfo avFile = Arrays.stream(downloadResponse.filesInfo()) + .filter(fileInfo1 -> fileInfo1.getType().equals(DOCUMENT)) + .findAny() + .get(); + FileStatus fileStatus1 = avFile.getFileStatus(); + if (fileStatus1.getCode().equals(FILE_INFECTED.getCode())) { + interactionService.updateStatus(avFile.getFileId(), avFile.getFileStatus().getStatus(), + downloadResponse.orgInfo().getOrgId()); + sendMessage(response); + throw new LocalizedException("av_file_infected", MESSAGE_SOURCE); + } + else if (fileStatus1.getCode().equals(FILE_NOT_CHECKED.getCode())) { + interactionService.updateStatus(avFile.getFileId(), avFile.getFileStatus().getStatus(), + downloadResponse.orgInfo().getOrgId()); + auditService.processUploadEvent(downloadResponse.orgInfo(), downloadResponse.filesInfo()); + throw new RuntimeException("File not checked: " + avFile.getFileName()); + } + return downloadResponse; + } } diff --git a/backend/src/main/java/ervu/service/fileupload/EmployeeInfoKafkaMessageService.java b/backend/src/main/java/ervu/service/fileupload/EmployeeInfoKafkaMessageService.java index 90413bab..4e6b86bd 100644 --- a/backend/src/main/java/ervu/service/fileupload/EmployeeInfoKafkaMessageService.java +++ b/backend/src/main/java/ervu/service/fileupload/EmployeeInfoKafkaMessageService.java @@ -26,7 +26,7 @@ public class EmployeeInfoKafkaMessageService { public EmployeeInfoKafkaMessage getKafkaMessage(String fileId, String fileUrl, String fileName, EmployeeInfoFileFormType formType, String departureDateTime, String accessToken, String offset, FileStatus fileStatus, String ervuId, String prnOid, - PersonModel personModel, long fileSize) { + PersonModel personModel, long fileSize, String type) { return new EmployeeInfoKafkaMessage( getOrgInfo(accessToken, ervuId, prnOid, personModel), new FileInfo[] { @@ -38,7 +38,8 @@ public class EmployeeInfoKafkaMessageService { departureDateTime, offset, fileStatus, - fileSize + fileSize, + type ), } ); @@ -50,7 +51,7 @@ public class EmployeeInfoKafkaMessageService { public FileInfo getFileInfo(String fileId, String fileUrl, String fileName, EmployeeInfoFileFormType formType, String departureDateTime, String offset, - FileStatus fileStatus, long fileSize) { + FileStatus fileStatus, long fileSize, String type) { return new FileInfo( fileId, fileUrl, @@ -60,7 +61,8 @@ public class EmployeeInfoKafkaMessageService { String.valueOf(fileSize), departureDateTime, offset, - fileStatus + fileStatus, + type ); } diff --git a/backend/src/main/java/ru/micord/ervu/kafka/ReplyingKafkaConfig.java b/backend/src/main/java/ru/micord/ervu/kafka/ReplyingKafkaConfig.java index e33d1c5e..d3dfc4ca 100644 --- a/backend/src/main/java/ru/micord/ervu/kafka/ReplyingKafkaConfig.java +++ b/backend/src/main/java/ru/micord/ervu/kafka/ReplyingKafkaConfig.java @@ -12,10 +12,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.kafka.annotation.EnableKafka; import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory; -import org.springframework.kafka.core.ConsumerFactory; -import org.springframework.kafka.core.DefaultKafkaConsumerFactory; -import org.springframework.kafka.core.DefaultKafkaProducerFactory; -import org.springframework.kafka.core.ProducerFactory; +import org.springframework.kafka.core.*; import org.springframework.kafka.listener.ConcurrentMessageListenerContainer; import org.springframework.kafka.requestreply.CorrelationKey; import org.springframework.kafka.requestreply.ReplyingKafkaTemplate; @@ -52,6 +49,8 @@ public class ReplyingKafkaConfig { private String password; @Value("${kafka.auth_sasl_mech}") private String saslMechanism; + @Value("${av.kafka.download.response}") + private String avReplyTopic; @Bean("ervuProducerFactory") public ProducerFactory producerFactory() { @@ -61,7 +60,7 @@ public class ReplyingKafkaConfig { configProps.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class); configProps.put(CommonClientConfigs.SECURITY_PROTOCOL_CONFIG, securityProtocol); configProps.put(SaslConfigs.SASL_JAAS_CONFIG, loginModule + " required username=\"" - + username + "\" password=\"" + password + "\";"); + + username + "\" password=\"" + password + "\";"); configProps.put(SaslConfigs.SASL_MECHANISM, saslMechanism); return new DefaultKafkaProducerFactory<>(configProps); } @@ -91,8 +90,11 @@ public class ReplyingKafkaConfig { @Bean public ConcurrentMessageListenerContainer replyContainer( - @Qualifier("ervuContainerFactory") ConcurrentKafkaListenerContainerFactory kafkaListenerContainerFactory) { - return kafkaListenerContainerFactory.createContainer(orgReplyTopic, excerptReplyTopic, journalReplyTopic); + @Qualifier("ervuContainerFactory") + ConcurrentKafkaListenerContainerFactory kafkaListenerContainerFactory) { + return kafkaListenerContainerFactory.createContainer(orgReplyTopic, excerptReplyTopic, + journalReplyTopic, avReplyTopic + ); } @Bean diff --git a/backend/src/main/java/ru/micord/ervu/security/esia/model/MchdInfoModel.java b/backend/src/main/java/ru/micord/ervu/security/esia/model/MchdInfoModel.java index 662dccba..b2f25961 100644 --- a/backend/src/main/java/ru/micord/ervu/security/esia/model/MchdInfoModel.java +++ b/backend/src/main/java/ru/micord/ervu/security/esia/model/MchdInfoModel.java @@ -12,26 +12,11 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @JsonIgnoreProperties(ignoreUnknown = true) public class MchdInfoModel implements Serializable { private static final long serialVersionUID = 1L; - - private List stateFacts; private String guid; - private String number; private String status; - private String issuedOn; - private String expiredOn; - - private Principals principals; - private Agents agents; - private Systems systems; - private Revocations revocations; - - public List getStateFacts() { - return stateFacts; - } - - public void setStateFacts(List stateFacts) { - this.stateFacts = stateFacts; - } + private String parentGuid; + private Member principals; + private Member agents; public String getGuid() { return guid; @@ -41,14 +26,6 @@ public class MchdInfoModel implements Serializable { this.guid = guid; } - public String getNumber() { - return number; - } - - public void setNumber(String number) { - this.number = number; - } - public String getStatus() { return status; } @@ -57,98 +34,48 @@ public class MchdInfoModel implements Serializable { this.status = status; } - public String getIssuedOn() { - return issuedOn; + public String getParentGuid() { + return parentGuid; } - public void setIssuedOn(String issuedOn) { - this.issuedOn = issuedOn; + public void setParentGuid(String parentGuid) { + this.parentGuid = parentGuid; } - public String getExpiredOn() { - return expiredOn; - } - - public void setExpiredOn(String expiredOn) { - this.expiredOn = expiredOn; - } - - public Principals getPrincipals() { + public Member getPrincipals() { return principals; } - public void setPrincipals(Principals principals) { + public void setPrincipals(Member principals) { this.principals = principals; } - public Agents getAgents() { + public Member getAgents() { return agents; } - public void setAgents(Agents agents) { + public void setAgents(Member agents) { this.agents = agents; } - public Systems getSystems() { - return systems; - } - - public void setSystems(Systems systems) { - this.systems = systems; - } - - public Revocations getRevocations() { - return revocations; - } - - public void setRevocations(Revocations revocations) { - this.revocations = revocations; - } - @JsonIgnoreProperties(ignoreUnknown = true) - public static class Principals implements Serializable { - private List stateFacts; - private int size; - private List elements; + public static class Member implements Serializable { + private List elements; - public List getStateFacts() { - return stateFacts; - } - - public void setStateFacts(List stateFacts) { - this.stateFacts = stateFacts; - } - - public int getSize() { - return size; - } - - public void setSize(int size) { - this.size = size; - } - - public List getElements() { + public List getElements() { return elements; } - public void setElements(List elements) { + public void setElements(List elements) { this.elements = elements; } } @JsonIgnoreProperties(ignoreUnknown = true) - public static class PrincipalElement implements Serializable { - private List stateFacts; + public static class Element implements Serializable { + private Person person; private Organization organization; - public List getStateFacts() { - return stateFacts; - } - - public void setStateFacts(List stateFacts) { - this.stateFacts = stateFacts; - } - public Organization getOrganization() { return organization; } @@ -156,51 +83,6 @@ public class MchdInfoModel implements Serializable { public void setOrganization(Organization organization) { this.organization = organization; } - } - - @JsonIgnoreProperties(ignoreUnknown = true) - public static class Agents implements Serializable { - private List stateFacts; - private int size; - private List elements; - - public List getStateFacts() { - return stateFacts; - } - - public void setStateFacts(List stateFacts) { - this.stateFacts = stateFacts; - } - - public int getSize() { - return size; - } - - public void setSize(int size) { - this.size = size; - } - - public List getElements() { - return elements; - } - - public void setElements(List elements) { - this.elements = elements; - } - } - - @JsonIgnoreProperties(ignoreUnknown = true) - public static class AgentElement implements Serializable { - private List stateFacts; - private Person person; - - public List getStateFacts() { - return stateFacts; - } - - public void setStateFacts(List stateFacts) { - this.stateFacts = stateFacts; - } public Person getPerson() { return person; @@ -211,171 +93,10 @@ public class MchdInfoModel implements Serializable { } } - @JsonIgnoreProperties(ignoreUnknown = true) - public static class Systems implements Serializable { - private List stateFacts; - private int size; - private List elements; - - public List getStateFacts() { - return stateFacts; - } - - public void setStateFacts(List stateFacts) { - this.stateFacts = stateFacts; - } - - public int getSize() { - return size; - } - - public void setSize(int size) { - this.size = size; - } - - public List getElements() { - return elements; - } - - public void setElements(List elements) { - this.elements = elements; - } - } - - @JsonIgnoreProperties(ignoreUnknown = true) - public static class SystemElement implements Serializable { - private String nsiId; - private String name; - - public String getNsiId() { - return nsiId; - } - - public void setNsiId(String nsiId) { - this.nsiId = nsiId; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - } - - @JsonIgnoreProperties(ignoreUnknown = true) - public static class Revocations implements Serializable { - private List stateFacts; - private int size; - private List elements; - - public List getStateFacts() { - return stateFacts; - } - - public void setStateFacts(List stateFacts) { - this.stateFacts = stateFacts; - } - - public int getSize() { - return size; - } - - public void setSize(int size) { - this.size = size; - } - - public List getElements() { - return elements; - } - - public void setElements(List elements) { - this.elements = elements; - } - } - - @JsonIgnoreProperties(ignoreUnknown = true) - public static class RevocationElement implements Serializable { - private String number; - private String poaGuid; - private String revokedOn; - private Principals principals; - private Agents agents; - private Signatory signatory; - private Systems systems; - - public String getNumber() { - return number; - } - - public void setNumber(String number) { - this.number = number; - } - - public String getPoaGuid() { - return poaGuid; - } - - public void setPoaGuid(String poaGuid) { - this.poaGuid = poaGuid; - } - - public String getRevokedOn() { - return revokedOn; - } - - public void setRevokedOn(String revokedOn) { - this.revokedOn = revokedOn; - } - - public Principals getPrincipals() { - return principals; - } - - public void setPrincipals(Principals principals) { - this.principals = principals; - } - - public Agents getAgents() { - return agents; - } - - public void setAgents(Agents agents) { - this.agents = agents; - } - - public Signatory getSignatory() { - return signatory; - } - - public void setSignatory(Signatory signatory) { - this.signatory = signatory; - } - - public Systems getSystems() { - return systems; - } - - public void setSystems(Systems systems) { - this.systems = systems; - } - } - @JsonIgnoreProperties(ignoreUnknown = true) public static class Organization implements Serializable { - private String ogrn; private String name; - private String inn; - private String kpp; - - public String getOgrn() { - return ogrn; - } - - public void setOgrn(String ogrn) { - this.ogrn = ogrn; - } + private Person chief; public String getName() { return name; @@ -385,55 +106,43 @@ public class MchdInfoModel implements Serializable { this.name = name; } - public String getInn() { - return inn; + public Person getChief() { + return chief; } - public void setInn(String inn) { - this.inn = inn; - } - - public String getKpp() { - return kpp; - } - - public void setKpp(String kpp) { - this.kpp = kpp; + public void setChief(Person chief) { + this.chief = chief; } } @JsonIgnoreProperties(ignoreUnknown = true) public static class Person implements Serializable { - private String snils; + private String firstName; + private String lastName; + private String middleName; - public String getSnils() { - return snils; + public String getFirstName() { + return firstName; } - public void setSnils(String snils) { - this.snils = snils; - } - } - - @JsonIgnoreProperties(ignoreUnknown = true) - public static class Signatory implements Serializable { - private List stateFacts; - private Person person; - - public List getStateFacts() { - return stateFacts; + public void setFirstName(String firstName) { + this.firstName = firstName; } - public void setStateFacts(List stateFacts) { - this.stateFacts = stateFacts; + public String getLastName() { + return lastName; } - public Person getPerson() { - return person; + public void setLastName(String lastName) { + this.lastName = lastName; } - public void setPerson(Person person) { - this.person = person; + public String getMiddleName() { + return middleName; + } + + public void setMiddleName(String middleName) { + this.middleName = middleName; } } } diff --git a/backend/src/main/java/ru/micord/ervu/security/esia/service/UlDataService.java b/backend/src/main/java/ru/micord/ervu/security/esia/service/UlDataService.java index 8a5cee16..8d5aabe5 100644 --- a/backend/src/main/java/ru/micord/ervu/security/esia/service/UlDataService.java +++ b/backend/src/main/java/ru/micord/ervu/security/esia/service/UlDataService.java @@ -22,4 +22,6 @@ public interface UlDataService { String getAllUserRoles(String accessToken); EsiaHeader readHeader(String accessToken); + + MchdInfoModel getMchdInfoModel(String guid, String accessToken); } diff --git a/backend/src/main/java/ru/micord/ervu/security/esia/service/UlDataServiceImpl.java b/backend/src/main/java/ru/micord/ervu/security/esia/service/UlDataServiceImpl.java index de584e9f..14b6d984 100644 --- a/backend/src/main/java/ru/micord/ervu/security/esia/service/UlDataServiceImpl.java +++ b/backend/src/main/java/ru/micord/ervu/security/esia/service/UlDataServiceImpl.java @@ -17,12 +17,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpHeaders; import org.springframework.stereotype.Service; import ru.micord.ervu.security.esia.exception.EsiaException; -import ru.micord.ervu.security.esia.model.BrhsModel; -import ru.micord.ervu.security.esia.model.EmployeeModel; -import ru.micord.ervu.security.esia.model.EsiaAccessToken; -import ru.micord.ervu.security.esia.model.EsiaHeader; -import ru.micord.ervu.security.esia.model.OrganizationModel; -import ru.micord.ervu.security.esia.model.PersonModel; +import ru.micord.ervu.security.esia.model.*; /** * @author Eduard Tihomirov @@ -282,4 +277,25 @@ public class UlDataServiceImpl implements UlDataService { throw new EsiaException(e); } } + + public MchdInfoModel getMchdInfoModel(String guid, String accessToken) { + try { + String url = esiaConfig.getEsiaBaseUri() + "/poa-registry/api/public/v1/poa/" + guid; + HttpRequest getReq = HttpRequest.newBuilder(URI.create(url)) + .header(HttpHeaders.CONTENT_TYPE, "application/x-www-form-urlencoded") + .header("Authorization", "Bearer ".concat(accessToken)) + .GET() + .timeout(Duration.ofSeconds(esiaConfig.getRequestTimeout())) + .build(); + HttpResponse getResp = HttpClient.newBuilder() + .connectTimeout(Duration.ofSeconds(esiaConfig.getConnectionTimeout())) + .build() + .send(getReq, HttpResponse.BodyHandlers.ofString()); + errorHandler(getResp); + return objectMapper.readValue(getResp.body(), MchdInfoModel.class); + } + catch (Exception e) { + throw new EsiaException(e); + } + } } diff --git a/backend/src/main/resources/messages/common_errors_messages.properties b/backend/src/main/resources/messages/common_errors_messages.properties index c6fba19d..62293ae7 100644 --- a/backend/src/main/resources/messages/common_errors_messages.properties +++ b/backend/src/main/resources/messages/common_errors_messages.properties @@ -1,4 +1,12 @@ kafka_reply_timeout=Превышено время ожидания ответа от сервера. access_denied=Доступ запрещен. Пользователь должен быть включен в группу "Сотрудник, ответственный за военно-учетную работу" в ЕСИА login_attempts_exceeded=Слишком большое количество попыток авторизоваться в ЕСИА за короткий промежуток времени. Рекомендуем почистить cookie и cash браузера, после повторить авторизацию. -crl_certificate_expired=Превышено время ожидания ответа из ЕСИА \ No newline at end of file +crl_certificate_expired=Превышено время ожидания ответа из ЕСИА +file_sign_validate=Ошибка проверки файлов. Некорректная электронная подпись +mchd_validate_agent=Ошибка проверки файлов. Некорректная машиночитаемая доверенность. Представитель не совпадает с подписантом +mchd_null=Ошибка проверки файлов. Отсутствует машиночитаемая доверенность. Подписант не является руководителем организации +mchd_expired=Ошибка проверки файлов. Недействующая машиночитаемая доверенность. +mchd_tree_expired=Ошибка проверки файлов. Одна из родительский доверенностей недействующая. +mchd_validate_principal=Ошибка проверки файлов. Некорректная машиночитаемая доверенность. Доверитель не совпадает с руководителем организации +av_file_infected=Ошибка проверки файлов. Файлы заражены вирусом +mchd_cant_parse=Ошибка проверки файлов. Некорректный формат машиночитаемой доверенности diff --git a/config/standalone/dev/standalone.xml b/config/standalone/dev/standalone.xml index 74055d41..cc632fd9 100644 --- a/config/standalone/dev/standalone.xml +++ b/config/standalone/dev/standalone.xml @@ -88,7 +88,7 @@ - + From 007a06258b8c5263e1463a0f36260799d79f8339 Mon Sep 17 00:00:00 2001 From: Eduard Tihomirov Date: Thu, 4 Sep 2025 10:55:46 +0300 Subject: [PATCH 03/45] SUPPORT-93339: Fix --- .../EmployeeInfoFileUploadService.java | 50 +++++++++---------- .../common_errors_messages_ru_RU.properties | 10 +++- 2 files changed, 32 insertions(+), 28 deletions(-) diff --git a/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java b/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java index 2642f22b..509c3376 100644 --- a/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java +++ b/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java @@ -1,24 +1,26 @@ package ervu.service.fileupload; import java.io.IOException; -import java.net.URI; -import java.net.http.HttpClient; -import java.net.http.HttpRequest; -import java.net.http.HttpResponse; import java.nio.charset.StandardCharsets; import java.sql.Timestamp; -import java.time.Duration; import java.time.LocalDateTime; -import java.util.*; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.UUID; import javax.xml.parsers.DocumentBuilderFactory; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ObjectNode; -import com.google.gson.Gson; import ervu.client.fileupload.WebDavClient; -import ervu.model.fileupload.*; +import ervu.model.fileupload.DownloadResponse; +import ervu.model.fileupload.EmployeeInfoFileFormType; +import ervu.model.fileupload.EmployeeInfoKafkaMessage; +import ervu.model.fileupload.FileInfo; +import ervu.model.fileupload.FileStatus; +import ervu.model.fileupload.UploadOrgInfo; import org.apache.http.HttpEntity; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; @@ -40,8 +42,6 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.context.support.MessageSourceAccessor; import org.springframework.kafka.annotation.KafkaListener; import org.springframework.kafka.core.KafkaTemplate; -import org.springframework.kafka.support.Acknowledgment; -import org.springframework.messaging.handler.annotation.Header; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; import org.w3c.dom.Document; @@ -50,7 +50,6 @@ import ru.micord.ervu.audit.service.AuditService; import ru.micord.ervu.exception.JsonParsingException; import ru.micord.ervu.kafka.service.ReplyingKafkaService; import ru.micord.ervu.security.esia.config.EsiaConfig; -import ru.micord.ervu.security.esia.exception.EsiaException; import ru.micord.ervu.security.esia.model.EmployeeModel; import ru.micord.ervu.security.esia.model.MchdInfoModel; import ru.micord.ervu.security.esia.model.PersonModel; @@ -66,7 +65,6 @@ import ru.cg.webbpm.modules.core.runtime.api.LocalizedException; import ru.cg.webbpm.modules.core.runtime.api.MessageBundleUtils; import static ervu.enums.FileStatusCode.FILE_ACCEPTED; -import static ervu.enums.FileStatusCode.FILE_CLEAN; import static ervu.enums.FileStatusCode.FILE_INFECTED; import static ervu.enums.FileStatusCode.FILE_NOT_CHECKED; import static ervu.enums.FileStatusCode.FILE_UPLOADED; @@ -214,6 +212,8 @@ public class EmployeeInfoFileUploadService { catch (JsonProcessingException e) { fileStatus.setCode(FILE_NOT_CHECKED.getCode()); fileStatus.setStatus("Невозможно проверить файл ЛК РП"); + fileStatus.setDescription( + "Невозможно проверить файл по причине недоступности или ошибки в работе антивируса"); interactionService.updateStatus(fileInfo.getFileId(), fileInfo.getFileStatus().getStatus(), uploadOrgInfo.getOrgId() ); @@ -228,6 +228,7 @@ public class EmployeeInfoFileUploadService { catch (Exception e) { fileStatus.setCode(FILE_NOT_CHECKED.getCode()); fileStatus.setStatus("Некорректная ЭП"); + fileStatus.setDescription("Не пройдена проверка ЭП"); Arrays.stream(downloadResponse.filesInfo()) .forEach(fileInfo1 -> fileInfo1.setFileStatus(fileStatus)); @@ -241,8 +242,7 @@ public class EmployeeInfoFileUploadService { } return validateSignerAndMchd(verifyDocumentSignResponse, chiefModel, uploadOrgInfo, - mchdFile, accessToken, fileStatus, fileInfo, signFileInfo, mchdFileInfo, - now, fio, ervuId, employeeInfoFileFormType, response + mchdFile, accessToken, fileStatus, fileInfo, signFileInfo, mchdFileInfo, ervuId, response ); } @@ -314,8 +314,7 @@ public class EmployeeInfoFileUploadService { private boolean validateSignerAndMchd(VerifyDocumentSignResponse verifyDocumentSignResponse, EmployeeModel chiefModel, UploadOrgInfo uploadOrgInfo, MultipartFile mchdFile, String accessToken, FileStatus fileStatus, FileInfo fileInfo, FileInfo signFileInfo, - FileInfo mchdFileInfo, LocalDateTime now, String fio, String ervuId, - EmployeeInfoFileFormType formType, String response) { + FileInfo mchdFileInfo, String ervuId, String response) { String signerInfo = verifyDocumentSignResponse.getSignerInfo(); Map signerInfoMap = parseKeyValuePairs(signerInfo); @@ -330,13 +329,13 @@ public class EmployeeInfoFileUploadService { signerInfoMap.get("O").equalsIgnoreCase(uploadOrgInfo.getOrgName()); if (isSignerValid) { + interactionService.updateStatus(fileInfo.getFileId(), "Направлено в ЕРВУ", ervuId); sendMessage(response); return true; } if (mchdFile == null) { - handleMchdValidationError(fileStatus, uploadOrgInfo, fileInfo, signFileInfo, null, - now, fio, ervuId, formType, response + handleMchdValidationError(fileStatus, uploadOrgInfo, fileInfo, signFileInfo, null, ervuId, response ); throw new LocalizedException("mchd_null", MESSAGE_SOURCE); } @@ -345,28 +344,25 @@ public class EmployeeInfoFileUploadService { validateMchd(mchdFile, accessToken, signerInfoMap.get("SN") + " " + signerInfoMap.get("G"), chiefFirstName, chiefLastName, chiefMiddleName ); + interactionService.updateStatus(fileInfo.getFileId(), "Направлено в ЕРВУ", ervuId); sendMessage(response); return true; } catch (Exception e) { - handleMchdValidationError(fileStatus, uploadOrgInfo, fileInfo, signFileInfo, mchdFileInfo, - now, fio, ervuId, formType, response + handleMchdValidationError(fileStatus, uploadOrgInfo, fileInfo, signFileInfo, mchdFileInfo, ervuId, response ); throw e; } } private void handleMchdValidationError(FileStatus fileStatus, UploadOrgInfo uploadOrgInfo, - FileInfo fileInfo, FileInfo signFileInfo, FileInfo mchdFileInfo, LocalDateTime now, - String fio, String ervuId, EmployeeInfoFileFormType formType, String response) { + FileInfo fileInfo, FileInfo signFileInfo, FileInfo mchdFileInfo, String ervuId, String response) { fileStatus.setCode(FILE_NOT_CHECKED.getCode()); fileStatus.setStatus("Некорректная МЧД"); + fileStatus.setDescription("Проверка МЧД не пройдена"); - interactionService.setStatus(fileInfo.getFileId(), fileStatus.getStatus(), - fileInfo.getFileName(), formType.getFilePatternCode(), Timestamp.valueOf(now), - fio, ervuId - ); + interactionService.updateStatus(fileInfo.getFileId(), fileStatus.getStatus(), ervuId); FileInfo[] fileInfos = mchdFileInfo != null ? new FileInfo[] {fileInfo, signFileInfo, mchdFileInfo} : diff --git a/backend/src/main/resources/messages/common_errors_messages_ru_RU.properties b/backend/src/main/resources/messages/common_errors_messages_ru_RU.properties index c6fba19d..17307978 100644 --- a/backend/src/main/resources/messages/common_errors_messages_ru_RU.properties +++ b/backend/src/main/resources/messages/common_errors_messages_ru_RU.properties @@ -1,4 +1,12 @@ kafka_reply_timeout=Превышено время ожидания ответа от сервера. access_denied=Доступ запрещен. Пользователь должен быть включен в группу "Сотрудник, ответственный за военно-учетную работу" в ЕСИА login_attempts_exceeded=Слишком большое количество попыток авторизоваться в ЕСИА за короткий промежуток времени. Рекомендуем почистить cookie и cash браузера, после повторить авторизацию. -crl_certificate_expired=Превышено время ожидания ответа из ЕСИА \ No newline at end of file +crl_certificate_expired=Превышено время ожидания ответа из ЕСИА +file_sign_validate=Ошибка проверки файлов. Некорректная электронная подпись +mchd_validate_agent=Ошибка проверки файлов. Некорректная машиночитаемая доверенность. Представитель не совпадает с подписантом +mchd_null=Ошибка проверки файлов. Отсутствует машиночитаемая доверенность. Подписант не является руководителем организации +mchd_expired=Ошибка проверки файлов. Недействующая машиночитаемая доверенность. +mchd_tree_expired=Ошибка проверки файлов. Одна из родительский доверенностей недействующая. +mchd_validate_principal=Ошибка проверки файлов. Некорректная машиночитаемая доверенность. Доверитель не совпадает с руководителем организации +av_file_infected=Ошибка проверки файлов. Файлы заражены вирусом +mchd_cant_parse=Ошибка проверки файлов. Некорректный формат машиночитаемой доверенности \ No newline at end of file From c512f99e321ed8ac99318c309ca542269139d50f Mon Sep 17 00:00:00 2001 From: Eduard Tihomirov Date: Fri, 5 Sep 2025 15:09:52 +0300 Subject: [PATCH 04/45] SUPPORT-9339: Fix --- .../ervu/client/fileupload/WebDavClient.java | 7 ++++++- .../ervu/exception/FileUploadException.java | 19 +++++++++++++++++++ .../EmployeeInfoFileUploadService.java | 10 +++++----- .../ervu/kafka/ReplyingKafkaConfig.java | 5 ++++- .../esia/service/UlDataServiceImpl.java | 9 +++++++-- .../common_errors_messages.properties | 2 +- .../common_errors_messages_ru_RU.properties | 2 +- 7 files changed, 43 insertions(+), 11 deletions(-) create mode 100644 backend/src/main/java/ervu/exception/FileUploadException.java diff --git a/backend/src/main/java/ervu/client/fileupload/WebDavClient.java b/backend/src/main/java/ervu/client/fileupload/WebDavClient.java index 2250c1bf..3e622ede 100644 --- a/backend/src/main/java/ervu/client/fileupload/WebDavClient.java +++ b/backend/src/main/java/ervu/client/fileupload/WebDavClient.java @@ -13,7 +13,12 @@ import java.net.http.HttpResponse; import java.nio.charset.StandardCharsets; import java.time.Duration; import java.time.ZonedDateTime; -import java.util.*; +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; diff --git a/backend/src/main/java/ervu/exception/FileUploadException.java b/backend/src/main/java/ervu/exception/FileUploadException.java new file mode 100644 index 00000000..cdddfd7e --- /dev/null +++ b/backend/src/main/java/ervu/exception/FileUploadException.java @@ -0,0 +1,19 @@ +package ervu.exception; + +/** + * @author Eduard Tihomirov + */ +public class FileUploadException extends RuntimeException{ + + public FileUploadException(String message) { + super(message); + } + + public FileUploadException(String message, Throwable cause) { + super(message, cause); + } + + public FileUploadException(Throwable cause) { + super(cause); + } +} diff --git a/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java b/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java index 509c3376..8b76fcfd 100644 --- a/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java +++ b/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java @@ -15,6 +15,7 @@ import javax.xml.parsers.DocumentBuilderFactory; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import ervu.client.fileupload.WebDavClient; +import ervu.exception.FileUploadException; import ervu.model.fileupload.DownloadResponse; import ervu.model.fileupload.EmployeeInfoFileFormType; import ervu.model.fileupload.EmployeeInfoKafkaMessage; @@ -218,7 +219,7 @@ public class EmployeeInfoFileUploadService { uploadOrgInfo.getOrgId() ); auditService.processUploadEvent(uploadOrgInfo, fileInfos); - throw new RuntimeException(e); + throw new FileUploadException(e); } VerifyDocumentSignResponse verifyDocumentSignResponse; @@ -471,13 +472,13 @@ public class EmployeeInfoFileUploadService { throw new LocalizedException("crl_certificate_expired", MESSAGE_SOURCE); } if (statusCode != 200) { - throw new RuntimeException("Unknown error in verify module. Error code " + errorCode); + throw new FileUploadException("Unknown error in verify module. Error code " + errorCode); } return objectMapper.readValue(body, VerifyDocumentSignResponse.class); } } catch (IOException e) { - throw new RuntimeException("Failed to process sign module response ", e); + throw new FileUploadException("Failed to process sign module response ", e); } } @@ -571,7 +572,6 @@ public class EmployeeInfoFileUploadService { catch (Exception exception) { LOGGER.error("Failed to clear s3", exception); } - } private DownloadResponse processMessageFromAv(String response) throws JsonProcessingException { @@ -591,7 +591,7 @@ public class EmployeeInfoFileUploadService { interactionService.updateStatus(avFile.getFileId(), avFile.getFileStatus().getStatus(), downloadResponse.orgInfo().getOrgId()); auditService.processUploadEvent(downloadResponse.orgInfo(), downloadResponse.filesInfo()); - throw new RuntimeException("File not checked: " + avFile.getFileName()); + throw new FileUploadException("File not checked: " + avFile.getFileName()); } return downloadResponse; } diff --git a/backend/src/main/java/ru/micord/ervu/kafka/ReplyingKafkaConfig.java b/backend/src/main/java/ru/micord/ervu/kafka/ReplyingKafkaConfig.java index d3dfc4ca..3f6df834 100644 --- a/backend/src/main/java/ru/micord/ervu/kafka/ReplyingKafkaConfig.java +++ b/backend/src/main/java/ru/micord/ervu/kafka/ReplyingKafkaConfig.java @@ -12,7 +12,10 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.kafka.annotation.EnableKafka; import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory; -import org.springframework.kafka.core.*; +import org.springframework.kafka.core.ConsumerFactory; +import org.springframework.kafka.core.DefaultKafkaConsumerFactory; +import org.springframework.kafka.core.DefaultKafkaProducerFactory; +import org.springframework.kafka.core.ProducerFactory; import org.springframework.kafka.listener.ConcurrentMessageListenerContainer; import org.springframework.kafka.requestreply.CorrelationKey; import org.springframework.kafka.requestreply.ReplyingKafkaTemplate; diff --git a/backend/src/main/java/ru/micord/ervu/security/esia/service/UlDataServiceImpl.java b/backend/src/main/java/ru/micord/ervu/security/esia/service/UlDataServiceImpl.java index 14b6d984..d4214a6b 100644 --- a/backend/src/main/java/ru/micord/ervu/security/esia/service/UlDataServiceImpl.java +++ b/backend/src/main/java/ru/micord/ervu/security/esia/service/UlDataServiceImpl.java @@ -16,8 +16,13 @@ import ru.micord.ervu.security.esia.config.EsiaConfig; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpHeaders; import org.springframework.stereotype.Service; -import ru.micord.ervu.security.esia.exception.EsiaException; -import ru.micord.ervu.security.esia.model.*; +import ru.micord.ervu.security.esia.model.BrhsModel; +import ru.micord.ervu.security.esia.model.EmployeeModel; +import ru.micord.ervu.security.esia.model.EsiaAccessToken; +import ru.micord.ervu.security.esia.model.EsiaHeader; +import ru.micord.ervu.security.esia.model.MchdInfoModel; +import ru.micord.ervu.security.esia.model.OrganizationModel; +import ru.micord.ervu.security.esia.model.PersonModel; /** * @author Eduard Tihomirov diff --git a/backend/src/main/resources/messages/common_errors_messages.properties b/backend/src/main/resources/messages/common_errors_messages.properties index 62293ae7..b7ea549b 100644 --- a/backend/src/main/resources/messages/common_errors_messages.properties +++ b/backend/src/main/resources/messages/common_errors_messages.properties @@ -6,7 +6,7 @@ file_sign_validate=Ошибка проверки файлов. Некоррек mchd_validate_agent=Ошибка проверки файлов. Некорректная машиночитаемая доверенность. Представитель не совпадает с подписантом mchd_null=Ошибка проверки файлов. Отсутствует машиночитаемая доверенность. Подписант не является руководителем организации mchd_expired=Ошибка проверки файлов. Недействующая машиночитаемая доверенность. -mchd_tree_expired=Ошибка проверки файлов. Одна из родительский доверенностей недействующая. +mchd_tree_expired=Ошибка проверки файлов. Одна из родительских доверенностей недействующая. mchd_validate_principal=Ошибка проверки файлов. Некорректная машиночитаемая доверенность. Доверитель не совпадает с руководителем организации av_file_infected=Ошибка проверки файлов. Файлы заражены вирусом mchd_cant_parse=Ошибка проверки файлов. Некорректный формат машиночитаемой доверенности diff --git a/backend/src/main/resources/messages/common_errors_messages_ru_RU.properties b/backend/src/main/resources/messages/common_errors_messages_ru_RU.properties index 17307978..aa81d4e3 100644 --- a/backend/src/main/resources/messages/common_errors_messages_ru_RU.properties +++ b/backend/src/main/resources/messages/common_errors_messages_ru_RU.properties @@ -6,7 +6,7 @@ file_sign_validate=Ошибка проверки файлов. Некоррек mchd_validate_agent=Ошибка проверки файлов. Некорректная машиночитаемая доверенность. Представитель не совпадает с подписантом mchd_null=Ошибка проверки файлов. Отсутствует машиночитаемая доверенность. Подписант не является руководителем организации mchd_expired=Ошибка проверки файлов. Недействующая машиночитаемая доверенность. -mchd_tree_expired=Ошибка проверки файлов. Одна из родительский доверенностей недействующая. +mchd_tree_expired=Ошибка проверки файлов. Одна из родительских доверенностей недействующая. mchd_validate_principal=Ошибка проверки файлов. Некорректная машиночитаемая доверенность. Доверитель не совпадает с руководителем организации av_file_infected=Ошибка проверки файлов. Файлы заражены вирусом mchd_cant_parse=Ошибка проверки файлов. Некорректный формат машиночитаемой доверенности \ No newline at end of file From 4a08b2c7a1738410c5795ef107e662bc0091a127 Mon Sep 17 00:00:00 2001 From: Eduard Tihomirov Date: Mon, 8 Sep 2025 12:01:40 +0300 Subject: [PATCH 05/45] SUPPORT-9339: Fix --- .../component/fileupload/ErvuFileUpload.ts | 50 +-- .../ErvuFileUploadWithAdditionalFiles.ts | 56 +++ frontend/src/ts/modules/app/app.module.ts | 4 + .../Личный кабинет юр лица.page | 383 ++++++++++++++++-- 4 files changed, 436 insertions(+), 57 deletions(-) create mode 100644 frontend/src/ts/ervu/component/fileupload/ErvuFileUploadWithAdditionalFiles.ts diff --git a/frontend/src/ts/ervu/component/fileupload/ErvuFileUpload.ts b/frontend/src/ts/ervu/component/fileupload/ErvuFileUpload.ts index b79f69f9..eea94ad0 100644 --- a/frontend/src/ts/ervu/component/fileupload/ErvuFileUpload.ts +++ b/frontend/src/ts/ervu/component/fileupload/ErvuFileUpload.ts @@ -5,7 +5,7 @@ import { Event, MessagesService, UnsupportedOperationError, - AppConfigService, ObjectRef + AppConfigService } from "@webbpm/base-package"; import {ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef} from "@angular/core"; import {FileItem, FileUploader} from "ng2-file-upload"; @@ -23,10 +23,6 @@ import {TokenConstants} from "../../../modules/security/TokenConstants"; export class ErvuFileUpload extends InputControl { private static readonly BACKEND_URL: string = "backend.context"; - @ObjectRef() - public signFileUploadRef: ErvuFileUpload; - @ObjectRef() - public mchdFileUploadRef: ErvuFileUpload; @NotNull("true") public selectFileFieldText: string; @NotNull("true") @@ -65,7 +61,7 @@ export class ErvuFileUpload extends InputControl { private messagesService: MessagesService; private isUploadErrorOccurred = false; private appConfigService: AppConfigService; - private cookieService: CookieService; + public cookieService: CookieService; constructor(el: ElementRef, cd: ChangeDetectorRef) { super(el, cd); @@ -139,34 +135,26 @@ export class ErvuFileUpload extends InputControl { this.cd.markForCheck(); } - private setUploaderMethods() { + public setUploaderMethods() { this.uploader.onBeforeUploadItem = (fileItem: FileItem) => { - const additionalParams: any = { - signFile: this.signFileUploadRef.uploader.queue[0]._file - }; - - if (this.mchdFileUploadRef && this.mchdFileUploadRef.uploader.queue.length > 0) { - additionalParams.mchdFile = this.mchdFileUploadRef.uploader.queue[0]._file; - } //refresh headers - this.uploader.setOptions({ - headers: [ - { - name: "X-Employee-Info-File-Form-Type", - value: EmployeeInfoFileFormType[this.formType] - }, - { - name: "Client-Time-Zone", - value: Intl.DateTimeFormat().resolvedOptions().timeZone - }, - { - name: TokenConstants.CSRF_HEADER_NAME, - value: this.cookieService.get(TokenConstants.CSRF_TOKEN_NAME) - } - ], - additionalParameter: additionalParams - }); + this.uploader.setOptions({ + headers: [ + { + name: "X-Employee-Info-File-Form-Type", + value: EmployeeInfoFileFormType[this.formType] + }, + { + name: "Client-Time-Zone", + value: Intl.DateTimeFormat().resolvedOptions().timeZone + }, + { + name: TokenConstants.CSRF_HEADER_NAME, + value: this.cookieService.get(TokenConstants.CSRF_TOKEN_NAME) + } + ] + }); this.fileUploadStartEvent.trigger(); this.isDropZoneVisible = false; this.isFilesListVisible = false; diff --git a/frontend/src/ts/ervu/component/fileupload/ErvuFileUploadWithAdditionalFiles.ts b/frontend/src/ts/ervu/component/fileupload/ErvuFileUploadWithAdditionalFiles.ts new file mode 100644 index 00000000..bad8e760 --- /dev/null +++ b/frontend/src/ts/ervu/component/fileupload/ErvuFileUploadWithAdditionalFiles.ts @@ -0,0 +1,56 @@ +import {ObjectRef} from "@webbpm/base-package"; +import {ChangeDetectionStrategy, Component} from "@angular/core"; +import {FileItem} from "ng2-file-upload"; +import {EmployeeInfoFileFormType} from "./EmployeeInfoFileFormType"; +import {TokenConstants} from "../../../modules/security/TokenConstants"; +import {ErvuFileUpload} from "./ErvuFileUpload"; + +@Component({ + moduleId: module.id, + selector: "ervu-additional-file-upload", + templateUrl: "./../../../../../src/resources/template/ervu/component/ErvuFileUpload.html", + changeDetection: ChangeDetectionStrategy.OnPush + }) +export class ErvuFileUploadWithAdditionalFiles extends ErvuFileUpload { + + @ObjectRef() + public signFileUploadRef: ErvuFileUpload; + @ObjectRef() + public mchdFileUploadRef: ErvuFileUpload; + + public setUploaderMethods() { + this.uploader.onBeforeUploadItem = (fileItem: FileItem) => { + const additionalParams: any = { + signFile: this.signFileUploadRef.uploader.queue[0]._file + }; + + if (this.mchdFileUploadRef && this.mchdFileUploadRef.uploader.queue.length > 0) { + additionalParams.mchdFile = this.mchdFileUploadRef.uploader.queue[0]._file; + } + + //refresh headers + this.uploader.setOptions({ + headers: [ + { + name: "X-Employee-Info-File-Form-Type", + value: EmployeeInfoFileFormType[this.formType] + }, + { + name: "Client-Time-Zone", + value: Intl.DateTimeFormat().resolvedOptions().timeZone + }, + { + name: TokenConstants.CSRF_HEADER_NAME, + value: this.cookieService.get(TokenConstants.CSRF_TOKEN_NAME) + } + ], + additionalParameter: additionalParams + }); + this.fileUploadStartEvent.trigger(); + this.isDropZoneVisible = false; + this.isFilesListVisible = false; + this.isProgressBarVisible = true; + this.cd.markForCheck(); + }; + } +} diff --git a/frontend/src/ts/modules/app/app.module.ts b/frontend/src/ts/modules/app/app.module.ts index 792a4c3f..a24ea8fd 100644 --- a/frontend/src/ts/modules/app/app.module.ts +++ b/frontend/src/ts/modules/app/app.module.ts @@ -27,6 +27,9 @@ import {ErvuDownloadFileButton} from "../../ervu/component/button/ErvuDownloadFi import {AuthenticationService} from "../security/authentication.service"; import {HomeLandingComponent} from "./component/home-landing.component"; import {AuditService} from "../../ervu/service/AuditService"; +import { + ErvuFileUploadWithAdditionalFiles +} from "../../ervu/component/fileupload/ErvuFileUploadWithAdditionalFiles"; registerLocaleData(localeRu); export const DIRECTIVES = [ @@ -36,6 +39,7 @@ export const DIRECTIVES = [ forwardRef(() => AccessDeniedComponent), forwardRef(() => AppProgressIndicationComponent), forwardRef(() => ErvuFileUpload), + forwardRef(() => ErvuFileUploadWithAdditionalFiles), forwardRef(() => ErvuDownloadFileButton), forwardRef(() => InMemoryStaticGrid), forwardRef(() => HomeLandingComponent), diff --git a/resources/src/main/resources/business-model/Личный кабинет юр лица.page b/resources/src/main/resources/business-model/Личный кабинет юр лица.page index 4e016a8e..0f128268 100644 --- a/resources/src/main/resources/business-model/Личный кабинет юр лица.page +++ b/resources/src/main/resources/business-model/Личный кабинет юр лица.page @@ -1078,7 +1078,7 @@ behavior - {"objectId":"7d338f47-6d12-4040-ba18-f31f520dce8d","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"7d338f47-6d12-4040-ba18-f31f520dce8d","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} @@ -1113,7 +1113,7 @@ behavior - {"objectId":"7d338f47-6d12-4040-ba18-f31f520dce8d","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"7d338f47-6d12-4040-ba18-f31f520dce8d","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} @@ -1514,6 +1514,16 @@ false false +false +true + + + + ErvuFileUploadWithAdditionalFiles + ervu.component.fileupload + +true +true collectible @@ -1531,6 +1541,18 @@ + + displayFileSize + + true + + + + displayProgressBar + + true + + extensionFilter @@ -1590,6 +1612,12 @@ {"objectId":"409c0323-011e-4416-a631-f8f852299e1f","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + visible + + true + + @@ -1802,21 +1830,302 @@ + + 98594cec-0a9b-4cef-af09-e1b71cb2ad9e + 81d2a10f-eb67-4002-9cbd-1e67b87888ef + Action Controller + false + false + + + + elseActions + + + + + + behavior + +{"objectId":"1dc25461-a60e-456b-93cb-cc0f28dc347b","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + method + +"setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + +{"objectId":"9b446462-16e5-4241-b409-0287dea92b3a","packageName":"component","className":"Text","type":"TS"} + + + + method + +"setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + eventRefs + + + + + + behavior + +{"objectId":"e958f2ee-e112-4bef-9c8a-40e2f8278ca9","packageName":"component.field","className":"CheckBox","type":"TS"} + + + + propertyName + +"userChangeValueEvent" + + + + + + + + + ifCondition + + + + conditions + + + + + + _isGroupSelected + + false + + + + one + + + + conditionFirstPart + + + +objectValue + + + + behavior + + {"objectId":"e958f2ee-e112-4bef-9c8a-40e2f8278ca9","packageName":"component.field","className":"CheckBox","type":"TS"} + + + + method + + "getValue" + + + + + + + + + + conditionSecondPart + + + +staticValue + + + boolean + + + true + + + + + + + operation + + "EQUALS" + + + + + + + + + + + + logicalOperation + + null + + + + + + + thenActions + + + + + + behavior + +{"objectId":"1dc25461-a60e-456b-93cb-cc0f28dc347b","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + method + +"setVisible" + + + + value + + + + staticValue + + + boolean + + + true + + + + + + + + + + + + + behavior + +{"objectId":"9b446462-16e5-4241-b409-0287dea92b3a","packageName":"component","className":"Text","type":"TS"} + + + + method + +"setVisible" + + + + value + + + + staticValue + + + boolean + + + true + + + + + + + + + + + + + 8b755f7b-e52b-4800-830a-f01467cd5cbb e958f2ee-e112-4bef-9c8a-40e2f8278ca9 Check box false false - + + + + label + + null + + + + - + +false + - + ba24d307-0b91-4299-ba82-9d0b52384ff2 - f0794dfe-f0e9-446b-8029-c4ff9640f650 + 88c07c71-e76e-4ff6-a24d-b838bc495737 Текст false false @@ -1825,16 +2134,11 @@ cssClasses - + "subtitle" - - - - - @@ -1843,12 +2147,6 @@ "Внимание: если файл подписан не руководителем организации, рекомендуется приложить МЧД" - - label - - null - - @@ -1858,6 +2156,13 @@ false + + d7d54cfb-26b5-4dba-b56f-b6247183c24d + 5751d822-0921-4109-9004-ba5d948cf2c4 + Hbox + true + true + 5694e7c5-bbb5-4d23-be6c-7ad71b8ad38c 1dc25461-a60e-456b-93cb-cc0f28dc347b @@ -1930,6 +2235,12 @@ "Перетащите файл или выберите на компьютере" + + visible + + false + + @@ -1957,6 +2268,12 @@ "Поддерживаемый формат файла - xml" + + visible + + false + + @@ -1966,6 +2283,20 @@ false + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + b758e782-c557-445b-a0da-fcc5a79a36e9 + Vbox + true + true + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + ddb464de-7f23-40dd-88bb-a06578ecf0e3 + Vbox + true + true + 5ebd2885-0972-4e51-8376-1fa66aed9a90 @@ -2032,7 +2363,7 @@ behavior - {"objectId":"7d338f47-6d12-4040-ba18-f31f520dce8d","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"7d338f47-6d12-4040-ba18-f31f520dce8d","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} @@ -2095,7 +2426,7 @@ behavior - {"objectId":"7d338f47-6d12-4040-ba18-f31f520dce8d","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"7d338f47-6d12-4040-ba18-f31f520dce8d","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} @@ -2252,7 +2583,7 @@ behavior - {"objectId":"7d338f47-6d12-4040-ba18-f31f520dce8d","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"7d338f47-6d12-4040-ba18-f31f520dce8d","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} @@ -2315,7 +2646,7 @@ behavior - {"objectId":"7d338f47-6d12-4040-ba18-f31f520dce8d","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"7d338f47-6d12-4040-ba18-f31f520dce8d","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} @@ -2515,7 +2846,7 @@ behavior - {"objectId":"7d338f47-6d12-4040-ba18-f31f520dce8d","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"7d338f47-6d12-4040-ba18-f31f520dce8d","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} @@ -2594,7 +2925,7 @@ behavior - {"objectId":"7d338f47-6d12-4040-ba18-f31f520dce8d","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"7d338f47-6d12-4040-ba18-f31f520dce8d","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} @@ -2860,7 +3191,7 @@ behavior - {"objectId":"7d338f47-6d12-4040-ba18-f31f520dce8d","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"7d338f47-6d12-4040-ba18-f31f520dce8d","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} @@ -2967,7 +3298,7 @@ behavior - {"objectId":"7d338f47-6d12-4040-ba18-f31f520dce8d","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"7d338f47-6d12-4040-ba18-f31f520dce8d","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} From 09400ff0f5bb2af8a000777fd8adc4d47a1e9f78 Mon Sep 17 00:00:00 2001 From: Eduard Tihomirov Date: Mon, 8 Sep 2025 12:54:14 +0300 Subject: [PATCH 06/45] SUPPORT-9339: Fix --- .../component/fileupload/ErvuFileUploadWithAdditionalFiles.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/frontend/src/ts/ervu/component/fileupload/ErvuFileUploadWithAdditionalFiles.ts b/frontend/src/ts/ervu/component/fileupload/ErvuFileUploadWithAdditionalFiles.ts index bad8e760..554da959 100644 --- a/frontend/src/ts/ervu/component/fileupload/ErvuFileUploadWithAdditionalFiles.ts +++ b/frontend/src/ts/ervu/component/fileupload/ErvuFileUploadWithAdditionalFiles.ts @@ -1,4 +1,4 @@ -import {ObjectRef} from "@webbpm/base-package"; +import {NotNull, ObjectRef} from "@webbpm/base-package"; import {ChangeDetectionStrategy, Component} from "@angular/core"; import {FileItem} from "ng2-file-upload"; import {EmployeeInfoFileFormType} from "./EmployeeInfoFileFormType"; @@ -14,8 +14,10 @@ import {ErvuFileUpload} from "./ErvuFileUpload"; export class ErvuFileUploadWithAdditionalFiles extends ErvuFileUpload { @ObjectRef() + @NotNull() public signFileUploadRef: ErvuFileUpload; @ObjectRef() + @NotNull() public mchdFileUploadRef: ErvuFileUpload; public setUploaderMethods() { From 7153e347eb7b78414280ed4f6c974c20d3d73fdf Mon Sep 17 00:00:00 2001 From: Eduard Tihomirov Date: Mon, 8 Sep 2025 15:31:43 +0300 Subject: [PATCH 07/45] SUPPORT-9339: fix --- .../EmployeeInfoFileUploadService.java | 6 ++-- config.md | 29 ++++++++++--------- config/local.env | 2 +- config/micord.env | 2 +- config/standalone/dev/standalone.xml | 3 +- 5 files changed, 23 insertions(+), 19 deletions(-) diff --git a/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java b/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java index 8b76fcfd..7340d842 100644 --- a/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java +++ b/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java @@ -90,10 +90,10 @@ public class EmployeeInfoFileUploadService { private final ObjectMapper objectMapper; private final EsiaConfig esiaConfig; - @Value("${kafka.clear.s3}") + @Value("${av.kafka.clear.s3}") private String kafkaClearS3Topic; - @Value("${kafka.download.request}") + @Value("${ervu.kafka.download.request}") private String kafkaDownloadRequestTopic; @Value("${av.kafka.download.request}") @@ -425,7 +425,7 @@ public class EmployeeInfoFileUploadService { } } - @KafkaListener(id = "${file.kafka.group.id}", topics = "${kafka.download.response}", + @KafkaListener(id = "${file.kafka.group.id}", topics = "${ervu.kafka.download.response}", containerFactory = "fileContainerFactory") public void listenKafka(String kafkaMessage) { ObjectMapper mapper = new ObjectMapper(); diff --git a/config.md b/config.md index 9ad2aa56..ab43a8bf 100644 --- a/config.md +++ b/config.md @@ -106,20 +106,23 @@ ## Топики Kafka -| Наименование | Описание | Пишет | Читает | Имя конфигурации | -| ---------------------------------- | ------------------------------------------------------------------------------------------ | -------------------------- | -------------------------------------------------- | -------------------------------- | -| ervu.organization.request | топик для записи данных об организации, для получения id организации из ЕРВУ | ervu-lkrp-ul-backend | ervu-organization-registry | ERVU_KAFKA_ORG_REQUEST_TOPIC | -| ervu.organization.response | топик для чтения id организации из ЕРВУ | ervu-organization-registry | ervu-lkrp-ul-backend | ERVU_KAFKA_ORG_REPLY_TOPIC | -| ervu.organization.journal.request | топик для записи запроса для получения данных по журналу взаимодействия | ervu-lkrp-ul-backend | ervu-organization-registry | ERVU_KAFKA_JOURNAL_REQUEST_TOPIC | -| ervu.organization.journal.response | топик для чтения данных по журналу взаимодействия | ervu-organization-registry | ervu-lkrp-ul-backend | ERVU_KAFKA_JOURNAL_REPLY_TOPIC | -| ervu.lkrp.excerpt.request | топик для записи запроса для получения выписки по журналу взаимодействия | ervu-lkrp-ul-backend | ervu-organization-registry | ERVU_KAFKA_EXCERPT_REQUEST_TOPIC | -| ervu.lkrp.excerpt.response | топик для чтения выписки по журналу взаимодействия. Содержит ссылку на S3 с файлом выписки | ervu-organization-registry | ervu-lkrp-ul-backend | ERVU_KAFKA_EXCERPT_REPLY_TOPIC | -| ervu.lkrp.av-fileupload | топик для записи данных по файлу для перекладчика | ervu-lkrp-ul-backend | ervu-lkrp-av | AV_KAFKA_MESSAGE_TOPIC_NAME | -| ervu.lkrp.av-fileupload-status | топик для чтения статусов файла, полученных от перекладчика | ervu-lkrp-av | ervu-lkrp-ul-backend | AV_KAFKA_DOWNLOAD_RESPONSE | -| ervu.lkrp.auth.events | топик для отправки аудита в журнал авторизации | ervu-lkrp-ul-backend | ervu-lkrp-journal-service | AUDIT_KAFKA_AUTHORIZATION_TOPIC | -| ervu.lkrp.action.events | топик для отправки аудита в журнал действий пользователя | ervu-lkrp-ul-backend | ervu-lkrp-journal-service | AUDIT_KAFKA_ACTION_TOPIC | +| Наименование | Описание | Пишет | Читает | Имя конфигурации | +|------------------------------------|--------------------------------------------------------------------------------------------|----------------------------|---------------------------------------------------|----------------------------------| +| ervu.organization.request | топик для записи данных об организации, для получения id организации из ЕРВУ | ervu-lkrp-ul-backend | ervu-organization-registry | ERVU_KAFKA_ORG_REQUEST_TOPIC | +| ervu.organization.response | топик для чтения id организации из ЕРВУ | ervu-organization-registry | ervu-lkrp-ul-backend | ERVU_KAFKA_ORG_REPLY_TOPIC | +| ervu.organization.journal.request | топик для записи запроса для получения данных по журналу взаимодействия | ervu-lkrp-ul-backend | ervu-organization-registry | ERVU_KAFKA_JOURNAL_REQUEST_TOPIC | +| ervu.organization.journal.response | топик для чтения данных по журналу взаимодействия | ervu-organization-registry | ervu-lkrp-ul-backend | ERVU_KAFKA_JOURNAL_REPLY_TOPIC | +| ervu.lkrp.excerpt.request | топик для записи запроса для получения выписки по журналу взаимодействия | ervu-lkrp-ul-backend | ervu-organization-registry | ERVU_KAFKA_EXCERPT_REQUEST_TOPIC | +| ervu.lkrp.excerpt.response | топик для чтения выписки по журналу взаимодействия. Содержит ссылку на S3 с файлом выписки | ervu-organization-registry | ervu-lkrp-ul-backend | ERVU_KAFKA_EXCERPT_REPLY_TOPIC | +| ervu.lkrp.av-fileupload | топик для записи данных по файлу для перекладчика | ervu-lkrp-ul-backend | ervu-lkrp-av | AV_KAFKA_DOWNLOAD_REQUEST | +| ervu.lkrp.av-fileupload-status | топик для чтения статусов файла, полученных от перекладчика | ervu-lkrp-av | ervu-lkrp-ul-backend | AV_KAFKA_DOWNLOAD_RESPONSE | +| ervu.lkrp.auth.events | топик для отправки аудита в журнал авторизации | ervu-lkrp-ul-backend | ervu-lkrp-journal-service | AUDIT_KAFKA_AUTHORIZATION_TOPIC | +| ervu.lkrp.action.events | топик для отправки аудита в журнал действий пользователя | ervu-lkrp-ul-backend | ervu-lkrp-journal-service | AUDIT_KAFKA_ACTION_TOPIC | | ervu.lkrp.download.request | топик для отправки аудита в журнал обмена файлами | ervu-lkrp-ul-backend | ervu-organization-registry, ervu-validate-recruits | AUDIT_KAFKA_FILE_UPLOAD_TOPIC | -| ervu.lkrp.import.file | топик для отправки аудита в журнал загрузки ЮЛ и ФЛ | ervu-lkrp-ul-backend | ervu-lkrp-journal-service | AUDIT_KAFKA_FILE_DOWNLOAD_TOPIC | +| ervu.lkrp.import.file | топик для отправки аудита в журнал загрузки ЮЛ и ФЛ | ervu-lkrp-ul-backend | ervu-lkrp-journal-service | AUDIT_KAFKA_FILE_DOWNLOAD_TOPIC | +| ervu.lkrp.download.request | топик для записи данных по загруженным файлам | ervu-lkrp-ul-backend | ervu-organization-registry, ervu-validate-recruits| ERVU_KAFKA_DOWNLOAD_REQUEST | +| ervu.lkrp.download.response | топик для чтения данных по загруженным файлам после обработки ерву | ervu-validate-recruits | ervu-lkrp-ul-backend | ERVU_KAFKA_DOWNLOAR_RESPONSE | +| ervu.lkrp.av-clear-s3 | топик для отчистки фалов из s3 | ervu-lkrp-ul-backend | ervu-lkrp-av | AV_KAFKA_CLEAR_S3 | ## Прочее diff --git a/config/local.env b/config/local.env index 80a01839..349163db 100644 --- a/config/local.env +++ b/config/local.env @@ -16,7 +16,7 @@ KAFKA_AUTH_SASL_MODULE=org.apache.kafka.common.security.plain.PlainLoginModule KAFKA_USER=user1 KAFKA_PASS=Blfi9d2OFG -AV_KAFKA_MESSAGE_TOPIC_NAME=file-to-upload +AV_KAFKA_DOWNLOAD_REQUEST=file-to-upload AV_KAFKA_GROUP_ID=local-ervu-lkrp-ul-backend AV_KAFKA_DOWNLOAD_RESPONSE=ervu.lkrp.av-fileupload-status diff --git a/config/micord.env b/config/micord.env index b7a13457..3664f24a 100644 --- a/config/micord.env +++ b/config/micord.env @@ -13,7 +13,7 @@ KAFKA_AUTH_SASL_MODULE=org.apache.kafka.common.security.scram.ScramLoginModule KAFKA_USER=user1 KAFKA_PASS=Blfi9d2OFG -AV_KAFKA_MESSAGE_TOPIC_NAME=file-to-upload +AV_KAFKA_DOWNLOAD_REQUEST=file-to-upload AV_KAFKA_GROUP_ID=1 AV_KAFKA_DOWNLOAD_RESPONSE=ervu.lkrp.av-fileupload-status diff --git a/config/standalone/dev/standalone.xml b/config/standalone/dev/standalone.xml index cc632fd9..e20d194a 100644 --- a/config/standalone/dev/standalone.xml +++ b/config/standalone/dev/standalone.xml @@ -64,7 +64,8 @@ - + + From a4336807cbe86cbfbc83710f14c463b0a8bafb0a Mon Sep 17 00:00:00 2001 From: Eduard Tihomirov Date: Mon, 8 Sep 2025 15:39:20 +0300 Subject: [PATCH 08/45] SUPPORT-9339: Fix --- .../service/fileupload/EmployeeInfoFileUploadService.java | 2 +- .../ru/micord/ervu/security/esia/config/EsiaConfig.java | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java b/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java index 7340d842..26055fbd 100644 --- a/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java +++ b/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java @@ -449,7 +449,7 @@ public class EmployeeInfoFileUploadService { private VerifyDocumentSignResponse validateSign(MultipartFile file, MultipartFile signFile) { try (CloseableHttpClient httpClient = HttpClients.createDefault()) { - HttpPost upload = new HttpPost(esiaConfig.getSignUrl()); + HttpPost upload = new HttpPost(esiaConfig.getFileSignVerifyUrl()); HttpEntity multipart = MultipartEntityBuilder.create() .addBinaryBody("data", file.getBytes(), ContentType.APPLICATION_OCTET_STREAM, file.getOriginalFilename() diff --git a/backend/src/main/java/ru/micord/ervu/security/esia/config/EsiaConfig.java b/backend/src/main/java/ru/micord/ervu/security/esia/config/EsiaConfig.java index 20470f54..decebcdb 100644 --- a/backend/src/main/java/ru/micord/ervu/security/esia/config/EsiaConfig.java +++ b/backend/src/main/java/ru/micord/ervu/security/esia/config/EsiaConfig.java @@ -71,6 +71,9 @@ public class EsiaConfig { @Value("${esia.login.attempts.count:5}") private long esiaLoginAttemptsCount; + @Value("${file.sign.verify.url}") + private String fileSignVerifyUrl; + public String getEsiaOrgScopes() { String[] scopeItems = esiaOrgScopes.split(","); return String.join(" ", Arrays.stream(scopeItems).map(item -> orgScopeUrl + item.trim()).toArray(String[]::new)); @@ -146,4 +149,8 @@ public class EsiaConfig { public long getEsiaLoginAttemptsCount() { return esiaLoginAttemptsCount; } + + public String getFileSignVerifyUrl() { + return fileSignVerifyUrl; + } } From 0bd01d3f30463c69d92fca79038a39cd792ac3a5 Mon Sep 17 00:00:00 2001 From: Eduard Tihomirov Date: Mon, 8 Sep 2025 16:14:33 +0300 Subject: [PATCH 09/45] SUPPORT-9339: Fix --- .../ru/micord/ervu/audit/service/impl/StubAuditService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/main/java/ru/micord/ervu/audit/service/impl/StubAuditService.java b/backend/src/main/java/ru/micord/ervu/audit/service/impl/StubAuditService.java index b45a907e..de364efe 100644 --- a/backend/src/main/java/ru/micord/ervu/audit/service/impl/StubAuditService.java +++ b/backend/src/main/java/ru/micord/ervu/audit/service/impl/StubAuditService.java @@ -26,7 +26,7 @@ public class StubAuditService implements AuditService { String status, String eventType) {} @Override - public void processUploadEvent(UploadOrgInfo uploadOrgInfo, FileInfo fileInfo) {} + public void processUploadEvent(UploadOrgInfo uploadOrgInfo, FileInfo[] filesInfo) {} @Override public void processDownloadEvent(HttpServletRequest request, long fileSize, String fileName, From 5fed7056d61d9e7d750da21f89938454b1e00aae Mon Sep 17 00:00:00 2001 From: Eduard Tihomirov Date: Tue, 9 Sep 2025 13:13:04 +0300 Subject: [PATCH 10/45] SUPPORT-9339: Fix --- .../ervu/client/fileupload/WebDavClient.java | 16 +++++++++------- .../EmployeeInfoFileUploadService.java | 2 +- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/backend/src/main/java/ervu/client/fileupload/WebDavClient.java b/backend/src/main/java/ervu/client/fileupload/WebDavClient.java index 3e622ede..9710792b 100644 --- a/backend/src/main/java/ervu/client/fileupload/WebDavClient.java +++ b/backend/src/main/java/ervu/client/fileupload/WebDavClient.java @@ -100,10 +100,8 @@ public class WebDavClient { for (Map.Entry entry : files.entrySet()) { String key = entry.getKey(); MultipartFile file = entry.getValue(); - if (file != null) { - String fileName = fileCatalog + file.getOriginalFilename(); - result.put(key, putAndGetUrl(file.getBytes(), fileName, sardine)); + result.put(key, putAndGetUrl(file.getBytes(), file.getOriginalFilename(), sardine, fileCatalog)); } } return result; @@ -121,7 +119,7 @@ public class WebDavClient { } } - public String putAndGetUrl(byte[] fileBytes, String fileName, Sardine client) throws IOException { + public String putAndGetUrl(byte[] fileBytes, String fileName, Sardine client, String fileCatalog) throws IOException { if (badServersCache.size() == urls.length) { return null; } @@ -146,19 +144,23 @@ public class WebDavClient { } boolean isBad = false; String serverUrl = server.getUrl(); - String fileUploadUrl = serverUrl + "/" + fileName; - + String directory = serverUrl + "/" + fileCatalog + "/"; + if (!client.exists(directory)) { + client.createDirectory(directory); + } + String fileUploadUrl = directory + fileName; try { client.put(fileUploadUrl, fileBytes); server.setLastCallTime(System.currentTimeMillis()); } catch (ConnectException | ClientProtocolException ignore) { isBad = true; + LOGGER.error("WebDAV error. Url: {}, Message: {}", fileUploadUrl, ignore.getMessage());; } if (isBad) { badServersCache.getUnchecked(serverUrl); - return putAndGetUrl(fileBytes, fileName, client); + return putAndGetUrl(fileBytes, fileName, client, fileCatalog); } return fileUploadUrl; diff --git a/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java b/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java index 26055fbd..f47ff281 100644 --- a/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java +++ b/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java @@ -425,7 +425,7 @@ public class EmployeeInfoFileUploadService { } } - @KafkaListener(id = "${file.kafka.group.id}", topics = "${ervu.kafka.download.response}", + @KafkaListener(id = "${av.kafka.group.id}", topics = "${ervu.kafka.download.response}", containerFactory = "fileContainerFactory") public void listenKafka(String kafkaMessage) { ObjectMapper mapper = new ObjectMapper(); From 583bba3c662c6dd0dcef3035905d8176e8c67c0d Mon Sep 17 00:00:00 2001 From: Eduard Tihomirov Date: Tue, 9 Sep 2025 13:17:52 +0300 Subject: [PATCH 11/45] return --- config/standalone/dev/standalone.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/standalone/dev/standalone.xml b/config/standalone/dev/standalone.xml index e20d194a..39ba4353 100644 --- a/config/standalone/dev/standalone.xml +++ b/config/standalone/dev/standalone.xml @@ -89,7 +89,7 @@ - + From fd1cc8c8711d8daab9f6bead30779a111f84e04a Mon Sep 17 00:00:00 2001 From: Eduard Tihomirov Date: Wed, 10 Sep 2025 10:12:10 +0300 Subject: [PATCH 12/45] SUPPORT-9339: Fix --- .../component/fileupload/ErvuFileUpload.ts | 6 +- .../ErvuFileUploadWithAdditionalFiles.ts | 48 ++ .../Личный кабинет юр лица.page | 545 +++++++++++++++++- 3 files changed, 590 insertions(+), 9 deletions(-) diff --git a/frontend/src/ts/ervu/component/fileupload/ErvuFileUpload.ts b/frontend/src/ts/ervu/component/fileupload/ErvuFileUpload.ts index eea94ad0..e4657e7e 100644 --- a/frontend/src/ts/ervu/component/fileupload/ErvuFileUpload.ts +++ b/frontend/src/ts/ervu/component/fileupload/ErvuFileUpload.ts @@ -56,10 +56,10 @@ export class ErvuFileUpload extends InputControl { public isFilesListVisible: boolean = true; public isProgressBarVisible: boolean = false; - private fileInputEl: HTMLInputElement; + public fileInputEl: HTMLInputElement; private url: string = '/backend/employee/document'; - private messagesService: MessagesService; - private isUploadErrorOccurred = false; + public messagesService: MessagesService; + public isUploadErrorOccurred = false; private appConfigService: AppConfigService; public cookieService: CookieService; diff --git a/frontend/src/ts/ervu/component/fileupload/ErvuFileUploadWithAdditionalFiles.ts b/frontend/src/ts/ervu/component/fileupload/ErvuFileUploadWithAdditionalFiles.ts index 554da959..89877279 100644 --- a/frontend/src/ts/ervu/component/fileupload/ErvuFileUploadWithAdditionalFiles.ts +++ b/frontend/src/ts/ervu/component/fileupload/ErvuFileUploadWithAdditionalFiles.ts @@ -4,6 +4,7 @@ import {FileItem} from "ng2-file-upload"; import {EmployeeInfoFileFormType} from "./EmployeeInfoFileFormType"; import {TokenConstants} from "../../../modules/security/TokenConstants"; import {ErvuFileUpload} from "./ErvuFileUpload"; +import {FileLikeObject} from "ng2-file-upload/file-upload/file-like-object.class"; @Component({ moduleId: module.id, @@ -54,5 +55,52 @@ export class ErvuFileUploadWithAdditionalFiles extends ErvuFileUpload { this.isProgressBarVisible = true; this.cd.markForCheck(); }; + this.uploader.onErrorItem = (item: FileItem, + response: string) => { + this.fileUploadFailedEvent.trigger(); + this.uploader.cancelAll(); + this.messagesService.error(`Не удалось отправить следующие файлы: ${item.file.name},` + + ` ${this.uploader.getNotUploadedItems() + .map(notUploadeditem => notUploadeditem.file.name) + .join(', ')}.`); + this.uploader.clearQueue(); + this.isDropZoneVisible = true; + this.isFilesListVisible = true; + this.isProgressBarVisible = false; + this.isUploadErrorOccurred = true; + this.cd.markForCheck(); + }; + + this.uploader.onCompleteAll = () => { + if (!this.isUploadErrorOccurred) { + this.uploader.clearQueue(); + this.fileUploadEndEvent.trigger(); + this.isProgressBarVisible = false; + this.cd.markForCheck(); + } + }; + + this.uploader.onAfterAddingFile = (fileItem: FileItem) => { + this.fileAddedEvent.trigger(); + } + + this.uploader.onWhenAddingFileFailed = (item: FileLikeObject, filter: any, options: any) => { + switch (filter.name) { + case "fileSize": + this.messagesService.error(`Размер файла ${item.name} превышает предельно допустимый = ${this.maxFileSizeMb} MB`); + break; + case "queueLimit": + this.messagesService.error(`Не удалось добавить файл ${item.name}. ` + + `Достигнуто максимальное количество файлов для загрузки = ${this.maxFilesToUpload}`); + break; + case "extension": + this.messagesService.error(`Файл ${item.name} имеет недопустимое расширение.`); + break; + default: + this.messagesService.error(`Не удалось добавить файл ${item.name}.`); + } + this.fileInputEl.value = null; + this.cd.markForCheck(); + }; } } diff --git a/resources/src/main/resources/business-model/Личный кабинет юр лица.page b/resources/src/main/resources/business-model/Личный кабинет юр лица.page index 0f128268..901c4492 100644 --- a/resources/src/main/resources/business-model/Личный кабинет юр лица.page +++ b/resources/src/main/resources/business-model/Личный кабинет юр лица.page @@ -904,6 +904,12 @@ false + + elseActions + + + + eventRefs @@ -1119,13 +1125,24 @@ method - "reset" + "setVisible" value - null + + + staticValue + + + boolean + + + true + + + @@ -1194,6 +1211,181 @@ false + + + + + + + + + + + + behavior + + {"objectId":"bc8596fe-1e46-4cf9-b306-43498f61909c","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + true + + + + + + + + + + + + + behavior + + {"objectId":"409c0323-011e-4416-a631-f8f852299e1f","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + true + + + + + + + + + + + + + behavior + + {"objectId":"7f70dc30-8116-47da-ad8d-01cec26a60d7","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + true + + + + + + + + + + + + + behavior + + {"objectId":"e958f2ee-e112-4bef-9c8a-40e2f8278ca9","packageName":"component.field","className":"CheckBox","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + true + + + + + + + + + + + + + behavior + + {"objectId":"88c07c71-e76e-4ff6-a24d-b838bc495737","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + true + @@ -1509,7 +1701,7 @@ 5694e7c5-bbb5-4d23-be6c-7ad71b8ad38c 7d338f47-6d12-4040-ba18-f31f520dce8d - FileUploadV2 + Csv file false false false @@ -1707,7 +1899,7 @@ 5694e7c5-bbb5-4d23-be6c-7ad71b8ad38c 409c0323-011e-4416-a631-f8f852299e1f - FileUploadV2 + Подпись File false false @@ -1833,7 +2025,7 @@ 98594cec-0a9b-4cef-af09-e1b71cb2ad9e 81d2a10f-eb67-4002-9cbd-1e67b87888ef - Action Controller + AC_МЧД false false @@ -2166,7 +2358,7 @@ 5694e7c5-bbb5-4d23-be6c-7ad71b8ad38c 1dc25461-a60e-456b-93cb-cc0f28dc347b - FileUploadV2 + Мчд File false false false @@ -3162,6 +3354,251 @@ false + + + + + + + + + + + + behavior + + {"objectId":"bc8596fe-1e46-4cf9-b306-43498f61909c","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + + {"objectId":"409c0323-011e-4416-a631-f8f852299e1f","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + + {"objectId":"7f70dc30-8116-47da-ad8d-01cec26a60d7","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + + {"objectId":"e958f2ee-e112-4bef-9c8a-40e2f8278ca9","packageName":"component.field","className":"CheckBox","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + + {"objectId":"88c07c71-e76e-4ff6-a24d-b838bc495737","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + + {"objectId":"1dc25461-a60e-456b-93cb-cc0f28dc347b","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + + {"objectId":"9b446462-16e5-4241-b409-0287dea92b3a","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + @@ -3273,6 +3710,54 @@ + + + + + behavior + + {"objectId":"409c0323-011e-4416-a631-f8f852299e1f","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + method + + "clearValue" + + + + value + + null + + + + + + + + + + behavior + + {"objectId":"1dc25461-a60e-456b-93cb-cc0f28dc347b","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + method + + "clearValue" + + + + value + + null + + + + + @@ -3380,6 +3865,54 @@ + + + + + behavior + + {"objectId":"1dc25461-a60e-456b-93cb-cc0f28dc347b","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + method + + "clearValue" + + + + value + + null + + + + + + + + + + behavior + + {"objectId":"409c0323-011e-4416-a631-f8f852299e1f","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + method + + "clearValue" + + + + value + + null + + + + + From accc88da08addbec031448cb3f342290a0fb6fe4 Mon Sep 17 00:00:00 2001 From: "m.epshtein" Date: Wed, 10 Sep 2025 10:44:47 +0300 Subject: [PATCH 13/45] SUPPORT-9397 --- .../src/resources/css/components-lkrp.css | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/frontend/src/resources/css/components-lkrp.css b/frontend/src/resources/css/components-lkrp.css index 9c84f7de..ff4a5efb 100644 --- a/frontend/src/resources/css/components-lkrp.css +++ b/frontend/src/resources/css/components-lkrp.css @@ -818,11 +818,14 @@ margin-bottom: 0; } -.webbpm.ervu_lkrp_ul .modal.show ervu-file-upload { +.webbpm.ervu_lkrp_ul .modal.show :is(ervu-file-upload, ervu-additional-file-upload) { display: block; margin: var(--indent-mini) 0; } -.webbpm.ervu_lkrp_ul .modal.show ervu-file-upload .file-drop-zone { +.webbpm.ervu_lkrp_ul .modal.show :is(ervu-file-upload, ervu-additional-file-upload) ~ button-component:not(.info):not(.link) { + padding-top: 0; +} +.webbpm.ervu_lkrp_ul .modal.show .ervu-file-upload .file-drop-zone { display: flex; flex-direction: column; align-items: center; @@ -831,14 +834,14 @@ border: 2px dashed var(--border-light); background: transparent; } -.webbpm.ervu_lkrp_ul .modal.show ervu-file-upload .file-drop-zone .select-file-field-text { +.webbpm.ervu_lkrp_ul .modal.show .ervu-file-upload .file-drop-zone .select-file-field-text { color: var(--color-light); } -.webbpm.ervu_lkrp_ul .modal.show ervu-file-upload .file-drop-zone .select-file-btn { +.webbpm.ervu_lkrp_ul .modal.show .ervu-file-upload .file-drop-zone .select-file-btn { margin-top: var(--indent-mini); outline: transparent; } -.webbpm.ervu_lkrp_ul .modal.show ervu-file-upload .selected-file { +.webbpm.ervu_lkrp_ul .modal.show .ervu-file-upload .selected-file { display: flex; white-space: nowrap; padding: 12px 16px; @@ -846,7 +849,7 @@ border-radius: 4px; background-color: var(--bg-light); } -.webbpm.ervu_lkrp_ul .modal.show ervu-file-upload .selected-file .selected-file-name { +.webbpm.ervu_lkrp_ul .modal.show .ervu-file-upload .selected-file .selected-file-name { position: relative; color: var(--color-text-primary); font-size: var(--size-text-secondary); @@ -855,24 +858,24 @@ overflow: hidden; padding-left: 56px; } -.webbpm.ervu_lkrp_ul .modal.show ervu-file-upload .selected-file .icon-csv::before { +.webbpm.ervu_lkrp_ul .modal.show .ervu-file-upload .selected-file .icon-csv::before { position: absolute; content: url(../img/svg/file-csv.svg); top: -2px; left: 0; } -.webbpm.ervu_lkrp_ul .modal.show ervu-file-upload .selected-file .selected-file-size { +.webbpm.ervu_lkrp_ul .modal.show .ervu-file-upload .selected-file .selected-file-size { color: var(--border-light); font-family: 'InterL'; font-size: var(--size-text-secondary); margin-left: 8px; } -.webbpm.ervu_lkrp_ul .modal.show ervu-file-upload .selected-file .selected-file-size::before { +.webbpm.ervu_lkrp_ul .modal.show .ervu-file-upload .selected-file .selected-file-size::before { position: relative; content: "|"; margin-right: 8px; } -.webbpm.ervu_lkrp_ul .modal.show ervu-file-upload .selected-file .selected-file-delete-btn { +.webbpm.ervu_lkrp_ul .modal.show .ervu-file-upload .selected-file .selected-file-delete-btn { color: var(--color-link); font-size: var(--size-text-secondary); margin-left: auto; @@ -880,12 +883,9 @@ background-color: transparent; outline: transparent; } -.webbpm.ervu_lkrp_ul .modal.show ervu-file-upload .selected-file .selected-file-delete-btn:is(:hover, :focus, :active) { +.webbpm.ervu_lkrp_ul .modal.show .ervu-file-upload .selected-file .selected-file-delete-btn:is(:hover, :focus, :active) { color: var(--color-link-hover); } -.webbpm.ervu_lkrp_ul .modal.show ervu-file-upload ~ button-component:not(.info):not(.link) { - padding-top: 0; -} .webbpm.ervu_lkrp_ul .modal.show .modal-content .paragraph-list > .vertical-container > * { position: relative; From 2b5df5129d31e624317e0f4a3b5df96cc037cb12 Mon Sep 17 00:00:00 2001 From: Eduard Tihomirov Date: Thu, 11 Sep 2025 12:33:20 +0300 Subject: [PATCH 14/45] SUPPORT-9339: Fix --- .../model/fileupload/FileStatusResponse.java | 7 ++++ .../EmployeeInfoFileUploadService.java | 33 ++++++++++++------- 2 files changed, 28 insertions(+), 12 deletions(-) create mode 100644 backend/src/main/java/ervu/model/fileupload/FileStatusResponse.java diff --git a/backend/src/main/java/ervu/model/fileupload/FileStatusResponse.java b/backend/src/main/java/ervu/model/fileupload/FileStatusResponse.java new file mode 100644 index 00000000..ac6a7a85 --- /dev/null +++ b/backend/src/main/java/ervu/model/fileupload/FileStatusResponse.java @@ -0,0 +1,7 @@ +package ervu.model.fileupload; + +/** + * @author Eduard Tihomirov + */ +public record FileStatusResponse(UploadOrgInfo orgInfo, FileInfo[] filesInfo, FileStatus packInfo) { +} diff --git a/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java b/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java index f47ff281..d264f110 100644 --- a/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java +++ b/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java @@ -21,6 +21,7 @@ import ervu.model.fileupload.EmployeeInfoFileFormType; import ervu.model.fileupload.EmployeeInfoKafkaMessage; import ervu.model.fileupload.FileInfo; import ervu.model.fileupload.FileStatus; +import ervu.model.fileupload.FileStatusResponse; import ervu.model.fileupload.UploadOrgInfo; import org.apache.http.HttpEntity; import org.apache.http.client.methods.CloseableHttpResponse; @@ -428,18 +429,16 @@ public class EmployeeInfoFileUploadService { @KafkaListener(id = "${av.kafka.group.id}", topics = "${ervu.kafka.download.response}", containerFactory = "fileContainerFactory") public void listenKafka(String kafkaMessage) { - ObjectMapper mapper = new ObjectMapper(); - try { - DownloadResponse downloadResponse = mapper.readValue(kafkaMessage, DownloadResponse.class); - FileInfo fileInfo = Arrays.stream(downloadResponse.filesInfo()) + FileStatusResponse fileStatusResponse = objectMapper.readValue(kafkaMessage, FileStatusResponse.class); + FileInfo fileInfo = Arrays.stream(fileStatusResponse.filesInfo()) .filter(fileInfo1 -> fileInfo1.getType().equals(DOCUMENT)) .findFirst() .get(); - String statusCode = fileInfo.getFileStatus().getCode(); + String statusCode = fileStatusResponse.packInfo().getCode(); if (FILE_ACCEPTED.getCode().equals(statusCode)) { - interactionService.delete(fileInfo.getFileId(), downloadResponse.orgInfo().getOrgId()); + interactionService.delete(fileInfo.getFileId(), fileStatusResponse.orgInfo().getOrgId()); } } catch (JsonProcessingException e) { @@ -465,14 +464,24 @@ public class EmployeeInfoFileUploadService { if (statusCode == 401) { throw new LocalizedException("file_sign_validate", MESSAGE_SOURCE); } - String errorCode = objectMapper.readTree(body) - .get("error_code") - .asText(); - if (errorCode.equals("CERT_TRUST_REVOCATION_STATUS_UNKNOWN")) { - throw new LocalizedException("crl_certificate_expired", MESSAGE_SOURCE); + if (statusCode == 500) { + try { + String errorCode = objectMapper.readTree(body) + .get("error_code") + .asText(); + if (errorCode.equals("CERT_TRUST_REVOCATION_STATUS_UNKNOWN")) { + throw new LocalizedException("crl_certificate_expired", MESSAGE_SOURCE); + } + else { + throw new FileUploadException("Unknown 500 error in verify module. Error code " + errorCode); + } + } + catch (Exception e) { + throw new FileUploadException("Unknown 500 error in verify module. Response body " + body, e); + } } if (statusCode != 200) { - throw new FileUploadException("Unknown error in verify module. Error code " + errorCode); + throw new FileUploadException("Unknown error in verify module. StatusCode " + statusCode + " .Response body " + body); } return objectMapper.readValue(body, VerifyDocumentSignResponse.class); } From 9df894b78e69dad6bb1c0f4afe343f5609dbd3ec Mon Sep 17 00:00:00 2001 From: Eduard Tihomirov Date: Thu, 11 Sep 2025 14:11:44 +0300 Subject: [PATCH 15/45] SUPPORT-9339: Fix --- .../ErvuFileUploadWithAdditionalFiles.ts | 20 +++- .../Личный кабинет юр лица.page | 100 +----------------- 2 files changed, 20 insertions(+), 100 deletions(-) diff --git a/frontend/src/ts/ervu/component/fileupload/ErvuFileUploadWithAdditionalFiles.ts b/frontend/src/ts/ervu/component/fileupload/ErvuFileUploadWithAdditionalFiles.ts index 89877279..b69b6043 100644 --- a/frontend/src/ts/ervu/component/fileupload/ErvuFileUploadWithAdditionalFiles.ts +++ b/frontend/src/ts/ervu/component/fileupload/ErvuFileUploadWithAdditionalFiles.ts @@ -59,11 +59,21 @@ export class ErvuFileUploadWithAdditionalFiles extends ErvuFileUpload { response: string) => { this.fileUploadFailedEvent.trigger(); this.uploader.cancelAll(); - this.messagesService.error(`Не удалось отправить следующие файлы: ${item.file.name},` - + ` ${this.uploader.getNotUploadedItems() - .map(notUploadeditem => notUploadeditem.file.name) - .join(', ')}.`); + try { + var error = JSON.parse(response); + error.messages.forEach((errorMessage) => { + this.messagesService.error(errorMessage, error); + }) + } + catch (error) { + this.messagesService.error(`Не удалось отправить следующие файлы: ${item.file.name}` + + ` ${this.uploader.getNotUploadedItems() + .map(notUploadeditem => notUploadeditem.file.name) + .join(', ')}.`); + } this.uploader.clearQueue(); + this.mchdFileUploadRef.uploader.clearQueue(); + this.signFileUploadRef.uploader.clearQueue(); this.isDropZoneVisible = true; this.isFilesListVisible = true; this.isProgressBarVisible = false; @@ -74,6 +84,8 @@ export class ErvuFileUploadWithAdditionalFiles extends ErvuFileUpload { this.uploader.onCompleteAll = () => { if (!this.isUploadErrorOccurred) { this.uploader.clearQueue(); + this.mchdFileUploadRef.uploader.clearQueue(); + this.signFileUploadRef.uploader.clearQueue(); this.fileUploadEndEvent.trigger(); this.isProgressBarVisible = false; this.cd.markForCheck(); diff --git a/resources/src/main/resources/business-model/Личный кабинет юр лица.page b/resources/src/main/resources/business-model/Личный кабинет юр лица.page index 901c4492..20deeacb 100644 --- a/resources/src/main/resources/business-model/Личный кабинет юр лица.page +++ b/resources/src/main/resources/business-model/Личный кабинет юр лица.page @@ -3710,54 +3710,8 @@ - - - - - behavior - - {"objectId":"409c0323-011e-4416-a631-f8f852299e1f","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} - - - - method - - "clearValue" - - - - value - - null - - - - - - - - - - behavior - - {"objectId":"1dc25461-a60e-456b-93cb-cc0f28dc347b","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} - - - - method - - "clearValue" - - - - value - - null - - - - - + + @@ -3865,54 +3819,8 @@ - - - - - behavior - - {"objectId":"1dc25461-a60e-456b-93cb-cc0f28dc347b","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} - - - - method - - "clearValue" - - - - value - - null - - - - - - - - - - behavior - - {"objectId":"409c0323-011e-4416-a631-f8f852299e1f","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} - - - - method - - "clearValue" - - - - value - - null - - - - - + + From ede5bf85a0fd5c4a9c9d8fbd9078de561e4ff9e0 Mon Sep 17 00:00:00 2001 From: Eduard Tihomirov Date: Fri, 12 Sep 2025 10:27:54 +0300 Subject: [PATCH 16/45] SUPPORT-9339: Fix --- .../service/fileupload/EmployeeInfoFileUploadService.java | 6 ++++-- .../fileupload/ErvuFileUploadWithAdditionalFiles.ts | 3 +++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java b/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java index d264f110..a20fed24 100644 --- a/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java +++ b/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java @@ -548,11 +548,13 @@ public class EmployeeInfoFileUploadService { .newDocumentBuilder() .parse(mchdFile.getInputStream()); doc.getDocumentElement().normalize(); - Node node = doc.getElementsByTagNameNS("*", "СвДов").item(0); + Node node = doc.getElementsByTagName("СвДов").item(0); if (node != null && node.getAttributes().getNamedItem("НомДовер") != null) { return node.getAttributes().getNamedItem("НомДовер").getNodeValue(); } - return null; + else { + throw new FileUploadException("Cannot parse mchd guid"); + } } private boolean sendMessage(String message) { diff --git a/frontend/src/ts/ervu/component/fileupload/ErvuFileUploadWithAdditionalFiles.ts b/frontend/src/ts/ervu/component/fileupload/ErvuFileUploadWithAdditionalFiles.ts index b69b6043..78b0988a 100644 --- a/frontend/src/ts/ervu/component/fileupload/ErvuFileUploadWithAdditionalFiles.ts +++ b/frontend/src/ts/ervu/component/fileupload/ErvuFileUploadWithAdditionalFiles.ts @@ -71,9 +71,12 @@ export class ErvuFileUploadWithAdditionalFiles extends ErvuFileUpload { .map(notUploadeditem => notUploadeditem.file.name) .join(', ')}.`); } + this.fileInputEl.value = null; this.uploader.clearQueue(); this.mchdFileUploadRef.uploader.clearQueue(); + this.mchdFileUploadRef.fileInputEl.value = null; this.signFileUploadRef.uploader.clearQueue(); + this.signFileUploadRef.fileInputEl.value = null; this.isDropZoneVisible = true; this.isFilesListVisible = true; this.isProgressBarVisible = false; From ab7cdbb6b90669a2a3ae46ae48c000cf94d97690 Mon Sep 17 00:00:00 2001 From: Eduard Tihomiorv Date: Wed, 1 Oct 2025 18:44:14 +0300 Subject: [PATCH 17/45] SUPPORT-9339: Fix --- .../ervu/client/fileupload/WebDavClient.java | 8 +- .../main/java/ervu/enums/FileStatusCode.java | 4 +- .../EmployeeInfoFileUploadService.java | 128 +++++++++----- .../ervu/audit/model/AuditUploadEvent.java | 13 +- .../ervu/audit/service/AuditService.java | 3 +- .../audit/service/impl/BaseAuditService.java | 6 +- .../audit/service/impl/StubAuditService.java | 3 +- .../micord/ervu/journal/JournalFileInfo.java | 158 ++++++++++-------- .../ervu/journal/mapper/JournalDtoMapper.java | 13 +- .../kafka/controller/ErvuKafkaController.java | 4 +- .../JournalInMemoryStaticGridLoadService.java | 5 +- .../ervu/component/grid/filter/SetFilter.ts | 2 +- 12 files changed, 223 insertions(+), 124 deletions(-) diff --git a/backend/src/main/java/ervu/client/fileupload/WebDavClient.java b/backend/src/main/java/ervu/client/fileupload/WebDavClient.java index 9710792b..06ce0a68 100644 --- a/backend/src/main/java/ervu/client/fileupload/WebDavClient.java +++ b/backend/src/main/java/ervu/client/fileupload/WebDavClient.java @@ -101,7 +101,7 @@ public class WebDavClient { String key = entry.getKey(); MultipartFile file = entry.getValue(); if (file != null) { - result.put(key, putAndGetUrl(file.getBytes(), file.getOriginalFilename(), sardine, fileCatalog)); + result.put(key, putAndGetUrl(file.getBytes(), getNewFilename(file.getOriginalFilename()), sardine, fileCatalog)); } } return result; @@ -119,6 +119,12 @@ public class WebDavClient { } } + private String getNewFilename(String filename) { + int lastIndexOf = filename.lastIndexOf("."); + String fileExtension = lastIndexOf == -1 ? "" : filename.substring(lastIndexOf); + return UUID.randomUUID() + fileExtension; + } + public String putAndGetUrl(byte[] fileBytes, String fileName, Sardine client, String fileCatalog) throws IOException { if (badServersCache.size() == urls.length) { return null; diff --git a/backend/src/main/java/ervu/enums/FileStatusCode.java b/backend/src/main/java/ervu/enums/FileStatusCode.java index f357e7f1..6c507920 100644 --- a/backend/src/main/java/ervu/enums/FileStatusCode.java +++ b/backend/src/main/java/ervu/enums/FileStatusCode.java @@ -8,7 +8,9 @@ public enum FileStatusCode { FILE_INFECTED("02"), FILE_CLEAN("03"), FILE_ACCEPTED("04"), - FILE_NOT_CHECKED("11"); + FILE_NOT_CHECKED("11"), + SIGN_INVALID("12"), + MCHD_INVALID("13"); private final String code; diff --git a/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java b/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java index a20fed24..4d580e4c 100644 --- a/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java +++ b/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java @@ -70,6 +70,8 @@ import static ervu.enums.FileStatusCode.FILE_ACCEPTED; import static ervu.enums.FileStatusCode.FILE_INFECTED; import static ervu.enums.FileStatusCode.FILE_NOT_CHECKED; import static ervu.enums.FileStatusCode.FILE_UPLOADED; +import static ervu.enums.FileStatusCode.MCHD_INVALID; +import static ervu.enums.FileStatusCode.SIGN_INVALID; import static ru.micord.ervu.util.StringUtils.convertToFio; /** @@ -79,6 +81,8 @@ import static ru.micord.ervu.util.StringUtils.convertToFio; public class EmployeeInfoFileUploadService { private static final Logger LOGGER = LoggerFactory.getLogger(EmployeeInfoFileUploadService.class); private static final String DOCUMENT = "DOCUMENT"; + private static final String SIGNATURE = "SIGNATURE"; + private static final String MCHD = "MCHD"; private static final MessageSourceAccessor MESSAGE_SOURCE = MessageBundleUtils.createAccessor( "messages/common_errors_messages"); private final WebDavClient webDavClient; @@ -157,11 +161,11 @@ public class EmployeeInfoFileUploadService { departureDateTime, offset, DOCUMENT ); FileInfo signFileInfo = createFileInfo(signFile, employeeInfoFileFormType, - departureDateTime, offset, "SIGNATURE" + departureDateTime, offset, SIGNATURE ); FileInfo mchdFileInfo = mchdFile != null ? createFileInfo(mchdFile, employeeInfoFileFormType, departureDateTime, - offset, "MCHD" + offset, MCHD ) : null; FileStatus fileStatus = new FileStatus(); @@ -219,32 +223,35 @@ public class EmployeeInfoFileUploadService { interactionService.updateStatus(fileInfo.getFileId(), fileInfo.getFileStatus().getStatus(), uploadOrgInfo.getOrgId() ); - auditService.processUploadEvent(uploadOrgInfo, fileInfos); + auditService.processUploadEvent(uploadOrgInfo, fileInfos, fileStatus); throw new FileUploadException(e); } + fileInfo = Arrays.stream(downloadResponse.filesInfo()) + .filter(file -> DOCUMENT.equals(file.getType())) + .findFirst() + .orElse(fileInfo); + + signFileInfo = Arrays.stream(downloadResponse.filesInfo()) + .filter(file -> SIGNATURE.equals(file.getType())) + .findFirst() + .orElse(signFileInfo); + + mchdFileInfo = Arrays.stream(downloadResponse.filesInfo()) + .filter(file -> MCHD.equals(file.getType())) + .findFirst() + .orElse(mchdFileInfo); VerifyDocumentSignResponse verifyDocumentSignResponse; try { verifyDocumentSignResponse = validateSign(multipartFile, signFile); } catch (Exception e) { - fileStatus.setCode(FILE_NOT_CHECKED.getCode()); - fileStatus.setStatus("Некорректная ЭП"); - fileStatus.setDescription("Не пройдена проверка ЭП"); - Arrays.stream(downloadResponse.filesInfo()) - .forEach(fileInfo1 -> fileInfo1.setFileStatus(fileStatus)); - - interactionService.updateStatus(fileInfo.getFileId(), fileInfo.getFileStatus().getStatus(), - uploadOrgInfo.getOrgId() - ); - - auditService.processUploadEvent(uploadOrgInfo, new FileInfo[] {fileInfo, signFileInfo}); - clearS3(response); + handeSignError(fileInfo, signFileInfo, uploadOrgInfo, response); throw e; } return validateSignerAndMchd(verifyDocumentSignResponse, chiefModel, uploadOrgInfo, - mchdFile, accessToken, fileStatus, fileInfo, signFileInfo, mchdFileInfo, ervuId, response + mchdFile, accessToken, fileInfo, signFileInfo, mchdFileInfo, ervuId, response ); } @@ -295,7 +302,7 @@ public class EmployeeInfoFileUploadService { new FileInfo[] {fileInfo, signFileInfo, mchdFileInfo} : new FileInfo[] {fileInfo, signFileInfo}; - auditService.processUploadEvent(uploadOrgInfo, fileInfos); + auditService.processUploadEvent(uploadOrgInfo, fileInfos, fileStatus); } private String sendKafkaMessage(UploadOrgInfo uploadOrgInfo, FileInfo[] fileInfos, @@ -315,7 +322,7 @@ public class EmployeeInfoFileUploadService { private boolean validateSignerAndMchd(VerifyDocumentSignResponse verifyDocumentSignResponse, EmployeeModel chiefModel, UploadOrgInfo uploadOrgInfo, MultipartFile mchdFile, - String accessToken, FileStatus fileStatus, FileInfo fileInfo, FileInfo signFileInfo, + String accessToken, FileInfo fileInfo, FileInfo signFileInfo, FileInfo mchdFileInfo, String ervuId, String response) { String signerInfo = verifyDocumentSignResponse.getSignerInfo(); @@ -332,45 +339,68 @@ public class EmployeeInfoFileUploadService { if (isSignerValid) { interactionService.updateStatus(fileInfo.getFileId(), "Направлено в ЕРВУ", ervuId); - sendMessage(response); - return true; + FileStatusResponse fileStatusResponse = new FileStatusResponse(uploadOrgInfo, + new FileInfo[] {fileInfo, signFileInfo}, fileInfo.getFileStatus() + ); + try { + sendMessage(fileStatusResponse); + return true; + } + catch (JsonProcessingException e) { + handeSignError(fileInfo, signFileInfo, uploadOrgInfo, response); + throw new JsonParsingException( + String.format("Fail get json from: %s", fileStatusResponse), e); + } } if (mchdFile == null) { - handleMchdValidationError(fileStatus, uploadOrgInfo, fileInfo, signFileInfo, null, ervuId, response + handleMchdValidationError(uploadOrgInfo, fileInfo, signFileInfo, null, ervuId, response ); throw new LocalizedException("mchd_null", MESSAGE_SOURCE); } - + FileStatusResponse fileStatusResponse = new FileStatusResponse(uploadOrgInfo, + new FileInfo[] {fileInfo, signFileInfo, mchdFileInfo}, fileInfo.getFileStatus() + ); try { validateMchd(mchdFile, accessToken, signerInfoMap.get("SN") + " " + signerInfoMap.get("G"), chiefFirstName, chiefLastName, chiefMiddleName ); interactionService.updateStatus(fileInfo.getFileId(), "Направлено в ЕРВУ", ervuId); - sendMessage(response); + + sendMessage(fileStatusResponse); return true; } + catch (JsonProcessingException e) { + handleMchdValidationError(uploadOrgInfo, fileInfo, signFileInfo, mchdFileInfo, ervuId, response + ); + throw new JsonParsingException( + String.format("Fail get json from: %s", fileStatusResponse), e); + } catch (Exception e) { - handleMchdValidationError(fileStatus, uploadOrgInfo, fileInfo, signFileInfo, mchdFileInfo, ervuId, response + handleMchdValidationError(uploadOrgInfo, fileInfo, signFileInfo, mchdFileInfo, ervuId, response ); throw e; } } - private void handleMchdValidationError(FileStatus fileStatus, UploadOrgInfo uploadOrgInfo, + private void handleMchdValidationError(UploadOrgInfo uploadOrgInfo, FileInfo fileInfo, FileInfo signFileInfo, FileInfo mchdFileInfo, String ervuId, String response) { + FileStatus packInfo = new FileStatus(); + packInfo.setCode(MCHD_INVALID.getCode()); + packInfo.setStatus("Некорректная МЧД"); + packInfo.setDescription("Проверка МЧД не пройдена"); - fileStatus.setCode(FILE_NOT_CHECKED.getCode()); - fileStatus.setStatus("Некорректная МЧД"); - fileStatus.setDescription("Проверка МЧД не пройдена"); + interactionService.updateStatus(fileInfo.getFileId(), packInfo.getStatus(), ervuId); + FileInfo[] fileInfos; + if (mchdFileInfo != null) { + mchdFileInfo.setFileStatus(packInfo); + fileInfos = new FileInfo[] {fileInfo, signFileInfo, mchdFileInfo}; + } + else { + fileInfos = new FileInfo[] {fileInfo, signFileInfo}; + } - interactionService.updateStatus(fileInfo.getFileId(), fileStatus.getStatus(), ervuId); - - FileInfo[] fileInfos = mchdFileInfo != null ? - new FileInfo[] {fileInfo, signFileInfo, mchdFileInfo} : - new FileInfo[] {fileInfo, signFileInfo}; - - auditService.processUploadEvent(uploadOrgInfo, fileInfos); + auditService.processUploadEvent(uploadOrgInfo, fileInfos, packInfo); clearS3(response); } @@ -416,9 +446,8 @@ public class EmployeeInfoFileUploadService { } private String getJsonKafkaMessage(EmployeeInfoKafkaMessage employeeInfoKafkaMessage) { - ObjectMapper mapper = new ObjectMapper(); try { - return mapper.writeValueAsString(employeeInfoKafkaMessage); + return objectMapper.writeValueAsString(employeeInfoKafkaMessage); } catch (JsonProcessingException e) { throw new JsonParsingException( @@ -557,7 +586,8 @@ public class EmployeeInfoFileUploadService { } } - private boolean sendMessage(String message) { + private boolean sendMessage(FileStatusResponse fileStatusResponse) throws JsonProcessingException { + String message = objectMapper.writeValueAsString(fileStatusResponse); ProducerRecord record = new ProducerRecord<>(this.kafkaDownloadRequestTopic, message); record.headers() .add("messageId", UUID.randomUUID().toString().getBytes(StandardCharsets.UTF_8)); @@ -595,15 +625,33 @@ public class EmployeeInfoFileUploadService { if (fileStatus1.getCode().equals(FILE_INFECTED.getCode())) { interactionService.updateStatus(avFile.getFileId(), avFile.getFileStatus().getStatus(), downloadResponse.orgInfo().getOrgId()); - sendMessage(response); + FileStatusResponse fileStatusResponse = new FileStatusResponse(downloadResponse.orgInfo(), + downloadResponse.filesInfo(), fileStatus1 + ); + sendMessage(fileStatusResponse); throw new LocalizedException("av_file_infected", MESSAGE_SOURCE); } else if (fileStatus1.getCode().equals(FILE_NOT_CHECKED.getCode())) { interactionService.updateStatus(avFile.getFileId(), avFile.getFileStatus().getStatus(), downloadResponse.orgInfo().getOrgId()); - auditService.processUploadEvent(downloadResponse.orgInfo(), downloadResponse.filesInfo()); + auditService.processUploadEvent(downloadResponse.orgInfo(), downloadResponse.filesInfo(), fileStatus1); throw new FileUploadException("File not checked: " + avFile.getFileName()); } return downloadResponse; } + + private void handeSignError(FileInfo fileInfo, FileInfo signFileInfo, UploadOrgInfo uploadOrgInfo, String response) { + FileStatus packInfo = new FileStatus(); + packInfo.setCode(SIGN_INVALID.getCode()); + packInfo.setStatus("Некорректная ЭП"); + packInfo.setDescription("Не пройдена проверка ЭП"); + signFileInfo.setFileStatus(packInfo); + + interactionService.updateStatus(fileInfo.getFileId(), packInfo.getStatus(), + uploadOrgInfo.getOrgId() + ); + + auditService.processUploadEvent(uploadOrgInfo, new FileInfo[] {fileInfo, signFileInfo}, packInfo); + clearS3(response); + } } diff --git a/backend/src/main/java/ru/micord/ervu/audit/model/AuditUploadEvent.java b/backend/src/main/java/ru/micord/ervu/audit/model/AuditUploadEvent.java index 12cca209..e7346494 100644 --- a/backend/src/main/java/ru/micord/ervu/audit/model/AuditUploadEvent.java +++ b/backend/src/main/java/ru/micord/ervu/audit/model/AuditUploadEvent.java @@ -2,6 +2,7 @@ package ru.micord.ervu.audit.model; import ervu.model.fileupload.FileInfo; +import ervu.model.fileupload.FileStatus; import ervu.model.fileupload.UploadOrgInfo; /** @@ -11,10 +12,12 @@ import ervu.model.fileupload.UploadOrgInfo; public class AuditUploadEvent { private UploadOrgInfo orgInfo; private FileInfo[] filesInfo; + private FileStatus packInfo; - public AuditUploadEvent(UploadOrgInfo orgInfo, FileInfo[] filesInfo) { + public AuditUploadEvent(UploadOrgInfo orgInfo, FileInfo[] filesInfo, FileStatus packInfo) { this.orgInfo = orgInfo; this.filesInfo = filesInfo; + this.packInfo = packInfo; } public UploadOrgInfo getOrgInfo() { @@ -32,4 +35,12 @@ public class AuditUploadEvent { public void setFilesInfo(FileInfo[] filesInfo) { this.filesInfo = filesInfo; } + + public FileStatus getPackInfo() { + return packInfo; + } + + public void setPackInfo(FileStatus packInfo) { + this.packInfo = packInfo; + } } diff --git a/backend/src/main/java/ru/micord/ervu/audit/service/AuditService.java b/backend/src/main/java/ru/micord/ervu/audit/service/AuditService.java index c189f19a..fec9091c 100644 --- a/backend/src/main/java/ru/micord/ervu/audit/service/AuditService.java +++ b/backend/src/main/java/ru/micord/ervu/audit/service/AuditService.java @@ -3,6 +3,7 @@ package ru.micord.ervu.audit.service; import javax.servlet.http.HttpServletRequest; import ervu.model.fileupload.FileInfo; +import ervu.model.fileupload.FileStatus; import ervu.model.fileupload.UploadOrgInfo; import ru.micord.ervu.audit.model.AuditActionRequest; import ru.micord.ervu.kafka.model.OrgInfo; @@ -16,7 +17,7 @@ public interface AuditService { void processAuthEvent(HttpServletRequest request, OrgInfo orgInfo, String prnOid, String status, String eventType); - void processUploadEvent(UploadOrgInfo uploadOrgInfo, FileInfo[] filesInfo); + void processUploadEvent(UploadOrgInfo uploadOrgInfo, FileInfo[] filesInfo, FileStatus packInfo); void processDownloadEvent(HttpServletRequest request, long fileSize, String fileName, int formatRegistry, String status, String s3FileUrl); diff --git a/backend/src/main/java/ru/micord/ervu/audit/service/impl/BaseAuditService.java b/backend/src/main/java/ru/micord/ervu/audit/service/impl/BaseAuditService.java index 14a5a2cc..40aad7a8 100644 --- a/backend/src/main/java/ru/micord/ervu/audit/service/impl/BaseAuditService.java +++ b/backend/src/main/java/ru/micord/ervu/audit/service/impl/BaseAuditService.java @@ -9,6 +9,7 @@ import javax.servlet.http.HttpServletRequest; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import ervu.model.fileupload.FileInfo; +import ervu.model.fileupload.FileStatus; import ervu.model.fileupload.UploadOrgInfo; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Conditional; @@ -110,10 +111,11 @@ public class BaseAuditService implements AuditService { } @Override - public void processUploadEvent(UploadOrgInfo orgInfo, FileInfo[] filesInfo) { + public void processUploadEvent(UploadOrgInfo orgInfo, FileInfo[] filesInfo, FileStatus packInfo) { AuditUploadEvent auditUploadEvent = new AuditUploadEvent( orgInfo, - filesInfo + filesInfo, + packInfo ); String message = convertToMessage(auditUploadEvent); diff --git a/backend/src/main/java/ru/micord/ervu/audit/service/impl/StubAuditService.java b/backend/src/main/java/ru/micord/ervu/audit/service/impl/StubAuditService.java index de364efe..ca82cab4 100644 --- a/backend/src/main/java/ru/micord/ervu/audit/service/impl/StubAuditService.java +++ b/backend/src/main/java/ru/micord/ervu/audit/service/impl/StubAuditService.java @@ -3,6 +3,7 @@ package ru.micord.ervu.audit.service.impl; import javax.servlet.http.HttpServletRequest; import ervu.model.fileupload.FileInfo; +import ervu.model.fileupload.FileStatus; import ervu.model.fileupload.UploadOrgInfo; import org.springframework.context.annotation.Conditional; import org.springframework.stereotype.Service; @@ -26,7 +27,7 @@ public class StubAuditService implements AuditService { String status, String eventType) {} @Override - public void processUploadEvent(UploadOrgInfo uploadOrgInfo, FileInfo[] filesInfo) {} + public void processUploadEvent(UploadOrgInfo uploadOrgInfo, FileInfo[] filesInfo, FileStatus packInfo) {} @Override public void processDownloadEvent(HttpServletRequest request, long fileSize, String fileName, diff --git a/backend/src/main/java/ru/micord/ervu/journal/JournalFileInfo.java b/backend/src/main/java/ru/micord/ervu/journal/JournalFileInfo.java index 8cb0fafe..6dc27a6e 100644 --- a/backend/src/main/java/ru/micord/ervu/journal/JournalFileInfo.java +++ b/backend/src/main/java/ru/micord/ervu/journal/JournalFileInfo.java @@ -1,6 +1,7 @@ package ru.micord.ervu.journal; import java.time.LocalDateTime; +import java.util.List; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; @@ -9,78 +10,17 @@ import ru.micord.ervu.journal.deserializer.DepartureDateTimeDeserializer; @JsonIgnoreProperties(ignoreUnknown = true) public class JournalFileInfo { - private String fileId; //ИД файла полученный при создании записи о файле в реестр организаций (в ЕРВУ) - private String fileName; // Название файла - private Integer filePatternCode; // Номер шаблона(Формы) - private String filePatternName; - @JsonDeserialize(using = DepartureDateTimeDeserializer.class) - private LocalDateTime departureDateTime; // Дата-время отправки файла - private String timeZone; //Таймзона - private JournalFileStatus fileStatus; + private List packFiles; // Список файлов (csv, sig, mchd) private SenderInfo senderInfo; private Integer rowsCount; //Общее количество записей отправленных в файле private Integer rowsSuccess; //Количество записей принятых в файле - public String getFileId() { - return fileId; + public List getPackFiles() { + return packFiles; } - public JournalFileInfo setFileId(String fileId) { - this.fileId = fileId; - return this; - } - - public String getFileName() { - return fileName; - } - - public JournalFileInfo setFileName(String fileName) { - this.fileName = fileName; - return this; - } - - public Integer getFilePatternCode() { - return filePatternCode; - } - - public JournalFileInfo setFilePatternCode(Integer filePatternCode) { - this.filePatternCode = filePatternCode; - return this; - } - - public String getFilePatternName() { - return filePatternName; - } - - public JournalFileInfo setFilePatternName(String filePatternName) { - this.filePatternName = filePatternName; - return this; - } - - public LocalDateTime getDepartureDateTime() { - return departureDateTime; - } - - public JournalFileInfo setDepartureDateTime(LocalDateTime departureDateTime) { - this.departureDateTime = departureDateTime; - return this; - } - - public String getTimeZone() { - return timeZone; - } - - public JournalFileInfo setTimeZone(String timeZone) { - this.timeZone = timeZone; - return this; - } - - public JournalFileStatus getFileStatus() { - return fileStatus; - } - - public JournalFileInfo setFileStatus(JournalFileStatus fileStatus) { - this.fileStatus = fileStatus; + public JournalFileInfo setPackFiles(List packFiles) { + this.packFiles = packFiles; return this; } @@ -110,4 +50,88 @@ public class JournalFileInfo { this.rowsSuccess = rowsSuccess; return this; } -} + + @JsonIgnoreProperties(ignoreUnknown = true) + public static class JournalFileDetails { + private String fileId; //ИД файла полученный при создании записи о файле в реестр организаций (в ЕРВУ) + private String fileName; // Название файла + private Integer filePatternCode; // Номер шаблона(Формы) + private String filePatternName; + @JsonDeserialize(using = DepartureDateTimeDeserializer.class) + private LocalDateTime departureDateTime; // Дата-время отправки файла + private String timeZone; //Таймзона + private JournalFileStatus fileStatus; + private String type; + + public String getFileId() { + return fileId; + } + + public JournalFileDetails setFileId(String fileId) { + this.fileId = fileId; + return this; + } + + public String getFileName() { + return fileName; + } + + public JournalFileDetails setFileName(String fileName) { + this.fileName = fileName; + return this; + } + + public Integer getFilePatternCode() { + return filePatternCode; + } + + public JournalFileDetails setFilePatternCode(Integer filePatternCode) { + this.filePatternCode = filePatternCode; + return this; + } + + public String getFilePatternName() { + return filePatternName; + } + + public JournalFileDetails setFilePatternName(String filePatternName) { + this.filePatternName = filePatternName; + return this; + } + + public LocalDateTime getDepartureDateTime() { + return departureDateTime; + } + + public JournalFileDetails setDepartureDateTime(LocalDateTime departureDateTime) { + this.departureDateTime = departureDateTime; + return this; + } + + public String getTimeZone() { + return timeZone; + } + + public JournalFileDetails setTimeZone(String timeZone) { + this.timeZone = timeZone; + return this; + } + + public JournalFileStatus getFileStatus() { + return fileStatus; + } + + public JournalFileDetails setFileStatus(JournalFileStatus fileStatus) { + this.fileStatus = fileStatus; + return this; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + } +} \ No newline at end of file diff --git a/backend/src/main/java/ru/micord/ervu/journal/mapper/JournalDtoMapper.java b/backend/src/main/java/ru/micord/ervu/journal/mapper/JournalDtoMapper.java index 86bf1ef9..cbc144d9 100644 --- a/backend/src/main/java/ru/micord/ervu/journal/mapper/JournalDtoMapper.java +++ b/backend/src/main/java/ru/micord/ervu/journal/mapper/JournalDtoMapper.java @@ -11,18 +11,19 @@ import static ru.micord.ervu.util.StringUtils.convertToFio; public class JournalDtoMapper { - public static JournalDto mapToJournalDto(JournalFileInfo journalFileInfo) { + public static JournalDto mapToJournalDto(JournalFileInfo journalFileInfo, + JournalFileInfo.JournalFileDetails journalFileDetails) { SenderInfo senderInfo = journalFileInfo.getSenderInfo(); return new JournalDto() - .setFileId(journalFileInfo.getFileId()) - .setDepartureDateTime(Timestamp.valueOf(journalFileInfo.getDepartureDateTime()).toString()) - .setFileName(journalFileInfo.getFileName()) - .setFilePatternCode(journalFileInfo.getFilePatternCode()) + .setFileId(journalFileDetails.getFileId()) + .setDepartureDateTime(Timestamp.valueOf(journalFileDetails.getDepartureDateTime()).toString()) + .setFileName(journalFileDetails.getFileName()) + .setFilePatternCode(journalFileDetails.getFilePatternCode()) .setSenderFio(convertToFio(senderInfo.getFirstName(), senderInfo.getMiddleName(), senderInfo.getLastName() ) ) - .setStatus(journalFileInfo.getFileStatus().getStatus()) + .setStatus(journalFileDetails.getFileStatus().getStatus()) .setFilesSentCount(journalFileInfo.getRowsCount()) .setAcceptedFilesCount(journalFileInfo.getRowsSuccess()); } diff --git a/backend/src/main/java/ru/micord/ervu/kafka/controller/ErvuKafkaController.java b/backend/src/main/java/ru/micord/ervu/kafka/controller/ErvuKafkaController.java index e3e41e6a..6b29a84e 100644 --- a/backend/src/main/java/ru/micord/ervu/kafka/controller/ErvuKafkaController.java +++ b/backend/src/main/java/ru/micord/ervu/kafka/controller/ErvuKafkaController.java @@ -11,7 +11,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.io.Resource; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import ru.micord.ervu.audit.constants.AuditConstants; import ru.micord.ervu.audit.service.AuditService; @@ -48,7 +48,7 @@ public class ErvuKafkaController { @Autowired private ObjectMapper objectMapper; - @RequestMapping(value = "/kafka/excerpt") + @GetMapping(value = "/kafka/excerpt") public ResponseEntity getExcerptFile(HttpServletRequest request) { String fileUrl = null; String fileName = null; diff --git a/backend/src/main/java/ru/micord/ervu/service/grid/impl/JournalInMemoryStaticGridLoadService.java b/backend/src/main/java/ru/micord/ervu/service/grid/impl/JournalInMemoryStaticGridLoadService.java index 43cc0983..6b27696d 100644 --- a/backend/src/main/java/ru/micord/ervu/service/grid/impl/JournalInMemoryStaticGridLoadService.java +++ b/backend/src/main/java/ru/micord/ervu/service/grid/impl/JournalInMemoryStaticGridLoadService.java @@ -68,7 +68,10 @@ public class JournalInMemoryStaticGridLoadService implements JournalFileDataResponse journalFileDataResponse = objectMapper.readValue(responseJsonString, JournalFileDataResponse.class); ervuJournalList = journalFileDataResponse.getFilesInfo().stream() - .map(JournalDtoMapper::mapToJournalDto) + .flatMap(fileInfo -> fileInfo.getPackFiles().stream() + .filter(fileDetail -> "DOCUMENT".equals(fileDetail.getType())) + .map(fileDetail -> JournalDtoMapper.mapToJournalDto(fileInfo, fileDetail)) + ) .toList(); } catch (JsonProcessingException e) { diff --git a/frontend/src/ts/ervu/component/grid/filter/SetFilter.ts b/frontend/src/ts/ervu/component/grid/filter/SetFilter.ts index 6b13c5a1..0a439e24 100644 --- a/frontend/src/ts/ervu/component/grid/filter/SetFilter.ts +++ b/frontend/src/ts/ervu/component/grid/filter/SetFilter.ts @@ -49,7 +49,7 @@ export class SetFilter implements IFilterComp { private initCheckBox(id: string, value: string, index: number): HTMLInputElement { this.eGui.insertAdjacentHTML('beforeend', this.OPTION_TEMPLATE); - this.eGui.querySelectorAll('.ag-filter-value')[index].innerHTML = value; + (this.eGui.querySelectorAll('.ag-filter-value')[index] as HTMLElement).textContent = value; let checkbox = this.eGui.querySelectorAll('.ag-filter-checkbox')[index] as HTMLInputElement; checkbox.setAttribute('id', id); checkbox.addEventListener('change', this.onCheckBoxChanged.bind(this)); From e6f0fb3587d3493e5a1585520aac9a66b152d141 Mon Sep 17 00:00:00 2001 From: Eduard Tihomiorv Date: Thu, 2 Oct 2025 18:04:38 +0300 Subject: [PATCH 18/45] SUPPORT-9339: Fix --- .../service/fileupload/EmployeeInfoFileUploadService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java b/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java index 4d580e4c..854891f6 100644 --- a/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java +++ b/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java @@ -388,7 +388,7 @@ public class EmployeeInfoFileUploadService { FileStatus packInfo = new FileStatus(); packInfo.setCode(MCHD_INVALID.getCode()); packInfo.setStatus("Некорректная МЧД"); - packInfo.setDescription("Проверка МЧД не пройдена"); + packInfo.setDescription("Неуспешная проверка МЧД"); interactionService.updateStatus(fileInfo.getFileId(), packInfo.getStatus(), ervuId); FileInfo[] fileInfos; @@ -644,7 +644,7 @@ public class EmployeeInfoFileUploadService { FileStatus packInfo = new FileStatus(); packInfo.setCode(SIGN_INVALID.getCode()); packInfo.setStatus("Некорректная ЭП"); - packInfo.setDescription("Не пройдена проверка ЭП"); + packInfo.setDescription("Неуспешная проверка ЭП"); signFileInfo.setFileStatus(packInfo); interactionService.updateStatus(fileInfo.getFileId(), packInfo.getStatus(), From ce4d9219512ec1427ad0a7205747f7cb7ddff2f2 Mon Sep 17 00:00:00 2001 From: Eduard Tihomiorv Date: Mon, 13 Oct 2025 13:01:00 +0300 Subject: [PATCH 19/45] SUPPORT-9339: Fix --- backend/src/main/java/ervu/FileConstants.java | 10 ++++++++++ .../fileupload/EmployeeInfoFileUploadService.java | 6 +++--- .../impl/JournalInMemoryStaticGridLoadService.java | 4 +++- 3 files changed, 16 insertions(+), 4 deletions(-) create mode 100644 backend/src/main/java/ervu/FileConstants.java diff --git a/backend/src/main/java/ervu/FileConstants.java b/backend/src/main/java/ervu/FileConstants.java new file mode 100644 index 00000000..ba09dc65 --- /dev/null +++ b/backend/src/main/java/ervu/FileConstants.java @@ -0,0 +1,10 @@ +package ervu; + +/** + * @author Eduard Tihomirov + */ +public final class FileConstants { + public static final String DOCUMENT = "DOCUMENT"; + public static final String SIGNATURE = "SIGNATURE"; + public static final String MCHD = "MCHD"; +} diff --git a/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java b/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java index 854891f6..1c863da6 100644 --- a/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java +++ b/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java @@ -66,6 +66,9 @@ import ru.micord.ervu.util.DateUtils; import ru.cg.webbpm.modules.core.runtime.api.LocalizedException; import ru.cg.webbpm.modules.core.runtime.api.MessageBundleUtils; +import static ervu.FileConstants.DOCUMENT; +import static ervu.FileConstants.MCHD; +import static ervu.FileConstants.SIGNATURE; import static ervu.enums.FileStatusCode.FILE_ACCEPTED; import static ervu.enums.FileStatusCode.FILE_INFECTED; import static ervu.enums.FileStatusCode.FILE_NOT_CHECKED; @@ -80,9 +83,6 @@ import static ru.micord.ervu.util.StringUtils.convertToFio; @Service public class EmployeeInfoFileUploadService { private static final Logger LOGGER = LoggerFactory.getLogger(EmployeeInfoFileUploadService.class); - private static final String DOCUMENT = "DOCUMENT"; - private static final String SIGNATURE = "SIGNATURE"; - private static final String MCHD = "MCHD"; private static final MessageSourceAccessor MESSAGE_SOURCE = MessageBundleUtils.createAccessor( "messages/common_errors_messages"); private final WebDavClient webDavClient; diff --git a/backend/src/main/java/ru/micord/ervu/service/grid/impl/JournalInMemoryStaticGridLoadService.java b/backend/src/main/java/ru/micord/ervu/service/grid/impl/JournalInMemoryStaticGridLoadService.java index 6b27696d..8c956256 100644 --- a/backend/src/main/java/ru/micord/ervu/service/grid/impl/JournalInMemoryStaticGridLoadService.java +++ b/backend/src/main/java/ru/micord/ervu/service/grid/impl/JournalInMemoryStaticGridLoadService.java @@ -25,6 +25,8 @@ import org.springframework.stereotype.Service; import ru.micord.ervu.kafka.service.ReplyingKafkaService; import ru.micord.ervu.security.webbpm.jwt.service.JwtTokenService; +import static ervu.FileConstants.DOCUMENT; + @Service public class JournalInMemoryStaticGridLoadService implements InMemoryStaticGridLoadService { @@ -69,7 +71,7 @@ public class JournalInMemoryStaticGridLoadService implements JournalFileDataResponse.class); ervuJournalList = journalFileDataResponse.getFilesInfo().stream() .flatMap(fileInfo -> fileInfo.getPackFiles().stream() - .filter(fileDetail -> "DOCUMENT".equals(fileDetail.getType())) + .filter(fileDetail -> DOCUMENT.equals(fileDetail.getType())) .map(fileDetail -> JournalDtoMapper.mapToJournalDto(fileInfo, fileDetail)) ) .toList(); From d8fe629e65b72222375e21813b825ee6aeae1c60 Mon Sep 17 00:00:00 2001 From: Eduard Tihomiorv Date: Tue, 14 Oct 2025 15:40:16 +0300 Subject: [PATCH 20/45] SUPPORT-9339: Fix --- .../EmployeeInfoFileUploadService.java | 17 ++++++++++++----- .../messages/common_errors_messages.properties | 3 +++ .../common_errors_messages_ru_RU.properties | 3 +++ 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java b/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java index 1c863da6..b7952719 100644 --- a/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java +++ b/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java @@ -498,13 +498,20 @@ public class EmployeeInfoFileUploadService { String errorCode = objectMapper.readTree(body) .get("error_code") .asText(); - if (errorCode.equals("CERT_TRUST_REVOCATION_STATUS_UNKNOWN")) { - throw new LocalizedException("crl_certificate_expired", MESSAGE_SOURCE); - } - else { - throw new FileUploadException("Unknown 500 error in verify module. Error code " + errorCode); + switch (errorCode) { + case "CERT_TRUST_REVOCATION_STATUS_UNKNOWN" -> + throw new LocalizedException("cert_trust_revocation", MESSAGE_SOURCE); + case "CERT_TRUST_IS_UNTRUSTED_ROOT" -> + throw new LocalizedException("cert_untrusted_root", MESSAGE_SOURCE); + case "CERT_TRUST_IS_NOT_TIME_VALID" -> + throw new LocalizedException("cert_is_not_time_valid", MESSAGE_SOURCE); + default -> throw new FileUploadException( + "Unknown 500 error in verify module. Error code " + errorCode); } } + catch (LocalizedException e) { + throw e; + } catch (Exception e) { throw new FileUploadException("Unknown 500 error in verify module. Response body " + body, e); } diff --git a/backend/src/main/resources/messages/common_errors_messages.properties b/backend/src/main/resources/messages/common_errors_messages.properties index b7ea549b..7f06599b 100644 --- a/backend/src/main/resources/messages/common_errors_messages.properties +++ b/backend/src/main/resources/messages/common_errors_messages.properties @@ -2,6 +2,9 @@ kafka_reply_timeout=Превышено время ожидания ответа access_denied=Доступ запрещен. Пользователь должен быть включен в группу "Сотрудник, ответственный за военно-учетную работу" в ЕСИА login_attempts_exceeded=Слишком большое количество попыток авторизоваться в ЕСИА за короткий промежуток времени. Рекомендуем почистить cookie и cash браузера, после повторить авторизацию. crl_certificate_expired=Превышено время ожидания ответа из ЕСИА +cert_trust_revocation=Неизвестен статус отзыва сертификата или одного из сертификатов в цепочке (проблема с CRL) +cert_untrusted_root=Сертификат или цепочка сертификатов основана на ненадежном корневом сертификате +cert_is_not_time_valid=Этот сертификат или один из сертификатов в цепочке сертификатов является недопустимым по времени file_sign_validate=Ошибка проверки файлов. Некорректная электронная подпись mchd_validate_agent=Ошибка проверки файлов. Некорректная машиночитаемая доверенность. Представитель не совпадает с подписантом mchd_null=Ошибка проверки файлов. Отсутствует машиночитаемая доверенность. Подписант не является руководителем организации diff --git a/backend/src/main/resources/messages/common_errors_messages_ru_RU.properties b/backend/src/main/resources/messages/common_errors_messages_ru_RU.properties index aa81d4e3..acca81c5 100644 --- a/backend/src/main/resources/messages/common_errors_messages_ru_RU.properties +++ b/backend/src/main/resources/messages/common_errors_messages_ru_RU.properties @@ -2,6 +2,9 @@ kafka_reply_timeout=Превышено время ожидания ответа access_denied=Доступ запрещен. Пользователь должен быть включен в группу "Сотрудник, ответственный за военно-учетную работу" в ЕСИА login_attempts_exceeded=Слишком большое количество попыток авторизоваться в ЕСИА за короткий промежуток времени. Рекомендуем почистить cookie и cash браузера, после повторить авторизацию. crl_certificate_expired=Превышено время ожидания ответа из ЕСИА +cert_trust_revocation=Неизвестен статус отзыва сертификата или одного из сертификатов в цепочке (проблема с CRL) +cert_untrusted_root=Сертификат или цепочка сертификатов основана на ненадежном корневом сертификате +cert_is_not_time_valid=Этот сертификат или один из сертификатов в цепочке сертификатов является недопустимым по времени file_sign_validate=Ошибка проверки файлов. Некорректная электронная подпись mchd_validate_agent=Ошибка проверки файлов. Некорректная машиночитаемая доверенность. Представитель не совпадает с подписантом mchd_null=Ошибка проверки файлов. Отсутствует машиночитаемая доверенность. Подписант не является руководителем организации From d84b08411d1bdf48ce5672d7bc4bcde70eb6b13c Mon Sep 17 00:00:00 2001 From: "adel.ka" Date: Tue, 9 Sep 2025 09:15:53 +0300 Subject: [PATCH 21/45] SUPPORT-9368: new journal --- backend/pom.xml | 39 +- .../EmployeeInfoFileUploadService.java | 4 +- .../controller/ValidationFileController.java | 88 ++++ .../ervu/exception/ExportException.java | 14 + .../ru/micord/ervu/journal/JournalDto.java | 51 ++- .../micord/ervu/journal/JournalFileInfo.java | 10 + .../ervu/journal/mapper/JournalDtoMapper.java | 10 +- .../ervu/kafka/ReplyingKafkaConfig.java | 119 ++++-- .../kafka/controller/ErvuKafkaController.java | 2 +- .../kafka/service/ReplyingKafkaService.java | 11 +- ...mpl.java => BaseReplyingKafkaService.java} | 39 +- .../impl/ErvuReplyingKafkaService.java | 31 ++ .../impl/ValidateReplyingKafkaService.java | 32 ++ .../ervu/model/ValidateExportRequest.java | 4 + .../ervu/model/ValidateExportResponse.java | 28 ++ .../micord/ervu/property/grid/FilterType.java | 3 +- .../esia/service/EsiaAuthService.java | 2 +- .../ervu/service/InteractionServiceImpl.java | 2 - .../ervu/service/ValidationFileService.java | 48 +++ .../JournalInMemoryStaticGridLoadService.java | 11 +- .../ervu/component/grid/InMemoryStaticGrid.ts | 24 +- .../filter/CustomGridColumnFilterUtils.ts | 3 + .../grid/filter/FileAvailableFilterComp.ts | 77 ++++ .../grid/renderer/FileDownloadCellRenderer.ts | 36 ++ frontend/src/ts/ervu/service/FilterService.ts | 17 + .../ts/ervu/service/ValidateFileService.ts | 34 ++ frontend/src/ts/modules/app/app.module.ts | 3 +- pom.xml | 15 + .../business-model/Журнал взаимодействия.page | 395 +++++++++++++++++- 29 files changed, 1045 insertions(+), 107 deletions(-) create mode 100644 backend/src/main/java/ru/micord/ervu/controller/ValidationFileController.java create mode 100644 backend/src/main/java/ru/micord/ervu/exception/ExportException.java rename backend/src/main/java/ru/micord/ervu/kafka/service/impl/{BaseReplyingKafkaServiceImpl.java => BaseReplyingKafkaService.java} (54%) create mode 100644 backend/src/main/java/ru/micord/ervu/kafka/service/impl/ErvuReplyingKafkaService.java create mode 100644 backend/src/main/java/ru/micord/ervu/kafka/service/impl/ValidateReplyingKafkaService.java create mode 100644 backend/src/main/java/ru/micord/ervu/model/ValidateExportRequest.java create mode 100644 backend/src/main/java/ru/micord/ervu/model/ValidateExportResponse.java create mode 100644 backend/src/main/java/ru/micord/ervu/service/ValidationFileService.java create mode 100644 frontend/src/ts/ervu/component/grid/filter/FileAvailableFilterComp.ts create mode 100644 frontend/src/ts/ervu/component/grid/renderer/FileDownloadCellRenderer.ts create mode 100644 frontend/src/ts/ervu/service/ValidateFileService.ts diff --git a/backend/pom.xml b/backend/pom.xml index d1592c8b..77e756f5 100644 --- a/backend/pom.xml +++ b/backend/pom.xml @@ -97,14 +97,6 @@ org.xerial.snappy snappy-java - - org.apache.kafka - kafka-clients - - - org.xerial.snappy - snappy-java - ru.cg.webbpm.modules inject @@ -206,9 +198,24 @@ org.apache.httpcomponents httpmime + + com.google.protobuf + protobuf-java + + + com.google.protobuf + protobuf-java-util + ${project.parent.artifactId} + + + kr.motd.maven + os-maven-plugin + 1.6.2 + + maven-compiler-plugin @@ -237,6 +244,22 @@ + + org.xolstice.maven.plugins + protobuf-maven-plugin + + + + compile + + + + + com.google.protobuf:protoc:4.27.3:exe:${os.detected.classifier} + ${project.parent.basedir}/backend/src/main/resources + ${project.parent.basedir}/backend/target/generated-sources/java/protobuf + + diff --git a/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java b/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java index b7952719..40be9cca 100644 --- a/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java +++ b/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java @@ -87,7 +87,7 @@ public class EmployeeInfoFileUploadService { "messages/common_errors_messages"); private final WebDavClient webDavClient; private final EmployeeInfoKafkaMessageService employeeInfoKafkaMessageService; - private final ReplyingKafkaService replyingKafkaService; + private final ReplyingKafkaService replyingKafkaService; private final KafkaTemplate kafkaTemplate; private final InteractionService interactionService; private final UlDataService ulDataService; @@ -110,7 +110,7 @@ public class EmployeeInfoFileUploadService { public EmployeeInfoFileUploadService( WebDavClient webDavClient, EmployeeInfoKafkaMessageService employeeInfoKafkaMessageService, - ReplyingKafkaService replyingKafkaService, + ReplyingKafkaService replyingKafkaService, InteractionService interactionService, UlDataService ulDataService, AuditService auditService, diff --git a/backend/src/main/java/ru/micord/ervu/controller/ValidationFileController.java b/backend/src/main/java/ru/micord/ervu/controller/ValidationFileController.java new file mode 100644 index 00000000..6384ea29 --- /dev/null +++ b/backend/src/main/java/ru/micord/ervu/controller/ValidationFileController.java @@ -0,0 +1,88 @@ +package ru.micord.ervu.controller; + +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import javax.servlet.http.HttpServletRequest; + +import org.apache.kafka.shaded.com.google.protobuf.ByteString; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.io.InputStreamResource; +import org.springframework.core.io.Resource; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.util.StringUtils; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import ru.micord.ervu.audit.constants.AuditConstants; +import ru.micord.ervu.audit.service.AuditService; +import ru.micord.ervu.model.ValidateExportResponse; +import ru.micord.ervu.security.webbpm.jwt.UserIdsPair; +import ru.micord.ervu.security.webbpm.jwt.util.SecurityUtil; +import ru.micord.ervu.service.ValidationFileService; + +@RestController +@RequestMapping("/validate") +public class ValidationFileController { + private static final Logger LOGGER = LoggerFactory.getLogger(ValidationFileController.class); + private final AuditService auditService; + private final ValidationFileService validationFileService; + + public ValidationFileController(AuditService auditService, + ValidationFileService validationFileService) { + this.auditService = auditService; + this.validationFileService = validationFileService; + } + + @GetMapping("/export/{fileId}") + public ResponseEntity exportFile(HttpServletRequest servletRequest, + @PathVariable String fileId) { + int size = 0; + String fileName = null; + String status = null; + + try { + if (!StringUtils.hasText(fileId)) { + return ResponseEntity.notFound().build(); + } + + UserIdsPair userIdsPair = SecurityUtil.getUserIdsPair(); + String ervuId = userIdsPair.getErvuId(); + ValidateExportResponse validateExportResponse = validationFileService.exportFile(ervuId, + fileId + ); + + if (!validateExportResponse.hasFile()) { + LOGGER.error("Response does not contain file content for fileId: {}, user: {}", fileId, ervuId); + status = AuditConstants.FAILURE_STATUS_TYPE; + return ResponseEntity.noContent().build(); + } + + ByteString file = validateExportResponse.getFile(); + size = file.size(); + fileName = validateExportResponse.getFileName(); + String encodedFilename = URLEncoder.encode(fileName, StandardCharsets.UTF_8); + InputStreamResource resource = new InputStreamResource(file.newInput()); + + status = AuditConstants.SUCCESS_STATUS_TYPE; + return ResponseEntity.ok() + .header(HttpHeaders.CONTENT_DISPOSITION, + "attachment; filename*=UTF-8''" + encodedFilename + ) + .contentType(MediaType.APPLICATION_OCTET_STREAM) + .body(resource); + } + catch (Exception e) { + if (status == null) { + status = AuditConstants.FAILURE_STATUS_TYPE; + } + throw e; + } + finally { + auditService.processDownloadEvent(servletRequest, size, fileName, 2, status, null); + } + } +} diff --git a/backend/src/main/java/ru/micord/ervu/exception/ExportException.java b/backend/src/main/java/ru/micord/ervu/exception/ExportException.java new file mode 100644 index 00000000..5603f06f --- /dev/null +++ b/backend/src/main/java/ru/micord/ervu/exception/ExportException.java @@ -0,0 +1,14 @@ +package ru.micord.ervu.exception; + +/** + * @author Adel Kalimullin + */ +public class ExportException extends RuntimeException { + public ExportException(String message) { + super(message); + } + + public ExportException(String message, Throwable cause) { + super(message, cause); + } +} \ No newline at end of file diff --git a/backend/src/main/java/ru/micord/ervu/journal/JournalDto.java b/backend/src/main/java/ru/micord/ervu/journal/JournalDto.java index 6adc2e2f..cbc23a70 100644 --- a/backend/src/main/java/ru/micord/ervu/journal/JournalDto.java +++ b/backend/src/main/java/ru/micord/ervu/journal/JournalDto.java @@ -2,14 +2,18 @@ package ru.micord.ervu.journal; public class JournalDto { + private Integer documentNumber; private String fileId; private String departureDateTime; private String fileName; private Integer filePatternCode; private String senderFio; private String status; - public Integer filesSentCount; - public Integer acceptedFilesCount; + private Integer rowsCount; + private Integer rowsSuccess; + private Integer rowsError; + private Boolean hasFailedRows; + public String getDepartureDateTime() { return departureDateTime; @@ -65,21 +69,48 @@ public class JournalDto { return this; } - public Integer getFilesSentCount() { - return filesSentCount; + public Integer getRowsCount() { + return rowsCount; } - public JournalDto setFilesSentCount(Integer filesSentCount) { - this.filesSentCount = filesSentCount; + public JournalDto setRowsCount(Integer rowsCount) { + this.rowsCount = rowsCount; return this; } - public Integer getAcceptedFilesCount() { - return acceptedFilesCount; + public Integer getRowsSuccess() { + return rowsSuccess; } - public JournalDto setAcceptedFilesCount(Integer acceptedFilesCount) { - this.acceptedFilesCount = acceptedFilesCount; + public JournalDto setRowsSuccess(Integer rowsSuccess) { + this.rowsSuccess = rowsSuccess; + return this; + } + + public Integer getRowsError() { + return rowsError; + } + + public JournalDto setRowsError(Integer rowsError) { + this.rowsError = rowsError; + return this; + } + + public Integer getDocumentNumber() { + return documentNumber; + } + + public JournalDto setDocumentNumber(Integer documentNumber) { + this.documentNumber = documentNumber; + return this; + } + + public Boolean isHasFailedRows() { + return hasFailedRows; + } + + public JournalDto setHasFailedRows(Boolean hasFailedRows) { + this.hasFailedRows = hasFailedRows; return this; } } diff --git a/backend/src/main/java/ru/micord/ervu/journal/JournalFileInfo.java b/backend/src/main/java/ru/micord/ervu/journal/JournalFileInfo.java index 6dc27a6e..7ffb5251 100644 --- a/backend/src/main/java/ru/micord/ervu/journal/JournalFileInfo.java +++ b/backend/src/main/java/ru/micord/ervu/journal/JournalFileInfo.java @@ -14,6 +14,7 @@ public class JournalFileInfo { private SenderInfo senderInfo; private Integer rowsCount; //Общее количество записей отправленных в файле private Integer rowsSuccess; //Количество записей принятых в файле + private Integer rowsError; //Количество записей непринятых в файле public List getPackFiles() { return packFiles; @@ -51,6 +52,15 @@ public class JournalFileInfo { return this; } + public Integer getRowsError() { + return rowsError; + } + + public JournalFileInfo setRowsError(Integer rowsError) { + this.rowsError = rowsError; + return this; + } + @JsonIgnoreProperties(ignoreUnknown = true) public static class JournalFileDetails { private String fileId; //ИД файла полученный при создании записи о файле в реестр организаций (в ЕРВУ) diff --git a/backend/src/main/java/ru/micord/ervu/journal/mapper/JournalDtoMapper.java b/backend/src/main/java/ru/micord/ervu/journal/mapper/JournalDtoMapper.java index cbc144d9..332e1e28 100644 --- a/backend/src/main/java/ru/micord/ervu/journal/mapper/JournalDtoMapper.java +++ b/backend/src/main/java/ru/micord/ervu/journal/mapper/JournalDtoMapper.java @@ -24,8 +24,9 @@ public class JournalDtoMapper { ) ) .setStatus(journalFileDetails.getFileStatus().getStatus()) - .setFilesSentCount(journalFileInfo.getRowsCount()) - .setAcceptedFilesCount(journalFileInfo.getRowsSuccess()); + .setRowsCount(journalFileInfo.getRowsCount()) + .setRowsSuccess(journalFileInfo.getRowsSuccess()) + .setRowsError(journalFileInfo.getRowsError()); } public static JournalDto mapToJournalDto(InteractionLogRecord record) { @@ -35,8 +36,9 @@ public class JournalDtoMapper { .setFilePatternCode(Integer.valueOf(record.getForm())) .setSenderFio(record.getSender()) .setStatus(record.getStatus()) - .setFilesSentCount(record.getRecordsSent()) - .setAcceptedFilesCount(record.getRecordsAccepted()) + .setRowsCount(0) + .setRowsSuccess(0) + .setRowsError(0) .setFileId(record.getFileId()); } } diff --git a/backend/src/main/java/ru/micord/ervu/kafka/ReplyingKafkaConfig.java b/backend/src/main/java/ru/micord/ervu/kafka/ReplyingKafkaConfig.java index 3f6df834..0c7b28a9 100644 --- a/backend/src/main/java/ru/micord/ervu/kafka/ReplyingKafkaConfig.java +++ b/backend/src/main/java/ru/micord/ervu/kafka/ReplyingKafkaConfig.java @@ -4,8 +4,10 @@ import org.apache.kafka.clients.CommonClientConfigs; import org.apache.kafka.clients.consumer.ConsumerConfig; import org.apache.kafka.clients.producer.ProducerConfig; import org.apache.kafka.common.config.SaslConfigs; +import org.apache.kafka.common.serialization.BytesDeserializer; import org.apache.kafka.common.serialization.StringDeserializer; import org.apache.kafka.common.serialization.StringSerializer; +import org.apache.kafka.common.utils.Bytes; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; @@ -54,63 +56,102 @@ public class ReplyingKafkaConfig { private String saslMechanism; @Value("${av.kafka.download.response}") private String avReplyTopic; + @Value("${ervu.kafka.validate.export.reply.topic}") + private String validateReplyTopic; - @Bean("ervuProducerFactory") + @Bean public ProducerFactory producerFactory() { - Map configProps = new HashMap<>(); - configProps.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers); - configProps.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class); + Map configProps = commonProducerConfig(); configProps.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class); - configProps.put(CommonClientConfigs.SECURITY_PROTOCOL_CONFIG, securityProtocol); - configProps.put(SaslConfigs.SASL_JAAS_CONFIG, loginModule + " required username=\"" - + username + "\" password=\"" + password + "\";"); - configProps.put(SaslConfigs.SASL_MECHANISM, saslMechanism); return new DefaultKafkaProducerFactory<>(configProps); } - @Bean - public ConsumerFactory consumerFactory() { + private Map commonProducerConfig() { Map configProps = new HashMap<>(); - configProps.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers); - configProps.put(ConsumerConfig.GROUP_ID_CONFIG, groupId + "-" + UUID.randomUUID()); - configProps.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "latest"); - configProps.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); - configProps.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); + configProps.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers); configProps.put(CommonClientConfigs.SECURITY_PROTOCOL_CONFIG, securityProtocol); - configProps.put(SaslConfigs.SASL_JAAS_CONFIG, loginModule + " required username=\"" - + username + "\" password=\"" + password + "\";"); + configProps.put(SaslConfigs.SASL_JAAS_CONFIG, loginModule + " required username=\"" + username + "\" password=\"" + password + "\";"); configProps.put(SaslConfigs.SASL_MECHANISM, saslMechanism); + configProps.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class); + return configProps; + } + + @Bean("ervuConsumerFactory") + public ConsumerFactory ervuConsumerFactory() { + Map configProps = commonConsumerConfig(); + configProps.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); return new DefaultKafkaConsumerFactory<>(configProps); } + @Bean("validateConsumerFactory") + public ConsumerFactory validateConsumerFactory() { + Map configProps = commonConsumerConfig(); + configProps.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, BytesDeserializer.class); + return new DefaultKafkaConsumerFactory<>(configProps); + } + + private Map commonConsumerConfig() { + Map configProps = new HashMap<>(); + configProps.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers); + configProps.put(ConsumerConfig.GROUP_ID_CONFIG, groupId + "-" + UUID.randomUUID()); + configProps.put(CommonClientConfigs.SECURITY_PROTOCOL_CONFIG, securityProtocol); + configProps.put(SaslConfigs.SASL_JAAS_CONFIG, loginModule + " required username=\"" + username + "\" password=\"" + password + "\";"); + configProps.put(SaslConfigs.SASL_MECHANISM, saslMechanism); + configProps.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "latest"); + configProps.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); + return configProps; + } + @Bean("ervuContainerFactory") - public ConcurrentKafkaListenerContainerFactory kafkaListenerContainerFactory() { - ConcurrentKafkaListenerContainerFactory factory = - new ConcurrentKafkaListenerContainerFactory<>(); - factory.setConsumerFactory(consumerFactory()); + public ConcurrentKafkaListenerContainerFactory ervuContainerFactory() { + ConcurrentKafkaListenerContainerFactory factory = new ConcurrentKafkaListenerContainerFactory<>(); + factory.setConsumerFactory(ervuConsumerFactory()); return factory; } - @Bean - public ConcurrentMessageListenerContainer replyContainer( - @Qualifier("ervuContainerFactory") - ConcurrentKafkaListenerContainerFactory kafkaListenerContainerFactory) { - return kafkaListenerContainerFactory.createContainer(orgReplyTopic, excerptReplyTopic, - journalReplyTopic, avReplyTopic - ); + @Bean("validateContainerFactory") + public ConcurrentKafkaListenerContainerFactory validateContainerFactory() { + ConcurrentKafkaListenerContainerFactory factory = new ConcurrentKafkaListenerContainerFactory<>(); + factory.setConsumerFactory(validateConsumerFactory()); + return factory; } - @Bean - public ReplyingKafkaTemplate replyingKafkaTemplate( - @Qualifier("ervuProducerFactory") ProducerFactory producerFactory, - ConcurrentMessageListenerContainer replyContainer) { - ReplyingKafkaTemplate replyingKafkaTemplate = - new ReplyingKafkaTemplate<>(producerFactory, replyContainer); - replyingKafkaTemplate.setCorrelationHeaderName("messageId"); - replyingKafkaTemplate.setCorrelationIdStrategy(record -> + @Bean("ervuContainer") + public ConcurrentMessageListenerContainer ervuContainer( + @Qualifier("ervuContainerFactory") ConcurrentKafkaListenerContainerFactory factory) { + return factory.createContainer(orgReplyTopic, excerptReplyTopic, + journalReplyTopic, avReplyTopic); + } + + @Bean("validateContainer") + public ConcurrentMessageListenerContainer validateContainer( + @Qualifier("validateContainerFactory") ConcurrentKafkaListenerContainerFactory factory) { + return factory.createContainer(validateReplyTopic); + } + + @Bean("ervuReplyingTemplate") + public ReplyingKafkaTemplate ervuReplyingTemplate( + ProducerFactory producerFactory, + @Qualifier("ervuContainer") ConcurrentMessageListenerContainer container) { + ReplyingKafkaTemplate template = new ReplyingKafkaTemplate<>(producerFactory, container); + customizeTemplate(template); + return template; + } + + @Bean("validateReplyingTemplate") + public ReplyingKafkaTemplate validateReplyingTemplate( + ProducerFactory producerFactory, + @Qualifier("validateContainer") ConcurrentMessageListenerContainer container) { + ReplyingKafkaTemplate template = new ReplyingKafkaTemplate<>(producerFactory, container); + customizeTemplate(template); + return template; + } + + private void customizeTemplate(ReplyingKafkaTemplate template) { + template.setCorrelationHeaderName("messageId"); + template.setCorrelationIdStrategy(record -> new CorrelationKey(UUID.randomUUID().toString().getBytes(StandardCharsets.UTF_8))); - replyingKafkaTemplate.setDefaultReplyTimeout(Duration.ofSeconds(replyTimeout)); - replyingKafkaTemplate.setSharedReplyTopic(true); - return replyingKafkaTemplate; + template.setDefaultReplyTimeout(Duration.ofSeconds(replyTimeout)); + template.setSharedReplyTopic(true); } } diff --git a/backend/src/main/java/ru/micord/ervu/kafka/controller/ErvuKafkaController.java b/backend/src/main/java/ru/micord/ervu/kafka/controller/ErvuKafkaController.java index 6b29a84e..1488cb8f 100644 --- a/backend/src/main/java/ru/micord/ervu/kafka/controller/ErvuKafkaController.java +++ b/backend/src/main/java/ru/micord/ervu/kafka/controller/ErvuKafkaController.java @@ -31,7 +31,7 @@ import ru.micord.ervu.util.UrlUtils; public class ErvuKafkaController { @Autowired - private ReplyingKafkaService replyingKafkaService; + private ReplyingKafkaService replyingKafkaService; @Autowired private AuditService auditService; diff --git a/backend/src/main/java/ru/micord/ervu/kafka/service/ReplyingKafkaService.java b/backend/src/main/java/ru/micord/ervu/kafka/service/ReplyingKafkaService.java index 49918e61..876048d6 100644 --- a/backend/src/main/java/ru/micord/ervu/kafka/service/ReplyingKafkaService.java +++ b/backend/src/main/java/ru/micord/ervu/kafka/service/ReplyingKafkaService.java @@ -1,8 +1,9 @@ package ru.micord.ervu.kafka.service; -public interface ReplyingKafkaService { - String sendMessageAndGetReply(String requestTopic, - String requestReplyTopic, - String requestMessage); -} +public interface ReplyingKafkaService { + + V sendMessageAndGetReply(String requestTopic, + String replyTopic, + T requestMessage); +} \ No newline at end of file diff --git a/backend/src/main/java/ru/micord/ervu/kafka/service/impl/BaseReplyingKafkaServiceImpl.java b/backend/src/main/java/ru/micord/ervu/kafka/service/impl/BaseReplyingKafkaService.java similarity index 54% rename from backend/src/main/java/ru/micord/ervu/kafka/service/impl/BaseReplyingKafkaServiceImpl.java rename to backend/src/main/java/ru/micord/ervu/kafka/service/impl/BaseReplyingKafkaService.java index 03e19f72..c849daa2 100644 --- a/backend/src/main/java/ru/micord/ervu/kafka/service/impl/BaseReplyingKafkaServiceImpl.java +++ b/backend/src/main/java/ru/micord/ervu/kafka/service/impl/BaseReplyingKafkaService.java @@ -6,12 +6,10 @@ import java.util.concurrent.ExecutionException; import org.apache.kafka.clients.consumer.ConsumerRecord; import org.apache.kafka.clients.producer.ProducerRecord; -import org.apache.kafka.common.header.internals.RecordHeader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.kafka.requestreply.ReplyingKafkaTemplate; import org.springframework.kafka.requestreply.RequestReplyFuture; -import org.springframework.kafka.support.KafkaHeaders; import org.springframework.stereotype.Service; import ru.micord.ervu.kafka.exception.KafkaMessageException; import ru.micord.ervu.kafka.exception.KafkaMessageReplyTimeoutException; @@ -21,35 +19,30 @@ import ru.micord.ervu.kafka.service.ReplyingKafkaService; * @author Eduard Tihomirov */ @Service -public class BaseReplyingKafkaServiceImpl implements ReplyingKafkaService { +public abstract class BaseReplyingKafkaService implements ReplyingKafkaService { private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); - private final ReplyingKafkaTemplate replyingKafkaTemplate; - public BaseReplyingKafkaServiceImpl( - ReplyingKafkaTemplate replyingKafkaTemplate) { - this.replyingKafkaTemplate = replyingKafkaTemplate; - } - - public String sendMessageAndGetReply(String requestTopic, - String replyTopic, - String requestMessage) { + @Override + public V sendMessageAndGetReply(String requestTopic, String replyTopic, T requestMessage) { long startTime = System.currentTimeMillis(); - ProducerRecord record = new ProducerRecord<>(requestTopic, requestMessage); - record.headers().add(new RecordHeader(KafkaHeaders.REPLY_TOPIC, replyTopic.getBytes())); - RequestReplyFuture replyFuture = replyingKafkaTemplate.sendAndReceive(record); + RequestReplyFuture replyFuture = getTemplate().sendAndReceive( + getProducerRecord(requestTopic, replyTopic, requestMessage)); try { - String result = Optional.ofNullable(replyFuture.get()) - .map(ConsumerRecord::value) - .orElseThrow(() -> new KafkaMessageException("Kafka return result is null.")); - LOGGER.info("Thread {} - KafkaSendMessageAndGetReply: {} ms", - Thread.currentThread().getId(), System.currentTimeMillis() - startTime); - return result; + ConsumerRecord result = Optional.ofNullable(replyFuture.get()) + .orElseThrow(() -> new KafkaMessageException("Kafka return result is null")); + LOGGER.info("Thread {} - KafkaSendMessageAndGetReply: {} ms, replyTopic: {}", + Thread.currentThread().getId(), System.currentTimeMillis() - startTime, replyTopic); + return result.value(); } catch (InterruptedException | ExecutionException e) { - LOGGER.error("Thread {} - KafkaSendMessageAndGetReply: {} ms", - Thread.currentThread().getId(), System.currentTimeMillis() - startTime); + LOGGER.error("Thread {} - KafkaSendMessageAndGetReply: {} ms, replyTopic: {}", + Thread.currentThread().getId(), System.currentTimeMillis() - startTime, replyTopic); throw new KafkaMessageReplyTimeoutException(e); } } + protected abstract ReplyingKafkaTemplate getTemplate(); + + protected abstract ProducerRecord getProducerRecord(String requestTopic, + String replyTopic, T requestMessage); } diff --git a/backend/src/main/java/ru/micord/ervu/kafka/service/impl/ErvuReplyingKafkaService.java b/backend/src/main/java/ru/micord/ervu/kafka/service/impl/ErvuReplyingKafkaService.java new file mode 100644 index 00000000..51c3d452 --- /dev/null +++ b/backend/src/main/java/ru/micord/ervu/kafka/service/impl/ErvuReplyingKafkaService.java @@ -0,0 +1,31 @@ +package ru.micord.ervu.kafka.service.impl; + +import org.apache.kafka.clients.producer.ProducerRecord; +import org.apache.kafka.common.header.internals.RecordHeader; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.kafka.requestreply.ReplyingKafkaTemplate; +import org.springframework.kafka.support.KafkaHeaders; +import org.springframework.stereotype.Service; + +@Service +public class ErvuReplyingKafkaService extends BaseReplyingKafkaService{ + private final ReplyingKafkaTemplate replyingKafkaTemplate; + + public ErvuReplyingKafkaService( + @Qualifier("ervuReplyingTemplate") ReplyingKafkaTemplate replyingKafkaTemplate) { + this.replyingKafkaTemplate = replyingKafkaTemplate; + } + + @Override + protected ReplyingKafkaTemplate getTemplate() { + return replyingKafkaTemplate; + } + + @Override + protected ProducerRecord getProducerRecord(String requestTopic, String replyTopic, + String requestMessage) { + ProducerRecord record = new ProducerRecord<>(requestTopic, requestMessage); + record.headers().add(new RecordHeader(KafkaHeaders.REPLY_TOPIC, replyTopic.getBytes())); + return record; + } +} diff --git a/backend/src/main/java/ru/micord/ervu/kafka/service/impl/ValidateReplyingKafkaService.java b/backend/src/main/java/ru/micord/ervu/kafka/service/impl/ValidateReplyingKafkaService.java new file mode 100644 index 00000000..10903afc --- /dev/null +++ b/backend/src/main/java/ru/micord/ervu/kafka/service/impl/ValidateReplyingKafkaService.java @@ -0,0 +1,32 @@ +package ru.micord.ervu.kafka.service.impl; + + +import org.apache.kafka.clients.producer.ProducerRecord; +import org.apache.kafka.common.header.internals.RecordHeader; +import org.apache.kafka.common.utils.Bytes; +import org.springframework.kafka.requestreply.ReplyingKafkaTemplate; +import org.springframework.kafka.support.KafkaHeaders; +import org.springframework.stereotype.Service; + +@Service +public class ValidateReplyingKafkaService extends BaseReplyingKafkaService { + private final ReplyingKafkaTemplate replyingKafkaTemplate; + + public ValidateReplyingKafkaService( + ReplyingKafkaTemplate replyingKafkaTemplate) { + this.replyingKafkaTemplate = replyingKafkaTemplate; + } + + @Override + protected ReplyingKafkaTemplate getTemplate() { + return replyingKafkaTemplate; + } + + @Override + protected ProducerRecord getProducerRecord(String requestTopic, String replyTopic, + String requestMessage) { + ProducerRecord record = new ProducerRecord<>(requestTopic, requestMessage); + record.headers().add(new RecordHeader(KafkaHeaders.REPLY_TOPIC, replyTopic.getBytes())); + return record; + } +} diff --git a/backend/src/main/java/ru/micord/ervu/model/ValidateExportRequest.java b/backend/src/main/java/ru/micord/ervu/model/ValidateExportRequest.java new file mode 100644 index 00000000..65dbdd7a --- /dev/null +++ b/backend/src/main/java/ru/micord/ervu/model/ValidateExportRequest.java @@ -0,0 +1,4 @@ +package ru.micord.ervu.model; + +public record ValidateExportRequest(String orgId, String fileId) { +} diff --git a/backend/src/main/java/ru/micord/ervu/model/ValidateExportResponse.java b/backend/src/main/java/ru/micord/ervu/model/ValidateExportResponse.java new file mode 100644 index 00000000..8e8c8cd9 --- /dev/null +++ b/backend/src/main/java/ru/micord/ervu/model/ValidateExportResponse.java @@ -0,0 +1,28 @@ +package ru.micord.ervu.model; + +import org.apache.kafka.shaded.com.google.protobuf.ByteString; +import org.apache.kafka.shaded.com.google.protobuf.InvalidProtocolBufferException; + +public class ValidateExportResponse { + private String fileName; + private ByteString file; + + public ValidateExportResponse(byte[] bytes) throws InvalidProtocolBufferException { + // TODO: Заменить ValidateExportResponseProto на реальный protobuf класс +// ValidateExportResponseProto protoResponse = ValidateExportResponseProto.parseFrom(bytes); +// this.fileName = protoResponse.getFileName(); +// this.file = protoResponse.getFile(); + } + + public String getFileName() { + return fileName; + } + + public ByteString getFile() { + return file; + } + + public boolean hasFile() { + return file != null && !file.isEmpty(); + } +} \ No newline at end of file diff --git a/backend/src/main/java/ru/micord/ervu/property/grid/FilterType.java b/backend/src/main/java/ru/micord/ervu/property/grid/FilterType.java index 7795ebc0..8d3f53f1 100644 --- a/backend/src/main/java/ru/micord/ervu/property/grid/FilterType.java +++ b/backend/src/main/java/ru/micord/ervu/property/grid/FilterType.java @@ -7,5 +7,6 @@ public enum FilterType { TEXT, DATE, NUMBER, - SET + SET, + FILE } diff --git a/backend/src/main/java/ru/micord/ervu/security/esia/service/EsiaAuthService.java b/backend/src/main/java/ru/micord/ervu/security/esia/service/EsiaAuthService.java index 63143c16..a9f403db 100644 --- a/backend/src/main/java/ru/micord/ervu/security/esia/service/EsiaAuthService.java +++ b/backend/src/main/java/ru/micord/ervu/security/esia/service/EsiaAuthService.java @@ -86,7 +86,7 @@ public class EsiaAuthService { @Autowired private JwtTokenService jwtTokenService; @Autowired - private ReplyingKafkaService replyingKafkaService; + private ReplyingKafkaService replyingKafkaService; @Autowired private OkopfService okopfService; @Autowired diff --git a/backend/src/main/java/ru/micord/ervu/service/InteractionServiceImpl.java b/backend/src/main/java/ru/micord/ervu/service/InteractionServiceImpl.java index c5688d16..b02c9e2c 100644 --- a/backend/src/main/java/ru/micord/ervu/service/InteractionServiceImpl.java +++ b/backend/src/main/java/ru/micord/ervu/service/InteractionServiceImpl.java @@ -41,8 +41,6 @@ public class InteractionServiceImpl implements InteractionService { .set(INTERACTION_LOG.SENT_DATE, timestamp) .set(INTERACTION_LOG.SENDER, sender) .set(INTERACTION_LOG.FILE_NAME, fileName) - .set(INTERACTION_LOG.RECORDS_SENT, 0) - .set(INTERACTION_LOG.RECORDS_ACCEPTED, 0) .set(INTERACTION_LOG.ERVU_ID, ervuId) .execute(); } diff --git a/backend/src/main/java/ru/micord/ervu/service/ValidationFileService.java b/backend/src/main/java/ru/micord/ervu/service/ValidationFileService.java new file mode 100644 index 00000000..c116bf26 --- /dev/null +++ b/backend/src/main/java/ru/micord/ervu/service/ValidationFileService.java @@ -0,0 +1,48 @@ +package ru.micord.ervu.service; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.kafka.common.utils.Bytes; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import ru.micord.ervu.exception.ExportException; +import ru.micord.ervu.kafka.service.ReplyingKafkaService; +import ru.micord.ervu.model.ValidateExportRequest; +import ru.micord.ervu.model.ValidateExportResponse; + +/** + * @author Adel Kalimullin + */ +@Service +public class ValidationFileService { + private final ReplyingKafkaService replyingKafkaService; + private final ObjectMapper objectMapper; + private final String validateExportRequestTopic; + private final String validateExportReplyTopic; + + public ValidationFileService( + ReplyingKafkaService replyingKafkaService, + ObjectMapper objectMapper, + @Value("${ervu.kafka.validate.export.request.topic}") String validateExportRequestTopic, + @Value("${ervu.kafka.validate.export.reply.topic}") String validateExportReplyTopic) { + this.replyingKafkaService = replyingKafkaService; + this.objectMapper = objectMapper; + this.validateExportRequestTopic = validateExportRequestTopic; + this.validateExportReplyTopic = validateExportReplyTopic; + } + + + public ValidateExportResponse exportFile(String ervuId, String fileId) { + try { + ValidateExportRequest validateExportRequest = new ValidateExportRequest(ervuId, fileId); + + byte[] bytes = replyingKafkaService.sendMessageAndGetReply( + validateExportRequestTopic, validateExportReplyTopic, + objectMapper.writeValueAsString(validateExportRequest) + ).get(); + return new ValidateExportResponse(bytes); + } + catch (Exception e) { + throw new ExportException("Failed to export file: " + e.getMessage(), e); + } + } +} diff --git a/backend/src/main/java/ru/micord/ervu/service/grid/impl/JournalInMemoryStaticGridLoadService.java b/backend/src/main/java/ru/micord/ervu/service/grid/impl/JournalInMemoryStaticGridLoadService.java index 8c956256..71624244 100644 --- a/backend/src/main/java/ru/micord/ervu/service/grid/impl/JournalInMemoryStaticGridLoadService.java +++ b/backend/src/main/java/ru/micord/ervu/service/grid/impl/JournalInMemoryStaticGridLoadService.java @@ -5,6 +5,7 @@ import java.util.Comparator; import java.util.List; import java.util.HashSet; import java.util.Optional; +import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Stream; import javax.servlet.http.Cookie; @@ -33,7 +34,7 @@ public class JournalInMemoryStaticGridLoadService implements private final JwtTokenService jwtTokenService; private final InteractionService interactionService; - private final ReplyingKafkaService replyingKafkaService; + private final ReplyingKafkaService replyingKafkaService; private final ObjectMapper objectMapper; private final HttpServletRequest request; @@ -46,7 +47,7 @@ public class JournalInMemoryStaticGridLoadService implements public JournalInMemoryStaticGridLoadService(JwtTokenService jwtTokenService, InteractionService interactionService, - ReplyingKafkaService replyingKafkaService, + ReplyingKafkaService replyingKafkaService, ObjectMapper objectMapper, HttpServletRequest request) { this.jwtTokenService = jwtTokenService; this.interactionService = interactionService; @@ -80,10 +81,16 @@ public class JournalInMemoryStaticGridLoadService implements throw new JsonParsingException("Failed to parse JournalFileDataResponse.", e); } + AtomicInteger counter = new AtomicInteger(1); HashSet seenFileIds = new HashSet<>(); return Stream.concat(dbJournalList.stream(), ervuJournalList.stream()) .filter(journal -> seenFileIds.add(journal.getFileId())) .sorted(Comparator.comparing(JournalDto::getDepartureDateTime)) + .map(journal -> + journal + .setDocumentNumber(counter.getAndIncrement()) + .setHasFailedRows(journal.getRowsError() != null && journal.getRowsError() > 0) + ) .toList(); } diff --git a/frontend/src/ts/ervu/component/grid/InMemoryStaticGrid.ts b/frontend/src/ts/ervu/component/grid/InMemoryStaticGrid.ts index 99d7fd14..581b33d9 100644 --- a/frontend/src/ts/ervu/component/grid/InMemoryStaticGrid.ts +++ b/frontend/src/ts/ervu/component/grid/InMemoryStaticGrid.ts @@ -1,5 +1,4 @@ import { - Event, GridColumnIdUtils, GridRow, GridRowModelType, @@ -11,17 +10,19 @@ import {ChangeDetectionStrategy, Component} from "@angular/core"; import { ColDef, GridReadyEvent, - FilterChangedEvent, ICellRendererParams, ITooltipParams, ValueFormatterParams, - ValueGetterParams + ValueGetterParams, } from "ag-grid-community"; import {StaticColumnInitializer} from "./StaticColumnInitializer"; -import {InMemoryStaticGridRpcService} from "../../../generated/ru/micord/ervu/service/rpc/InMemoryStaticGridRpcService"; +import { + InMemoryStaticGridRpcService +} from "../../../generated/ru/micord/ervu/service/rpc/InMemoryStaticGridRpcService"; import {StaticGridColumn} from "../../../generated/ru/micord/ervu/property/grid/StaticGridColumn"; -import { FilterService } from "../../service/FilterService"; +import {FilterService} from "../../service/FilterService"; import {AuditConstants, AuditService, FilterInfo} from "../../service/AuditService"; +import {ValidateFileService} from "../../service/ValidateFileService"; @Component({ @@ -34,6 +35,7 @@ export class InMemoryStaticGrid extends GridV2 { private rpcService: InMemoryStaticGridRpcService; private auditService: AuditService; + private validateFileService: ValidateFileService; getRowModelType(): string { return GridRowModelType.CLIENT_SIDE; @@ -42,6 +44,7 @@ export class InMemoryStaticGrid extends GridV2 { protected initGrid() { super.initGrid(); this.auditService = this.injector.get(AuditService); + this.validateFileService = this.injector.get(ValidateFileService); this.rpcService = this.getScript(InMemoryStaticGridRpcService); if (this.rpcService) { this.rpcService.loadData().then(response => { @@ -85,6 +88,10 @@ export class InMemoryStaticGrid extends GridV2 { } } + public downloadFile(fileId : string){ + this.validateFileService.exportFile(fileId); + } + getColumns(): any[] { return this.getScriptsInChildren(GridV2Column) .map(columnV2 => columnV2.getScript(StaticGridColumn)); @@ -125,6 +132,13 @@ export class InMemoryStaticGrid extends GridV2 { return columnComp.valueFormatter.format(params); } } + + colDef.cellRendererParams = { + context : { + parentComponent: this + } + }; + return colDef; } diff --git a/frontend/src/ts/ervu/component/grid/filter/CustomGridColumnFilterUtils.ts b/frontend/src/ts/ervu/component/grid/filter/CustomGridColumnFilterUtils.ts index 19f8c910..620bd559 100644 --- a/frontend/src/ts/ervu/component/grid/filter/CustomGridColumnFilterUtils.ts +++ b/frontend/src/ts/ervu/component/grid/filter/CustomGridColumnFilterUtils.ts @@ -1,6 +1,7 @@ import {FilterType} from "../../../../generated/ru/micord/ervu/property/grid/FilterType"; import {DateFilter, NumberFilter, TextFilter} from "ag-grid-community"; import {SetFilter} from "./SetFilter"; +import {FileAvailableFilterComp} from "./FileAvailableFilterComp"; export class CustomGridColumnFilterUtils { @@ -16,6 +17,8 @@ export class CustomGridColumnFilterUtils { return DateFilter; case FilterType.SET: return SetFilter; + case FilterType.FILE: + return FileAvailableFilterComp; case FilterType.TEXT: default: return TextFilter; diff --git a/frontend/src/ts/ervu/component/grid/filter/FileAvailableFilterComp.ts b/frontend/src/ts/ervu/component/grid/filter/FileAvailableFilterComp.ts new file mode 100644 index 00000000..a1be012b --- /dev/null +++ b/frontend/src/ts/ervu/component/grid/filter/FileAvailableFilterComp.ts @@ -0,0 +1,77 @@ +import {BaseBooleanComboBoxFilter} from "@webbpm/base-package"; +import {IFilterParams, IDoesFilterPassParams, IFilterComp} from "ag-grid-community"; + + +export class FileAvailableFilterComp extends BaseBooleanComboBoxFilter implements IFilterComp { + private filterValue: boolean | undefined = undefined; + private params!: IFilterParams; + + init(params: IFilterParams): void { + this.params = params; + this.createComboBox("ag-combobox-file-filter"); + this.populateComboBoxWithFixedValues(); + + this.comboBox.addEventListener("change", () => this.onComboBoxChanged()); + } + + protected populateComboBoxWithFixedValues(): void { + const options = [ + { label: "Все", value: undefined }, + { label: "Файл присутствует", value: true }, + { label: "Файл отсутствует", value: false } + ]; + + this.comboBox.innerHTML = ""; + options.forEach(({ label }) => { + const option = document.createElement("option"); + option.textContent = label; + this.comboBox.appendChild(option); + }); + } + + protected onComboBoxChanged(): void { + const selectedIndex = this.comboBox.selectedIndex; + + this.filterValue = + selectedIndex === 1 ? true : + selectedIndex === 2 ? false : + undefined; + + this.params.filterChangedCallback(); + } + + doesFilterPass(params: IDoesFilterPassParams): boolean { + const cellValue = params.data[this.params.colDef.field!]; + return this.filterValue === undefined || cellValue === this.filterValue; + } + + isFilterActive(): boolean { + return this.filterValue !== undefined; + } + + getModel(): any { + return this.filterValue === undefined + ? undefined + : { filter: this.filterValue, type: "equals" }; + } + + setModel(model: any): void { + this.filterValue = model ? model.filter : undefined; + + if (this.filterValue === true) { + this.comboBox.selectedIndex = 1; + } + else if (this.filterValue === false) { + this.comboBox.selectedIndex = 2; + } + else { + this.comboBox.selectedIndex = 0; + } + + this.params.filterChangedCallback(); + } + + getGui(): HTMLElement { + return this.eGui; + } +} \ No newline at end of file diff --git a/frontend/src/ts/ervu/component/grid/renderer/FileDownloadCellRenderer.ts b/frontend/src/ts/ervu/component/grid/renderer/FileDownloadCellRenderer.ts new file mode 100644 index 00000000..1a71de29 --- /dev/null +++ b/frontend/src/ts/ervu/component/grid/renderer/FileDownloadCellRenderer.ts @@ -0,0 +1,36 @@ +import {ICellRendererParams} from "ag-grid-community"; +import {GridCellValueRenderer} from "@webbpm/base-package"; + +export class FileDownloadCellRenderer implements GridCellValueRenderer { + render(params: ICellRendererParams): HTMLElement { + const container = document.createElement('div'); + container.className = 'download-cell-renderer'; + + const button = document.createElement('button'); + button.className = 'download-btn'; + button.innerHTML = ''; + button.title = params.data.fileName || 'Скачать файл'; + + if (!params.value) { + button.style.display = 'none'; + } + + button.addEventListener('click', () => { + const fileId = params.data.fileId; + if (!fileId) { + return; + } + + const parentComponent = params.context.parentComponent; + if (parentComponent) { + parentComponent.downloadFile(fileId, params.data); + } + + button.blur(); + }); + + container.appendChild(button); + return container; + } + +} \ No newline at end of file diff --git a/frontend/src/ts/ervu/service/FilterService.ts b/frontend/src/ts/ervu/service/FilterService.ts index b9a9863d..db617807 100644 --- a/frontend/src/ts/ervu/service/FilterService.ts +++ b/frontend/src/ts/ervu/service/FilterService.ts @@ -1,6 +1,7 @@ import {DateFilter, NumberFilter, TextFilter} from "ag-grid-community"; import {SetFilter} from "../component/grid/filter/SetFilter"; import {FilterInfo} from "./AuditService"; +import {FileAvailableFilterComp} from "../component/grid/filter/FileAvailableFilterComp"; export class FilterService { static getFilterData(columnDef: any, agFilter: any): FilterInfo { @@ -15,6 +16,8 @@ export class FilterService { return this.processSetFilter(agFilter); case TextFilter: return this.processTextFilter(agFilter); + case FileAvailableFilterComp: + return this.processFileAvailableFilter(agFilter); default: return; } @@ -63,6 +66,20 @@ export class FilterService { return this.createDualConditionData(agFilter); } + private static processFileAvailableFilter(agFilter: any): FilterInfo { + const displayValue = agFilter.filter + ? "Файл присутствует" + : "Файл отсутствует"; + + return { + conditionOperator: undefined, + conditions: [{ + filterValue: displayValue, + filterType: "equals" + }] + }; + } + private static createSingleConditionData( filterValue: string, filterType: string, diff --git a/frontend/src/ts/ervu/service/ValidateFileService.ts b/frontend/src/ts/ervu/service/ValidateFileService.ts new file mode 100644 index 00000000..8db951ee --- /dev/null +++ b/frontend/src/ts/ervu/service/ValidateFileService.ts @@ -0,0 +1,34 @@ +import {Injectable} from "@angular/core"; +import {HttpClient} from "@angular/common/http"; +import {MessagesService} from "@webbpm/base-package"; + +@Injectable({ + providedIn: "root" + }) +export class ValidateFileService { + constructor(private httpClient: HttpClient, + private messageService: MessagesService) { + } + + public exportFile(fileId: string) { + this.httpClient.get('validate/export/' + fileId, { + responseType: 'blob', + observe: 'response' + }).toPromise() + .then((response) => { + const data = window.URL.createObjectURL(response.body); + const link = document.createElement("a"); + link.href = data; + const contentDisposition = response.headers.get('Content-Disposition'); + const fileNameMatch = contentDisposition.match(/filename\*=?UTF-8''(.+)/i); + link.download = decodeURIComponent(fileNameMatch[1].replace(/\+/g, '%20')); + document.body.appendChild(link); + link.click(); + window.URL.revokeObjectURL(data); + link.remove(); + }) + .catch(() => { + this.messageService.error("Не удалось скачать файл") + }); + } +} \ No newline at end of file diff --git a/frontend/src/ts/modules/app/app.module.ts b/frontend/src/ts/modules/app/app.module.ts index a24ea8fd..517811de 100644 --- a/frontend/src/ts/modules/app/app.module.ts +++ b/frontend/src/ts/modules/app/app.module.ts @@ -30,6 +30,7 @@ import {AuditService} from "../../ervu/service/AuditService"; import { ErvuFileUploadWithAdditionalFiles } from "../../ervu/component/fileupload/ErvuFileUploadWithAdditionalFiles"; +import {ValidateFileService} from "../../ervu/service/ValidateFileService"; registerLocaleData(localeRu); export const DIRECTIVES = [ @@ -70,7 +71,7 @@ export function checkAuthentication(authService: AuthenticationService): () => P DIRECTIVES ], providers: [ - AuthenticationService, AuditService, + AuthenticationService, AuditService, ValidateFileService, { provide: APP_INITIALIZER, useFactory: checkAuthentication, diff --git a/pom.xml b/pom.xml index 801331eb..5a6faaf2 100644 --- a/pom.xml +++ b/pom.xml @@ -270,6 +270,16 @@ 1.1.10.7 runtime + + com.google.protobuf + protobuf-java + 4.28.3 + + + com.google.protobuf + protobuf-java-util + 4.28.3 + org.slf4j slf4j-api @@ -392,6 +402,11 @@ + + org.xolstice.maven.plugins + protobuf-maven-plugin + 0.6.1 + diff --git a/resources/src/main/resources/business-model/Журнал взаимодействия.page b/resources/src/main/resources/business-model/Журнал взаимодействия.page index 714d98cf..57f0567b 100644 --- a/resources/src/main/resources/business-model/Журнал взаимодействия.page +++ b/resources/src/main/resources/business-model/Журнал взаимодействия.page @@ -982,11 +982,106 @@ + + c556264f-221b-4af8-9e64-f380a67c41ec + 7094b2c2-60c7-43d0-84d7-3adbd3c77fe9 + №П/П + false + false + + + + floatingFilter + +false + + + + + + + + autoHeight + +true + + + + displayName + +"№П/П" + + + + displayPopup + +true + + + + field + + + + column + + "documentNumber" + + + + filterType + + "NUMBER" + + + + type + + "java.lang.Number" + + + + + + + filter + +true + + + + pinned + +"LEFT" + + + + sortable + +true + + + + width + +50 + + + + widthFixed + +null + + + + + c556264f-221b-4af8-9e64-f380a67c41ec 9b895ce1-494f-4f2e-b87b-78d019fc3760 Дата и время направления false + false false @@ -1064,7 +1159,7 @@ filter -true +null @@ -1118,6 +1213,12 @@ true + + disableHiding + +null + + displayName @@ -1489,7 +1590,7 @@ column - "filesSentCount" + "rowsCount" @@ -1539,6 +1640,7 @@ 4c070d5d-cac7-4cc4-8ee4-e9a4b9b289a5 Записей принято false + false false @@ -1577,7 +1679,7 @@ column - "acceptedFilesCount" + "rowsSuccess" @@ -1616,6 +1718,293 @@ widthFixed +null + + + + + + + c556264f-221b-4af8-9e64-f380a67c41ec + 7189ce80-0090-44c9-a8ba-b3860b1fefb8 + Записей не принято + false + false + false + + + + floatingFilter + +false + + + + + + + + autoHeight + +true + + + + displayName + +"Записей не принято" + + + + displayPopup + +true + + + + field + + + + column + + "rowsError" + + + + filterType + + "NUMBER" + + + + type + + "java.lang.Number" + + + + + + + filter + +true + + + + sortable + +true + + + + width + +80 + + + + widthFixed + +null + + + + + + + c556264f-221b-4af8-9e64-f380a67c41ec + 33376e71-d633-464c-bd0a-da95caeae54b + Загрузка файла + false + false + false + + + + floatingFilter + +false + + + + renderer + + + FileDownloadCellRenderer + ervu.component.grid.renderer + + + + + + + + + autoHeight + +true + + + + displayName + +"Описание ошибки" + + + + displayPopup + +true + + + + field + + + + column + + "hasFailedRows" + + + + filterType + + "FILE" + + + + type + + "java.lang.Boolean" + + + + + + + filter + +true + + + + hidden + +null + + + + sortable + +true + + + + width + +80 + + + + widthFixed + +null + + + + + + + c556264f-221b-4af8-9e64-f380a67c41ec + 438864af-918d-4264-bc37-192f94263673 + id Файла(скрытое) + false + false + + + + floatingFilter + +false + + + + + + + + autoHeight + +true + + + + displayName + +"id файла" + + + + displayPopup + +true + + + + field + + + + column + + "fileId" + + + + filterType + + "TEXT" + + + + type + + "java.lang.String" + + + + + + + filter + +true + + + + hidden + +true + + + + sortable + +true + + + + width + +null + + + + widthFixed + null From d1f1a092948f2b88efc08e8e6dc917f984602db8 Mon Sep 17 00:00:00 2001 From: "adel.ka" Date: Wed, 15 Oct 2025 11:28:42 +0300 Subject: [PATCH 22/45] SUPPORT-9368: fixes --- frontend/src/ts/ervu/service/ValidateFileService.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/frontend/src/ts/ervu/service/ValidateFileService.ts b/frontend/src/ts/ervu/service/ValidateFileService.ts index 8db951ee..2b827948 100644 --- a/frontend/src/ts/ervu/service/ValidateFileService.ts +++ b/frontend/src/ts/ervu/service/ValidateFileService.ts @@ -11,8 +11,12 @@ export class ValidateFileService { } public exportFile(fileId: string) { + const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone; this.httpClient.get('validate/export/' + fileId, { responseType: 'blob', + headers: { + "Client-Time-Zone": timeZone, + }, observe: 'response' }).toPromise() .then((response) => { From 79f97792003c2d5e7a666a792db9717141e74706 Mon Sep 17 00:00:00 2001 From: "adel.ka" Date: Wed, 15 Oct 2025 12:15:23 +0300 Subject: [PATCH 23/45] SUPPORT-9368: fixes --- .../ts/ervu/component/grid/renderer/FileDownloadCellRenderer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/ts/ervu/component/grid/renderer/FileDownloadCellRenderer.ts b/frontend/src/ts/ervu/component/grid/renderer/FileDownloadCellRenderer.ts index 1a71de29..ab1338f4 100644 --- a/frontend/src/ts/ervu/component/grid/renderer/FileDownloadCellRenderer.ts +++ b/frontend/src/ts/ervu/component/grid/renderer/FileDownloadCellRenderer.ts @@ -23,7 +23,7 @@ export class FileDownloadCellRenderer implements GridCellValueRenderer { const parentComponent = params.context.parentComponent; if (parentComponent) { - parentComponent.downloadFile(fileId, params.data); + parentComponent.downloadFile(fileId); } button.blur(); From 9ec8e6d0ab1619f7b033181d488eba36f16c7629 Mon Sep 17 00:00:00 2001 From: Eduard Tihomiorv Date: Wed, 15 Oct 2025 14:17:43 +0300 Subject: [PATCH 24/45] add 2 form --- .../Личный кабинет юр лица.page | 1582 +++++++++++++++-- 1 file changed, 1447 insertions(+), 135 deletions(-) diff --git a/resources/src/main/resources/business-model/Личный кабинет юр лица.page b/resources/src/main/resources/business-model/Личный кабинет юр лица.page index 20deeacb..1aa31c0a 100644 --- a/resources/src/main/resources/business-model/Личный кабинет юр лица.page +++ b/resources/src/main/resources/business-model/Личный кабинет юр лица.page @@ -251,6 +251,7 @@ c9898352-a317-4117-bfcc-28b5c4d9a0d1 Горизонтальный контейнер true + false false @@ -355,6 +356,7 @@ 1be5e2cd-f42e-40c6-971c-e92f997a7139 Горизонтальный контейнер true + false false @@ -444,6 +446,7 @@ a6c5ce4e-9773-45ee-9153-9cd79ef13a97 Hbox_9 true + false false @@ -623,6 +626,7 @@ ea3e7a6d-e69c-4ef0-8a80-d8fd038a8c1e Hbox_9_ true + false false @@ -780,6 +784,7 @@ 8a23f760-b1fc-436a-8bd5-f87664c7a50f Hbox true + false false @@ -1654,6 +1659,7 @@ 7ea732e4-71c7-4325-8569-462acbea89b8 Вертикальный контейнер true + false false @@ -1852,6 +1858,7 @@ b8373653-133f-43cd-a8c2-ba83f232d379 Вертикальный контейнер true + false false @@ -2016,6 +2023,7 @@ c4d501b1-7565-45c3-8722-d624126f6cfb Вертикальный контейнер true + false false @@ -4637,7 +4645,7 @@ behavior - {"objectId":"ba4ed1be-acc7-456d-aec2-e29d56f05113","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"f8c09ec6-d52a-4398-a40e-a237f4305b88","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} @@ -4672,7 +4680,7 @@ behavior - {"objectId":"ba4ed1be-acc7-456d-aec2-e29d56f05113","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"f8c09ec6-d52a-4398-a40e-a237f4305b88","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} @@ -4753,6 +4761,181 @@ false + + + + + + + + + + + + behavior + + {"objectId":"bee4c3f7-05b0-4b21-81bb-48249a1bdfdb","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + true + + + + + + + + + + + + + behavior + + {"objectId":"a8e76694-830b-4f22-9a5b-bb0efe0d7745","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + true + + + + + + + + + + + + + behavior + + {"objectId":"77096468-9f51-4e2c-93a8-97a38d89570e","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + true + + + + + + + + + + + + + behavior + + {"objectId":"2b87f279-a5eb-445f-b96e-4d12b6217126","packageName":"component.field","className":"CheckBox","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + true + + + + + + + + + + + + + behavior + + {"objectId":"c22b9906-4d5e-48df-b084-f0ac331de04c","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + true + @@ -4917,38 +5100,7 @@ c12d155e-947d-41b6-aef0-23080fdc82c0 Текст false - false - - - - cssClasses - - - - "subtitle" - - - - - - - - - - - initialValue - - "Изменения сведений сотрудников, необходимых для ведения воинского учета" - - - - - - - - - false - + true 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 @@ -5039,6 +5191,868 @@ + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + 998f235b-9d72-4f87-950b-1ec90e3d8b90 + Вертикальный контейнер + true + false + + + + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + c12d155e-947d-41b6-aef0-23080fdc82c0 + Текст + false + false + + + + cssClasses + + + + "subtitle" + + + + + + + + + + + initialValue + + "Изменения сведений сотрудников, необходимых для ведения воинского учета" + + + + + + + + +false + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + dbb46f04-0f9b-42bc-8a85-2b8b8dc7c246 + Текст + false + true + + + 5694e7c5-bbb5-4d23-be6c-7ad71b8ad38c + f8c09ec6-d52a-4398-a40e-a237f4305b88 + Csv file + false + false + false + +false +true + + + + ErvuFileUploadWithAdditionalFiles + ervu.component.fileupload + +true +true + + + collectible + + false + + + + cssClasses + + + + "btn-main" + + + + + + displayFileSize + + true + + + + displayProgressBar + + true + + + + extensionFilter + + + + "csv" + + + + + + + formType + + "FORM_2" + + + + maxFileSizeMb + + 5.0 + + + + maxFilesToUpload + + 1.0 + + + + mchdFileUploadRef + + {"objectId":"0f616bc4-9148-4d3e-b374-269a91ed54f2","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + removeFileButtonName + + "Удалить" + + + + selectFileButtonName + + "Выбрать" + + + + selectFileFieldText + + "Перетащите файл или выберите на компьютере" + + + + signFileUploadRef + + {"objectId":"a8e76694-830b-4f22-9a5b-bb0efe0d7745","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + visible + + true + + + + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + a3c544ce-f22e-4d7c-a879-4827832742f8 + Текст + false + true + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + 0970e79a-7130-4c23-bdc8-13f8554daa59 + Текст + false + false + + + + cssClasses + + + + "mute" + + + + + + initialValue + + "Поддерживаемый формат файла - csv" + + + + + + + + +false + + + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + 6345d7cc-c606-4195-acc2-bc9cfcd300ae + Вертикальный контейнер + true + false + + + + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + bee4c3f7-05b0-4b21-81bb-48249a1bdfdb + Текст + false + false + + + + cssClasses + + + + "subtitle" + + + + + + + + + + + initialValue + + "Ваша открепленная усиленная квалифицированная электронная подпись в формате sig" + + + + + + + + +false + + + + 5694e7c5-bbb5-4d23-be6c-7ad71b8ad38c + a8e76694-830b-4f22-9a5b-bb0efe0d7745 + Подпись File + false + false + + + + collectible + + false + + + + cssClasses + + + + "btn-main" + + + + + + extensionFilter + + + + + "sig" + + + + + + formType + + "FORM_2" + + + + maxFileSizeMb + + 5.0 + + + + maxFilesToUpload + + 1.0 + + + + removeFileButtonName + + "Удалить" + + + + selectFileButtonName + + "Выбрать" + + + + selectFileFieldText + + "Перетащите файл или выберите на компьютере" + + + + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + 77096468-9f51-4e2c-93a8-97a38d89570e + Текст + false + false + + + + cssClasses + + + + "mute" + + + + + + initialValue + + "Поддерживаемый формат файла - sig" + + + + + + + + +false + + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + 4234108a-2b52-4b6f-be54-75590266b711 + Вертикальный контейнер + true + true + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + d320bfdf-0416-43da-bed8-a4a2131869d7 + Вертикальный контейнер + true + true + + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + 4234108a-2b52-4b6f-be54-75590266b711 + Вертикальный контейнер + true + false + + + + + + + 98594cec-0a9b-4cef-af09-e1b71cb2ad9e + bb63fe78-e531-4e67-921a-91eff8cd6406 + AC_МЧД + false + false + + + + elseActions + + + + + + behavior + +{"objectId":"0f616bc4-9148-4d3e-b374-269a91ed54f2","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + method + +"setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + +{"objectId":"f6e4a42a-020e-4b2a-b6e7-6f48024fb438","packageName":"component","className":"Text","type":"TS"} + + + + method + +"setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + eventRefs + + + + + + behavior + +{"objectId":"2b87f279-a5eb-445f-b96e-4d12b6217126","packageName":"component.field","className":"CheckBox","type":"TS"} + + + + propertyName + +"userChangeValueEvent" + + + + + + + + + ifCondition + + + + conditions + + + + + + _isGroupSelected + + false + + + + one + + + + conditionFirstPart + + + +objectValue + + + + behavior + + {"objectId":"2b87f279-a5eb-445f-b96e-4d12b6217126","packageName":"component.field","className":"CheckBox","type":"TS"} + + + + method + + "getValue" + + + + + + + + + + conditionSecondPart + + + +staticValue + + + boolean + + + true + + + + + + + operation + + "EQUALS" + + + + + + + + + + + + logicalOperation + + null + + + + + + + thenActions + + + + + + behavior + +{"objectId":"0f616bc4-9148-4d3e-b374-269a91ed54f2","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + method + +"setVisible" + + + + value + + + + staticValue + + + boolean + + + true + + + + + + + + + + + + + behavior + +{"objectId":"f6e4a42a-020e-4b2a-b6e7-6f48024fb438","packageName":"component","className":"Text","type":"TS"} + + + + method + +"setVisible" + + + + value + + + + staticValue + + + boolean + + + true + + + + + + + + + + + + + + + 8b755f7b-e52b-4800-830a-f01467cd5cbb + 2b87f279-a5eb-445f-b96e-4d12b6217126 + Check box + false + false + + + + label + + null + + + + + + + + +false + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + c22b9906-4d5e-48df-b084-f0ac331de04c + Текст + false + false + + + + cssClasses + + + + "subtitle" + + + + + + initialValue + + "Внимание: если файл подписан не руководителем организации, рекомендуется приложить МЧД" + + + + + + + + +false + + + + d7d54cfb-26b5-4dba-b56f-b6247183c24d + 871b2ab1-fd6a-41fc-97a7-c60052da8272 + Hbox + true + true + + + 5694e7c5-bbb5-4d23-be6c-7ad71b8ad38c + 0f616bc4-9148-4d3e-b374-269a91ed54f2 + Мчд File + false + false + false + + + + collectible + + false + + + + cssClasses + + + + "btn-main" + + + + + + extensionFilter + + + + + "xml" + + + + + + formType + + "FORM_2" + + + + maxFileSizeMb + + 5.0 + + + + maxFilesToUpload + + 1.0 + + + + removeFileButtonName + + "Удалить" + + + + selectFileButtonName + + "Выбрать" + + + + selectFileFieldText + + "Перетащите файл или выберите на компьютере" + + + + visible + + false + + + + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + f6e4a42a-020e-4b2a-b6e7-6f48024fb438 + Текст + false + false + + + + cssClasses + + + + "mute" + + + + + + initialValue + + "Поддерживаемый формат файла - xml" + + + + visible + + false + + + + + + + + +false + + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + 77c7fa81-93a3-49fb-8263-696fd3acda92 + Vbox + true + true + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + c965e7ee-ddce-446c-a8cd-7264f9bbc2fd + Vbox + true + true + + 5ebd2885-0972-4e51-8376-1fa66aed9a90 e4d86cdd-b219-46e8-a59f-3c9b38108de4 @@ -5051,73 +6065,7 @@ ba4ed1be-acc7-456d-aec2-e29d56f05113 FileUploadV2 false - false - - - - collectible - - false - - - - cssClasses - - - - "btn-main" - - - - - - extensionFilter - - - - "csv" - - - - - - formType - - "FORM_2" - - - - maxFileSizeMb - - 5.0 - - - - maxFilesToUpload - - 1.0 - - - - removeFileButtonName - - "Удалить" - - - - selectFileButtonName - - "Выбрать" - - - - selectFileFieldText - - "Перетащите файл или выберите на компьютере" - - - - + true 98594cec-0a9b-4cef-af09-e1b71cb2ad9e @@ -5142,7 +6090,25 @@ behavior - {"objectId":"ba4ed1be-acc7-456d-aec2-e29d56f05113","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"f8c09ec6-d52a-4398-a40e-a237f4305b88","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} + + + + propertyName + + "fileAddedEvent" + + + + + + + + + + behavior + + {"objectId":"a8e76694-830b-4f22-9a5b-bb0efe0d7745","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} @@ -5163,6 +6129,108 @@ conditions + + + + + _isGroupSelected + + false + + + + one + + + + conditionFirstPart + + + + objectValue + + + + behavior + + {"objectId":"f8c09ec6-d52a-4398-a40e-a237f4305b88","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} + + + + method + + "getValue" + + + + + + + + + + operation + + "IS_NOT_EMPTY" + + + + + + + + + + + + + _isGroupSelected + + false + + + + one + + + + conditionFirstPart + + + + objectValue + + + + behavior + + {"objectId":"a8e76694-830b-4f22-9a5b-bb0efe0d7745","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + method + + "getValue" + + + + + + + + + + operation + + "IS_NOT_EMPTY" + + + + + + + + @@ -5170,7 +6238,7 @@ logicalOperation - null + "AND" @@ -5242,7 +6310,25 @@ behavior - {"objectId":"ba4ed1be-acc7-456d-aec2-e29d56f05113","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"f8c09ec6-d52a-4398-a40e-a237f4305b88","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} + + + + propertyName + + "fileDeletedEvent" + + + + + + + + + + behavior + + {"objectId":"a8e76694-830b-4f22-9a5b-bb0efe0d7745","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} @@ -5385,7 +6471,7 @@ behavior - {"objectId":"ba4ed1be-acc7-456d-aec2-e29d56f05113","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"f8c09ec6-d52a-4398-a40e-a237f4305b88","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} @@ -5413,33 +6499,7 @@ 0970e79a-7130-4c23-bdc8-13f8554daa59 Текст false - false - - - - cssClasses - - - - "mute" - - - - - - initialValue - - "Поддерживаемый формат файла - csv" - - - - - - - - - false - + true fd7e47b9-dce1-4d14-9f3a-580c79f59579 @@ -5497,7 +6557,7 @@ behavior - {"objectId":"ba4ed1be-acc7-456d-aec2-e29d56f05113","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"f8c09ec6-d52a-4398-a40e-a237f4305b88","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} @@ -5734,6 +6794,251 @@ false + + + + + + + + + + + + behavior + + {"objectId":"bee4c3f7-05b0-4b21-81bb-48249a1bdfdb","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + + {"objectId":"a8e76694-830b-4f22-9a5b-bb0efe0d7745","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + + {"objectId":"77096468-9f51-4e2c-93a8-97a38d89570e","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + + {"objectId":"2b87f279-a5eb-445f-b96e-4d12b6217126","packageName":"component.field","className":"CheckBox","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + + {"objectId":"c22b9906-4d5e-48df-b084-f0ac331de04c","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + + {"objectId":"0f616bc4-9148-4d3e-b374-269a91ed54f2","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + + {"objectId":"f6e4a42a-020e-4b2a-b6e7-6f48024fb438","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + @@ -5763,7 +7068,7 @@ behavior - {"objectId":"ba4ed1be-acc7-456d-aec2-e29d56f05113","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"f8c09ec6-d52a-4398-a40e-a237f4305b88","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} @@ -5870,7 +7175,7 @@ behavior - {"objectId":"ba4ed1be-acc7-456d-aec2-e29d56f05113","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"f8c09ec6-d52a-4398-a40e-a237f4305b88","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} @@ -5961,6 +7266,13 @@ + + 86f297f1-ab3d-40e0-ac2f-89cc944b7f0a + 89bcd110-0dc0-4aaa-9a4f-172858b2ce03 + Диалог - выбор файла и отправка + true + true + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 b50c6499-5c81-4fc3-b9ce-02cc1124e673 From 120f851d195d7857acb22d74669a9be38c687eb0 Mon Sep 17 00:00:00 2001 From: Eduard Tihomiorv Date: Wed, 15 Oct 2025 15:16:54 +0300 Subject: [PATCH 25/45] form 10 --- .../Личный кабинет юр лица.page | 1595 +++++++++++++++-- 1 file changed, 1465 insertions(+), 130 deletions(-) diff --git a/resources/src/main/resources/business-model/Личный кабинет юр лица.page b/resources/src/main/resources/business-model/Личный кабинет юр лица.page index 1aa31c0a..1fc0d641 100644 --- a/resources/src/main/resources/business-model/Личный кабинет юр лица.page +++ b/resources/src/main/resources/business-model/Личный кабинет юр лица.page @@ -221,6 +221,7 @@ 3e78f422-3db3-45b9-b531-f4aec5314dab Группа полей true + false false @@ -4044,6 +4045,7 @@ be4863fe-d25f-4965-ae52-8552ab5e8cf9 Hbox_2 true + false false @@ -4227,6 +4229,7 @@ 52b1c7dd-7763-4f50-84ed-0471a83dd0b4 Hbox_2_ true + false false @@ -4396,6 +4399,7 @@ dd3fd6ce-3f62-436c-b641-522042f189ff Hbox true + false false @@ -7479,6 +7483,7 @@ 47a51ceb-a4db-4caf-a234-4389e2456f66 Hbox_10 true + false false @@ -7655,6 +7660,7 @@ fb8317a8-f9ed-4da3-8018-be3d8efbc469 Hbox_10_ true + false false @@ -7798,6 +7804,13 @@ + + 86f297f1-ab3d-40e0-ac2f-89cc944b7f0a + b59b46f5-0b45-4a0e-99d9-1eed7060be97 + Диалог - выбор файла и отправка НОВЫЙ + true + true + e32ae1f5-5b14-45f1-abb6-f52c34b3b570 aa0a8f53-5fdb-4c6c-b364-008e09088c3a @@ -7822,7 +7835,7 @@ 86f297f1-ab3d-40e0-ac2f-89cc944b7f0a 89fcffd4-3cf5-4c26-9b49-f8a366aa6f27 - Диалог - выбор файла и отправка + Диалог - выбор файла и отправка СТАРЫЙ true false @@ -7849,6 +7862,12 @@ false + + elseActions + + + + eventRefs @@ -8023,7 +8042,7 @@ behavior - {"objectId":"623c8ded-d64e-40b4-b1bb-2db70083ae4f","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"3d0d0314-ea5f-4974-a92d-32c11ce90861","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} @@ -8058,7 +8077,7 @@ behavior - {"objectId":"623c8ded-d64e-40b4-b1bb-2db70083ae4f","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"3d0d0314-ea5f-4974-a92d-32c11ce90861","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} @@ -8139,6 +8158,184 @@ false + + + + + + + + + + + + + + + behavior + + {"objectId":"9b35b57e-d173-4f38-a1b3-7caf32b601f4","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + true + + + + + + + + + + + + + behavior + + {"objectId":"b7b6470e-64df-4930-9522-c8a5f01ab493","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + true + + + + + + + + + + + + + behavior + + {"objectId":"c3389547-2857-4464-8d11-87646677e390","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + true + + + + + + + + + + + + + behavior + + {"objectId":"f8953a9f-71f4-4095-873c-f924960167bb","packageName":"component.field","className":"CheckBox","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + true + + + + + + + + + + + + + behavior + + {"objectId":"95fb07cd-5071-412e-8c70-19fbbcf1f70f","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + true + @@ -8303,38 +8500,7 @@ faef9178-0311-4a71-957b-54bcd5221517 Текст false - false - - - - cssClasses - - - - "subtitle" - - - - - - - - - - - initialValue - - "Сообщение о гражданах, не состоящих, но обязанных состоять на воинском учете" - - - - - - - - - false - + true 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 @@ -8437,73 +8603,876 @@ 623c8ded-d64e-40b4-b1bb-2db70083ae4f FileUploadV2 false + true + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + ce89da7c-85a0-4670-870b-f6ad52d65591 + Вертикальный контейнер + true false - - - - collectible - - false - - - - cssClasses - - + + + + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + faef9178-0311-4a71-957b-54bcd5221517 + Текст + false + false + + + + cssClasses + + + + "subtitle" + + + + + + + + + + + initialValue + + "Сообщение о гражданах, не состоящих, но обязанных состоять на воинском учете" + + + + + + + + +false + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + b93c1470-2577-4be2-ba77-4081940486ce + Текст + false + true + + + 5694e7c5-bbb5-4d23-be6c-7ad71b8ad38c + 3d0d0314-ea5f-4974-a92d-32c11ce90861 + Csv file + false + false + false + +false +true + + + + ErvuFileUploadWithAdditionalFiles + ervu.component.fileupload + +true +true + + + collectible + + false + + + + cssClasses + + + + "btn-main" + + + + + + displayFileSize + + true + + + + displayProgressBar + + true + + + + extensionFilter + + + + "csv" + + + + + + + formType + + "FORM_10" + + + + maxFileSizeMb + + 5.0 + + + + maxFilesToUpload + + 1.0 + + + + mchdFileUploadRef + + {"objectId":"51b39385-98cf-4890-8a14-96e371027d05","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + removeFileButtonName + + "Удалить" + + + + selectFileButtonName + + "Выбрать" + + + + selectFileFieldText + + "Перетащите файл или выберите на компьютере" + + + + signFileUploadRef + + {"objectId":"b7b6470e-64df-4930-9522-c8a5f01ab493","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + visible + + true + + + + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + 87df2042-bf8e-4182-9abc-34a7b2ec8165 + Текст + false + false + + + + cssClasses + + + + "mute" + + + + + + initialValue + + "Поддерживаемый формат файла - csv" + + + + + + + + +false + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + 1bb8f548-1b8e-4bda-b265-0ed6fce660e8 + Текст + false + true + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + 276d7e73-eba9-4859-942f-af19f7ff535f + Вертикальный контейнер + true + true + + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + 276d7e73-eba9-4859-942f-af19f7ff535f + Вертикальный контейнер + true + false + + + + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + 9b35b57e-d173-4f38-a1b3-7caf32b601f4 + Текст + false + false + + + + cssClasses + + + + "subtitle" + + + + + + + + + + + initialValue + + "Ваша открепленная усиленная квалифицированная электронная подпись в формате sig" + + + + + + + + +false + + + + 5694e7c5-bbb5-4d23-be6c-7ad71b8ad38c + b7b6470e-64df-4930-9522-c8a5f01ab493 + Подпись File + false + false + + + + collectible + + false + + + + cssClasses + + + + "btn-main" + + + + + + extensionFilter + + + + + "sig" + + + + + + formType + + "FORM_10" + + + + maxFileSizeMb + + 5.0 + + + + maxFilesToUpload + + 1.0 + + + + removeFileButtonName + + "Удалить" + + + + selectFileButtonName + + "Выбрать" + + + + selectFileFieldText + + "Перетащите файл или выберите на компьютере" + + + + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + c3389547-2857-4464-8d11-87646677e390 + Текст + false + false + + + + cssClasses + + + + "mute" + + + + + + initialValue + + "Поддерживаемый формат файла - sig" + + + + + + + + +false + + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + fe37be88-aca5-4191-b5e1-a914a8603965 + Вертикальный контейнер + true + true + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + bdf5ede5-8183-4e07-9f5b-a49dc03978bd + Вертикальный контейнер + true + true + + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + 137c8ee8-cee3-474c-8883-733aaa7ed95e + Вертикальный контейнер + true + false + + + + + + + 98594cec-0a9b-4cef-af09-e1b71cb2ad9e + 6485951d-c7ca-4df8-835f-6d326215fb99 + AC_МЧД + false + false + + + + elseActions + + + + + + behavior + +{"objectId":"51b39385-98cf-4890-8a14-96e371027d05","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + method + +"setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + +{"objectId":"71bc3168-e362-4cd1-8c0f-2df719d8f3fe","packageName":"component","className":"Text","type":"TS"} + + + + method + +"setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + eventRefs + + + + + + behavior + +{"objectId":"f8953a9f-71f4-4095-873c-f924960167bb","packageName":"component.field","className":"CheckBox","type":"TS"} + + + + propertyName + +"userChangeValueEvent" + + + + + + + + + ifCondition + + + + conditions + + + + + + _isGroupSelected + + false + + + + one + + + + conditionFirstPart + + + +objectValue + + + + behavior - "btn-main" + {"objectId":"f8953a9f-71f4-4095-873c-f924960167bb","packageName":"component.field","className":"CheckBox","type":"TS"} - - - - - extensionFilter - - + + + method - "csv" + "getValue" - - - - - formType - - "FORM_10" - - - - maxFileSizeMb - - 5.0 - - - - maxFilesToUpload - - 1.0 - - - - removeFileButtonName - - "Удалить" - - - - selectFileButtonName - - "Выбрать" - - - - selectFileFieldText - - "Перетащите файл или выберите на компьютере" - - - - + + + + + + + + + conditionSecondPart + + + +staticValue + + + boolean + + + true + + + + + + + operation + + "EQUALS" + + + + + + + + + + + + logicalOperation + + null + + + + + + + thenActions + + + + + + behavior + +{"objectId":"51b39385-98cf-4890-8a14-96e371027d05","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + method + +"setVisible" + + + + value + + + + staticValue + + + boolean + + + true + + + + + + + + + + + + + behavior + +{"objectId":"71bc3168-e362-4cd1-8c0f-2df719d8f3fe","packageName":"component","className":"Text","type":"TS"} + + + + method + +"setVisible" + + + + value + + + + staticValue + + + boolean + + + true + + + + + + + + + + + + + + + 8b755f7b-e52b-4800-830a-f01467cd5cbb + f8953a9f-71f4-4095-873c-f924960167bb + Check box + false + false + + + + label + + null + + + + + + + + +false + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + 95fb07cd-5071-412e-8c70-19fbbcf1f70f + Текст + false + false + + + + cssClasses + + + + "subtitle" + + + + + + initialValue + + "Внимание: если файл подписан не руководителем организации, рекомендуется приложить МЧД" + + + + + + + + +false + + + + d7d54cfb-26b5-4dba-b56f-b6247183c24d + adbccde8-fa29-4c5f-961e-a042a90d5d12 + Hbox + true + true + + + 5694e7c5-bbb5-4d23-be6c-7ad71b8ad38c + 51b39385-98cf-4890-8a14-96e371027d05 + Мчд File + false + false + false + + + + collectible + + false + + + + cssClasses + + + + "btn-main" + + + + + + extensionFilter + + + + + "xml" + + + + + + formType + + "FORM_10" + + + + maxFileSizeMb + + 5.0 + + + + maxFilesToUpload + + 1.0 + + + + removeFileButtonName + + "Удалить" + + + + selectFileButtonName + + "Выбрать" + + + + selectFileFieldText + + "Перетащите файл или выберите на компьютере" + + + + visible + + false + + + + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + 71bc3168-e362-4cd1-8c0f-2df719d8f3fe + Текст + false + false + + + + cssClasses + + + + "mute" + + + + + + initialValue + + "Поддерживаемый формат файла - xml" + + + + visible + + false + + + + + + + + +false + + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + e6ff2b0f-a6a1-4ab2-aefe-6ba208808eb3 + Vbox + true + true + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + 56a78b9f-9f5f-4d7c-85a4-e71860886d5a + Vbox + true + true + 98594cec-0a9b-4cef-af09-e1b71cb2ad9e @@ -8528,7 +9497,25 @@ behavior - {"objectId":"623c8ded-d64e-40b4-b1bb-2db70083ae4f","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"3d0d0314-ea5f-4974-a92d-32c11ce90861","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} + + + + propertyName + + "fileAddedEvent" + + + + + + + + + + behavior + + {"objectId":"b7b6470e-64df-4930-9522-c8a5f01ab493","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} @@ -8549,6 +9536,108 @@ conditions + + + + + _isGroupSelected + + false + + + + one + + + + conditionFirstPart + + + + objectValue + + + + behavior + + {"objectId":"3d0d0314-ea5f-4974-a92d-32c11ce90861","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} + + + + method + + "getValue" + + + + + + + + + + operation + + "IS_NOT_EMPTY" + + + + + + + + + + + + + _isGroupSelected + + false + + + + one + + + + conditionFirstPart + + + + objectValue + + + + behavior + + {"objectId":"b7b6470e-64df-4930-9522-c8a5f01ab493","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + method + + "getValue" + + + + + + + + + + operation + + "IS_NOT_EMPTY" + + + + + + + + @@ -8628,7 +9717,25 @@ behavior - {"objectId":"623c8ded-d64e-40b4-b1bb-2db70083ae4f","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"3d0d0314-ea5f-4974-a92d-32c11ce90861","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} + + + + propertyName + + "fileDeletedEvent" + + + + + + + + + + behavior + + {"objectId":"b7b6470e-64df-4930-9522-c8a5f01ab493","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} @@ -8771,7 +9878,7 @@ behavior - {"objectId":"623c8ded-d64e-40b4-b1bb-2db70083ae4f","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"3d0d0314-ea5f-4974-a92d-32c11ce90861","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} @@ -8799,33 +9906,7 @@ 87df2042-bf8e-4182-9abc-34a7b2ec8165 Текст false - false - - - - cssClasses - - - - "mute" - - - - - - initialValue - - "Поддерживаемый формат файла - csv" - - - - - - - - - false - + true fd7e47b9-dce1-4d14-9f3a-580c79f59579 @@ -8883,7 +9964,7 @@ behavior - {"objectId":"623c8ded-d64e-40b4-b1bb-2db70083ae4f","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"3d0d0314-ea5f-4974-a92d-32c11ce90861","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} @@ -9120,6 +10201,251 @@ false + + + + + + + + + + + + behavior + + {"objectId":"9b35b57e-d173-4f38-a1b3-7caf32b601f4","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + + {"objectId":"b7b6470e-64df-4930-9522-c8a5f01ab493","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + + {"objectId":"c3389547-2857-4464-8d11-87646677e390","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + + {"objectId":"f8953a9f-71f4-4095-873c-f924960167bb","packageName":"component.field","className":"CheckBox","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + + {"objectId":"95fb07cd-5071-412e-8c70-19fbbcf1f70f","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + + {"objectId":"51b39385-98cf-4890-8a14-96e371027d05","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + + {"objectId":"71bc3168-e362-4cd1-8c0f-2df719d8f3fe","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + @@ -9149,7 +10475,7 @@ behavior - {"objectId":"623c8ded-d64e-40b4-b1bb-2db70083ae4f","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"3d0d0314-ea5f-4974-a92d-32c11ce90861","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} @@ -9256,7 +10582,7 @@ behavior - {"objectId":"623c8ded-d64e-40b4-b1bb-2db70083ae4f","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"3d0d0314-ea5f-4974-a92d-32c11ce90861","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} @@ -9346,6 +10672,13 @@ + + 86f297f1-ab3d-40e0-ac2f-89cc944b7f0a + b59b46f5-0b45-4a0e-99d9-1eed7060be97 + Диалог - выбор файла и отправка + true + true + d7d54cfb-26b5-4dba-b56f-b6247183c24d @@ -9413,6 +10746,7 @@ 7057bbcb-cff2-4e31-812d-ba7e043a4bcc Группа полей true + false false @@ -11538,6 +12872,7 @@ 991237d3-8cb9-48af-8501-030a3c8c6cfc Группа полей true + false false From 3fe3ef6921a8ce75383bde5068508949b6dd83a2 Mon Sep 17 00:00:00 2001 From: Eduard Tihomiorv Date: Wed, 15 Oct 2025 15:40:21 +0300 Subject: [PATCH 26/45] form 11 --- .../Личный кабинет юр лица.page | 1554 +++++++++++++++-- 1 file changed, 1423 insertions(+), 131 deletions(-) diff --git a/resources/src/main/resources/business-model/Личный кабинет юр лица.page b/resources/src/main/resources/business-model/Личный кабинет юр лица.page index 1fc0d641..3fc09859 100644 --- a/resources/src/main/resources/business-model/Личный кабинет юр лица.page +++ b/resources/src/main/resources/business-model/Личный кабинет юр лица.page @@ -7290,6 +7290,7 @@ ae731885-3bdd-433d-a29c-37d5811585a7 Группа полей true + false false @@ -7835,7 +7836,7 @@ 86f297f1-ab3d-40e0-ac2f-89cc944b7f0a 89fcffd4-3cf5-4c26-9b49-f8a366aa6f27 - Диалог - выбор файла и отправка СТАРЫЙ + Диалог - выбор файла и отправка true false @@ -10746,7 +10747,6 @@ 7057bbcb-cff2-4e31-812d-ba7e043a4bcc Группа полей true - false false @@ -10940,6 +10940,7 @@ 87d2523e-91a0-49ef-9f87-2adc4ff1165a Hbox_11 true + false false @@ -11116,6 +11117,7 @@ a072a677-36d0-4616-a0bd-3541ebae45c8 Hbox_11_ true + false false @@ -11535,7 +11537,7 @@ behavior - {"objectId":"9c49eea1-7d04-4d99-b213-59aa51499acc","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"d202441d-c2ae-4e34-970e-78166e8c4ea9","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} @@ -11570,7 +11572,7 @@ behavior - {"objectId":"9c49eea1-7d04-4d99-b213-59aa51499acc","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"d202441d-c2ae-4e34-970e-78166e8c4ea9","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} @@ -11651,6 +11653,181 @@ false + + + + + + + + + + + + behavior + + {"objectId":"e6baac95-674d-4833-8eaa-142ed5ed0f02","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + true + + + + + + + + + + + + + behavior + + {"objectId":"f4ef5c50-9c44-4165-bcdf-5b6e7f4aaab1","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + true + + + + + + + + + + + + + behavior + + {"objectId":"52a47dd7-0448-4934-aba0-72c95b93b091","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + true + + + + + + + + + + + + + behavior + + {"objectId":"17c7b00e-9ea7-47e8-a841-3c49fc24123b","packageName":"component.field","className":"CheckBox","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + true + + + + + + + + + + + + + behavior + + {"objectId":"780dba1a-b9a9-4a98-9bf0-40dc91577fb7","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + true + @@ -11815,38 +11992,7 @@ e901dbfc-6ae7-4e81-8702-3a99005376f2 Текст false - false - - - - cssClasses - - - - "subtitle" - - - - - - - - - - - initialValue - - "Ежегодное предоставление списка граждан мужского пола, подлежащих постановке на воинский учет в следующем году по достижении 17 - летнего возраста" - - - - - - - - - false - + true 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 @@ -11949,73 +12095,862 @@ 9c49eea1-7d04-4d99-b213-59aa51499acc FileUploadV2 false + true + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + 36ed9134-7e97-45ea-8215-261f65e69077 + Вертикальный контейнер + true false - - - - collectible - - false - - - - cssClasses - - + + + + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + e901dbfc-6ae7-4e81-8702-3a99005376f2 + Текст + false + false + + + + cssClasses + + + + "subtitle" + + + + + + + + + + + initialValue + + "Ежегодное предоставление списка граждан мужского пола, подлежащих постановке на воинский учет в следующем году по достижении 17 - летнего возраста" + + + + + + + + +false + + + + 5694e7c5-bbb5-4d23-be6c-7ad71b8ad38c + d202441d-c2ae-4e34-970e-78166e8c4ea9 + Csv file + false + false + false + +false +true + + + + ErvuFileUploadWithAdditionalFiles + ervu.component.fileupload + +true +true + + + collectible + + false + + + + cssClasses + + + + "btn-main" + + + + + + displayFileSize + + true + + + + displayProgressBar + + true + + + + extensionFilter + + + + "csv" + + + + + + + formType + + "FORM_11" + + + + maxFileSizeMb + + 5.0 + + + + maxFilesToUpload + + 1.0 + + + + mchdFileUploadRef + + {"objectId":"f47e3ad9-6065-41a7-862d-991c41f1bdfa","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + removeFileButtonName + + "Удалить" + + + + selectFileButtonName + + "Выбрать" + + + + selectFileFieldText + + "Перетащите файл или выберите на компьютере" + + + + signFileUploadRef + + {"objectId":"f4ef5c50-9c44-4165-bcdf-5b6e7f4aaab1","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + visible + + true + + + + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + d4f96505-0b2a-4e7b-87c6-e79f1987da66 + Текст + false + false + + + + cssClasses + + + + "mute" + + + + + + initialValue + + "Поддерживаемый формат файла - csv" + + + + + + + + +false + + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + 4c2623d7-445f-4d4c-9c1a-98e033e206fb + Вертикальный контейнер + true + true + + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + 40e1b3d6-57ff-4ef7-b93f-72e0c2661b70 + Вертикальный контейнер + true + false + + + + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + e6baac95-674d-4833-8eaa-142ed5ed0f02 + Текст + false + false + + + + cssClasses + + + + "subtitle" + + + + + + + + + + + initialValue + + "Ваша открепленная усиленная квалифицированная электронная подпись в формате sig" + + + + + + + + +false + + + + 5694e7c5-bbb5-4d23-be6c-7ad71b8ad38c + f4ef5c50-9c44-4165-bcdf-5b6e7f4aaab1 + Подпись File + false + false + + + + collectible + + false + + + + cssClasses + + + + "btn-main" + + + + + + extensionFilter + + + + + "sig" + + + + + + formType + + "FORM_11" + + + + maxFileSizeMb + + 5.0 + + + + maxFilesToUpload + + 1.0 + + + + removeFileButtonName + + "Удалить" + + + + selectFileButtonName + + "Выбрать" + + + + selectFileFieldText + + "Перетащите файл или выберите на компьютере" + + + + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + 52a47dd7-0448-4934-aba0-72c95b93b091 + Текст + false + false + + + + cssClasses + + + + "mute" + + + + + + initialValue + + "Поддерживаемый формат файла - sig" + + + + + + + + +false + + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + 4b296222-446c-45d5-8a74-019f505c9e7e + Вертикальный контейнер + true + true + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + 77cd6896-3da8-47f7-8ce3-c08a5e42b959 + Вертикальный контейнер + true + true + + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + cae94ea0-75e1-408a-8ad7-34a07b52a381 + Вертикальный контейнер + true + false + + + + + + + 98594cec-0a9b-4cef-af09-e1b71cb2ad9e + 3f217825-2344-4196-bd31-cf0b27c887a4 + AC_МЧД + false + false + + + + elseActions + + + + + + behavior + +{"objectId":"f47e3ad9-6065-41a7-862d-991c41f1bdfa","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + method + +"setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + +{"objectId":"97a5367f-cc24-4d04-87c5-efaa5d9b7d0b","packageName":"component","className":"Text","type":"TS"} + + + + method + +"setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + eventRefs + + + + + + behavior + +{"objectId":"17c7b00e-9ea7-47e8-a841-3c49fc24123b","packageName":"component.field","className":"CheckBox","type":"TS"} + + + + propertyName + +"userChangeValueEvent" + + + + + + + + + ifCondition + + + + conditions + + + + + + _isGroupSelected + + false + + + + one + + + + conditionFirstPart + + + +objectValue + + + + behavior - "btn-main" + {"objectId":"17c7b00e-9ea7-47e8-a841-3c49fc24123b","packageName":"component.field","className":"CheckBox","type":"TS"} - - - - - extensionFilter - - + + + method - "csv" + "getValue" - - - - - formType - - "FORM_11" - - - - maxFileSizeMb - - 5.0 - - - - maxFilesToUpload - - 1.0 - - - - removeFileButtonName - - "Удалить" - - - - selectFileButtonName - - "Выбрать" - - - - selectFileFieldText - - "Перетащите файл или выберите на компьютере" - - - - + + + + + + + + + conditionSecondPart + + + +staticValue + + + boolean + + + true + + + + + + + operation + + "EQUALS" + + + + + + + + + + + + logicalOperation + + null + + + + + + + thenActions + + + + + + behavior + +{"objectId":"f47e3ad9-6065-41a7-862d-991c41f1bdfa","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + method + +"setVisible" + + + + value + + + + staticValue + + + boolean + + + true + + + + + + + + + + + + + behavior + +{"objectId":"97a5367f-cc24-4d04-87c5-efaa5d9b7d0b","packageName":"component","className":"Text","type":"TS"} + + + + method + +"setVisible" + + + + value + + + + staticValue + + + boolean + + + true + + + + + + + + + + + + + + + 8b755f7b-e52b-4800-830a-f01467cd5cbb + 17c7b00e-9ea7-47e8-a841-3c49fc24123b + Check box + false + false + + + + label + + null + + + + + + + + +false + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + 780dba1a-b9a9-4a98-9bf0-40dc91577fb7 + Текст + false + false + + + + cssClasses + + + + "subtitle" + + + + + + initialValue + + "Внимание: если файл подписан не руководителем организации, рекомендуется приложить МЧД" + + + + + + + + +false + + + + d7d54cfb-26b5-4dba-b56f-b6247183c24d + a29b15a1-79b1-4dfa-ac78-223742ac2698 + Hbox + true + true + + + 5694e7c5-bbb5-4d23-be6c-7ad71b8ad38c + f47e3ad9-6065-41a7-862d-991c41f1bdfa + Мчд File + false + false + false + + + + collectible + + false + + + + cssClasses + + + + "btn-main" + + + + + + extensionFilter + + + + + "xml" + + + + + + formType + + "FORM_11" + + + + maxFileSizeMb + + 5.0 + + + + maxFilesToUpload + + 1.0 + + + + removeFileButtonName + + "Удалить" + + + + selectFileButtonName + + "Выбрать" + + + + selectFileFieldText + + "Перетащите файл или выберите на компьютере" + + + + visible + + false + + + + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + 97a5367f-cc24-4d04-87c5-efaa5d9b7d0b + Текст + false + false + + + + cssClasses + + + + "mute" + + + + + + initialValue + + "Поддерживаемый формат файла - xml" + + + + visible + + false + + + + + + + + +false + + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + dc182bde-6e42-4ddf-b83d-a5ca05a99cbd + Vbox + true + true + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + be915f17-da79-45f3-afe3-0fa6cac060f4 + Vbox + true + true + 98594cec-0a9b-4cef-af09-e1b71cb2ad9e @@ -12040,7 +12975,25 @@ behavior - {"objectId":"9c49eea1-7d04-4d99-b213-59aa51499acc","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"d202441d-c2ae-4e34-970e-78166e8c4ea9","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} + + + + propertyName + + "fileAddedEvent" + + + + + + + + + + behavior + + {"objectId":"f4ef5c50-9c44-4165-bcdf-5b6e7f4aaab1","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} @@ -12061,6 +13014,108 @@ conditions + + + + + _isGroupSelected + + false + + + + one + + + + conditionFirstPart + + + + objectValue + + + + behavior + + {"objectId":"d202441d-c2ae-4e34-970e-78166e8c4ea9","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} + + + + method + + "getValue" + + + + + + + + + + operation + + "IS_NOT_EMPTY" + + + + + + + + + + + + + _isGroupSelected + + false + + + + one + + + + conditionFirstPart + + + + objectValue + + + + behavior + + {"objectId":"f4ef5c50-9c44-4165-bcdf-5b6e7f4aaab1","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + method + + "getValue" + + + + + + + + + + operation + + "IS_NOT_EMPTY" + + + + + + + + @@ -12140,7 +13195,25 @@ behavior - {"objectId":"9c49eea1-7d04-4d99-b213-59aa51499acc","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"d202441d-c2ae-4e34-970e-78166e8c4ea9","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} + + + + propertyName + + "fileDeletedEvent" + + + + + + + + + + behavior + + {"objectId":"f4ef5c50-9c44-4165-bcdf-5b6e7f4aaab1","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} @@ -12283,7 +13356,7 @@ behavior - {"objectId":"9c49eea1-7d04-4d99-b213-59aa51499acc","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"d202441d-c2ae-4e34-970e-78166e8c4ea9","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} @@ -12311,33 +13384,7 @@ d4f96505-0b2a-4e7b-87c6-e79f1987da66 Текст false - false - - - - cssClasses - - - - "mute" - - - - - - initialValue - - "Поддерживаемый формат файла - csv" - - - - - - - - - false - + true fd7e47b9-dce1-4d14-9f3a-580c79f59579 @@ -12395,7 +13442,7 @@ behavior - {"objectId":"9c49eea1-7d04-4d99-b213-59aa51499acc","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"d202441d-c2ae-4e34-970e-78166e8c4ea9","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} @@ -12632,6 +13679,251 @@ false + + + + + + + + + + + + behavior + + {"objectId":"e6baac95-674d-4833-8eaa-142ed5ed0f02","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + + {"objectId":"f4ef5c50-9c44-4165-bcdf-5b6e7f4aaab1","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + + {"objectId":"52a47dd7-0448-4934-aba0-72c95b93b091","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + + {"objectId":"17c7b00e-9ea7-47e8-a841-3c49fc24123b","packageName":"component.field","className":"CheckBox","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + + {"objectId":"780dba1a-b9a9-4a98-9bf0-40dc91577fb7","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + + {"objectId":"f47e3ad9-6065-41a7-862d-991c41f1bdfa","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + + {"objectId":"97a5367f-cc24-4d04-87c5-efaa5d9b7d0b","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + @@ -12661,7 +13953,7 @@ behavior - {"objectId":"9c49eea1-7d04-4d99-b213-59aa51499acc","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"d202441d-c2ae-4e34-970e-78166e8c4ea9","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} @@ -12768,7 +14060,7 @@ behavior - {"objectId":"9c49eea1-7d04-4d99-b213-59aa51499acc","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"d202441d-c2ae-4e34-970e-78166e8c4ea9","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} From 64cc5ff7a91b8c92ccb26e3c9fc86ae92d91a79a Mon Sep 17 00:00:00 2001 From: "adel.ka" Date: Wed, 15 Oct 2025 16:14:52 +0300 Subject: [PATCH 27/45] =?UTF-8?q?=D0=94=D0=BB=D1=8F=20=D0=BA=D0=BE=D0=BC?= =?UTF-8?q?=D0=B0=D0=BD=D0=B4=D1=8B=20=D0=A1=D0=BE=D0=BB=D0=BE=D0=B2=D1=8C?= =?UTF-8?q?=D0=B5=D0=B2=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ru/micord/ervu/journal/JournalDto.java | 30 ++++-- .../micord/ervu/journal/JournalFileInfo.java | 10 ++ .../ervu/journal/mapper/JournalDtoMapper.java | 10 +- .../ervu/service/InteractionServiceImpl.java | 2 - .../business-model/Журнал взаимодействия.page | 92 ++++++++++++++++++- 5 files changed, 126 insertions(+), 18 deletions(-) diff --git a/backend/src/main/java/ru/micord/ervu/journal/JournalDto.java b/backend/src/main/java/ru/micord/ervu/journal/JournalDto.java index 6adc2e2f..c9caf0c6 100644 --- a/backend/src/main/java/ru/micord/ervu/journal/JournalDto.java +++ b/backend/src/main/java/ru/micord/ervu/journal/JournalDto.java @@ -8,8 +8,9 @@ public class JournalDto { private Integer filePatternCode; private String senderFio; private String status; - public Integer filesSentCount; - public Integer acceptedFilesCount; + public Integer rowsCount; + public Integer rowsSuccess; + public Integer rowsError; public String getDepartureDateTime() { return departureDateTime; @@ -65,21 +66,30 @@ public class JournalDto { return this; } - public Integer getFilesSentCount() { - return filesSentCount; + public Integer getRowsCount() { + return rowsCount; } - public JournalDto setFilesSentCount(Integer filesSentCount) { - this.filesSentCount = filesSentCount; + public JournalDto setRowsCount(Integer rowsCount) { + this.rowsCount = rowsCount; return this; } - public Integer getAcceptedFilesCount() { - return acceptedFilesCount; + public Integer getRowsSuccess() { + return rowsSuccess; } - public JournalDto setAcceptedFilesCount(Integer acceptedFilesCount) { - this.acceptedFilesCount = acceptedFilesCount; + public JournalDto setRowsSuccess(Integer rowsSuccess) { + this.rowsSuccess = rowsSuccess; + return this; + } + + public Integer getRowsError() { + return rowsError; + } + + public JournalDto setRowsError(Integer rowsError) { + this.rowsError = rowsError; return this; } } diff --git a/backend/src/main/java/ru/micord/ervu/journal/JournalFileInfo.java b/backend/src/main/java/ru/micord/ervu/journal/JournalFileInfo.java index 6dc27a6e..7ffb5251 100644 --- a/backend/src/main/java/ru/micord/ervu/journal/JournalFileInfo.java +++ b/backend/src/main/java/ru/micord/ervu/journal/JournalFileInfo.java @@ -14,6 +14,7 @@ public class JournalFileInfo { private SenderInfo senderInfo; private Integer rowsCount; //Общее количество записей отправленных в файле private Integer rowsSuccess; //Количество записей принятых в файле + private Integer rowsError; //Количество записей непринятых в файле public List getPackFiles() { return packFiles; @@ -51,6 +52,15 @@ public class JournalFileInfo { return this; } + public Integer getRowsError() { + return rowsError; + } + + public JournalFileInfo setRowsError(Integer rowsError) { + this.rowsError = rowsError; + return this; + } + @JsonIgnoreProperties(ignoreUnknown = true) public static class JournalFileDetails { private String fileId; //ИД файла полученный при создании записи о файле в реестр организаций (в ЕРВУ) diff --git a/backend/src/main/java/ru/micord/ervu/journal/mapper/JournalDtoMapper.java b/backend/src/main/java/ru/micord/ervu/journal/mapper/JournalDtoMapper.java index cbc144d9..332e1e28 100644 --- a/backend/src/main/java/ru/micord/ervu/journal/mapper/JournalDtoMapper.java +++ b/backend/src/main/java/ru/micord/ervu/journal/mapper/JournalDtoMapper.java @@ -24,8 +24,9 @@ public class JournalDtoMapper { ) ) .setStatus(journalFileDetails.getFileStatus().getStatus()) - .setFilesSentCount(journalFileInfo.getRowsCount()) - .setAcceptedFilesCount(journalFileInfo.getRowsSuccess()); + .setRowsCount(journalFileInfo.getRowsCount()) + .setRowsSuccess(journalFileInfo.getRowsSuccess()) + .setRowsError(journalFileInfo.getRowsError()); } public static JournalDto mapToJournalDto(InteractionLogRecord record) { @@ -35,8 +36,9 @@ public class JournalDtoMapper { .setFilePatternCode(Integer.valueOf(record.getForm())) .setSenderFio(record.getSender()) .setStatus(record.getStatus()) - .setFilesSentCount(record.getRecordsSent()) - .setAcceptedFilesCount(record.getRecordsAccepted()) + .setRowsCount(0) + .setRowsSuccess(0) + .setRowsError(0) .setFileId(record.getFileId()); } } diff --git a/backend/src/main/java/ru/micord/ervu/service/InteractionServiceImpl.java b/backend/src/main/java/ru/micord/ervu/service/InteractionServiceImpl.java index c5688d16..b02c9e2c 100644 --- a/backend/src/main/java/ru/micord/ervu/service/InteractionServiceImpl.java +++ b/backend/src/main/java/ru/micord/ervu/service/InteractionServiceImpl.java @@ -41,8 +41,6 @@ public class InteractionServiceImpl implements InteractionService { .set(INTERACTION_LOG.SENT_DATE, timestamp) .set(INTERACTION_LOG.SENDER, sender) .set(INTERACTION_LOG.FILE_NAME, fileName) - .set(INTERACTION_LOG.RECORDS_SENT, 0) - .set(INTERACTION_LOG.RECORDS_ACCEPTED, 0) .set(INTERACTION_LOG.ERVU_ID, ervuId) .execute(); } diff --git a/resources/src/main/resources/business-model/Журнал взаимодействия.page b/resources/src/main/resources/business-model/Журнал взаимодействия.page index 714d98cf..aef15eb6 100644 --- a/resources/src/main/resources/business-model/Журнал взаимодействия.page +++ b/resources/src/main/resources/business-model/Журнал взаимодействия.page @@ -1489,7 +1489,7 @@ column - "filesSentCount" + "rowsCount" @@ -1577,7 +1577,95 @@ column - "acceptedFilesCount" + "rowsSuccess" + + + + filterType + + "NUMBER" + + + + type + + "java.lang.Number" + + + + + + + filter + +true + + + + sortable + +true + + + + width + +80 + + + + widthFixed + +null + + + + + + + c556264f-221b-4af8-9e64-f380a67c41ec + bc82586d-42e3-4155-b215-a1a8f455c0d7 + Записей непринято + false + false + + + + floatingFilter + +false + + + + + + + + autoHeight + +true + + + + displayName + +"Записей не принято" + + + + displayPopup + +true + + + + field + + + + column + + "rowsError" From 33c0a425175eb4e3c2a7ae1e1ff7f0d524535e7e Mon Sep 17 00:00:00 2001 From: "adel.ka" Date: Wed, 15 Oct 2025 16:15:55 +0300 Subject: [PATCH 28/45] =?UTF-8?q?=D0=94=D0=BB=D1=8F=20=D0=BA=D0=BE=D0=BC?= =?UTF-8?q?=D0=B0=D0=BD=D0=B4=D1=8B=20=D0=A1=D0=BE=D0=BB=D0=BE=D0=B2=D1=8C?= =?UTF-8?q?=D0=B5=D0=B2=D0=B0=20fix?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/ru/micord/ervu/journal/JournalDto.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/src/main/java/ru/micord/ervu/journal/JournalDto.java b/backend/src/main/java/ru/micord/ervu/journal/JournalDto.java index c9caf0c6..69b15f19 100644 --- a/backend/src/main/java/ru/micord/ervu/journal/JournalDto.java +++ b/backend/src/main/java/ru/micord/ervu/journal/JournalDto.java @@ -8,9 +8,9 @@ public class JournalDto { private Integer filePatternCode; private String senderFio; private String status; - public Integer rowsCount; - public Integer rowsSuccess; - public Integer rowsError; + private Integer rowsCount; + private Integer rowsSuccess; + private Integer rowsError; public String getDepartureDateTime() { return departureDateTime; From 20bee06813200c2f7d0ef6b5373ef51f75dd68f2 Mon Sep 17 00:00:00 2001 From: Eduard Tihomiorv Date: Wed, 15 Oct 2025 16:29:36 +0300 Subject: [PATCH 29/45] form 12 --- .../Личный кабинет юр лица.page | 1574 +++++++++++++++-- 1 file changed, 1443 insertions(+), 131 deletions(-) diff --git a/resources/src/main/resources/business-model/Личный кабинет юр лица.page b/resources/src/main/resources/business-model/Личный кабинет юр лица.page index 3fc09859..60777fb8 100644 --- a/resources/src/main/resources/business-model/Личный кабинет юр лица.page +++ b/resources/src/main/resources/business-model/Личный кабинет юр лица.page @@ -3852,6 +3852,7 @@ 9138d81a-f635-42f6-915c-b3c7be4e2160 Группа полей true + false false @@ -12102,6 +12103,7 @@ 36ed9134-7e97-45ea-8215-261f65e69077 Вертикальный контейнер true + false false @@ -12307,6 +12309,7 @@ 40e1b3d6-57ff-4ef7-b93f-72e0c2661b70 Вертикальный контейнер true + false false @@ -12478,6 +12481,7 @@ cae94ea0-75e1-408a-8ad7-34a07b52a381 Вертикальный контейнер true + false false @@ -14164,7 +14168,6 @@ 991237d3-8cb9-48af-8501-030a3c8c6cfc Группа полей true - false false @@ -14195,6 +14198,7 @@ 754d7c80-d6f6-4d7f-a902-f5635d5dd60e Горизонтальный контейнер true + false false @@ -14487,6 +14491,7 @@ 025b7644-c658-49b2-a740-188668b8b81e Hbox_12 true + false false @@ -14663,6 +14668,7 @@ 7be4de32-b967-4629-9801-425457e4b9c5 Hbox_12_ true + false false @@ -14811,7 +14817,6 @@ fafbc151-0d60-4e80-90d1-501d426dadc9 Hbox true - false false @@ -14836,6 +14841,7 @@ f6310258-ec19-4d13-b04d-cfc09d6b5e77 Кнопка 5 false + false false @@ -15082,7 +15088,7 @@ behavior - {"objectId":"8f61be32-7f6c-40e3-afba-ad802f48a040","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"6f18b446-eb34-4894-92c8-6654e0380ef5","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} @@ -15117,7 +15123,7 @@ behavior - {"objectId":"8f61be32-7f6c-40e3-afba-ad802f48a040","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"6f18b446-eb34-4894-92c8-6654e0380ef5","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} @@ -15205,6 +15211,182 @@ + + + + + behavior + + {"objectId":"3db79e5b-7bea-4043-ab90-1080983f89d3","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + true + + + + + + + + + + + + + behavior + + {"objectId":"42e0e31c-1fc1-415f-9f4c-64fbd2ba0889","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + true + + + + + + + + + + + + + behavior + + {"objectId":"3808bc3e-cddf-4a55-b324-f4fe469e4938","packageName":"component.field","className":"CheckBox","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + true + + + + + + + + + + + + + behavior + + {"objectId":"da4bcc36-03cf-494d-9757-c3fc7486ef2f","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + true + + + + + + + + + + + + + behavior + + {"objectId":"d3bbecf6-acf6-468b-bb67-4b5549c44a58","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + true + + + + + + + + + @@ -15362,38 +15544,7 @@ d60694e9-0d17-4e07-ad1d-ad9871e3deaa Текст false - false - - - - cssClasses - - - - "subtitle" - - - - - - - - - - - initialValue - - "Ежегодное предоставление списка сотрудников/обучающихся в организации, подлежащих воинскому учету" - - - - - - - - - false - + true 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 @@ -15496,73 +15647,876 @@ 8f61be32-7f6c-40e3-afba-ad802f48a040 FileUploadV2 false + true + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + f095d2a8-2f04-4aca-9fe0-14cda3343bb0 + Вертикальный контейнер + true false - - - - collectible - - false - - - - cssClasses - - + + + + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + d60694e9-0d17-4e07-ad1d-ad9871e3deaa + Текст + false + false + + + + cssClasses + + + + "subtitle" + + + + + + + + + + + initialValue + + "Ежегодное предоставление списка сотрудников/обучающихся в организации, подлежащих воинскому учету" + + + + + + + + +false + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + 8e71cf03-601f-46dd-bb10-baa4852390bc + Текст + false + true + + + 5694e7c5-bbb5-4d23-be6c-7ad71b8ad38c + 6f18b446-eb34-4894-92c8-6654e0380ef5 + Csv file + false + false + false + +false +true + + + + ErvuFileUploadWithAdditionalFiles + ervu.component.fileupload + +true +true + + + collectible + + false + + + + cssClasses + + + + "btn-main" + + + + + + displayFileSize + + true + + + + displayProgressBar + + true + + + + extensionFilter + + + + "csv" + + + + + + + formType + + "FORM_12" + + + + maxFileSizeMb + + 5.0 + + + + maxFilesToUpload + + 1.0 + + + + mchdFileUploadRef + + {"objectId":"f47e3ad9-6065-41a7-862d-991c41f1bdfa","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + removeFileButtonName + + "Удалить" + + + + selectFileButtonName + + "Выбрать" + + + + selectFileFieldText + + "Перетащите файл или выберите на компьютере" + + + + signFileUploadRef + + {"objectId":"f4ef5c50-9c44-4165-bcdf-5b6e7f4aaab1","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + visible + + true + + + + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + 159c7f1e-c71c-4da1-bf51-18550aa8baa4 + Текст + false + false + + + + cssClasses + + + + "mute" + + + + + + initialValue + + "Поддерживаемый формат файла - csv" + + + + + + + + +false + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + 9c6cdd62-01b8-47c2-a0a3-43a4b2a44183 + Текст + false + true + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + b0903742-cbcd-4593-b98b-6055b71bb9ca + Вертикальный контейнер + true + true + + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + 64b7e42e-3501-4f93-8933-dfcbc75cb245 + Вертикальный контейнер + true + false + + + + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + 3db79e5b-7bea-4043-ab90-1080983f89d3 + Текст + false + false + + + + cssClasses + + + + "subtitle" + + + + + + + + + + + initialValue + + "Ваша открепленная усиленная квалифицированная электронная подпись в формате sig" + + + + + + + + +false + + + + 5694e7c5-bbb5-4d23-be6c-7ad71b8ad38c + d3bbecf6-acf6-468b-bb67-4b5549c44a58 + Подпись File + false + false + + + + collectible + + false + + + + cssClasses + + + + "btn-main" + + + + + + extensionFilter + + + + + "sig" + + + + + + formType + + "FORM_12" + + + + maxFileSizeMb + + 5.0 + + + + maxFilesToUpload + + 1.0 + + + + removeFileButtonName + + "Удалить" + + + + selectFileButtonName + + "Выбрать" + + + + selectFileFieldText + + "Перетащите файл или выберите на компьютере" + + + + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + 42e0e31c-1fc1-415f-9f4c-64fbd2ba0889 + Текст + false + false + + + + cssClasses + + + + "mute" + + + + + + initialValue + + "Поддерживаемый формат файла - sig" + + + + + + + + +false + + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + 47a8d746-1dc3-4237-ac8a-f395bed05493 + Вертикальный контейнер + true + true + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + a8c8be34-345b-42b4-a29a-499096a3e5af + Вертикальный контейнер + true + true + + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + 37859c01-1ec5-4557-8d56-31cf2702abda + Вертикальный контейнер + true + false + + + + + + + 98594cec-0a9b-4cef-af09-e1b71cb2ad9e + a8957fcd-8c8a-403c-b45f-2b8632bdf509 + AC_МЧД + false + false + + + + elseActions + + + + + + behavior + +{"objectId":"f0d9399b-fb2f-4874-a307-2006e7758f44","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + method + +"setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + +{"objectId":"14da9d8b-7687-4f09-a423-626db4f3e749","packageName":"component","className":"Text","type":"TS"} + + + + method + +"setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + eventRefs + + + + + + behavior + +{"objectId":"3808bc3e-cddf-4a55-b324-f4fe469e4938","packageName":"component.field","className":"CheckBox","type":"TS"} + + + + propertyName + +"userChangeValueEvent" + + + + + + + + + ifCondition + + + + conditions + + + + + + _isGroupSelected + + false + + + + one + + + + conditionFirstPart + + + +objectValue + + + + behavior - "btn-main" + {"objectId":"3808bc3e-cddf-4a55-b324-f4fe469e4938","packageName":"component.field","className":"CheckBox","type":"TS"} - - - - - extensionFilter - - + + + method - "csv" + "getValue" - - - - - formType - - "FORM_12" - - - - maxFileSizeMb - - 5.0 - - - - maxFilesToUpload - - 1.0 - - - - removeFileButtonName - - "Удалить" - - - - selectFileButtonName - - "Выбрать" - - - - selectFileFieldText - - "Перетащите файл или выберите на компьютере" - - - - + + + + + + + + + conditionSecondPart + + + +staticValue + + + boolean + + + true + + + + + + + operation + + "EQUALS" + + + + + + + + + + + + logicalOperation + + null + + + + + + + thenActions + + + + + + behavior + +{"objectId":"f0d9399b-fb2f-4874-a307-2006e7758f44","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + method + +"setVisible" + + + + value + + + + staticValue + + + boolean + + + true + + + + + + + + + + + + + behavior + +{"objectId":"14da9d8b-7687-4f09-a423-626db4f3e749","packageName":"component","className":"Text","type":"TS"} + + + + method + +"setVisible" + + + + value + + + + staticValue + + + boolean + + + true + + + + + + + + + + + + + + + 8b755f7b-e52b-4800-830a-f01467cd5cbb + 3808bc3e-cddf-4a55-b324-f4fe469e4938 + Check box + false + false + + + + label + + null + + + + + + + + +false + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + da4bcc36-03cf-494d-9757-c3fc7486ef2f + Текст + false + false + + + + cssClasses + + + + "subtitle" + + + + + + initialValue + + "Внимание: если файл подписан не руководителем организации, рекомендуется приложить МЧД" + + + + + + + + +false + + + + d7d54cfb-26b5-4dba-b56f-b6247183c24d + 085f831f-4df9-444b-b7fc-79b96e7f1f67 + Hbox + true + true + + + 5694e7c5-bbb5-4d23-be6c-7ad71b8ad38c + f0d9399b-fb2f-4874-a307-2006e7758f44 + Мчд File + false + false + false + + + + collectible + + false + + + + cssClasses + + + + "btn-main" + + + + + + extensionFilter + + + + + "xml" + + + + + + formType + + "FORM_12" + + + + maxFileSizeMb + + 5.0 + + + + maxFilesToUpload + + 1.0 + + + + removeFileButtonName + + "Удалить" + + + + selectFileButtonName + + "Выбрать" + + + + selectFileFieldText + + "Перетащите файл или выберите на компьютере" + + + + visible + + false + + + + + + + ba24d307-0b91-4299-ba82-9d0b52384ff2 + 14da9d8b-7687-4f09-a423-626db4f3e749 + Текст + false + false + + + + cssClasses + + + + "mute" + + + + + + initialValue + + "Поддерживаемый формат файла - xml" + + + + visible + + false + + + + + + + + +false + + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + 080a0c2f-b66c-4987-9cf7-80beb26edd1e + Vbox + true + true + + + 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 + 51813a15-4d66-4b61-b522-e9e24b65b972 + Vbox + true + true + 98594cec-0a9b-4cef-af09-e1b71cb2ad9e @@ -15587,7 +16541,25 @@ behavior - {"objectId":"8f61be32-7f6c-40e3-afba-ad802f48a040","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"6f18b446-eb34-4894-92c8-6654e0380ef5","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} + + + + propertyName + + "fileAddedEvent" + + + + + + + + + + behavior + + {"objectId":"d3bbecf6-acf6-468b-bb67-4b5549c44a58","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} @@ -15608,6 +16580,108 @@ conditions + + + + + _isGroupSelected + + false + + + + one + + + + conditionFirstPart + + + + objectValue + + + + behavior + + {"objectId":"6f18b446-eb34-4894-92c8-6654e0380ef5","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} + + + + method + + "getValue" + + + + + + + + + + operation + + "IS_NOT_EMPTY" + + + + + + + + + + + + + _isGroupSelected + + false + + + + one + + + + conditionFirstPart + + + + objectValue + + + + behavior + + {"objectId":"d3bbecf6-acf6-468b-bb67-4b5549c44a58","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + method + + "getValue" + + + + + + + + + + operation + + "IS_NOT_EMPTY" + + + + + + + + @@ -15687,7 +16761,25 @@ behavior - {"objectId":"8f61be32-7f6c-40e3-afba-ad802f48a040","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"6f18b446-eb34-4894-92c8-6654e0380ef5","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} + + + + propertyName + + "fileDeletedEvent" + + + + + + + + + + behavior + + {"objectId":"d3bbecf6-acf6-468b-bb67-4b5549c44a58","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} @@ -15830,7 +16922,7 @@ behavior - {"objectId":"8f61be32-7f6c-40e3-afba-ad802f48a040","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"6f18b446-eb34-4894-92c8-6654e0380ef5","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} @@ -15858,33 +16950,7 @@ 159c7f1e-c71c-4da1-bf51-18550aa8baa4 Текст false - false - - - - cssClasses - - - - "mute" - - - - - - initialValue - - "Поддерживаемый формат файла - csv" - - - - - - - - - false - + true fd7e47b9-dce1-4d14-9f3a-580c79f59579 @@ -15942,7 +17008,7 @@ behavior - {"objectId":"8f61be32-7f6c-40e3-afba-ad802f48a040","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"6f18b446-eb34-4894-92c8-6654e0380ef5","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} @@ -16179,6 +17245,252 @@ false + + + + + + + + + + + + behavior + + {"objectId":"3db79e5b-7bea-4043-ab90-1080983f89d3","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + + {"objectId":"d3bbecf6-acf6-468b-bb67-4b5549c44a58","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + + {"objectId":"42e0e31c-1fc1-415f-9f4c-64fbd2ba0889","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + + {"objectId":"3808bc3e-cddf-4a55-b324-f4fe469e4938","packageName":"component.field","className":"CheckBox","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + + {"objectId":"f0d9399b-fb2f-4874-a307-2006e7758f44","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + + behavior + + {"objectId":"da4bcc36-03cf-494d-9757-c3fc7486ef2f","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + + + + + + + + + + + + + behavior + + {"objectId":"14da9d8b-7687-4f09-a423-626db4f3e749","packageName":"component","className":"Text","type":"TS"} + + + + method + + "setVisible" + + + + value + + + + staticValue + + + boolean + + + false + @@ -16208,7 +17520,7 @@ behavior - {"objectId":"8f61be32-7f6c-40e3-afba-ad802f48a040","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"6f18b446-eb34-4894-92c8-6654e0380ef5","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} @@ -16315,7 +17627,7 @@ behavior - {"objectId":"8f61be32-7f6c-40e3-afba-ad802f48a040","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"6f18b446-eb34-4894-92c8-6654e0380ef5","packageName":"ervu.component.fileupload","className":"ErvuFileUploadWithAdditionalFiles","type":"TS"} From 3316cac2584ef62253268439b66bda4de9a5545d Mon Sep 17 00:00:00 2001 From: "adel.ka" Date: Thu, 16 Oct 2025 11:11:40 +0300 Subject: [PATCH 30/45] SUPPORT-9368: fix from review --- backend/pom.xml | 2 +- frontend/src/ts/ervu/service/FilterService.ts | 2 -- pom.xml | 1 + 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/backend/pom.xml b/backend/pom.xml index 77e756f5..d5b6baae 100644 --- a/backend/pom.xml +++ b/backend/pom.xml @@ -213,7 +213,7 @@ kr.motd.maven os-maven-plugin - 1.6.2 + ${os-maven-plugin.version} diff --git a/frontend/src/ts/ervu/service/FilterService.ts b/frontend/src/ts/ervu/service/FilterService.ts index db617807..52f24f84 100644 --- a/frontend/src/ts/ervu/service/FilterService.ts +++ b/frontend/src/ts/ervu/service/FilterService.ts @@ -72,7 +72,6 @@ export class FilterService { : "Файл отсутствует"; return { - conditionOperator: undefined, conditions: [{ filterValue: displayValue, filterType: "equals" @@ -86,7 +85,6 @@ export class FilterService { endValue?: string ): FilterInfo { return { - conditionOperator: undefined, conditions: [{ filterValue: endValue ? `${filterValue} to ${endValue}` : filterValue, filterType: filterType, diff --git a/pom.xml b/pom.xml index 5a6faaf2..5ba24ef6 100644 --- a/pom.xml +++ b/pom.xml @@ -22,6 +22,7 @@ 2.9.2 3.192.26 72000 + 1.6.2 From a63b20b1195deeb05819d78d0a78b5ac46f64462 Mon Sep 17 00:00:00 2001 From: "adel.ka" Date: Thu, 16 Oct 2025 13:16:53 +0300 Subject: [PATCH 31/45] =?UTF-8?q?SUPPORT-9368:=20=D0=BF=D0=BE=D0=BC=D0=B5?= =?UTF-8?q?=D0=BD=D1=8F=D0=BB=20=D0=BF=D0=B5=D1=80=D0=B5=D0=BC=D0=B5=D0=BD?= =?UTF-8?q?=D0=BD=D1=8B=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/ru/micord/ervu/kafka/ReplyingKafkaConfig.java | 2 +- .../java/ru/micord/ervu/service/ValidationFileService.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/src/main/java/ru/micord/ervu/kafka/ReplyingKafkaConfig.java b/backend/src/main/java/ru/micord/ervu/kafka/ReplyingKafkaConfig.java index 0c7b28a9..c408bd53 100644 --- a/backend/src/main/java/ru/micord/ervu/kafka/ReplyingKafkaConfig.java +++ b/backend/src/main/java/ru/micord/ervu/kafka/ReplyingKafkaConfig.java @@ -56,7 +56,7 @@ public class ReplyingKafkaConfig { private String saslMechanism; @Value("${av.kafka.download.response}") private String avReplyTopic; - @Value("${ervu.kafka.validate.export.reply.topic}") + @Value("${ervu.kafka.validate.export.response}") private String validateReplyTopic; @Bean diff --git a/backend/src/main/java/ru/micord/ervu/service/ValidationFileService.java b/backend/src/main/java/ru/micord/ervu/service/ValidationFileService.java index c116bf26..f3286c52 100644 --- a/backend/src/main/java/ru/micord/ervu/service/ValidationFileService.java +++ b/backend/src/main/java/ru/micord/ervu/service/ValidationFileService.java @@ -22,8 +22,8 @@ public class ValidationFileService { public ValidationFileService( ReplyingKafkaService replyingKafkaService, ObjectMapper objectMapper, - @Value("${ervu.kafka.validate.export.request.topic}") String validateExportRequestTopic, - @Value("${ervu.kafka.validate.export.reply.topic}") String validateExportReplyTopic) { + @Value("${ervu.kafka.validate.export.request}") String validateExportRequestTopic, + @Value("${ervu.kafka.validate.export.response}") String validateExportReplyTopic) { this.replyingKafkaService = replyingKafkaService; this.objectMapper = objectMapper; this.validateExportRequestTopic = validateExportRequestTopic; From f27bbe58b09d9748f08449deecabf290d3fbb362 Mon Sep 17 00:00:00 2001 From: "adel.ka" Date: Thu, 16 Oct 2025 13:48:42 +0300 Subject: [PATCH 32/45] =?UTF-8?q?SUPPORT-9368:=20=D0=A4=D0=98=D0=9A=D0=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/ru/micord/ervu/service/ValidationFileService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/main/java/ru/micord/ervu/service/ValidationFileService.java b/backend/src/main/java/ru/micord/ervu/service/ValidationFileService.java index f3286c52..05ed5e7d 100644 --- a/backend/src/main/java/ru/micord/ervu/service/ValidationFileService.java +++ b/backend/src/main/java/ru/micord/ervu/service/ValidationFileService.java @@ -14,13 +14,13 @@ import ru.micord.ervu.model.ValidateExportResponse; */ @Service public class ValidationFileService { - private final ReplyingKafkaService replyingKafkaService; + private final ReplyingKafkaService replyingKafkaService; private final ObjectMapper objectMapper; private final String validateExportRequestTopic; private final String validateExportReplyTopic; public ValidationFileService( - ReplyingKafkaService replyingKafkaService, + ReplyingKafkaService replyingKafkaService, ObjectMapper objectMapper, @Value("${ervu.kafka.validate.export.request}") String validateExportRequestTopic, @Value("${ervu.kafka.validate.export.response}") String validateExportReplyTopic) { From 9a9ff4930f0b1d634846ab34db6b379c6cd39c5a Mon Sep 17 00:00:00 2001 From: Eduard Tihomiorv Date: Thu, 16 Oct 2025 14:57:23 +0300 Subject: [PATCH 33/45] fix text --- .../Личный кабинет юр лица.page | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/resources/src/main/resources/business-model/Личный кабинет юр лица.page b/resources/src/main/resources/business-model/Личный кабинет юр лица.page index 60777fb8..20507167 100644 --- a/resources/src/main/resources/business-model/Личный кабинет юр лица.page +++ b/resources/src/main/resources/business-model/Личный кабинет юр лица.page @@ -1439,7 +1439,7 @@ initialValue - "Перед выбором файла убедитесь, что все данные в файле введены корректно. \u003cbr\u003eФайл подписан усиленной квалифицированной электронной подписью." + "Перед выбором файла убедитесь, что все данные в файле введены корректно. \u003cbr\u003eФайл подписан электронной подписью." @@ -1892,7 +1892,7 @@ initialValue - "Ваша открепленная усиленная квалифицированная электронная подпись в формате sig" + "Ваша откреплённая электронная подпись в формате sig" @@ -4988,7 +4988,7 @@ initialValue - "Перед выбором файла убедитесь, что все данные в файле введены корректно" + "Перед выбором файла убедитесь, что все данные в файле введены корректно. \u003cbr\u003eФайл подписан электронной подписью." @@ -5445,7 +5445,7 @@ initialValue - "Ваша открепленная усиленная квалифицированная электронная подпись в формате sig" + "Ваша откреплённая электронная подпись в формате sig" @@ -8385,7 +8385,7 @@ initialValue - "Перед выбором файла убедитесь, что все данные в файле введены корректно" + "Перед выбором файла убедитесь, что все данные в файле введены корректно. \u003cbr\u003eФайл подписан электронной подписью." @@ -8863,7 +8863,7 @@ initialValue - "Ваша открепленная усиленная квалифицированная электронная подпись в формате sig" + "Ваша откреплённая электронная подпись в формате sig" @@ -11876,7 +11876,7 @@ initialValue - "Перед выбором файла убедитесь, что все данные в файле введены корректно" + "Перед выбором файла убедитесь, что все данные в файле введены корректно. \u003cbr\u003eФайл подписан электронной подписью." @@ -12342,7 +12342,7 @@ initialValue - "Ваша открепленная усиленная квалифицированная электронная подпись в формате sig" + "Ваша откреплённая электронная подпись в формате sig" @@ -15427,7 +15427,7 @@ initialValue - "Перед выбором файла убедитесь, что все данные в файле введены корректно" + "Перед выбором файла убедитесь, что все данные в файле введены корректно. \u003cbr\u003eФайл подписан электронной подписью." @@ -15905,7 +15905,7 @@ initialValue - "Ваша открепленная усиленная квалифицированная электронная подпись в формате sig" + "Ваша откреплённая электронная подпись в формате sig" @@ -18378,4 +18378,4 @@ - + \ No newline at end of file From 19c3b69aa906f87e710e44444e14cecb6c332476 Mon Sep 17 00:00:00 2001 From: Eduard Tihomiorv Date: Thu, 16 Oct 2025 15:29:47 +0300 Subject: [PATCH 34/45] fix --- .../EmployeeInfoFileUploadService.java | 6 +- .../Личный кабинет юр лица.page | 73 +++---------------- 2 files changed, 13 insertions(+), 66 deletions(-) diff --git a/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java b/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java index b7952719..974e4384 100644 --- a/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java +++ b/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java @@ -343,8 +343,7 @@ public class EmployeeInfoFileUploadService { new FileInfo[] {fileInfo, signFileInfo}, fileInfo.getFileStatus() ); try { - sendMessage(fileStatusResponse); - return true; + return sendMessage(fileStatusResponse); } catch (JsonProcessingException e) { handeSignError(fileInfo, signFileInfo, uploadOrgInfo, response); @@ -367,8 +366,7 @@ public class EmployeeInfoFileUploadService { ); interactionService.updateStatus(fileInfo.getFileId(), "Направлено в ЕРВУ", ervuId); - sendMessage(fileStatusResponse); - return true; + return sendMessage(fileStatusResponse); } catch (JsonProcessingException e) { handleMchdValidationError(uploadOrgInfo, fileInfo, signFileInfo, mchdFileInfo, ervuId, response diff --git a/resources/src/main/resources/business-model/Личный кабинет юр лица.page b/resources/src/main/resources/business-model/Личный кабинет юр лица.page index 20507167..0379756f 100644 --- a/resources/src/main/resources/business-model/Личный кабинет юр лица.page +++ b/resources/src/main/resources/business-model/Личный кабинет юр лица.page @@ -5245,13 +5245,6 @@ false - - ba24d307-0b91-4299-ba82-9d0b52384ff2 - dbb46f04-0f9b-42bc-8a85-2b8b8dc7c246 - Текст - false - true - 5694e7c5-bbb5-4d23-be6c-7ad71b8ad38c f8c09ec6-d52a-4398-a40e-a237f4305b88 @@ -5367,13 +5360,6 @@ - - ba24d307-0b91-4299-ba82-9d0b52384ff2 - a3c544ce-f22e-4d7c-a879-4827832742f8 - Текст - false - true - ba24d307-0b91-4299-ba82-9d0b52384ff2 0970e79a-7130-4c23-bdc8-13f8554daa59 @@ -7271,13 +7257,6 @@ - - 86f297f1-ab3d-40e0-ac2f-89cc944b7f0a - 89bcd110-0dc0-4aaa-9a4f-172858b2ce03 - Диалог - выбор файла и отправка - true - true - 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 b50c6499-5c81-4fc3-b9ce-02cc1124e673 @@ -7806,13 +7785,6 @@ - - 86f297f1-ab3d-40e0-ac2f-89cc944b7f0a - b59b46f5-0b45-4a0e-99d9-1eed7060be97 - Диалог - выбор файла и отправка НОВЫЙ - true - true - e32ae1f5-5b14-45f1-abb6-f52c34b3b570 aa0a8f53-5fdb-4c6c-b364-008e09088c3a @@ -8656,13 +8628,6 @@ false - - ba24d307-0b91-4299-ba82-9d0b52384ff2 - b93c1470-2577-4be2-ba77-4081940486ce - Текст - false - true - 5694e7c5-bbb5-4d23-be6c-7ad71b8ad38c 3d0d0314-ea5f-4974-a92d-32c11ce90861 @@ -8811,13 +8776,6 @@ false - - ba24d307-0b91-4299-ba82-9d0b52384ff2 - 1bb8f548-1b8e-4bda-b265-0ed6fce660e8 - Текст - false - true - 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 276d7e73-eba9-4859-942f-af19f7ff535f @@ -10748,6 +10706,7 @@ 7057bbcb-cff2-4e31-812d-ba7e043a4bcc Группа полей true + false false @@ -12103,7 +12062,6 @@ 36ed9134-7e97-45ea-8215-261f65e69077 Вертикальный контейнер true - false false @@ -12309,7 +12267,6 @@ 40e1b3d6-57ff-4ef7-b93f-72e0c2661b70 Вертикальный контейнер true - false false @@ -12481,7 +12438,6 @@ cae94ea0-75e1-408a-8ad7-34a07b52a381 Вертикальный контейнер true - false false @@ -13945,6 +13901,7 @@ d31780d3-6bdf-4fea-b297-5405f122b0ad Обработка событий - end file upload false + false false @@ -14052,6 +14009,7 @@ 359f50fe-e079-435c-8844-efcf652c9313 Обработка событий - failed file upload false + false false @@ -14168,6 +14126,7 @@ 991237d3-8cb9-48af-8501-030a3c8c6cfc Группа полей true + false false @@ -15698,13 +15657,6 @@ false - - ba24d307-0b91-4299-ba82-9d0b52384ff2 - 8e71cf03-601f-46dd-bb10-baa4852390bc - Текст - false - true - 5694e7c5-bbb5-4d23-be6c-7ad71b8ad38c 6f18b446-eb34-4894-92c8-6654e0380ef5 @@ -15784,7 +15736,7 @@ mchdFileUploadRef - {"objectId":"f47e3ad9-6065-41a7-862d-991c41f1bdfa","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"f0d9399b-fb2f-4874-a307-2006e7758f44","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} @@ -15808,7 +15760,7 @@ signFileUploadRef - {"objectId":"f4ef5c50-9c44-4165-bcdf-5b6e7f4aaab1","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} + {"objectId":"d3bbecf6-acf6-468b-bb67-4b5549c44a58","packageName":"ervu.component.fileupload","className":"ErvuFileUpload","type":"TS"} @@ -15853,13 +15805,6 @@ false - - ba24d307-0b91-4299-ba82-9d0b52384ff2 - 9c6cdd62-01b8-47c2-a0a3-43a4b2a44183 - Текст - false - true - 9d1b5af1-0b8f-4b1b-b9a5-c2e6acf72d91 b0903742-cbcd-4593-b98b-6055b71bb9ca @@ -16743,6 +16688,7 @@ f897626f-4c10-4648-90fe-51b6f9b31fe9 Обработка событий - файл удален false + false false @@ -16996,6 +16942,7 @@ 09000d10-0142-4051-9ab8-0df98bd5dafa Обработка событий - start file upload false + false false @@ -17508,6 +17455,7 @@ e80f8dec-8222-4b94-bc94-e8b2119dfca1 Обработка событий - end file upload false + false false @@ -17615,6 +17563,7 @@ 6745088f-5c87-42e6-bc87-41efba248455 Обработка событий - failed file upload false + false false @@ -18378,4 +18327,4 @@ - \ No newline at end of file + From 2118f96c26cbf0a0d1ee82a5b82c4bcbc52141f4 Mon Sep 17 00:00:00 2001 From: Eduard Tihomiorv Date: Thu, 16 Oct 2025 16:07:10 +0300 Subject: [PATCH 35/45] SUPPORT-9339: Fix status --- .../EmployeeInfoFileUploadService.java | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java b/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java index 974e4384..6e33d84b 100644 --- a/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java +++ b/backend/src/main/java/ervu/service/fileupload/EmployeeInfoFileUploadService.java @@ -338,12 +338,18 @@ public class EmployeeInfoFileUploadService { signerInfoMap.get("O").equalsIgnoreCase(uploadOrgInfo.getOrgName()); if (isSignerValid) { - interactionService.updateStatus(fileInfo.getFileId(), "Направлено в ЕРВУ", ervuId); FileStatusResponse fileStatusResponse = new FileStatusResponse(uploadOrgInfo, new FileInfo[] {fileInfo, signFileInfo}, fileInfo.getFileStatus() ); try { - return sendMessage(fileStatusResponse); + if (sendMessage(fileStatusResponse)) { + interactionService.updateStatus(fileInfo.getFileId(), "Направлено в ЕРВУ", ervuId); + return true; + } + else { + interactionService.delete(fileInfo.getFileId(), ervuId); + return false; + } } catch (JsonProcessingException e) { handeSignError(fileInfo, signFileInfo, uploadOrgInfo, response); @@ -364,9 +370,15 @@ public class EmployeeInfoFileUploadService { validateMchd(mchdFile, accessToken, signerInfoMap.get("SN") + " " + signerInfoMap.get("G"), chiefFirstName, chiefLastName, chiefMiddleName ); - interactionService.updateStatus(fileInfo.getFileId(), "Направлено в ЕРВУ", ervuId); - return sendMessage(fileStatusResponse); + if (sendMessage(fileStatusResponse)) { + interactionService.updateStatus(fileInfo.getFileId(), "Направлено в ЕРВУ", ervuId); + return true; + } + else { + interactionService.delete(fileInfo.getFileId(), ervuId); + return false; + } } catch (JsonProcessingException e) { handleMchdValidationError(uploadOrgInfo, fileInfo, signFileInfo, mchdFileInfo, ervuId, response From ceb6a6ee43aff1609d1b569a2b31ca44b18e3c96 Mon Sep 17 00:00:00 2001 From: Eduard Tihomiorv Date: Fri, 17 Oct 2025 09:54:58 +0300 Subject: [PATCH 36/45] fix status packInfo --- .../java/ru/micord/ervu/journal/JournalFileInfo.java | 10 ++++++++++ .../micord/ervu/journal/mapper/JournalDtoMapper.java | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/backend/src/main/java/ru/micord/ervu/journal/JournalFileInfo.java b/backend/src/main/java/ru/micord/ervu/journal/JournalFileInfo.java index 7ffb5251..410a8e74 100644 --- a/backend/src/main/java/ru/micord/ervu/journal/JournalFileInfo.java +++ b/backend/src/main/java/ru/micord/ervu/journal/JournalFileInfo.java @@ -11,6 +11,7 @@ import ru.micord.ervu.journal.deserializer.DepartureDateTimeDeserializer; public class JournalFileInfo { private List packFiles; // Список файлов (csv, sig, mchd) + private JournalFileStatus packInfo; //общий статус по пакету private SenderInfo senderInfo; private Integer rowsCount; //Общее количество записей отправленных в файле private Integer rowsSuccess; //Количество записей принятых в файле @@ -61,6 +62,15 @@ public class JournalFileInfo { return this; } + public JournalFileStatus getPackInfo() { + return packInfo; + } + + public JournalFileInfo setPackInfo(JournalFileStatus packInfo) { + this.packInfo = packInfo; + return this; + } + @JsonIgnoreProperties(ignoreUnknown = true) public static class JournalFileDetails { private String fileId; //ИД файла полученный при создании записи о файле в реестр организаций (в ЕРВУ) diff --git a/backend/src/main/java/ru/micord/ervu/journal/mapper/JournalDtoMapper.java b/backend/src/main/java/ru/micord/ervu/journal/mapper/JournalDtoMapper.java index 332e1e28..f76f0941 100644 --- a/backend/src/main/java/ru/micord/ervu/journal/mapper/JournalDtoMapper.java +++ b/backend/src/main/java/ru/micord/ervu/journal/mapper/JournalDtoMapper.java @@ -23,7 +23,7 @@ public class JournalDtoMapper { senderInfo.getLastName() ) ) - .setStatus(journalFileDetails.getFileStatus().getStatus()) + .setStatus(journalFileInfo.getPackInfo().getStatus()) .setRowsCount(journalFileInfo.getRowsCount()) .setRowsSuccess(journalFileInfo.getRowsSuccess()) .setRowsError(journalFileInfo.getRowsError()); From 6af3c7e4b1dc61c62136f7d1d55bab5cfd95fb1a Mon Sep 17 00:00:00 2001 From: "adel.ka" Date: Fri, 17 Oct 2025 11:07:36 +0300 Subject: [PATCH 37/45] SUPPORT-9368: css --- .../src/resources/css/components-lkrp.css | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/frontend/src/resources/css/components-lkrp.css b/frontend/src/resources/css/components-lkrp.css index ff4a5efb..616aa694 100644 --- a/frontend/src/resources/css/components-lkrp.css +++ b/frontend/src/resources/css/components-lkrp.css @@ -306,6 +306,44 @@ background-image: url(../img/svg/pin.svg); } +.download-cell-renderer { + display: flex; + justify-content: center; + align-items: center; +} + +.download-btn { + background: transparent; + border: none; + cursor: pointer; + padding: 6px; + display: flex; + align-items: center; + justify-content: center; + transition: transform 0.15s ease, opacity 0.15s ease; + min-width: 36px; + min-height: 36px; +} + +.download-btn i { + color: #555; + font-size: 26px; +} + +.download-btn:hover i { + color: #000; +} + +.download-btn:active i { + transform: scale(0.9); +} + +.download-btn:disabled, +.download-btn[disabled] { + opacity: 0.3; + cursor: default; +} + .webbpm.ervu_lkrp_ul .paragraph-group > .vertical-container > * { margin-bottom: 16px; } From ada7f3398c9c2ccbfca479549a23b3d449a5829f Mon Sep 17 00:00:00 2001 From: "adel.ka" Date: Fri, 17 Oct 2025 14:16:02 +0300 Subject: [PATCH 38/45] =?UTF-8?q?SUPPORT-9368:=20=D1=83=D0=B1=D1=80=D0=B0?= =?UTF-8?q?=D0=BB=20=D0=B7=D0=B0=D0=BF=D0=B8=D1=81=D1=8C=20=D0=BE=D0=B1=20?= =?UTF-8?q?=D0=BE=D1=88=D0=B8=D0=B1=D0=BA=D0=B5=20=D0=B2=20validateFileSer?= =?UTF-8?q?vice?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/ts/ervu/service/ValidateFileService.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/frontend/src/ts/ervu/service/ValidateFileService.ts b/frontend/src/ts/ervu/service/ValidateFileService.ts index 2b827948..d217a3b3 100644 --- a/frontend/src/ts/ervu/service/ValidateFileService.ts +++ b/frontend/src/ts/ervu/service/ValidateFileService.ts @@ -30,9 +30,6 @@ export class ValidateFileService { link.click(); window.URL.revokeObjectURL(data); link.remove(); - }) - .catch(() => { - this.messageService.error("Не удалось скачать файл") }); } } \ No newline at end of file From 87c3e905f314358d6e7f10b307b18a42f2c5cb06 Mon Sep 17 00:00:00 2001 From: "adel.ka" Date: Fri, 17 Oct 2025 14:44:45 +0300 Subject: [PATCH 39/45] =?UTF-8?q?SUPPORT-9368:=20=D1=84=D0=B8=D0=BA=D1=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ts/ervu/component/grid/renderer/FileDownloadCellRenderer.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frontend/src/ts/ervu/component/grid/renderer/FileDownloadCellRenderer.ts b/frontend/src/ts/ervu/component/grid/renderer/FileDownloadCellRenderer.ts index ab1338f4..35198a39 100644 --- a/frontend/src/ts/ervu/component/grid/renderer/FileDownloadCellRenderer.ts +++ b/frontend/src/ts/ervu/component/grid/renderer/FileDownloadCellRenderer.ts @@ -16,6 +16,8 @@ export class FileDownloadCellRenderer implements GridCellValueRenderer { } button.addEventListener('click', () => { + event.stopPropagation(); + const fileId = params.data.fileId; if (!fileId) { return; From fbd25050f040f155739c941fa4f4a7ae7f3f696c Mon Sep 17 00:00:00 2001 From: "adel.ka" Date: Fri, 17 Oct 2025 16:33:47 +0300 Subject: [PATCH 40/45] =?UTF-8?q?SUPPORT-9368:=20=D0=BF=D0=BE=D0=BC=D0=B5?= =?UTF-8?q?=D0=BD=D1=8F=D0=BB=20=D1=81=D0=BE=D1=80=D1=82=D0=B8=D1=80=D0=BE?= =?UTF-8?q?=D0=B2=D0=BA=D1=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/grid/impl/JournalInMemoryStaticGridLoadService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/main/java/ru/micord/ervu/service/grid/impl/JournalInMemoryStaticGridLoadService.java b/backend/src/main/java/ru/micord/ervu/service/grid/impl/JournalInMemoryStaticGridLoadService.java index 71624244..18edee51 100644 --- a/backend/src/main/java/ru/micord/ervu/service/grid/impl/JournalInMemoryStaticGridLoadService.java +++ b/backend/src/main/java/ru/micord/ervu/service/grid/impl/JournalInMemoryStaticGridLoadService.java @@ -85,7 +85,7 @@ public class JournalInMemoryStaticGridLoadService implements HashSet seenFileIds = new HashSet<>(); return Stream.concat(dbJournalList.stream(), ervuJournalList.stream()) .filter(journal -> seenFileIds.add(journal.getFileId())) - .sorted(Comparator.comparing(JournalDto::getDepartureDateTime)) + .sorted(Comparator.comparing(JournalDto::getDepartureDateTime).reversed()) .map(journal -> journal .setDocumentNumber(counter.getAndIncrement()) From a7556b0af57af7c2937a8a2541307f780b385cec Mon Sep 17 00:00:00 2001 From: "adel.ka" Date: Fri, 17 Oct 2025 17:44:20 +0300 Subject: [PATCH 41/45] SUPPORT-9368: proto file --- .../controller/ValidationFileController.java | 2 +- .../ervu/model/ValidateExportResponse.java | 17 +++++++++-------- .../src/main/resources/ProtoResponseData.proto | 13 +++++++++++++ 3 files changed, 23 insertions(+), 9 deletions(-) create mode 100644 backend/src/main/resources/ProtoResponseData.proto diff --git a/backend/src/main/java/ru/micord/ervu/controller/ValidationFileController.java b/backend/src/main/java/ru/micord/ervu/controller/ValidationFileController.java index 6384ea29..c3b160aa 100644 --- a/backend/src/main/java/ru/micord/ervu/controller/ValidationFileController.java +++ b/backend/src/main/java/ru/micord/ervu/controller/ValidationFileController.java @@ -4,7 +4,7 @@ import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import javax.servlet.http.HttpServletRequest; -import org.apache.kafka.shaded.com.google.protobuf.ByteString; +import com.google.protobuf.ByteString; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.io.InputStreamResource; diff --git a/backend/src/main/java/ru/micord/ervu/model/ValidateExportResponse.java b/backend/src/main/java/ru/micord/ervu/model/ValidateExportResponse.java index 8e8c8cd9..566456f0 100644 --- a/backend/src/main/java/ru/micord/ervu/model/ValidateExportResponse.java +++ b/backend/src/main/java/ru/micord/ervu/model/ValidateExportResponse.java @@ -1,17 +1,18 @@ package ru.micord.ervu.model; -import org.apache.kafka.shaded.com.google.protobuf.ByteString; -import org.apache.kafka.shaded.com.google.protobuf.InvalidProtocolBufferException; +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; +import rtl.pgs.ervu.lkrp.journal.proto.ResponseData; + public class ValidateExportResponse { - private String fileName; - private ByteString file; + private final String fileName; + private final ByteString file; public ValidateExportResponse(byte[] bytes) throws InvalidProtocolBufferException { - // TODO: Заменить ValidateExportResponseProto на реальный protobuf класс -// ValidateExportResponseProto protoResponse = ValidateExportResponseProto.parseFrom(bytes); -// this.fileName = protoResponse.getFileName(); -// this.file = protoResponse.getFile(); + ResponseData responseData = ResponseData.parseFrom(bytes); + fileName = responseData.getFileName(); + file = responseData.getFile(); } public String getFileName() { diff --git a/backend/src/main/resources/ProtoResponseData.proto b/backend/src/main/resources/ProtoResponseData.proto new file mode 100644 index 00000000..63979db3 --- /dev/null +++ b/backend/src/main/resources/ProtoResponseData.proto @@ -0,0 +1,13 @@ +syntax = "proto3"; + +package rtl.pgs.ervu.lkrp.journal; +import "google/protobuf/timestamp.proto"; + +option java_multiple_files = true; +option java_outer_classname = "LkrpResponse"; +option java_package = "rtl.pgs.ervu.lkrp.journal.proto"; + +message ResponseData { + string fileName = 1; + bytes file = 2; +} \ No newline at end of file From 6fac9be527ec52197dcabf808f06065b566c6b4b Mon Sep 17 00:00:00 2001 From: Zaripov Emil Date: Wed, 8 Oct 2025 13:46:02 +0300 Subject: [PATCH 42/45] ERVU-587 --- .../main/resources/business-model/Личный кабинет юр лица.page | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/src/main/resources/business-model/Личный кабинет юр лица.page b/resources/src/main/resources/business-model/Личный кабинет юр лица.page index 20deeacb..c6341a5c 100644 --- a/resources/src/main/resources/business-model/Личный кабинет юр лица.page +++ b/resources/src/main/resources/business-model/Личный кабинет юр лица.page @@ -329,7 +329,7 @@ tooltip - "Сведения о гражданине, подлежащем воинскому учету, и принятии (поступлении его на работу в образовательную организацию) или увольнении (отчислении) его с работы (из образовательной организации)" + "Сведения о гражданине, подлежащем воинскому учету, и принятии (поступлении) его на работу (в образовательную организацию) или увольнении (отчислении) его с работы (из образовательной организации)" From 5b36227437eb6ae7a85e3f9f73c463849120af8c Mon Sep 17 00:00:00 2001 From: Zaripov Emil Date: Wed, 8 Oct 2025 13:46:02 +0300 Subject: [PATCH 43/45] ERVU-587 --- .../main/resources/business-model/Личный кабинет юр лица.page | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/src/main/resources/business-model/Личный кабинет юр лица.page b/resources/src/main/resources/business-model/Личный кабинет юр лица.page index 0379756f..eb8f7f58 100644 --- a/resources/src/main/resources/business-model/Личный кабинет юр лица.page +++ b/resources/src/main/resources/business-model/Личный кабинет юр лица.page @@ -331,7 +331,7 @@ tooltip - "Сведения о гражданине, подлежащем воинскому учету, и принятии (поступлении его на работу в образовательную организацию) или увольнении (отчислении) его с работы (из образовательной организации)" + "Сведения о гражданине, подлежащем воинскому учету, и принятии (поступлении) его на работу (в образовательную организацию) или увольнении (отчислении) его с работы (из образовательной организации)" From e74ecab7e5dd3bcfc3c835e4c6fd8078e1616140 Mon Sep 17 00:00:00 2001 From: "adel.ka" Date: Sun, 19 Oct 2025 04:36:15 +0300 Subject: [PATCH 44/45] =?UTF-8?q?SUPPORT-9368:=20=D0=B4=D0=BE=D0=B1=D0=B0?= =?UTF-8?q?=D0=B2=D0=B8=D0=BB=20=D0=B8=D0=BD=D1=84=D1=83=20=D0=B4=D0=BB?= =?UTF-8?q?=D1=8F=20=D0=B0=D1=83=D0=B4=D0=B8=D1=82=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/ru/micord/ervu/audit/constants/AuditConstants.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/main/java/ru/micord/ervu/audit/constants/AuditConstants.java b/backend/src/main/java/ru/micord/ervu/audit/constants/AuditConstants.java index bc06ec83..b4559353 100644 --- a/backend/src/main/java/ru/micord/ervu/audit/constants/AuditConstants.java +++ b/backend/src/main/java/ru/micord/ervu/audit/constants/AuditConstants.java @@ -21,10 +21,10 @@ public final class AuditConstants { ); private static final Map downloadTypes = Map.of( - 1, "Выписка из журнала взаимодействия ЮЛ" + 1, "Выписка из журнала взаимодействия ЮЛ", + 2, "Выгрузка описания ошибок по непринятым записям" ); - private AuditConstants() { } From 49385d576621b109a2da40fdef02fe869aa66d45 Mon Sep 17 00:00:00 2001 From: "adel.ka" Date: Mon, 20 Oct 2025 09:02:35 +0300 Subject: [PATCH 45/45] =?UTF-8?q?SUPPORT-9368:=20=D1=80=D0=B5=D1=84=D0=B0?= =?UTF-8?q?=D0=BA=D1=82=D0=BE=D1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/ts/ervu/service/ValidateFileService.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/frontend/src/ts/ervu/service/ValidateFileService.ts b/frontend/src/ts/ervu/service/ValidateFileService.ts index d217a3b3..91ab4a3b 100644 --- a/frontend/src/ts/ervu/service/ValidateFileService.ts +++ b/frontend/src/ts/ervu/service/ValidateFileService.ts @@ -6,8 +6,7 @@ import {MessagesService} from "@webbpm/base-package"; providedIn: "root" }) export class ValidateFileService { - constructor(private httpClient: HttpClient, - private messageService: MessagesService) { + constructor(private httpClient: HttpClient) { } public exportFile(fileId: string) {