Merge branch 'swt_exporter' into dev

# Conflicts:
#	platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/domain/Consts.java
This commit is contained in:
AKurakin 2023-05-26 15:17:18 +03:00
commit 5df646b6e9
26 changed files with 1128 additions and 0 deletions

View file

@ -38,6 +38,7 @@
<module>cleaning-builders</module>
<module>trade-importer</module>
<module>lim-exporter</module>
<module>swt-exporter</module>
</modules>
<properties>

View file

@ -0,0 +1,112 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>swt-exporter</artifactId>
<name>Swt exporter</name>
<version>SPCEX-1.0.0.0</version>
<packaging>jar</packaging>
<parent>
<groupId>ru.spcex.clearing</groupId>
<artifactId>clearing-parent</artifactId>
<version>SPCEX-1.0.0.0</version>
</parent>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<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>ru.spcex.clearing</groupId>
<artifactId>classes</artifactId>
<version>SPCEX-1.0.0.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>ru.spcex.platform</groupId>
<artifactId>platform-messaging</artifactId>
</dependency>
<dependency>
<groupId>ru.spcex.platform</groupId>
<artifactId>platform-imdg-api-hazelcast-impl</artifactId>
</dependency>
<dependency>
<groupId>ru.spcex.platform</groupId>
<artifactId>platform-enum</artifactId>
</dependency>
<!-- TEST -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ru.spcex.clearing</groupId>
<artifactId>test-clearing</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<excludes>
<exclude>application.properties</exclude>
</excludes>
<filtering>false</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
<configuration>
<finalName>${project.artifactId}</finalName>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.21.0</version>
<dependencies>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-surefire-provider</artifactId>
<version>1.2.0-M1</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.2.0-M1</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
</project>

View file

@ -0,0 +1,18 @@
package ru.spcex.clearing.swt.exporter;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
@SpringBootApplication
public class SwtExportApplication {
public static void main(String[] args) {
try {
SpringApplicationBuilder builder = new SpringApplicationBuilder(SwtExportApplication.class);
builder.run(args);
} catch (Throwable e) {
LoggerFactory.getLogger(SwtExportApplication.class).error("Swt-exporter start failed: {} -> {}", e.getClass().getSimpleName(), e.getMessage());
System.exit(-1);
}
}
}

View file

@ -0,0 +1,44 @@
package ru.spcex.clearing.swt.exporter.config;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import ru.spcex.clearing.swt.exporter.config.settings.ExportSwtServiceSettings;
import ru.spcex.platform.imdg.api.ImdgProvider;
import ru.spcex.platform.imdg.iml.hazelcast.service.HazelcastService;
@Configuration
public class ImdgConfig {
private static ThreadPoolTaskExecutor createThreadPoolTaskExecutor(int maxPoolSz, boolean waitForCompletion) {
ThreadPoolTaskExecutor pool = new ThreadPoolTaskExecutor();
if (maxPoolSz > 2) {
pool.setKeepAliveSeconds(60);
pool.setAllowCoreThreadTimeOut(true);
}
pool.setCorePoolSize(maxPoolSz);
pool.setWaitForTasksToCompleteOnShutdown(waitForCompletion);
return pool;
}
@Bean(name = "taskExecutorHazelcastClientInitializer")
public ThreadPoolTaskExecutor taskExecutorHazelcastClientInitializer() {
return createThreadPoolTaskExecutor(1, true);
}
@Bean(name = "taskExecutorIdGeneratorAwaiter")
public ThreadPoolTaskExecutor taskExecutorIdGeneratorAwaiter() {
return createThreadPoolTaskExecutor(1, false);
}
@Bean("imdgProvider")
public ImdgProvider imdgProvider(
@Qualifier("taskExecutorHazelcastClientInitializer") ThreadPoolTaskExecutor taskExecutorHazelcastClientInitializer,
@Qualifier("taskExecutorIdGeneratorAwaiter") ThreadPoolTaskExecutor taskExecutorIdGeneratorAwaiter,
ExportSwtServiceSettings settings) {
return new HazelcastService(taskExecutorHazelcastClientInitializer,
taskExecutorIdGeneratorAwaiter,
settings.getHazelcast());
}
}

View file

