This commit is contained in:
akulikov 2023-12-29 10:42:32 +03:00
parent 0f65c05f08
commit fd2ba48189
23 changed files with 1121 additions and 23 deletions

View file

@ -37,6 +37,10 @@
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-sftp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
@ -51,6 +55,13 @@
<artifactId>test-clearing</artifactId>
<scope>test</scope>
</dependency>
<!-- CSV -->
<dependency>
<groupId>com.opencsv</groupId>
<artifactId>opencsv</artifactId>
<version>5.5.2</version>
</dependency>
</dependencies>
<build>

View file

@ -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;
}
}

View file

@ -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<ChannelSftp.LsEntry> 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<ChannelSftp.LsEntry> 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<ChannelSftp.LsEntry> sessionFactory) {
String expression = "'/%s'".formatted(settings.getReportsStore().getOutDir());
return new SftpOutboundGateway(sessionFactory, LS.getCommand(), expression);
}
@Bean("reportsSftpOutboundListFlow")
public IntegrationFlow sftpOutboundListFlow(@Qualifier("reportsSftpSessionFactory") SessionFactory<ChannelSftp.LsEntry> 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<SftpFileInfo> listFiles(String dir);
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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<File> 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());
}
}
}
}
}

View file

@ -0,0 +1,4 @@
package ru.spcex.clearing.registry.reports.bean;
public interface CSVReport {
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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<R extends CSVReport, I extends BusinessObject> {
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<I> inputObj, File outFolder) {
assert outFolder != null && outFolder.isDirectory() && inputObj != null;
List<R> 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<R> customStrategy = new CustomStrategy<>();
customStrategy.setType(getReportClass());
StatefulBeanToCsv<R> sbc = new StatefulBeanToCsvBuilder<R>(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<R> 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<T> extends ColumnPositionMappingStrategy<T> {
public String[] generateHeader(T bean) throws CsvRequiredFieldEmptyException {
super.generateHeader(bean);
String[] headers = new String[this.getFieldMap().values().size()];
for (BeanField<T, Integer> 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;
}
}
}

View file

@ -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<DepoBalanceRegisterReport, Registry> {
private final static DateTimeFormatter yyyyMMddFormat = DateTimeFormatter.ofPattern("yyyyMMdd");
private final Imdg<Company> companyImdg;
public DepoBalanceRegisterReportBuilder(ImdgProvider imdgProvider) {
companyImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_Company, Company.class);
}
@Override
protected String reportName() {
return "depo_balance_register";
}
@Override
protected Class<DepoBalanceRegisterReport> 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;
}
}

View file

@ -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<MoneyBalanceRegisterReport, Registry> {
private final static DateTimeFormatter yyyyMMddFormat = DateTimeFormatter.ofPattern("yyyyMMdd");
private final Imdg<Company> companyImdg;
public MoneyBalanceRegisterReportBuilder(ImdgProvider imdgProvider) {
companyImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_Company, Company.class);
}
@Override
protected String reportName() {
return "money_balance_register";
}
@Override
protected Class<MoneyBalanceRegisterReport> 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;
}
}

View file

@ -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<RegistryHistory> registryHistoryMap;
private final PreClearMap<DepoBalanceRegister> 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<String, Object> kafkaQueue,
Producer<String, Object> 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<LauncherCommandRequest> userRequest) {
boolean csvMode = settings.isCsvMode();
log.debug("LauncherCommandRequest received from {}", createRegistry_GRRT.topic());
ImdgPredicate predicate = getPredicateForRegistries(userRequest.getRequestPayload().getCompanyId());
Collection<RegistryHistory> 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<Registry> 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");
}

View file

@ -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<Company> companyMap;
private final PreClearMap<MoneyBalanceRegister> preClearMap;
private final MoneyBalanceRegisterReportBuilder reportBuilder;
private final File outFolder;
RegistryServiceSettings settings;
private final SFTPReportsConfig.ReportsGateway reportsSftpGateway;
private final boolean deleteAfterSend;
@Autowired
public MoneyBalanceRegisterService(Consumer<String, Object> kafkaQueue,
Producer<String, Object> 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<LauncherCommandRequest> userRequest) {
boolean csvMode = settings.isCsvMode();
log.debug("LauncherCommandRequest received from {}", createRegistry_GBRR.topic());
ImdgPredicate predicate = getSqlForRegistryHistory(userRequest.getRequestPayload().getCompanyId());
Collection<RegistryHistory> 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<Registry> 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");
}

View file

@ -18,3 +18,12 @@ 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
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

View file

@ -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");
}
}

View file

@ -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<SftpFileInfo> listFiles(String dir) {
return new ArrayList<>();
}
}
}

View file

@ -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;
}
}

View file

@ -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<String, Object> 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);
}
}

View file

@ -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<String, Object> 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);
}
}

View file

@ -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<String> 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<String, Integer> expectedValueForColumn = new HashMap<>();
Map<String, Integer> 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<String[]> expectedLines = new ArrayList<>();
do {
String[] expectedLine = expectedCSVReader.readNext();
if (expectedLine == null) break;
expectedLines.add(expectedLine);
} while (true);
List<String[]> 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<String> 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);
}
}

View file

@ -0,0 +1,2 @@
"Наименование Расчетного депозитария","Номер отчета","Дата","Время","Краткое наименование УК","Номер торгового раздела счета депо/ код раздела «к размещению/выкупу»","Код ЦБ","Количество ЦБ"
,"20231228","28.12.2023","18:54:21",,,"SECURITY_SYMBOL","1"
1 Наименование Расчетного депозитария Номер отчета Дата Время Краткое наименование УК Номер торгового раздела счета депо/ код раздела «к размещению/выкупу» Код ЦБ Количество ЦБ
2 20231228 28.12.2023 18:54:21 SECURITY_SYMBOL 1

View file

@ -0,0 +1,2 @@
"Наименование Расчетного депозитария","Номер отчета","Дата","Время","Код валюты","Краткое наименование УК","Номер торгового /клирингового счета/ счета внутреннего учета СПВБ","Остаток денежных средств"
"ANY VALUE","20231229","29.12.2023","10:34:29","RUR",,"CLRN VALUE","123411"
1 Наименование Расчетного депозитария Номер отчета Дата Время Код валюты Краткое наименование УК Номер торгового /клирингового счета/ счета внутреннего учета СПВБ Остаток денежных средств
2 ANY VALUE 20231229 29.12.2023 10:34:29 RUR CLRN VALUE 123411