diff --git a/clearing-parent/registry-service/pom.xml b/clearing-parent/registry-service/pom.xml
index 90d1d2dbb..47737e986 100644
--- a/clearing-parent/registry-service/pom.xml
+++ b/clearing-parent/registry-service/pom.xml
@@ -37,6 +37,10 @@
com.fasterxml.jackson.core
jackson-databind
+
+ org.springframework.integration
+ spring-integration-sftp
+
org.springframework.boot
spring-boot-starter-test
@@ -51,6 +55,13 @@
test-clearing
test
+
+
+
+ com.opencsv
+ opencsv
+ 5.5.2
+
diff --git a/clearing-parent/registry-service/src/main/java/ru/spcex/clearing/registry/config/RegistryServiceConfig.java b/clearing-parent/registry-service/src/main/java/ru/spcex/clearing/registry/config/RegistryServiceConfig.java
new file mode 100644
index 000000000..e3239957e
--- /dev/null
+++ b/clearing-parent/registry-service/src/main/java/ru/spcex/clearing/registry/config/RegistryServiceConfig.java
@@ -0,0 +1,22 @@
+package ru.spcex.clearing.registry.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import ru.spcex.clearing.registry.config.settings.RegistryServiceSettings;
+
+import java.io.File;
+
+@Configuration
+public class RegistryServiceConfig {
+ @Bean("reportOutDir")
+ public File outDir(RegistryServiceSettings settings) {
+ File outFolder = new File(settings.getReportsStore().getLocalTempDir());
+ if (!outFolder.exists()) {
+ boolean mkDirOk = outFolder.mkdirs();
+ if (!mkDirOk) throw new IllegalStateException("Can't create output directory for reports, registry-service was terminated");
+ } else if (!outFolder.isDirectory()) {
+ throw new IllegalStateException("Output reports path from settings is not directory!");
+ }
+ return outFolder;
+ }
+}
diff --git a/clearing-parent/registry-service/src/main/java/ru/spcex/clearing/registry/config/SFTPReportsConfig.java b/clearing-parent/registry-service/src/main/java/ru/spcex/clearing/registry/config/SFTPReportsConfig.java
new file mode 100644
index 000000000..701d6bbcf
--- /dev/null
+++ b/clearing-parent/registry-service/src/main/java/ru/spcex/clearing/registry/config/SFTPReportsConfig.java
@@ -0,0 +1,100 @@
+package ru.spcex.clearing.registry.config;
+
+import com.jcraft.jsch.ChannelSftp;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.expression.common.LiteralExpression;
+import org.springframework.integration.annotation.Gateway;
+import org.springframework.integration.annotation.MessagingGateway;
+import org.springframework.integration.annotation.ServiceActivator;
+import org.springframework.integration.channel.DirectChannel;
+import org.springframework.integration.dsl.IntegrationFlow;
+import org.springframework.integration.dsl.IntegrationFlows;
+import org.springframework.integration.file.remote.session.CachingSessionFactory;
+import org.springframework.integration.file.remote.session.SessionFactory;
+import org.springframework.integration.sftp.gateway.SftpOutboundGateway;
+import org.springframework.integration.sftp.outbound.SftpMessageHandler;
+import org.springframework.integration.sftp.session.DefaultSftpSessionFactory;
+import org.springframework.integration.sftp.session.SftpFileInfo;
+import org.springframework.messaging.MessageChannel;
+import org.springframework.messaging.MessageHandler;
+import ru.spcex.clearing.registry.config.settings.RegistryServiceSettings;
+
+import java.io.File;
+import java.util.List;
+
+import static org.springframework.integration.file.remote.gateway.AbstractRemoteFileOutboundGateway.Command.LS;
+
+@Configuration
+public class SFTPReportsConfig {
+ private final RegistryServiceSettings settings;
+
+ public SFTPReportsConfig(RegistryServiceSettings settings) {
+ this.settings = settings;
+ }
+
+ @Bean("reportsSftpSessionFactory")
+ public SessionFactory sftpSessionFactory() {
+ DefaultSftpSessionFactory factory = new DefaultSftpSessionFactory(true);
+ factory.setHost(settings.getReportsStore().getServerIp());
+ factory.setPort(settings.getReportsStore().getServerPort());
+ factory.setUser(settings.getReportsStore().getUser());
+ factory.setPassword(settings.getReportsStore().getPassword());
+ factory.setAllowUnknownKeys(true);
+ return new CachingSessionFactory<>(factory);
+ }
+
+ @Bean("reportsSftpHandler")
+ @ServiceActivator(inputChannel = "reportsToSftpChannel")
+ public MessageHandler handler(@Qualifier("reportsSftpSessionFactory") SessionFactory sessionFactory) {
+ SftpMessageHandler handler = new SftpMessageHandler(sessionFactory);
+ handler.setRemoteDirectoryExpression(new LiteralExpression(settings.getReportsStore().getOutDir()));
+ handler.setAutoCreateDirectory(true);
+ handler.setFileNameGenerator(message -> {
+ if (message.getPayload() instanceof File) {
+ return ((File) message.getPayload()).getName();
+ } else {
+ throw new IllegalArgumentException("File must expected as payload.");
+ }
+ });
+ return handler;
+ }
+
+ @Bean("reportsListSftpChannel")
+ public MessageChannel listSftpChannel(@Qualifier("reportsSftpHandlerList") MessageHandler handler) {
+ DirectChannel dc = new DirectChannel();
+ dc.subscribe(handler);
+ return dc;
+ }
+
+ @Bean("reportsToSftpChannel")
+ public MessageChannel toSftpChannel(@Qualifier("reportsSftpHandler") MessageHandler handler) {
+ DirectChannel dc = new DirectChannel();
+ dc.subscribe(handler);
+ return dc;
+ }
+
+ @Bean("reportsSftpHandlerList")
+ @ServiceActivator(inputChannel = "reportsListSftpChannel")
+ public MessageHandler handlerList(@Qualifier("reportsSftpSessionFactory") SessionFactory sessionFactory) {
+ String expression = "'/%s'".formatted(settings.getReportsStore().getOutDir());
+ return new SftpOutboundGateway(sessionFactory, LS.getCommand(), expression);
+ }
+
+ @Bean("reportsSftpOutboundListFlow")
+ public IntegrationFlow sftpOutboundListFlow(@Qualifier("reportsSftpSessionFactory") SessionFactory sessionFactory) {
+ return IntegrationFlows.from("reportsListSftpChannel")
+ .handle(new SftpOutboundGateway(sessionFactory, "ls", "payload"))
+ .get();
+ }
+
+ @MessagingGateway(name = "reportsSftpGateway")
+ public interface ReportsGateway {
+ @Gateway(requestChannel = "reportsToSftpChannel")
+ void sendToSftp(File file);
+
+ @Gateway(requestChannel = "reportsListSftpChannel")
+ List listFiles(String dir);
+ }
+}
diff --git a/clearing-parent/registry-service/src/main/java/ru/spcex/clearing/registry/config/settings/RegistryServiceSettings.java b/clearing-parent/registry-service/src/main/java/ru/spcex/clearing/registry/config/settings/RegistryServiceSettings.java
index 797c0c13b..439d99cbb 100644
--- a/clearing-parent/registry-service/src/main/java/ru/spcex/clearing/registry/config/settings/RegistryServiceSettings.java
+++ b/clearing-parent/registry-service/src/main/java/ru/spcex/clearing/registry/config/settings/RegistryServiceSettings.java
@@ -14,6 +14,8 @@ public class RegistryServiceSettings {
private HazelcastClientParams hazelcast;
private KafkaConsumerSettings kafkaConsumer;
private KafkaProducerSettings kafkaProducer;
+ private ReportsStore reportsStore;
+ private boolean csvMode = false;
public HazelcastClientParams getHazelcast() {
return hazelcast;
@@ -38,4 +40,20 @@ public class RegistryServiceSettings {
public void setKafkaProducer(KafkaProducerSettings kafkaProducer) {
this.kafkaProducer = kafkaProducer;
}
+
+ public ReportsStore getReportsStore() {
+ return reportsStore;
+ }
+
+ public void setReportsStore(ReportsStore reportsStore) {
+ this.reportsStore = reportsStore;
+ }
+
+ public boolean isCsvMode() {
+ return csvMode;
+ }
+
+ public void setCsvMode(boolean csvMode) {
+ this.csvMode = csvMode;
+ }
}
diff --git a/clearing-parent/registry-service/src/main/java/ru/spcex/clearing/registry/config/settings/ReportsStore.java b/clearing-parent/registry-service/src/main/java/ru/spcex/clearing/registry/config/settings/ReportsStore.java
new file mode 100644
index 000000000..4415f3abf
--- /dev/null
+++ b/clearing-parent/registry-service/src/main/java/ru/spcex/clearing/registry/config/settings/ReportsStore.java
@@ -0,0 +1,68 @@
+package ru.spcex.clearing.registry.config.settings;
+
+public class ReportsStore {
+ private String outDir;
+ private String localTempDir;
+ private String user;
+ private String password;
+ private String serverIp;
+ private int serverPort;
+
+ public boolean isDeleteAfterSend() {
+ return deleteAfterSend;
+ }
+
+ public void setDeleteAfterSend(boolean deleteAfterSend) {
+ this.deleteAfterSend = deleteAfterSend;
+ }
+
+ private boolean deleteAfterSend;
+
+ public String getUser() {
+ return user;
+ }
+
+ public void setUser(String user) {
+ this.user = user;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ public String getServerIp() {
+ return serverIp;
+ }
+
+ public void setServerIp(String serverIp) {
+ this.serverIp = serverIp;
+ }
+
+ public int getServerPort() {
+ return serverPort;
+ }
+
+ public void setServerPort(int serverPort) {
+ this.serverPort = serverPort;
+ }
+
+ public String getOutDir() {
+ return outDir;
+ }
+
+ public void setOutDir(String outDir) {
+ this.outDir = outDir;
+ }
+
+ public String getLocalTempDir() {
+ return localTempDir;
+ }
+
+ public void setLocalTempDir(String localTempDir) {
+ this.localTempDir = localTempDir;
+ }
+}
diff --git a/clearing-parent/registry-service/src/main/java/ru/spcex/clearing/registry/reports/ReportUtils.java b/clearing-parent/registry-service/src/main/java/ru/spcex/clearing/registry/reports/ReportUtils.java
new file mode 100644
index 000000000..4bf999f7c
--- /dev/null
+++ b/clearing-parent/registry-service/src/main/java/ru/spcex/clearing/registry/reports/ReportUtils.java
@@ -0,0 +1,28 @@
+package ru.spcex.clearing.registry.reports;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import ru.spcex.clearing.registry.config.SFTPReportsConfig;
+
+import java.io.File;
+import java.util.Collection;
+
+public class ReportUtils {
+ private final static Logger log = LoggerFactory.getLogger(ReportUtils.class);
+
+ public static void sendFilesToSftp(Collection files,
+ SFTPReportsConfig.ReportsGateway reportsGateway,
+ boolean deleteAfterSend) {
+ for (File file : files) {
+ reportsGateway.sendToSftp(file);
+ if (deleteAfterSend) {
+ boolean delete = file.delete();
+ if (!delete) {
+ log.warn("Can't delete temp file {}", file.getAbsolutePath());
+ }
+ }
+ }
+ }
+
+
+}
diff --git a/clearing-parent/registry-service/src/main/java/ru/spcex/clearing/registry/reports/bean/CSVReport.java b/clearing-parent/registry-service/src/main/java/ru/spcex/clearing/registry/reports/bean/CSVReport.java
new file mode 100644
index 000000000..dea0634cc
--- /dev/null
+++ b/clearing-parent/registry-service/src/main/java/ru/spcex/clearing/registry/reports/bean/CSVReport.java
@@ -0,0 +1,4 @@
+package ru.spcex.clearing.registry.reports.bean;
+
+public interface CSVReport {
+}
diff --git a/clearing-parent/registry-service/src/main/java/ru/spcex/clearing/registry/reports/bean/DepoBalanceRegisterReport.java b/clearing-parent/registry-service/src/main/java/ru/spcex/clearing/registry/reports/bean/DepoBalanceRegisterReport.java
new file mode 100644
index 000000000..76fa6e752
--- /dev/null
+++ b/clearing-parent/registry-service/src/main/java/ru/spcex/clearing/registry/reports/bean/DepoBalanceRegisterReport.java
@@ -0,0 +1,109 @@
+package ru.spcex.clearing.registry.reports.bean;
+
+import com.opencsv.bean.CsvBindByName;
+import com.opencsv.bean.CsvBindByPosition;
+import com.opencsv.bean.CsvDate;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.LocalTime;
+
+public class DepoBalanceRegisterReport implements CSVReport {
+ @CsvBindByName(column = "Наименование Расчетного депозитария")
+ @CsvBindByPosition(position = 0)
+ private String houseName;
+
+ @CsvBindByName(column = "Номер отчета")
+ @CsvBindByPosition(position = 1)
+ private String number;
+
+ @CsvBindByName(column = "Дата")
+ @CsvBindByPosition(position = 2)
+ @CsvDate(value = "dd.MM.yyyy")
+ private LocalDate date;
+
+ @CsvBindByName(column = "Время")
+ @CsvBindByPosition(position = 3)
+ @CsvDate(value = "HH:mm:ss")
+ private LocalTime time;
+
+ @CsvBindByName(column = "Краткое наименование УК")
+ @CsvBindByPosition(position = 4)
+ private String company;
+
+ @CsvBindByName(column = "Номер торгового раздела счета депо/ код раздела «к размещению/выкупу»")
+ @CsvBindByPosition(position = 5)
+ private String account;
+
+ @CsvBindByName(column = "Код ЦБ")
+ @CsvBindByPosition(position = 6)
+ private String security;
+
+ @CsvBindByName(column = "Количество ЦБ")
+ @CsvBindByPosition(position = 7)
+ private BigDecimal sum;
+
+ public String getHouseName() {
+ return houseName;
+ }
+
+ public void setHouseName(String houseName) {
+ this.houseName = houseName;
+ }
+
+ public String getNumber() {
+ return number;
+ }
+
+ public void setNumber(String number) {
+ this.number = number;
+ }
+
+ public LocalDate getDate() {
+ return date;
+ }
+
+ public void setDate(LocalDate date) {
+ this.date = date;
+ }
+
+ public LocalTime getTime() {
+ return time;
+ }
+
+ public void setTime(LocalTime time) {
+ this.time = time;
+ }
+
+ public String getCompany() {
+ return company;
+ }
+
+ public void setCompany(String company) {
+ this.company = company;
+ }
+
+ public String getAccount() {
+ return account;
+ }
+
+ public void setAccount(String account) {
+ this.account = account;
+ }
+
+ public String getSecurity() {
+ return security;
+ }
+
+ public void setSecurity(String security) {
+ this.security = security;
+ }
+
+ public BigDecimal getSum() {
+ return sum;
+ }
+
+ public void setSum(BigDecimal sum) {
+ this.sum = sum;
+ }
+}
diff --git a/clearing-parent/registry-service/src/main/java/ru/spcex/clearing/registry/reports/bean/MoneyBalanceRegisterReport.java b/clearing-parent/registry-service/src/main/java/ru/spcex/clearing/registry/reports/bean/MoneyBalanceRegisterReport.java
new file mode 100644
index 000000000..0be588bc2
--- /dev/null
+++ b/clearing-parent/registry-service/src/main/java/ru/spcex/clearing/registry/reports/bean/MoneyBalanceRegisterReport.java
@@ -0,0 +1,110 @@
+package ru.spcex.clearing.registry.reports.bean;
+
+import com.opencsv.bean.CsvBindByName;
+import com.opencsv.bean.CsvBindByPosition;
+import com.opencsv.bean.CsvDate;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.LocalTime;
+
+public class MoneyBalanceRegisterReport implements CSVReport {
+ @CsvBindByName(column = "Наименование Расчетного депозитария")
+ @CsvBindByPosition(position = 0)
+ private String houseName;
+
+ @CsvBindByName(column = "Номер отчета")
+ @CsvBindByPosition(position = 1)
+ private String number;
+
+ @CsvBindByName(column = "Дата")
+ @CsvBindByPosition(position = 2)
+ @CsvDate(value = "dd.MM.yyyy")
+ private LocalDate date;
+
+ @CsvBindByName(column = "Время")
+ @CsvBindByPosition(position = 3)
+ @CsvDate(value = "HH:mm:ss")
+ private LocalTime time;
+
+ @CsvBindByName(column = "Код валюты")
+ @CsvBindByPosition(position = 4)
+ private String currency;
+
+ @CsvBindByName(column = "Краткое наименование УК")
+ @CsvBindByPosition(position = 5)
+ private String company;
+
+ @CsvBindByName(column = "Номер торгового /клирингового счета/ счета внутреннего учета СПВБ")
+ @CsvBindByPosition(position = 6)
+ private String account;
+
+ @CsvBindByName(column = "Остаток денежных средств")
+ @CsvBindByPosition(position = 7)
+ private BigDecimal sum;
+
+
+ public String getHouseName() {
+ return houseName;
+ }
+
+ public void setHouseName(String houseName) {
+ this.houseName = houseName;
+ }
+
+ public String getNumber() {
+ return number;
+ }
+
+ public void setNumber(String number) {
+ this.number = number;
+ }
+
+ public LocalDate getDate() {
+ return date;
+ }
+
+ public void setDate(LocalDate date) {
+ this.date = date;
+ }
+
+ public LocalTime getTime() {
+ return time;
+ }
+
+ public void setTime(LocalTime time) {
+ this.time = time;
+ }
+
+ public String getCurrency() {
+ return currency;
+ }
+
+ public void setCurrency(String currency) {
+ this.currency = currency;
+ }
+
+ public String getCompany() {
+ return company;
+ }
+
+ public void setCompany(String company) {
+ this.company = company;
+ }
+
+ public String getAccount() {
+ return account;
+ }
+
+ public void setAccount(String account) {
+ this.account = account;
+ }
+
+ public BigDecimal getSum() {
+ return sum;
+ }
+
+ public void setSum(BigDecimal sum) {
+ this.sum = sum;
+ }
+}
diff --git a/clearing-parent/registry-service/src/main/java/ru/spcex/clearing/registry/reports/builders/CSVReportBuilder.java b/clearing-parent/registry-service/src/main/java/ru/spcex/clearing/registry/reports/builders/CSVReportBuilder.java
new file mode 100644
index 000000000..4ab63cfb7
--- /dev/null
+++ b/clearing-parent/registry-service/src/main/java/ru/spcex/clearing/registry/reports/builders/CSVReportBuilder.java
@@ -0,0 +1,149 @@
+package ru.spcex.clearing.registry.reports.builders;
+
+import com.opencsv.CSVWriter;
+import com.opencsv.ICSVWriter;
+import com.opencsv.bean.*;
+import com.opencsv.exceptions.CsvBadConverterException;
+import com.opencsv.exceptions.CsvChainedException;
+import com.opencsv.exceptions.CsvFieldAssignmentException;
+import com.opencsv.exceptions.CsvRequiredFieldEmptyException;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import ru.clearing.classes.objects.BusinessObject;
+import ru.spcex.clearing.registry.reports.bean.CSVReport;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.Writer;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.*;
+
+/**
+ * Класс для построения отчетов в формате CSV
+ */
+public abstract class CSVReportBuilder {
+ protected final Logger log = LoggerFactory.getLogger(getClass());
+ protected static DateTimeFormatter dateTimeFormatter_YYMMDDHHmmssSSS = DateTimeFormatter.ofPattern("yyMMddHHmmssSSS");
+
+ /**
+ * Имя отчета (для имени файла)
+ */
+ protected abstract String reportName();
+
+ /**
+ * Преобразовать объект в строку отчета
+ */
+ protected abstract R toRow(I input);
+
+ /**
+ * Название отчета, используется для формирования имени отчета (без указания формата)
+ */
+ protected String getReportFilename() {
+ return "%s_%s".formatted(reportName(), dateTimeFormatter_YYMMDDHHmmssSSS.format(LocalDateTime.now()));
+ }
+
+ /**
+ * Формирование отчета на основе объектов
+ */
+ public File buildReport(Collection inputObj, File outFolder) {
+ assert outFolder != null && outFolder.isDirectory() && inputObj != null;
+ List rows = new ArrayList<>(inputObj.size());
+ for (I input : inputObj) rows.add(toRow(input));
+
+ File reportFile = new File(outFolder, normalizeFilenameForOS(getReportFilename() + ".csv"));
+
+ try (Writer writer = new FileWriter(reportFile);
+ ICSVWriter icsvWriter = new CustomCSVWriter(writer)) {
+ CustomStrategy customStrategy = new CustomStrategy<>();
+ customStrategy.setType(getReportClass());
+ StatefulBeanToCsv sbc = new StatefulBeanToCsvBuilder(icsvWriter)
+ .withMappingStrategy(customStrategy)
+ .withLineEnd("\n")
+ .withSeparator(',')
+ .withQuotechar('\"')
+ .build();
+ if (!rows.isEmpty()) sbc.write(rows);
+ else {
+ // Для записи заголовков в пустой файл
+ Constructor extends R> constructor = Objects.requireNonNull(getReportClass()).getDeclaredConstructor();
+ R obj = constructor.newInstance();
+ String[] headers = customStrategy.generateHeader(obj);
+ writer.write("\"" + String.join("\",\"", headers) + "\"");
+ }
+
+ } catch (Exception e) {
+ log.error("Can't create report", e);
+ return null;
+ }
+
+ return reportFile;
+ }
+
+ protected abstract Class getReportClass();
+
+ protected String normalizeFilenameForOS(String filename) {
+ if (System.getProperty("os.name").toLowerCase().contains("windows"))
+ return filename.replace(":", "_");
+ return filename;
+ }
+
+ static class CustomCSVWriter extends CSVWriter {
+ public CustomCSVWriter(Writer writer) {
+ super(writer);
+ }
+
+ @Override
+ protected void writeNext(String[] nextLine, boolean applyQuotesToAll, Appendable appendable) throws IOException {
+ if (nextLine != null) {
+ for (int i = 0; i < nextLine.length; i++) {
+ if (StringUtils.isEmpty(nextLine[i])) {
+ nextLine[i] = null;
+ }
+ }
+ }
+ super.writeNext(nextLine, applyQuotesToAll, appendable);
+ }
+ }
+
+ static class CustomStrategy extends ColumnPositionMappingStrategy {
+ public String[] generateHeader(T bean) throws CsvRequiredFieldEmptyException {
+ super.generateHeader(bean);
+ String[] headers = new String[this.getFieldMap().values().size()];
+ for (BeanField field : this.getFieldMap().values()) {
+ CsvBindByPosition positionAnnotation = field.getField().getAnnotation(CsvBindByPosition.class);
+ CsvBindByName nameAnnotation = field.getField().getAnnotation(CsvBindByName.class);
+ headers[positionAnnotation.position()] = nameAnnotation.column();
+ }
+ return headers;
+ }
+
+ @Override
+ public String[] transmuteBean(T bean) throws CsvFieldAssignmentException, CsvChainedException {
+ return super.transmuteBean(bean);
+ }
+
+ @Override
+ protected CsvConverter determineConverter(Field field, Class> elementType, String locale, String writeLocale, Class extends AbstractCsvConverter> customConverter) throws CsvBadConverterException {
+ CsvConverter csvConverter = super.determineConverter(field, elementType, locale, writeLocale, customConverter);
+ if (csvConverter instanceof ConverterNumber && field.getType() == BigDecimal.class) {
+ CsvNumber csvNumberAnnotation = field.getAnnotation(CsvNumber.class);
+ String formatValue = csvNumberAnnotation.value();
+ String writeFormat = csvNumberAnnotation.writeFormat();
+ csvConverter = new ConverterNumber(BigDecimal.class,
+ Locale.getDefault().toString(),
+ Locale.getDefault().toString(),
+ Locale.getDefault(),
+ formatValue,
+ writeFormat);
+ }
+ return csvConverter;
+ }
+
+ }
+}
diff --git a/clearing-parent/registry-service/src/main/java/ru/spcex/clearing/registry/reports/builders/DepoBalanceRegisterReportBuilder.java b/clearing-parent/registry-service/src/main/java/ru/spcex/clearing/registry/reports/builders/DepoBalanceRegisterReportBuilder.java
new file mode 100644
index 000000000..5ba136057
--- /dev/null
+++ b/clearing-parent/registry-service/src/main/java/ru/spcex/clearing/registry/reports/builders/DepoBalanceRegisterReportBuilder.java
@@ -0,0 +1,60 @@
+package ru.spcex.clearing.registry.reports.builders;
+
+import ru.clearing.classes.statics.data.company.Company;
+import ru.clearing.classes.statics.data.registry.Registry;
+import ru.spcex.clearing.imdg.IMDGDistributedNames;
+import ru.spcex.clearing.registry.reports.bean.DepoBalanceRegisterReport;
+import ru.spcex.platform.enumeration.AccountType;
+import ru.spcex.platform.imdg.api.Imdg;
+import ru.spcex.platform.imdg.api.ImdgProvider;
+import ru.spcex.platform.utils.enumeration.IEnumKey;
+
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.time.format.DateTimeFormatter;
+
+public class DepoBalanceRegisterReportBuilder extends CSVReportBuilder {
+ private final static DateTimeFormatter yyyyMMddFormat = DateTimeFormatter.ofPattern("yyyyMMdd");
+
+ private final Imdg companyImdg;
+
+ public DepoBalanceRegisterReportBuilder(ImdgProvider imdgProvider) {
+ companyImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_Company, Company.class);
+ }
+
+ @Override
+ protected String reportName() {
+ return "depo_balance_register";
+ }
+
+ @Override
+ protected Class getReportClass() {
+ return DepoBalanceRegisterReport.class;
+ }
+
+ @Override
+ protected DepoBalanceRegisterReport toRow(Registry input) {
+ LocalDate now = LocalDate.now();
+ LocalTime nowTime = LocalTime.now();
+ DepoBalanceRegisterReport entity = new DepoBalanceRegisterReport();
+
+ Company company = companyImdg.getSingleObjectByID(2L);
+ if (company != null) entity.setHouseName(company.getShortName());
+
+ entity.setNumber(yyyyMMddFormat.format(now));
+ entity.setDate(now);
+ entity.setTime(nowTime);
+
+ company = companyImdg.getSingleObjectByID(input.getCompanyId());
+ if (company != null) entity.setCompany(company.getShortName());
+
+ AccountType accountType = IEnumKey.getEnumByKey(AccountType.class, input.getAccountType());
+ if (accountType == AccountType.Clrn || accountType == AccountType.Info) entity.setAccount(input.getAccount());
+
+ entity.setSecurity(input.getSecuritySymbol());
+ entity.setSum(input.getBalance());
+
+ return entity;
+ }
+
+}
diff --git a/clearing-parent/registry-service/src/main/java/ru/spcex/clearing/registry/reports/builders/MoneyBalanceRegisterReportBuilder.java b/clearing-parent/registry-service/src/main/java/ru/spcex/clearing/registry/reports/builders/MoneyBalanceRegisterReportBuilder.java
new file mode 100644
index 000000000..5c9d8e5f6
--- /dev/null
+++ b/clearing-parent/registry-service/src/main/java/ru/spcex/clearing/registry/reports/builders/MoneyBalanceRegisterReportBuilder.java
@@ -0,0 +1,60 @@
+package ru.spcex.clearing.registry.reports.builders;
+
+import ru.clearing.classes.statics.data.company.Company;
+import ru.clearing.classes.statics.data.registry.Registry;
+import ru.spcex.clearing.imdg.IMDGDistributedNames;
+import ru.spcex.clearing.registry.reports.bean.MoneyBalanceRegisterReport;
+import ru.spcex.platform.enumeration.AccountType;
+import ru.spcex.platform.imdg.api.Imdg;
+import ru.spcex.platform.imdg.api.ImdgProvider;
+import ru.spcex.platform.utils.enumeration.IEnumKey;
+
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.time.format.DateTimeFormatter;
+
+public class MoneyBalanceRegisterReportBuilder extends CSVReportBuilder {
+ private final static DateTimeFormatter yyyyMMddFormat = DateTimeFormatter.ofPattern("yyyyMMdd");
+
+ private final Imdg companyImdg;
+
+ public MoneyBalanceRegisterReportBuilder(ImdgProvider imdgProvider) {
+ companyImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_Company, Company.class);
+ }
+
+ @Override
+ protected String reportName() {
+ return "money_balance_register";
+ }
+
+ @Override
+ protected Class getReportClass() {
+ return MoneyBalanceRegisterReport.class;
+ }
+
+ @Override
+ protected MoneyBalanceRegisterReport toRow(Registry input) {
+ LocalDate now = LocalDate.now();
+ LocalTime nowTime = LocalTime.now();
+ MoneyBalanceRegisterReport entity = new MoneyBalanceRegisterReport();
+
+ Company company = companyImdg.getSingleObjectByID(2L);
+ if (company != null) entity.setHouseName(company.getShortName());
+
+ entity.setNumber(yyyyMMddFormat.format(now));
+ entity.setDate(now);
+ entity.setTime(nowTime);
+ entity.setCurrency("RUR");
+
+ company = companyImdg.getSingleObjectByID(input.getCompanyId());
+ if (company != null) entity.setCompany(company.getShortName());
+
+ AccountType accountType = IEnumKey.getEnumByKey(AccountType.class, input.getAccountType());
+ if (accountType == AccountType.Clrn || accountType == AccountType.Info) entity.setAccount(input.getAccount());
+
+ entity.setSum(input.getBalance());
+
+ return entity;
+ }
+
+}
diff --git a/clearing-parent/registry-service/src/main/java/ru/spcex/clearing/registry/service/DepoBalanceRegisterService.java b/clearing-parent/registry-service/src/main/java/ru/spcex/clearing/registry/service/DepoBalanceRegisterService.java
index 9872262a4..b72d1a487 100644
--- a/clearing-parent/registry-service/src/main/java/ru/spcex/clearing/registry/service/DepoBalanceRegisterService.java
+++ b/clearing-parent/registry-service/src/main/java/ru/spcex/clearing/registry/service/DepoBalanceRegisterService.java
@@ -6,6 +6,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import ru.clearing.classes.statics.data.register.DepoBalanceRegister;
import ru.clearing.classes.statics.data.registry.Registry;
@@ -14,6 +15,10 @@ import ru.spcex.clearing.imdg.IMDGDistributedNames;
import ru.spcex.clearing.platform.messaging.domain.BaseRequest;
import ru.spcex.clearing.platform.messaging.domain.cud.schedule.LauncherCommandRequest;
import ru.spcex.clearing.platform.messaging.service.QueueConsumer;
+import ru.spcex.clearing.registry.config.SFTPReportsConfig;
+import ru.spcex.clearing.registry.config.settings.RegistryServiceSettings;
+import ru.spcex.clearing.registry.reports.ReportUtils;
+import ru.spcex.clearing.registry.reports.builders.DepoBalanceRegisterReportBuilder;
import ru.spcex.clearing.registry.util.PreClearMap;
import ru.spcex.platform.enumeration.AccountType;
import ru.spcex.platform.enumeration.RegistryTradingParams;
@@ -24,10 +29,13 @@ import ru.spcex.platform.imdg.api.predicate.ImdgPredicateBuilder;
import ru.spcex.platform.imdg.api.predicate.specific.RegistryCodeSqlBuilder;
import ru.spcex.platform.utils.enumeration.IEnumKey;
+import java.io.File;
import java.math.BigDecimal;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
+import java.util.ArrayList;
import java.util.Collection;
+import java.util.List;
import static ru.spcex.platform.enumeration.Task.createRegistry_GRRT;
@@ -39,14 +47,29 @@ public class DepoBalanceRegisterService extends QueueConsumer implements Initial
private final Imdg registryHistoryMap;
private final PreClearMap preClearMap;
+ private final DepoBalanceRegisterReportBuilder reportBuilder;
+ private final File outFolder;
+ private final RegistryServiceSettings settings;
+
+ private final SFTPReportsConfig.ReportsGateway reportsSftpGateway;
+ private final boolean deleteAfterSend;
+
@Autowired
public DepoBalanceRegisterService(Consumer kafkaQueue,
Producer kafkaProducer,
- ImdgProvider imdgProvider) {
+ ImdgProvider imdgProvider,
+ RegistryServiceSettings settings,
+ @Qualifier("reportOutDir") File outFolder,
+ SFTPReportsConfig.ReportsGateway reportsSftpGateway) {
super(kafkaQueue, kafkaProducer);
this.depoBalanceRegisterMap = imdgProvider.getImdg(IMDGDistributedNames.Map_DepoBalanceRegister, DepoBalanceRegister.class);
this.registryHistoryMap = imdgProvider.getImdg(IMDGDistributedNames.Map_RegistryHistory, RegistryHistory.class);
preClearMap = PreClearMap.instanceForInstantField(depoBalanceRegisterMap, "created");
+ reportBuilder = new DepoBalanceRegisterReportBuilder(imdgProvider);
+ this.outFolder = outFolder;
+ this.settings = settings;
+ this.reportsSftpGateway = reportsSftpGateway;
+ this.deleteAfterSend = settings.getReportsStore().isDeleteAfterSend();
}
@Override
@@ -58,6 +81,7 @@ public class DepoBalanceRegisterService extends QueueConsumer implements Initial
}
public void depoBalanceRegisterNew(BaseRequest userRequest) {
+ boolean csvMode = settings.isCsvMode();
log.debug("LauncherCommandRequest received from {}", createRegistry_GRRT.topic());
ImdgPredicate predicate = getPredicateForRegistries(userRequest.getRequestPayload().getCompanyId());
Collection registryHistories = registryHistoryMap.getCollectionObjectsByPredicate(predicate);
@@ -67,10 +91,16 @@ public class DepoBalanceRegisterService extends QueueConsumer implements Initial
return;
}
log.debug("Select {} Registry history by query {}", registryHistories.size(), predicate);
- preClearMap.preClearMap();
+ if (!csvMode) preClearMap.preClearMap();
+ List registryForReport = new ArrayList<>();
for (RegistryHistory registryHistory : registryHistories) {
if (BigDecimal.ZERO.compareTo(registryHistory.getObject().getDiffBalance()) != 0) continue;
- insertDepoBalanceRegister(registryHistory);
+ if (!csvMode) insertDepoBalanceRegister(registryHistory);
+ else registryForReport.add(registryHistory.getObject());
+ }
+ if (csvMode) {
+ File reportFile = reportBuilder.buildReport(registryForReport, outFolder);
+ ReportUtils.sendFilesToSftp(List.of(reportFile), reportsSftpGateway, deleteAfterSend);
}
log.debug("Successfully processed");
}
diff --git a/clearing-parent/registry-service/src/main/java/ru/spcex/clearing/registry/service/MoneyBalanceRegisterService.java b/clearing-parent/registry-service/src/main/java/ru/spcex/clearing/registry/service/MoneyBalanceRegisterService.java
index adf2bc9e9..134f91666 100644
--- a/clearing-parent/registry-service/src/main/java/ru/spcex/clearing/registry/service/MoneyBalanceRegisterService.java
+++ b/clearing-parent/registry-service/src/main/java/ru/spcex/clearing/registry/service/MoneyBalanceRegisterService.java
@@ -6,6 +6,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import ru.clearing.classes.statics.data.company.Company;
import ru.clearing.classes.statics.data.register.MoneyBalanceRegister;
@@ -15,6 +16,10 @@ import ru.spcex.clearing.imdg.IMDGDistributedNames;
import ru.spcex.clearing.platform.messaging.domain.BaseRequest;
import ru.spcex.clearing.platform.messaging.domain.cud.schedule.LauncherCommandRequest;
import ru.spcex.clearing.platform.messaging.service.QueueConsumer;
+import ru.spcex.clearing.registry.config.SFTPReportsConfig;
+import ru.spcex.clearing.registry.config.settings.RegistryServiceSettings;
+import ru.spcex.clearing.registry.reports.ReportUtils;
+import ru.spcex.clearing.registry.reports.builders.MoneyBalanceRegisterReportBuilder;
import ru.spcex.clearing.registry.util.PreClearMap;
import ru.spcex.platform.enumeration.AccountType;
import ru.spcex.platform.enumeration.RegistryTradingParams;
@@ -25,10 +30,13 @@ import ru.spcex.platform.imdg.api.predicate.ImdgPredicateBuilder;
import ru.spcex.platform.imdg.api.predicate.specific.RegistryCodeSqlBuilder;
import ru.spcex.platform.utils.enumeration.IEnumKey;
+import java.io.File;
import java.math.BigDecimal;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
+import java.util.ArrayList;
import java.util.Collection;
+import java.util.List;
import java.util.Map;
import static ru.spcex.platform.enumeration.Task.createRegistry_GBRR;
@@ -42,15 +50,31 @@ public class MoneyBalanceRegisterService extends QueueConsumer implements Initia
private final Imdg companyMap;
private final PreClearMap preClearMap;
+ private final MoneyBalanceRegisterReportBuilder reportBuilder;
+ private final File outFolder;
+ RegistryServiceSettings settings;
+
+ private final SFTPReportsConfig.ReportsGateway reportsSftpGateway;
+ private final boolean deleteAfterSend;
+
+
@Autowired
public MoneyBalanceRegisterService(Consumer kafkaQueue,
Producer kafkaProducer,
- ImdgProvider imdgProvider) {
+ ImdgProvider imdgProvider,
+ RegistryServiceSettings settings,
+ @Qualifier("reportOutDir") File outFolder,
+ SFTPReportsConfig.ReportsGateway reportsSftpGateway) {
super(kafkaQueue, kafkaProducer);
this.moneyBalanceRegisterMap = imdgProvider.getImdg(IMDGDistributedNames.Map_MoneyBalanceRegister, MoneyBalanceRegister.class);
this.registryHistoryMap = imdgProvider.getImdg(IMDGDistributedNames.Map_RegistryHistory, RegistryHistory.class);
this.companyMap = imdgProvider.getImdg(IMDGDistributedNames.Map_Company, Company.class);
preClearMap = PreClearMap.instanceForInstantField(moneyBalanceRegisterMap, "created");
+ reportBuilder = new MoneyBalanceRegisterReportBuilder(imdgProvider);
+ this.outFolder = outFolder;
+ this.settings = settings;
+ this.reportsSftpGateway = reportsSftpGateway;
+ this.deleteAfterSend = settings.getReportsStore().isDeleteAfterSend();
}
@Override
@@ -62,6 +86,7 @@ public class MoneyBalanceRegisterService extends QueueConsumer implements Initia
}
public void moneyBalanceRegisterNew(BaseRequest userRequest) {
+ boolean csvMode = settings.isCsvMode();
log.debug("LauncherCommandRequest received from {}", createRegistry_GBRR.topic());
ImdgPredicate predicate = getSqlForRegistryHistory(userRequest.getRequestPayload().getCompanyId());
Collection registryHistories = registryHistoryMap.getCollectionObjectsByPredicate(predicate);
@@ -70,10 +95,16 @@ public class MoneyBalanceRegisterService extends QueueConsumer implements Initia
return;
}
log.debug("Select {} Registry history by query {}", registryHistories.size(), predicate);
- preClearMap.preClearMap();
+ if (!csvMode) preClearMap.preClearMap();
+ List registryForReport = new ArrayList<>();
for (RegistryHistory registryHistory : registryHistories) {
if (BigDecimal.ZERO.compareTo(registryHistory.getObject().getDiffBalance()) != 0) continue;
- insertMoneyBalanceRegister(registryHistory);
+ if (!csvMode) insertMoneyBalanceRegister(registryHistory);
+ else registryForReport.add(registryHistory.getObject());
+ }
+ if (csvMode) {
+ File reportFile = reportBuilder.buildReport(registryForReport, outFolder);
+ ReportUtils.sendFilesToSftp(List.of(reportFile), reportsSftpGateway, deleteAfterSend);
}
log.debug("successfully processed");
}
diff --git a/clearing-parent/registry-service/src/main/resources/application.properties b/clearing-parent/registry-service/src/main/resources/application.properties
index 49b0bb9c0..78d638e78 100644
--- a/clearing-parent/registry-service/src/main/resources/application.properties
+++ b/clearing-parent/registry-service/src/main/resources/application.properties
@@ -17,4 +17,13 @@ registry-service.kafka-producer.acks=all
registry-service.kafka-producer.retries=0
registry-service.kafka-producer.batch-size=16384
registry-service.kafka-producer.linger-ms=1
-registry-service.kafka-producer.buffer-memory=33554432
\ No newline at end of file
+registry-service.kafka-producer.buffer-memory=33554432
+
+registry-service.csv-mode=true
+registry-service.reports-store.local-temp-dir=./reports_out_temp
+registry-service.reports-store.out-dir=reports
+registry-service.reports-store.user=tester
+registry-service.reports-store.password=password
+registry-service.reports-store.server-ip=10.230.238.53
+registry-service.reports-store.server-port=2222
+registry-service.reports-store.delete-after-send=true
\ No newline at end of file
diff --git a/clearing-parent/registry-service/src/test/java/ru/spcex/clearing/registry/config/KafkaConfigTest.java b/clearing-parent/registry-service/src/test/java/ru/spcex/clearing/registry/config/KafkaConfigTest.java
index 0ca299c99..5aaa2faa2 100644
--- a/clearing-parent/registry-service/src/test/java/ru/spcex/clearing/registry/config/KafkaConfigTest.java
+++ b/clearing-parent/registry-service/src/test/java/ru/spcex/clearing/registry/config/KafkaConfigTest.java
@@ -21,6 +21,8 @@ import ru.spcex.platform.imdg.api.Imdg;
import ru.spcex.platform.imdg.api.ImdgId;
import ru.spcex.platform.imdg.api.ImdgProvider;
+import java.io.File;
+
import static org.mockito.Mockito.mock;
import static ru.spcex.clearing.registry.utils.TestUtils.setFuture;
import static ru.spcex.clearing.test.config.KafkaTestConfig.producerCaptors;
@@ -58,4 +60,9 @@ public class KafkaConfigTest {
return mockProducer;
}
+ @Bean("outDir")
+ public File outDir() {
+ return new File("out");
+ }
+
}
diff --git a/clearing-parent/registry-service/src/test/java/ru/spcex/clearing/registry/config/SFTPTestConfig.java b/clearing-parent/registry-service/src/test/java/ru/spcex/clearing/registry/config/SFTPTestConfig.java
new file mode 100644
index 000000000..3eb5e0288
--- /dev/null
+++ b/clearing-parent/registry-service/src/test/java/ru/spcex/clearing/registry/config/SFTPTestConfig.java
@@ -0,0 +1,31 @@
+package ru.spcex.clearing.registry.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.integration.sftp.session.SftpFileInfo;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+@Configuration
+public class SFTPTestConfig {
+ @Bean
+ public SFTPReportsConfig.ReportsGateway reportsGateway(){
+ return new ReportsGateway();
+ }
+
+ public static class ReportsGateway implements SFTPReportsConfig.ReportsGateway {
+
+ @Override
+ public void sendToSftp(File file) {
+
+ }
+
+ @Override
+ public List listFiles(String dir) {
+ return new ArrayList<>();
+ }
+ }
+
+}
diff --git a/clearing-parent/registry-service/src/test/java/ru/spcex/clearing/registry/config/TestConfig.java b/clearing-parent/registry-service/src/test/java/ru/spcex/clearing/registry/config/TestConfig.java
new file mode 100644
index 000000000..4d61b0ad4
--- /dev/null
+++ b/clearing-parent/registry-service/src/test/java/ru/spcex/clearing/registry/config/TestConfig.java
@@ -0,0 +1,29 @@
+package ru.spcex.clearing.registry.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import ru.spcex.clearing.registry.config.settings.RegistryServiceSettings;
+import ru.spcex.clearing.registry.config.settings.ReportsStore;
+
+import java.io.File;
+
+@Configuration
+public class TestConfig {
+ @Bean
+ public RegistryServiceSettings testSettings() {
+ RegistryServiceSettings registryServiceSettings = new RegistryServiceSettings();
+ ReportsStore store = new ReportsStore();
+ store.setLocalTempDir("out");
+ registryServiceSettings.setReportsStore(store);
+ registryServiceSettings.setCsvMode(true);
+ return registryServiceSettings;
+ }
+
+ @Bean("reportOutDir")
+ public File reportOutDir() {
+ File outDir = new File("out");
+ outDir.mkdirs();
+ return outDir;
+ }
+
+}
diff --git a/clearing-parent/registry-service/src/test/java/ru/spcex/clearing/registry/service/DepoBalanceRegisterServiceTest.java b/clearing-parent/registry-service/src/test/java/ru/spcex/clearing/registry/service/DepoBalanceRegisterServiceTest.java
index ec13531f1..e4766d3e0 100644
--- a/clearing-parent/registry-service/src/test/java/ru/spcex/clearing/registry/service/DepoBalanceRegisterServiceTest.java
+++ b/clearing-parent/registry-service/src/test/java/ru/spcex/clearing/registry/service/DepoBalanceRegisterServiceTest.java
@@ -1,5 +1,6 @@
package ru.spcex.clearing.registry.service;
+import com.opencsv.exceptions.CsvValidationException;
import org.apache.kafka.clients.consumer.MockConsumer;
import org.apache.kafka.clients.producer.Producer;
import org.junit.jupiter.api.MethodOrderer;
@@ -7,6 +8,7 @@ import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
import org.junit.jupiter.api.extension.ExtendWith;
+import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration;
@@ -16,7 +18,10 @@ import ru.clearing.classes.statics.data.registry.Registry;
import ru.clearing.classes.statics.data.registry.RegistryHistory;
import ru.spcex.clearing.imdg.IMDGDistributedNames;
import ru.spcex.clearing.platform.messaging.domain.cud.schedule.LauncherCommandRequest;
+import ru.spcex.clearing.registry.config.SFTPTestConfig;
+import ru.spcex.clearing.registry.config.TestConfig;
import ru.spcex.clearing.registry.config.ValidationConfig;
+import ru.spcex.clearing.registry.config.settings.RegistryServiceSettings;
import ru.spcex.clearing.test.TestUtils;
import ru.spcex.clearing.test.config.ImdgTestConfig;
import ru.spcex.clearing.test.config.KafkaTestConfig;
@@ -24,9 +29,11 @@ import ru.spcex.platform.enumeration.*;
import ru.spcex.platform.imdg.api.Imdg;
import ru.spcex.platform.imdg.api.ImdgProvider;
-import javax.annotation.PostConstruct;
+import java.io.File;
+import java.io.IOException;
import java.math.BigDecimal;
import java.time.Instant;
+import java.util.Arrays;
import java.util.Collection;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -38,9 +45,11 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
DepoBalanceRegisterService.class,
ImdgTestConfig.class,
KafkaTestConfig.class,
+ TestConfig.class,
+ SFTPTestConfig.class,
})
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
-class DepoBalanceRegisterServiceTest {
+class DepoBalanceRegisterServiceTest implements InitializingBean {
private static int requestCount = 0;
private static final Long companyId = 1L;
@@ -63,15 +72,25 @@ class DepoBalanceRegisterServiceTest {
@Qualifier("mockProducer")
private Producer producer;
- @PostConstruct
- private void init() {
+ @Autowired
+ private RegistryServiceSettings settings;
+
+ @Autowired
+ @Qualifier("reportOutDir")
+ private File outFolder;
+
+
+ @Override
+ public void afterPropertiesSet() throws Exception {
ImdgTestConfig.waitAvailableImdgProviderAndAddAdminWithDefaultId();
//initializing of imdg
registryHistoryMap = imdgProvider.getImdg(IMDGDistributedNames.Map_RegistryHistory, RegistryHistory.class);
depoBalanceRegisterMap = imdgProvider.getImdg(IMDGDistributedNames.Map_DepoBalanceRegister, DepoBalanceRegister.class);
// initialing objects for imdg repos
+ }
+ private RegistryHistory init() {
Registry registry = new Registry();
registry.setRegistryDesignation(RegistryDesignation.A.getKey());
registry.setRegistryInstrumentType(RegistryInstrumentType.S.getKey());
@@ -87,11 +106,15 @@ class DepoBalanceRegisterServiceTest {
registryHistory.setObject(registry);
registryHistory.setEventTime(Instant.now());
registryHistoryMap.insert(registryHistory);
+ return registryHistory;
}
+
@Test
@Order(1)
void executionRegisterNewForEventTime() {
-
+ RegistryHistory source = init();
+ boolean oldCsvMode = settings.isCsvMode();
+ settings.setCsvMode(false);
//making launcherCommand
LauncherCommandRequest launcherCommandRequest = new LauncherCommandRequest();
@@ -112,5 +135,37 @@ class DepoBalanceRegisterServiceTest {
assertEquals(account, depoBalanceRegister.getDepoCode());
assertEquals(balance, depoBalanceRegister.getQuantity());
assertEquals(SECURITY_SYMBOL, depoBalanceRegister.getSecuritySymbol());
+ settings.setCsvMode(oldCsvMode);
+ registryHistoryMap.delete(source);
}
+
+ @Test
+ @Order(2)
+ void executionRegisterNewForEventTime_Csv() throws CsvValidationException, IOException {
+ RegistryHistory source = init();
+ ReportTestUtils.resetDir(outFolder);
+ boolean oldCsvMode = settings.isCsvMode();
+ settings.setCsvMode(true);
+ //making launcherCommand
+ LauncherCommandRequest launcherCommandRequest = new LauncherCommandRequest();
+
+ String jsonString = TestUtils.getJsonStringForSystem(launcherCommandRequest, 0L);
+
+ TestUtils.addRecordToKafka((MockConsumer) depoBalanceRegisterService.getConsumer(),
+ Task.createRegistry_GRRT.topic(),
+ requestCount++,
+ 0,
+ jsonString);
+
+ TestUtils.waitingSendAndCheckRecord(0L, producer);
+
+ File outFile = outFolder.listFiles()[0];
+ File expectedFile = new File(getClass().getClassLoader().getResource("expected_reports/depo_balance_register_expected.csv").getFile());
+ ReportTestUtils.compareCSVFiles(expectedFile, outFile, Arrays.asList("Дата", "Время", "Номер отчета"));
+
+ settings.setCsvMode(oldCsvMode);
+ ReportTestUtils.deleteDir(outFolder);
+ registryHistoryMap.delete(source);
+ }
+
}
\ No newline at end of file
diff --git a/clearing-parent/registry-service/src/test/java/ru/spcex/clearing/registry/service/MoneyBalanceRegisterServiceTest.java b/clearing-parent/registry-service/src/test/java/ru/spcex/clearing/registry/service/MoneyBalanceRegisterServiceTest.java
index c4482a65c..285a14b08 100644
--- a/clearing-parent/registry-service/src/test/java/ru/spcex/clearing/registry/service/MoneyBalanceRegisterServiceTest.java
+++ b/clearing-parent/registry-service/src/test/java/ru/spcex/clearing/registry/service/MoneyBalanceRegisterServiceTest.java
@@ -1,5 +1,6 @@
package ru.spcex.clearing.registry.service;
+import com.opencsv.exceptions.CsvValidationException;
import org.apache.kafka.clients.consumer.MockConsumer;
import org.apache.kafka.clients.producer.Producer;
import org.junit.jupiter.api.MethodOrderer;
@@ -7,6 +8,7 @@ import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
import org.junit.jupiter.api.extension.ExtendWith;
+import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration;
@@ -17,7 +19,10 @@ import ru.clearing.classes.statics.data.registry.Registry;
import ru.clearing.classes.statics.data.registry.RegistryHistory;
import ru.spcex.clearing.imdg.IMDGDistributedNames;
import ru.spcex.clearing.platform.messaging.domain.cud.schedule.LauncherCommandRequest;
+import ru.spcex.clearing.registry.config.SFTPTestConfig;
+import ru.spcex.clearing.registry.config.TestConfig;
import ru.spcex.clearing.registry.config.ValidationConfig;
+import ru.spcex.clearing.registry.config.settings.RegistryServiceSettings;
import ru.spcex.clearing.test.TestUtils;
import ru.spcex.clearing.test.config.ImdgTestConfig;
import ru.spcex.clearing.test.config.KafkaTestConfig;
@@ -25,10 +30,12 @@ import ru.spcex.platform.enumeration.*;
import ru.spcex.platform.imdg.api.Imdg;
import ru.spcex.platform.imdg.api.ImdgProvider;
-import javax.annotation.PostConstruct;
+import java.io.File;
+import java.io.IOException;
import java.math.BigDecimal;
import java.time.Instant;
import java.time.LocalDate;
+import java.util.Arrays;
import java.util.Collection;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -39,9 +46,11 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
MoneyBalanceRegisterService.class,
ImdgTestConfig.class,
KafkaTestConfig.class,
+ TestConfig.class,
+ SFTPTestConfig.class,
})
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
-class MoneyBalanceRegisterServiceTest {
+class MoneyBalanceRegisterServiceTest implements InitializingBean {
private static final String REGISTRY_DESIGNATION = RegistryDesignation.A.getKey();
private static final String REGISTRY_INSTRUMENT_TYPE = RegistryInstrumentType.M.getKey();
private static final String REGISTRY_FULL_NAME = "MY FULLNAME";
@@ -72,18 +81,28 @@ class MoneyBalanceRegisterServiceTest {
@Qualifier("mockProducer")
private Producer producer;
- @PostConstruct
- private void init() {
+ @Autowired
+ private RegistryServiceSettings settings;
+
+ @Autowired
+ @Qualifier("reportOutDir")
+ private File outFolder;
+
+ @Override
+ public void afterPropertiesSet() throws Exception {
ImdgTestConfig.waitAvailableImdgProviderAndAddAdminWithDefaultId();
moneyBalanceRegisterMap = imdgProvider.getImdg(IMDGDistributedNames.Map_MoneyBalanceRegister, MoneyBalanceRegister.class);
companyMap = imdgProvider.getImdg(IMDGDistributedNames.Map_Company, Company.class);
registryHistoryMap = imdgProvider.getImdg(IMDGDistributedNames.Map_RegistryHistory, RegistryHistory.class);
+
+ Company company = new Company();
+ company.setId(2L);
+ company.setShortName(COMPANY_SHORTNAME);
+ companyMap.insert(company);
}
- @Test
- @Order(1)
- void executionRegisterNewForClearingDate() {
+ private RegistryHistory init() {
Registry registry = new Registry();
registry.setTradingClearingRegistry("not null value");
registry.setDiffBalance(BigDecimal.ZERO);
@@ -105,10 +124,15 @@ class MoneyBalanceRegisterServiceTest {
registryHistory.setEventTime(Instant.now());
registryHistoryMap.insert(registryHistory);
- Company company = new Company();
- company.setId(2L);
- company.setShortName(COMPANY_SHORTNAME);
- companyMap.insert(company);
+ return registryHistory;
+ }
+
+ @Test
+ @Order(1)
+ void executionRegisterNewForClearingDate() {
+ RegistryHistory source = init();
+ boolean oldCsvMode = settings.isCsvMode();
+ settings.setCsvMode(false);
//making launcherCommand
LauncherCommandRequest launcherCommandRequest = new LauncherCommandRequest();
@@ -130,5 +154,38 @@ class MoneyBalanceRegisterServiceTest {
assertEquals(REGISTRY_BALANCE, moneyBalanceRegister.getRemainderSum());
assertEquals(REGISTRY_ACCOUNT, moneyBalanceRegister.getAccount());
assertEquals(COMPANY_SHORTNAME, moneyBalanceRegister.getSetHouseName());
+
+ settings.setCsvMode(oldCsvMode);
+ registryHistoryMap.delete(source);
+ }
+
+
+ @Test
+ @Order(2)
+ void executionRegisterNewForClearingDate_CSV() throws CsvValidationException, IOException {
+ RegistryHistory source = init();
+ boolean oldCsvMode = settings.isCsvMode();
+ settings.setCsvMode(true);
+
+ //making launcherCommand
+ LauncherCommandRequest launcherCommandRequest = new LauncherCommandRequest();
+ launcherCommandRequest.setCompanyId(REGISTRY_COMPANY_ID);
+ String jsonString = TestUtils.getJsonStringForSystem(launcherCommandRequest, 0L);
+
+ TestUtils.addRecordToKafka((MockConsumer) moneyBalanceRegisterService.getConsumer(),
+ Task.createRegistry_GBRR.topic(),
+ requestCount++,
+ 0,
+ jsonString);
+
+ TestUtils.waitingSendAndCheckRecord(0L, producer);
+
+ File outFile = outFolder.listFiles()[0];
+ File expectedFile = new File(getClass().getClassLoader().getResource("expected_reports/money_balance_register_expected.csv").getFile());
+ ReportTestUtils.compareCSVFiles(expectedFile, outFile, Arrays.asList("Дата", "Время", "Номер отчета"));
+
+ settings.setCsvMode(oldCsvMode);
+ ReportTestUtils.deleteDir(outFolder);
+ registryHistoryMap.delete(source);
}
}
\ No newline at end of file
diff --git a/clearing-parent/registry-service/src/test/java/ru/spcex/clearing/registry/service/ReportTestUtils.java b/clearing-parent/registry-service/src/test/java/ru/spcex/clearing/registry/service/ReportTestUtils.java
new file mode 100644
index 000000000..a0b0c28d6
--- /dev/null
+++ b/clearing-parent/registry-service/src/test/java/ru/spcex/clearing/registry/service/ReportTestUtils.java
@@ -0,0 +1,106 @@
+package ru.spcex.clearing.registry.service;
+
+import com.opencsv.CSVParser;
+import com.opencsv.CSVParserBuilder;
+import com.opencsv.CSVReader;
+import com.opencsv.CSVReaderBuilder;
+import com.opencsv.exceptions.CsvValidationException;
+import org.apache.commons.io.FileUtils;
+import org.junit.jupiter.api.Assertions;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.*;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class ReportTestUtils {
+
+ public static void compareCSVFiles(File expectedFile, File actualFile, List columnsForSkip) throws IOException, CsvValidationException {
+ if (columnsForSkip == null) columnsForSkip = Collections.emptyList();
+ CSVParser csvParser = new CSVParserBuilder().withSeparator(',').withQuoteChar('\"').build();
+ CSVReader expectedCSVReader = new CSVReaderBuilder(new FileReader(expectedFile)).withCSVParser(csvParser).build();
+ CSVReader actualCSVReader = new CSVReaderBuilder(new FileReader(actualFile)).withCSVParser(csvParser).build();
+
+ Map expectedValueForColumn = new HashMap<>();
+ Map actualValueForColumn = new HashMap<>();
+
+ String[] expectedHeaders = expectedCSVReader.readNext();
+ String[] actualHeaders = actualCSVReader.readNext();
+ assertEquals(expectedHeaders.length, actualHeaders.length);
+
+ for (int headerIdx = 0; headerIdx < expectedHeaders.length; headerIdx++) {
+ String expectedHeader = expectedHeaders[headerIdx];
+ String actualHeader = actualHeaders[headerIdx];
+ if (!columnsForSkip.contains(expectedHeader)) expectedValueForColumn.put(expectedHeader, headerIdx);
+ if (!columnsForSkip.contains(actualHeader)) actualValueForColumn.put(actualHeader, headerIdx);
+ }
+
+ List expectedLines = new ArrayList<>();
+ do {
+ String[] expectedLine = expectedCSVReader.readNext();
+ if (expectedLine == null) break;
+ expectedLines.add(expectedLine);
+ } while (true);
+
+ List actualLines = new ArrayList<>();
+ do {
+ String[] actualLine = actualCSVReader.readNext();
+ if (actualLine == null) break;
+ actualLines.add(actualLine);
+ } while (true);
+ if (expectedLines.size() != actualLines.size()) {
+ Assertions.fail("expected and actual lines not equal");
+ }
+
+ for (String[] actualLine : actualLines) {
+ boolean findEqual = false;
+ for (String[] expectedLine : expectedLines) {
+ boolean lineEqual = true;
+ for (String expectedHeader : expectedValueForColumn.keySet()) {
+ int expectedIdx = expectedValueForColumn.get(expectedHeader);
+ int actualIdx = actualValueForColumn.get(expectedHeader);
+ String expected = expectedLine[expectedIdx];
+ String actual = actualLine[actualIdx];
+ lineEqual = expected.equals(actual);
+ if (!lineEqual) break;
+ }
+ if (lineEqual) {
+ findEqual = true;
+ break;
+ }
+ }
+ if (findEqual) continue;
+ Assertions.fail("For actual line '%s' not found expected line".formatted(String.join(", ", actualLine)));
+ }
+
+ List errors = new ArrayList<>();
+ for (int idx = 0; idx < expectedHeaders.length; idx++) {
+ if (!Objects.equals(expectedHeaders[idx], actualHeaders[idx])) {
+ errors.add("expected header (%s) for idx = %d not equal actual header (%s)".formatted(
+ expectedHeaders[idx],
+ idx,
+ actualHeaders[idx])
+ );
+ }
+ }
+ if (!errors.isEmpty()) {
+ Assertions.fail("Files equal, but header sequence broken, cause: %s".formatted(String.join(",", errors)));
+ }
+
+ expectedCSVReader.close();
+ actualCSVReader.close();
+ }
+
+ public static void resetDir(File folder) throws IOException {
+ FileUtils.deleteDirectory(folder);
+ folder = new File("out");
+ folder.mkdirs();
+ }
+
+ public static void deleteDir(File folder) throws IOException {
+ FileUtils.deleteDirectory(folder);
+ }
+
+}
diff --git a/clearing-parent/registry-service/src/test/resources/expected_reports/depo_balance_register_expected.csv b/clearing-parent/registry-service/src/test/resources/expected_reports/depo_balance_register_expected.csv
new file mode 100644
index 000000000..e5a4204ac
--- /dev/null
+++ b/clearing-parent/registry-service/src/test/resources/expected_reports/depo_balance_register_expected.csv
@@ -0,0 +1,2 @@
+"Наименование Расчетного депозитария","Номер отчета","Дата","Время","Краткое наименование УК","Номер торгового раздела счета депо/ код раздела «к размещению/выкупу»","Код ЦБ","Количество ЦБ"
+,"20231228","28.12.2023","18:54:21",,,"SECURITY_SYMBOL","1"
diff --git a/clearing-parent/registry-service/src/test/resources/expected_reports/money_balance_register_expected.csv b/clearing-parent/registry-service/src/test/resources/expected_reports/money_balance_register_expected.csv
new file mode 100644
index 000000000..eb4bf85f1
--- /dev/null
+++ b/clearing-parent/registry-service/src/test/resources/expected_reports/money_balance_register_expected.csv
@@ -0,0 +1,2 @@
+"Наименование Расчетного депозитария","Номер отчета","Дата","Время","Код валюты","Краткое наименование УК","Номер торгового /клирингового счета/ счета внутреннего учета СПВБ","Остаток денежных средств"
+"ANY VALUE","20231229","29.12.2023","10:34:29","RUR",,"CLRN VALUE","123411"