@ -0,0 +1,56 @@
package ru.spcex.clearing.swt.exporter.config;
import org.apache.kafka.clients.consumer.Consumer;
import org.apache.kafka.clients.producer.Producer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.kafka.core.ProducerFactory;
import ru.spcex.clearing.imdg.IMDGDistributedNames;
import ru.spcex.clearing.swt.exporter.config.settings.ExportSwtServiceSettings;
import ru.spcex.clearing.platform.messaging.config.KafkaConsumerFactory;
import ru.spcex.clearing.platform.messaging.config.KafkaProducerFactory;
import ru.spcex.clearing.platform.messaging.service.RequestInfo;
import ru.spcex.clearing.platform.messaging.service.sender.KafkaSender;
import ru.spcex.platform.imdg.api.Imdg;
import ru.spcex.platform.imdg.api.ImdgId;
import ru.spcex.platform.imdg.api.ImdgProvider;
@Configuration
public class KafkaConfig {
@Autowired
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Bean
public Consumer<String, Object> createConsumer(ExportSwtServiceSettings settings) {
return KafkaConsumerFactory.consumer(settings.getKafkaConsumer());
}
@Autowired
@Bean
public Producer<String, Object> createProducer(ExportSwtServiceSettings settings) {
return KafkaProducerFactory.producer(settings.getKafkaProducer());
}
@Bean
public KafkaTemplate<String, Object> kafkaTemplate(ProducerFactory<String, Object> pf) {
return new KafkaTemplate<>(pf);
}
@Autowired
@Bean
public KafkaSender kafkaSender(KafkaTemplate<String, Object> kafkaTemplate, ImdgProvider imdgProvider) {
ImdgId imdgIdGenerator = imdgProvider.getImdgIdGenerator();
return KafkaSender
.setup()
.setKafkaTemplate(kafkaTemplate)
.idGenerator(imdgIdGenerator::nextId)
.imdgProvider(s -> {
Imdg<RequestInfo> imdg = imdgProvider.getImdg(IMDGDistributedNames.Map_RequestInfo, RequestInfo.class);
return imdg::insert;
})
.build();
}
}

View file

@ -0,0 +1,11 @@
package ru.spcex.clearing.swt.exporter.config;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableConfigurationProperties
@ComponentScan(basePackages = {"ru.spcex.clearing.swt.exporter"})
public class SwtExporterConfig {
}

View file

@ -0,0 +1,53 @@
package ru.spcex.clearing.swt.exporter.config.settings;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
import ru.spcex.clearing.platform.messaging.config.element.KafkaConsumerSettings;
import ru.spcex.clearing.platform.messaging.config.element.KafkaProducerSettings;
import ru.spcex.platform.imdg.iml.hazelcast.config.HazelcastClientParams;
@Component
@PropertySource("file:${spring.config.location}/application.properties")
@ConfigurationProperties("export-swt-service")
public class ExportSwtServiceSettings {
private HazelcastClientParams hazelcast;
private KafkaConsumerSettings kafkaConsumer;
private KafkaProducerSettings kafkaProducer;
private String docOut;
//todo ??? private Long interval
public HazelcastClientParams getHazelcast() {
return hazelcast;
}
public void setHazelcast(HazelcastClientParams hazelcast) {
this.hazelcast = hazelcast;
}
public KafkaConsumerSettings getKafkaConsumer() {
return kafkaConsumer;
}
public void setKafkaConsumer(KafkaConsumerSettings kafkaConsumer) {
this.kafkaConsumer = kafkaConsumer;
}
public KafkaProducerSettings getKafkaProducer() {
return kafkaProducer;
}
public void setKafkaProducer(KafkaProducerSettings kafkaProducer) {
this.kafkaProducer = kafkaProducer;
}
public String getDocOut() {
return docOut;
}
public void setDocOut(String docOut) {
this.docOut = docOut;
}
}

View file

@ -0,0 +1,156 @@
package ru.spcex.clearing.swt.exporter.services;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ru.spcex.clearing.platform.messaging.domain.cud.utilities.JournalEventExportedRequest;
import ru.spcex.clearing.platform.messaging.serialization.LogFormatter;
import ru.spcex.clearing.platform.messaging.service.sender.KafkaSender;
import ru.spcex.platform.classes.base.SpcexObjectBase;
import ru.spcex.platform.enumeration.ResultStatuses;
import ru.spcex.platform.enumeration.SwtTable;
import ru.spcex.platform.imdg.api.Imdg;
import ru.spcex.platform.imdg.api.ImdgProvider;
import java.io.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import static ru.spcex.clearing.platform.messaging.domain.Consts.JOURNAL_SERVICE;
public abstract class AbstractExporterService<T extends SpcexObjectBase> {
protected final Logger log = LoggerFactory.getLogger(getClass());
protected final SwtTable type;
protected final Imdg<T> sdfImdg;
private final DateTimeFormatter dtFileNameFormatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
private final DateTimeFormatter dtInFileHeaderFormatter = DateTimeFormatter.ofPattern("yyyyMMdd'/'HHmm");
private final KafkaSender kafkaSender;
protected final FileStorage fileStorage;
protected AbstractExporterService(FileStorage fileStorage,
KafkaSender kafkaSender, ImdgProvider imdgProvider,
SwtTable type,
String mapName, Class<T> mapClass) {
this.fileStorage = fileStorage;
this.kafkaSender = kafkaSender;
this.type = type;
Objects.requireNonNull(type, "SWT table type not set");
this.sdfImdg = imdgProvider.getImdg(mapName, mapClass);
}
public SwtTable getType() {
return type;
}
protected abstract String typeForFileName();
protected abstract String sectionForFileName();
protected abstract String typeForHeader();
public void process() {
LocalDateTime exportAt = LocalDateTime.now();
String fileName = formatFileName(typeForFileName(), sectionForFileName(), exportAt);
log.debug("Start export {} Lim file", fileName);
try {
byte[] data;
{
ByteArrayOutputStream outBuffer = new ByteArrayOutputStream();
Collection<T> records = selectItems();
log.debug("Prepared {} record from {} to file {}",
records.size(), sdfImdg.getMapName(), fileName);
makeSWTData(typeForHeader(), exportAt, records, outBuffer);
data = outBuffer.toByteArray();
}
fileStorage.saveFile(fileName, data);
} catch (Exception e) { // IOException, ...
log.error("Failed export {} file", fileName);
sendSwtExportedNotification(exportAt, null, ResultStatuses.notSuccess);
throw new RuntimeException(e);
}
log.debug("Successfully exported {} file", fileName);
sendSwtExportedNotification(exportAt, null, ResultStatuses.success);
}
protected abstract String getDocumentNameForJournal();
void sendSwtExportedNotification(LocalDateTime registrationAt, Long registrationNumber, ResultStatuses resultStatus) {
JournalEventExportedRequest exportedRequest = new JournalEventExportedRequest();
exportedRequest.setRegistratoinDate(registrationAt.toLocalDate());
exportedRequest.setRegistrationTime(registrationAt.toLocalTime());
exportedRequest.setRegistrationNumber(registrationNumber);
exportedRequest.setDocumentName(getDocumentNameForJournal());
exportedRequest.setResultStatus(resultStatus.getKey());
log.debug("Send message to kafka \"{}\": {}", JOURNAL_SERVICE, LogFormatter.toStringWrapper(exportedRequest));
kafkaSender.sendRequestToQueue(JOURNAL_SERVICE, exportedRequest);
}
/**
* @param type DF-09
* @param section bond/fund/""
* @param atTime LocalDateTime.now(), если null - текущее время
* @return пример "KS_RDC_DF-12_bond_220907151804503.txt"
*/
protected String formatFileName(String type, String section, LocalDateTime atTime) {
Objects.requireNonNull(type);
if (section == null) section = "";
if (section.length() > 0) section += "_";
if (atTime == null) atTime = LocalDateTime.now();
String dt = dtFileNameFormatter.format(atTime);
return String.format("KS_RDC_%s_%s%s.txt", type, section, dt);
}
// Выборка
protected Collection<T> selectItems() {
return sdfImdg.getAllValues();
}
// Конвертация (поля см. meta.xml)
protected abstract LinkedHashMap<String, Object> convertRecord(T record);
// protected abstract String[] swtHeader();
protected void makeSWTData(String type, LocalDateTime time, Collection<T> records, OutputStream outStream) {
PrintWriter out = new PrintWriter(outStream);
// todo SWT txt не понял формат. Надо уточнить формат файла. Должен соответствовать мете.
out.println("To:CSO");
out.println("From:SPCE");
if (type != null)
out.println("Type:" + type);// Type:009
if (time != null) {
String timeS = dtInFileHeaderFormatter.format(time);
out.println("Date/Time:" + timeS);// Date/Time:20230227/0932
}
/*
To:CSO
From:SPCE
Type:009
Date/Time:20230227/0932
:20:0ef63e17-83ac-4e22-a76b-fd8a4df10de3
:21:SDC230227084839
:18A:46
*/
StringBuilder line = new StringBuilder();
for (T row : records) {
LinkedHashMap<String, Object> rowData = convertRecord(row);
line.setLength(0);
for (Map.Entry<String, Object> r : rowData.entrySet()) {
String value = convertItem(r.getValue());
line.append(value).append(':');
}
if (line.length() > 0) // remove :
line.setLength(line.length() - 1);
out.println(line);
}
//out.println("2"); // todo что значит 2?
}
protected String convertItem(Object o) {
//todo date/time/etc.
return String.valueOf(o);
}
}

View file

@ -0,0 +1,37 @@
package ru.spcex.clearing.swt.exporter.services;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import ru.spcex.clearing.swt.exporter.config.settings.ExportSwtServiceSettings;
import java.io.File;
import java.io.IOException;
@Service
public class FileStorage {
protected final Logger log = LoggerFactory.getLogger(getClass());
protected File outPath;
@Autowired
public FileStorage(ExportSwtServiceSettings config) {
if (config.getDocOut() == null || config.getDocOut().isBlank()) {
throw new IllegalArgumentException("Out directory settings is empty.");
}
this.outPath = new File(config.getDocOut());
if (!outPath.isDirectory()) {
log.info("Path not exist. mkdir \"{}\"", outPath.getAbsolutePath());
if (!outPath.mkdir()) {
log.error("Can not make output directory \"{}\"", outPath);
}
}
log.info("Output directory \"{}\"", outPath);
}
public void saveFile(String fileName, byte[] data) throws IOException {
File toFile = new File(outPath, fileName);
FileUtils.writeByteArrayToFile(toFile, data);
}
}

View file

@ -0,0 +1,70 @@
package ru.spcex.clearing.swt.exporter.services;
import org.apache.kafka.clients.consumer.Consumer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Service;
import ru.spcex.clearing.platform.messaging.domain.BaseRequest;
import ru.spcex.clearing.platform.messaging.domain.Consts;
import ru.spcex.clearing.platform.messaging.domain.cud.importexport.SwtExporterRequest;
import ru.spcex.clearing.platform.messaging.domain.cud.schedule.LauncherCommandRequest;
import ru.spcex.clearing.platform.messaging.service.QueueConsumer;
import ru.spcex.platform.utils.log.ExceptionUtils;
import java.util.List;
@Service
public class LauncherCommandReceiver extends QueueConsumer implements InitializingBean {
private final Logger log = LoggerFactory.getLogger(getClass());
protected final List<AbstractExporterService> exporterServices;
public LauncherCommandReceiver(Consumer<String, Object> kafkaQueue,
List<AbstractExporterService> exporterServices
) {
super(kafkaQueue);
this.exporterServices = exporterServices;
}
@Override
public void afterPropertiesSet() {
// callback(LauncherCommandRequest.class)
// .setConsumer(this::exportAll)
// .forDestination(Task.unloadingSession_LIMM.topic(), callbacks::put); // todo task name?
callback(SwtExporterRequest.class)
.setConsumer(this::exportSpecial)
.forDestination(Consts.SWT_EXPORTER, callbacks::put);
init();
}
protected void exportAll(BaseRequest<LauncherCommandRequest> request) {
log.info("LauncherCommandRequest request received: {}", request);
for (AbstractExporterService exporter : exporterServices) {
log.debug("Export {}", exporter);
try {
exporter.process();
} catch (Exception e) {
log.error("One of exporter has error: {}", ExceptionUtils.getStackTrace(e));
}
}
log.info("All SWT export has finished.");
}
protected void exportSpecial(BaseRequest<SwtExporterRequest> request) {
log.info("SwtExporterRequest request received: {}", request);
SwtExporterRequest req = request.getRequestPayload();
for (AbstractExporterService exporter : exporterServices) {
if (exporter.getType()==req.getType()) {
log.debug("Export {}", exporter);
try {
exporter.process();
} catch (Exception e) {
log.error("Exporter has error: {}", ExceptionUtils.getStackTrace(e));
}
}
return;
}
log.error("SWT export not execute for type \"{}\" - unknown command", req.getType());
//todo return error?
}
}

View file

@ -0,0 +1,62 @@
package ru.spcex.clearing.swt.exporter.services.exportimpl;
import org.springframework.stereotype.Service;
import ru.clearing.classes.statics.data.sdf.SDf09;
import ru.spcex.clearing.imdg.IMDGDistributedNames;
import ru.spcex.clearing.platform.messaging.service.sender.KafkaSender;
import ru.spcex.clearing.swt.exporter.services.AbstractExporterService;
import ru.spcex.clearing.swt.exporter.services.FileStorage;
import ru.spcex.platform.enumeration.SwtTable;
import ru.spcex.platform.imdg.api.ImdgProvider;
import java.util.LinkedHashMap;
@Service
public class DF09Exporter extends AbstractExporterService<SDf09> {
public DF09Exporter(FileStorage fileStorage,
KafkaSender kafkaSender,
ImdgProvider imdgProvider) {
super(fileStorage, kafkaSender, imdgProvider,
SwtTable.SDF_09,
IMDGDistributedNames.Map_SDf09, SDf09.class);
}
@Override
protected String typeForFileName() {
return "DF-09";
}
@Override
protected String sectionForFileName() {
return null;
}
@Override
protected String typeForHeader() {
return "009";
}
@Override
protected String getDocumentNameForJournal() {
return "Уведомление об исполнении операции загрузки ценных бумаг или уведомление об ошибке";
}
@Override
protected LinkedHashMap<String, Object> convertRecord(SDf09 record) {
LinkedHashMap<String, Object> row = new LinkedHashMap<>();
row.put("id", record.getId());
row.put("outDocument", record.getOutDocument());
row.put("inDocument", record.getInDocument());
row.put("depoCode", record.getDepoCode());
row.put("quantity", record.getQuantity());
row.put("securityCode", record.getSecurityCode());
row.put("clientName", record.getClientName());
row.put("result", record.getResult());
row.put("generationTime", record.getGenerationTime());
row.put("generationId", record.getGenerationId());
return row;
}
}

View file

@ -0,0 +1,63 @@
package ru.spcex.clearing.swt.exporter.services.exportimpl;
import org.springframework.stereotype.Service;
import ru.clearing.classes.statics.data.sdf.SDf11;
import ru.spcex.clearing.imdg.IMDGDistributedNames;
import ru.spcex.clearing.platform.messaging.service.sender.KafkaSender;
import ru.spcex.clearing.swt.exporter.services.AbstractExporterService;
import ru.spcex.clearing.swt.exporter.services.FileStorage;
import ru.spcex.platform.enumeration.SwtTable;
import ru.spcex.platform.imdg.api.ImdgProvider;
import java.util.LinkedHashMap;
@Service
public class DF11Exporter extends AbstractExporterService<SDf11> {
public DF11Exporter(FileStorage fileStorage,
KafkaSender kafkaSender,
ImdgProvider imdgProvider) {
super(fileStorage, kafkaSender, imdgProvider,
SwtTable.SDF_11,
IMDGDistributedNames.Map_SDf11, SDf11.class);
}
@Override
protected String typeForFileName() {
return "DF-11";
}
@Override
protected String sectionForFileName() {
return null;
}
@Override
protected String typeForHeader() {
return "011";
}
@Override
protected String getDocumentNameForJournal() {
return "Ответ на Запрос на Зачисление или списание ценных бумаг";
}
@Override
protected LinkedHashMap<String, Object> convertRecord(SDf11 record) {
LinkedHashMap<String, Object> row = new LinkedHashMap<>();
row.put("id", record.getId());
row.put("outDocument", record.getOutDocument());
row.put("inDocument", record.getInDocument());
row.put("depoCode", record.getDepoCode());
row.put("quantity", record.getQuantity());
row.put("securityCode", record.getSecurityCode());
row.put("clientName", record.getClientName());
row.put("result", record.getResult());
row.put("generationTime", record.getGenerationTime());
row.put("generationId", record.getGenerationId());
return row;
}
}

View file

@ -0,0 +1,66 @@
package ru.spcex.clearing.swt.exporter.services.exportimpl;
import org.springframework.stereotype.Service;
import ru.clearing.classes.statics.data.sdf.SDf12;
import ru.spcex.clearing.imdg.IMDGDistributedNames;
import ru.spcex.clearing.platform.messaging.service.sender.KafkaSender;
import ru.spcex.clearing.swt.exporter.services.AbstractExporterService;
import ru.spcex.clearing.swt.exporter.services.FileStorage;
import ru.spcex.platform.enumeration.SwtTable;
import ru.spcex.platform.imdg.api.ImdgProvider;
import java.util.LinkedHashMap;
@Service
public class DF12Exporter extends AbstractExporterService<SDf12> {
public DF12Exporter(FileStorage fileStorage,
KafkaSender kafkaSender,
ImdgProvider imdgProvider) {
super(fileStorage, kafkaSender, imdgProvider,
SwtTable.SDF_12,
IMDGDistributedNames.Map_SDf12, SDf12.class);
}
@Override
protected String typeForFileName() {
return "DF-12";
}
@Override
protected String sectionForFileName() {
return "bond"; // todo bond / fund
}
@Override
protected String typeForHeader() {
return "012";
}
@Override
protected String getDocumentNameForJournal() {
//todo Выбор:
// Распоряжение на проведение операций по итогам клиринга (Фондовая секция)
//или
// Распоряжение на проведение операций по итогам клиринга (ОФЗ, ОБР)
return "Распоряжение на проведение операций по итогам клиринга (Фондовая секция / ОФЗ, ОБР)";
}
@Override
protected LinkedHashMap<String, Object> convertRecord(SDf12 record) {
LinkedHashMap<String, Object> row = new LinkedHashMap<>();
row.put("id", record.getId());
row.put("outDocument", record.getOutDocument());
row.put("quantity", record.getQuantity());
row.put("securityCode", record.getSecurityCode());
row.put("depoCodeSender", record.getDepoCodeSender());
row.put("depoCodeAdressee", record.getDepoCodeAdressee());
row.put("result", record.getResult());
row.put("generationTime", record.getGenerationTime());
row.put("generationId", record.getGenerationId());
return row;
}
}

View file

@ -0,0 +1,62 @@
package ru.spcex.clearing.swt.exporter.services.exportimpl;
import org.springframework.stereotype.Service;
import ru.clearing.classes.statics.data.sdf.SDf14;
import ru.spcex.clearing.imdg.IMDGDistributedNames;
import ru.spcex.clearing.platform.messaging.service.sender.KafkaSender;
import ru.spcex.clearing.swt.exporter.services.AbstractExporterService;
import ru.spcex.clearing.swt.exporter.services.FileStorage;
import ru.spcex.platform.enumeration.SwtTable;
import ru.spcex.platform.imdg.api.ImdgProvider;
import java.util.LinkedHashMap;
@Service
public class DF14Exporter extends AbstractExporterService<SDf14> {
public DF14Exporter(FileStorage fileStorage,
KafkaSender kafkaSender,
ImdgProvider imdgProvider) {
super(fileStorage, kafkaSender, imdgProvider,
SwtTable.SDF_14,
IMDGDistributedNames.Map_SDf14, SDf14.class);
}
@Override
protected String typeForFileName() {
return "DF-14";
}
@Override
protected String sectionForFileName() {
return "bond"; // todo bond / fund
}
@Override
protected String typeForHeader() {
return "014";
}
@Override
protected String getDocumentNameForJournal() {
//todo Выбор:
// Уведомление о завершении расчетов (ОФЗ, ОБР)
//или
// Уведомление о завершении расчетов (ОФЗ, ОБР)
return "Уведомление о завершении расчетов (ОФЗ, ОБР)";
}
@Override
protected LinkedHashMap<String, Object> convertRecord(SDf14 record) {
LinkedHashMap<String, Object> row = new LinkedHashMap<>();
row.put("id", record.getId());
row.put("outDocument", record.getOutDocument());
row.put("result", record.getResult());
row.put("generationTime", record.getGenerationTime());
row.put("generationId", record.getGenerationId());
return row;
}
}

View file

@ -0,0 +1,25 @@
spring.main.web-application-type=none
export-swt-service.hazelcast.cluster-members=10.200.200.181:5701
export-swt-service.hazelcast.login=dev
export-swt-service.hazelcast.password=dev-pass
export-swt-service.common.encoding=cp866
export-swt-service.common.threads-count=10
export-swt-service.docOut=/opt/spcex/clearing/files/swt/SettlementHouse_DocOut
export-swt-service.kafka-consumer.bootstrap-servers=localhost:9092
export-swt-service.kafka-consumer.group-id=dev-group-balance-service
export-swt-service.kafka-consumer.enable-auto-commit=false
export-swt-service.kafka-consumer.session-timeout-ms=30000
export-swt-service.kafka-consumer.auto-offset-reset=latest
export-swt-service.kafka-consumer.linger-ms=1
export-swt-service.kafka-consumer.buffer-memory=33554432
export-swt-service.kafka-producer.bootstrap-servers=localhost:9092
export-swt-service.kafka-producer.acks=all
export-swt-service.kafka-producer.retries=0
export-swt-service.kafka-producer.batch-size=16384
export-swt-service.kafka-producer.linger-ms=1
export-swt-service.kafka-producer.buffer-memory=33554432

View file

@ -0,0 +1,37 @@
<configuration>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<charset>UTF-8</charset>
<pattern>%date{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>./logs/swt-exporter.log</file>
<encoder>
<charset>UTF-8</charset>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
<fileNamePattern>
../logs/swt-exporter.%i.log
</fileNamePattern>
<minIndex>1</minIndex>
<maxIndex>10</maxIndex>
</rollingPolicy>
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<maxFileSize>500MB</maxFileSize>
</triggeringPolicy>
</appender>
<root level="warn">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE"/>
</root>
<logger name="ru.spcex" level="debug" additivity="false">
<appender-ref ref="FILE"/>
<appender-ref ref="CONSOLE"/>
</logger>
</configuration>

View file

@ -0,0 +1,53 @@
package ru.spcex.clearing.swt.exporter;
import org.apache.kafka.clients.producer.Producer;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import ru.clearing.classes.statics.data.registry.Registry;
import ru.clearing.classes.statics.data.registry.TradingClearingRegistry;
import ru.spcex.clearing.imdg.IMDGDistributedNames;
import ru.spcex.clearing.platform.messaging.service.sender.KafkaSender;
import ru.spcex.clearing.test.config.ImdgTestConfig;
import ru.spcex.clearing.test.config.KafkaTestConfig;
import ru.spcex.platform.imdg.api.Imdg;
import ru.spcex.platform.imdg.api.ImdgProvider;
import java.time.LocalDate;
import static ru.spcex.clearing.test.config.ImdgTestConfig.currentID;
import static ru.spcex.clearing.test.config.ImdgTestConfig.waitAvailableImdgProviderAndAddAdminWithDefaultId;
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = {
ImdgTestConfig.class,
KafkaTestConfig.class})
public abstract class AbstractServiceTest {
protected static final long id = currentID.getAndIncrement();
protected Imdg<Registry> registryImdg;
protected Imdg<TradingClearingRegistry> tradingClearingRegistryImdg;
protected LocalDate currentDate = LocalDate.now();
protected String tcrA = "1324A234";
protected String tcrD = "124324A234";
protected Long securityIdFirst = 12L;
protected Long securityIdSecond = 23L;
@Autowired
@Qualifier("mockProducer")
protected Producer<String, Object> mockProducer;
@Autowired
protected KafkaSender kafkaSender;
@Autowired
@Qualifier("hazelcastServiceTest")
protected ImdgProvider imdgProvider;
protected void init() {
waitAvailableImdgProviderAndAddAdminWithDefaultId();
this.registryImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_Registry, Registry.class);
this.tradingClearingRegistryImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_TradingClearingRegistry, TradingClearingRegistry.class);
}
}

View file

@ -0,0 +1,25 @@
package ru.spcex.clearing.swt.exporter.services;
import org.junit.jupiter.api.Test;
import ru.spcex.clearing.swt.exporter.AbstractServiceTest;
import ru.spcex.clearing.swt.exporter.services.exportimpl.DF09Exporter;
import ru.spcex.platform.enumeration.ResultStatuses;
import javax.annotation.PostConstruct;
import java.time.LocalDateTime;
class AbstractExporterServiceTest extends AbstractServiceTest {
@PostConstruct
public void init() {
super.init();
}
@Test
void sendSwtExportedNotification() {
AbstractExporterService moneyExporterService = new DF09Exporter(null, kafkaSender, imdgProvider);
String fileName = "KS_RDC_DF-14_bond_221005134616035.txt";
moneyExporterService.sendSwtExportedNotification(LocalDateTime.now(), 1L, ResultStatuses.notSuccess);
//TestUtils.waitingSendAndCheckRecord(null, mockProducer);
}
}

View file

@ -0,0 +1,24 @@
package ru.spcex.platform.enumeration;
import ru.spcex.platform.utils.enumeration.IEnumKey;
public enum SwtTable implements IEnumKey {
SDF_09("SDF_09"), SDF_11("SDF_11"),
SDF_12("SDF_12"), SDF_14("SDF_14");
SwtTable(String key) {
this.key = key;
}
private final String key;
@Override
public String getKey() {
return this.key;
}
@Override
public boolean equalsByKey(String key) {
return IEnumKey.super.equalsByKey(key);
}
}

View file

@ -123,11 +123,13 @@ public interface Consts {
String SDF56_PROCESS = "sdf56-process";
String CONTINUE_SESSION_BN_FIRST_PART = "sdf57-process";
String CONTINUE_SESSION_BN_SECOND_PART = "sdf13-process";
String SWT_EXPORTER = "swt-exporter";
String REVISE_PROCESS = "revise-process";
String EXPORT_PROCESS = "export-process";
String EXPORT_COMPLETED = "export_completed";
String S_TRADES_IMPORTED = "s_trades-imported";
String LIM_EXPORTED = "lim_exported";
String JOURNAL_SERVICE = "journal-service-exported";
String ACCOUNT_TERMINATION = "account-termination";
String BALANCE_ACCOUNT_NEW = "balance-account-new";
String BALANCE_ACCOUNT_UPDATE = "balance-account-update";

View file

@ -0,0 +1,17 @@
package ru.spcex.clearing.platform.messaging.domain.cud.importexport;
import com.fasterxml.jackson.annotation.JsonProperty;
import ru.spcex.platform.enumeration.SwtTable;
public class SwtExporterRequest {
@JsonProperty
public SwtTable type;
public SwtTable getType() {
return type;
}
public void setType(SwtTable type) {
this.type = type;
}
}

View file

@ -0,0 +1,83 @@
package ru.spcex.clearing.platform.messaging.domain.cud.utilities;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import ru.spcex.clearing.platform.messaging.domain.json.deserialize.LocalDateDeserializer;
import ru.spcex.clearing.platform.messaging.domain.json.deserialize.LocalTimeDeserializer;
import ru.spcex.clearing.platform.messaging.domain.json.serialize.LocalDateSerializer;
import ru.spcex.clearing.platform.messaging.domain.json.serialize.LocalTimeSerializer;
import java.time.LocalDate;
import java.time.LocalTime;
public class JournalEventExportedRequest {
@JsonSerialize(using = LocalDateSerializer.class)
@JsonDeserialize(using = LocalDateDeserializer.class)
@JsonProperty
private LocalDate registratoinDate;
@JsonSerialize(using = LocalTimeSerializer.class)
@JsonDeserialize(using = LocalTimeDeserializer.class)
@JsonProperty
private LocalTime registrationTime;
@JsonProperty
private Long registrationNumber;
@JsonProperty
private String documentName;
@JsonProperty
private String dossierNumber;
/**
* ACK при успешной загрузке
* NACK при ошибке загрузки
*/
@JsonProperty
private String resultStatus;
public LocalDate getRegistratoinDate() {
return registratoinDate;
}
public void setRegistratoinDate(LocalDate registratoinDate) {
this.registratoinDate = registratoinDate;
}
public LocalTime getRegistrationTime() {
return registrationTime;
}
public void setRegistrationTime(LocalTime registrationTime) {
this.registrationTime = registrationTime;
}
public Long getRegistrationNumber() {
return registrationNumber;
}
public void setRegistrationNumber(Long registrationNumber) {
this.registrationNumber = registrationNumber;
}
public String getDocumentName() {
return documentName;
}
public void setDocumentName(String documentName) {
this.documentName = documentName;
}
public String getDossierNumber() {
return dossierNumber;
}
public void setDossierNumber(String dossierNumber) {
this.dossierNumber = dossierNumber;
}
public String getResultStatus() {
return resultStatus;
}
public void setResultStatus(String resultStatus) {
this.resultStatus = resultStatus;
}
}

View file

@ -37,6 +37,8 @@
<folder_root_clearing_imdg>${folder_root_clearing}/clearing-parent/imdg</folder_root_clearing_imdg>
<folder_root_dbf-exporter>${folder_root_clearing}/clearing-parent/dbf-exporter</folder_root_dbf-exporter>
<folder_root_dbf-importer>${folder_root_clearing}/clearing-parent/dbf-importer</folder_root_dbf-importer>
<folder_root_lim-exporter>${folder_root_clearing}/clearing-parent/lim-exporter</folder_root_lim-exporter>
<folder_root_swt-exporter>${folder_root_clearing}/clearing-parent/swt-exporter</folder_root_swt-exporter>
<folder_root_trade-importer>${folder_root_clearing}/clearing-parent/trade-importer</folder_root_trade-importer>
<folder_root_account-service>${folder_root_clearing}/clearing-parent/account-service</folder_root_account-service>
<folder_root_balance-service>${folder_root_clearing}/clearing-parent/balance-service</folder_root_balance-service>

View file

@ -191,6 +191,45 @@
</fileSets>
</configuration>
</execution>
<execution>
<id>copy-lim-exporter-bin</id>
<phase>prepare-package</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<fileSets>
<fileSet>
<sourceFile>${folder_root_lim-exporter}/target/lim-exporter.jar</sourceFile>
<destinationFile>${folder.clearing.distr.modules}/lim-exporter/lim-exporter.jar</destinationFile>
</fileSet>
<fileSet>
<sourceFile>${folder_root_lim-exporter}/src/main/resources/application.properties</sourceFile>
<destinationFile>${folder.clearing.distr.modules}/lim-exporter/application.properties</destinationFile>
</fileSet>
</fileSets>
</configuration>
</execution>
<execution>
<id>copy-swt-exporter-bin</id>
<phase>prepare-package</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<fileSets>
<fileSet>
<sourceFile>${folder_root_swt-exporter}/target/swt-exporter.jar</sourceFile>
<destinationFile>${folder.clearing.distr.modules}/swt-exporter/swt-exporter.jar</destinationFile>
</fileSet>
<fileSet>
<sourceFile>${folder_root_swt-exporter}/src/main/resources/application.properties</sourceFile>
<destinationFile>${folder.clearing.distr.modules}/swt-exporter/application.properties</destinationFile>
</fileSet>
</fileSets>
</configuration>
</execution>
<execution>
<id>copy-trade-importer-bin</id>
<phase>prepare-package</phase>

View file

@ -7,6 +7,7 @@ kill -9 $(ps -ef | grep java | grep company-service.jar | awk '{print $2}')
kill -9 $(ps -ef | grep java | grep clearing-service.jar | awk '{print $2}')
kill -9 $(ps -ef | grep java | grep dbf-exporter.jar | awk '{print $2}')
kill -9 $(ps -ef | grep java | grep lim-exporter.jar | awk '{print $2}')
kill -9 $(ps -ef | grep java | grep swt-exporter.jar | awk '{print $2}')
kill -9 $(ps -ef | grep java | grep dbf-importer.jar | awk '{print $2}')
kill -9 $(ps -ef | grep java | grep trade-importer.jar | awk '{print $2}')
kill -9 $(ps -ef | grep java | grep imdg.jar | awk '{print $2}')

View file

@ -0,0 +1,9 @@
#!/bin/bash
CLEARING_HOME=/opt/mfd/clearing/
cd $CLEARING_HOME/bin
CMD="java -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=7120 -jar swt-exporter.jar --spring.config.location=$CLEARING_HOME/settings/swt-exporter/"
$CMD >/dev/null 2>&1 &