This commit is contained in:
Ivan Nikolaev-Axenov 2024-09-27 16:12:41 +03:00
parent 8e156bca82
commit 39d381178c
15 changed files with 545 additions and 0 deletions

View file

@ -0,0 +1,95 @@
<?xml version="1.0" encoding="UTF-8"?>
<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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>control-service</artifactId>
<name>control-service</name>
<description>Control service</description>
<version>SPCEX-3.15.207</version>
<parent>
<artifactId>clearing-parent</artifactId>
<groupId>ru.spcex.clearing</groupId>
<version>SPCEX-3.15.207</version>
</parent>
<properties>
<logstash-logback-encoder.version>7.0.1</logstash-logback-encoder.version>
</properties>
<dependencies>
<!-- Spring boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Database -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
</dependency>
<!-- special logging -->
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>${logstash-logback-encoder.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>ru.spcex.platform</groupId>
<artifactId>platform-enum</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</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>
</plugins>
</build>
</project>

View file

@ -0,0 +1,48 @@
package ru.spcex.clearing.control.config;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import java.beans.PropertyVetoException;
import javax.sql.DataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import ru.spcex.clearing.control.config.settings.ControlServiceSettings;
@Configuration
public class DataSourceConfiguration {
private final ControlServiceSettings settings;
public DataSourceConfiguration(ControlServiceSettings settings) {
this.settings = settings;
}
@Bean
public DataSource getDataSource() {
ComboPooledDataSource cpds = new ComboPooledDataSource();
try {
cpds.setDriverClass("org.postgresql.Driver");
cpds.setJdbcUrl(settings.getDatabase().getUrl());
cpds.setUser(settings.getDatabase().getUsername());
cpds.setPassword(settings.getDatabase().getPassword());
cpds.setMaxPoolSize(100);
cpds.setMinPoolSize(50);
cpds.setAcquireIncrement(5);
} catch (PropertyVetoException e) {
throw new RuntimeException(e);
}
return cpds;
}
@Bean
public JdbcTemplate getJdbcTemplate() {
return new JdbcTemplate(getDataSource());
}
@Bean
public NamedParameterJdbcTemplate getNamedParameterJdbcTemplate() {
return new NamedParameterJdbcTemplate(getDataSource());
}
}

View file

@ -0,0 +1,30 @@
package ru.spcex.clearing.control.config.settings;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties("control-service")
public class ControlServiceSettings {
@NestedConfigurationProperty
private Database database;
private String resultDirectory;
public Database getDatabase() {
return database;
}
public void setDatabase(Database database) {
this.database = database;
}
public String getResultDirectory() {
return resultDirectory;
}
public void setResultDirectory(String resultDirectory) {
this.resultDirectory = resultDirectory;
}
}

View file

@ -0,0 +1,31 @@
package ru.spcex.clearing.control.config.settings;
public class Database {
private String username;
private String password;
private String url;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
}

View file

@ -0,0 +1,33 @@
package ru.spcex.clearing.control.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import ru.spcex.clearing.control.service.ControlService;
import ru.spcex.platform.enumeration.SessionType;
@RestController
@RequestMapping("/api/v1/control")
public class ControlController {
private final ControlService controlService;
public ControlController(ControlService controlService) {
this.controlService = controlService;
}
@GetMapping("/general-control/{sessionType}")
public String generalControl(@PathVariable("sessionType") SessionType sessionType) {
return controlService.generalControl(sessionType);
}
@GetMapping("/internal-control/{sessionId}")
public String internalControl(@PathVariable("sessionId") Long sessionId) {
return controlService.internalControl(sessionId);
}
@GetMapping("/internal-control-finl/{sessionId}")
public String internalControlFinl(@PathVariable("sessionId") Long sessionId) {
return controlService.internalControlFinl(sessionId);
}
}

View file

@ -0,0 +1,19 @@
package ru.spcex.clearing.control.mapper;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Component;
import ru.spcex.clearing.control.model.ResultContainer;
@Component
public class ResultContainerMapper implements RowMapper<ResultContainer> {
@Override
public ResultContainer mapRow(ResultSet rs, int rowNum) throws SQLException {
return new ResultContainer(
rs.getLong("first_table"),
rs.getLong("second_table"),
rs.getLong("diff")
);
}
}

View file

@ -0,0 +1,4 @@
package ru.spcex.clearing.control.model;
public record ResultContainer(Long firstTable, Long secondTable, Long difference) {
}

View file

@ -0,0 +1,91 @@
package ru.spcex.clearing.control.repository;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import ru.spcex.clearing.control.mapper.ResultContainerMapper;
import ru.spcex.clearing.control.model.ResultContainer;
@Repository
public class ControlRepository {
private final JdbcTemplate jdbcTemplate;
private final NamedParameterJdbcTemplate namedParameterJdbcTemplate;
private final ResultContainerMapper resultContainerMapper;
public ControlRepository(JdbcTemplate jdbcTemplate,
NamedParameterJdbcTemplate namedParameterJdbcTemplate,
ResultContainerMapper resultContainerMapper) {
this.jdbcTemplate = jdbcTemplate;
this.namedParameterJdbcTemplate = namedParameterJdbcTemplate;
this.resultContainerMapper = resultContainerMapper;
}
@Transactional
public ResultContainer getFinlCountDiff() {
String sqlQuery = """
WITH s_trades_count AS (SELECT COUNT(*) FROM s_trades),
execution_deposit_count AS (SELECT COUNT(*) FROM execution_deposit)
SELECT (SELECT * FROM s_trades_count) AS first_table,
(SELECT * FROM execution_deposit_count) AS second_table,
(SELECT s_trades_count.count - execution_deposit_count.count) AS diff
FROM s_trades_count,
execution_deposit_count;
""";
return jdbcTemplate.queryForObject(sqlQuery, resultContainerMapper);
}
@Transactional
public ResultContainer getIpoTrdCountDiff() {
String sqlQuery = """
WITH s_trades_fond_count AS (SELECT COUNT(*) FROM s_trades WHERE section = 'FOND'),
execution_fond_count AS (SELECT COUNT(*) FROM execution_fond)
SELECT (SELECT * FROM s_trades_fond_count) AS first_table,
(SELECT * FROM execution_fond_count) AS second_table,
(SELECT s_trades_fond_count.count - execution_fond_count.count) AS diff
FROM s_trades_fond_count,
execution_fond_count;
""";
return jdbcTemplate.queryForObject(sqlQuery, resultContainerMapper);
}
@Transactional
public ResultContainer getCurrInitCountDiff() {
String sqlQuery = """
WITH s_trades_curr_count AS (SELECT COUNT(*) FROM s_trades WHERE section = 'CURR'),
execution_currency_count AS (SELECT COUNT(*) FROM execution_currency)
SELECT (SELECT * FROM s_trades_curr_count) AS first_table,
(SELECT * FROM execution_currency_count) AS second_table,
(SELECT s_trades_curr_count.count - execution_currency_count.count) AS diff
FROM s_trades_curr_count,
execution_currency_count;
""";
return jdbcTemplate.queryForObject(sqlQuery, resultContainerMapper);
}
@Transactional
public ResultContainer getInternalCountDiff(Long sessionId) {
return new ResultContainer(1L, 2L, 3L);
}
@Transactional
public ResultContainer getFinlInternalCountDiff(Long sessionId) {
String sqlQuery = """
WITH payment_instruction_ids AS (SELECT id FROM payment_instruction WHERE session_id = :sessionId),
s_df03_ids AS (SELECT s_df03.id
FROM payment_instruction_ids
LEFT JOIN s_df03 ON payment_instruction_ids.id = s_df03.payment_instruction_id),
payment_instruction_count AS (SELECT COUNT(*) FROM payment_instruction_ids),
s_df03_count AS (SELECT COUNT(*) FROM s_df03_ids)
SELECT (SELECT payment_instruction_count.count FROM payment_instruction_count) AS first_table,
(SELECT s_df03_count.count FROM s_df03_count) AS second_table,
(SELECT payment_instruction_count.count - s_df03_count.count) AS diff
FROM payment_instruction_count,
s_df03_count;
""";
return namedParameterJdbcTemplate.queryForObject(sqlQuery,
new MapSqlParameterSource("sessionId", sessionId),
resultContainerMapper);
}
}

View file

@ -0,0 +1,97 @@
package ru.spcex.clearing.control.service;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.file.Path;
import java.time.Instant;
import org.springframework.stereotype.Service;
import ru.spcex.clearing.control.config.settings.ControlServiceSettings;
import ru.spcex.clearing.control.model.ResultContainer;
import ru.spcex.clearing.control.repository.ControlRepository;
import ru.spcex.platform.enumeration.SessionType;
import ru.spcex.platform.utils.collection.Pair;
@Service
public class ControlService {
private final ControlRepository controlRepository;
private final ControlServiceSettings settings;
private final Path resultDirectory;
public ControlService(ControlRepository controlRepository,
ControlServiceSettings settings) {
this.controlRepository = controlRepository;
this.settings = settings;
resultDirectory = Path.of(settings.getResultDirectory());
}
public String generalControl(SessionType sessionType) {
Pair<String, String> tables;
ResultContainer resultContainer;
if (sessionType.equals(SessionType.FINL)) {
tables = new Pair<>("sTrade", "executionDeposit");
resultContainer = controlRepository.getFinlCountDiff();
} else if (sessionType.equals(SessionType.IPOB) || sessionType.equals(SessionType.IPOT) || sessionType.equals(SessionType.IPO0) || sessionType.equals(SessionType.TRDT)) {
tables = new Pair<>("sTradeFond", "executionFond");
resultContainer = controlRepository.getIpoTrdCountDiff();
} else if (sessionType.equals(SessionType.CURR)) {
tables = new Pair<>("sTradeCurrency", "executionCurrency");
resultContainer = controlRepository.getCurrInitCountDiff();
} else {
throw new IllegalStateException("Unexpected value: " + sessionType);
}
if (resultContainer.difference() != 0) {
writeToCsvFile(sessionType, resultContainer, tables);
}
return resultContainer.toString();
}
public String internalControl(Long sessionId) {
ResultContainer resultContainer = controlRepository.getInternalCountDiff(sessionId);
if (resultContainer.difference() != 0) {
writeToCsvFileInternal(sessionId, resultContainer);
}
return resultContainer.toString();
}
public String internalControlFinl(Long sessionId) {
ResultContainer resultContainer = controlRepository.getFinlInternalCountDiff(sessionId);
if (resultContainer.difference() != 0) {
writeToCsvFileInternal(sessionId, resultContainer);
}
return resultContainer.toString();
}
private void writeToCsvFile(SessionType sessionType, ResultContainer resultContainer, Pair<String, String> tables) {
try (PrintWriter printWriter = new PrintWriter(new FileWriter(resultDirectory.resolve("result-" + sessionType.name() + "-" + Instant.now().getEpochSecond() + ".csv").toFile()))) {
printWriter.printf("session_type,%s,%s,diff%n", tables.getFirst(), tables.getSecond());
printWriter.printf("%s,%s,%s,%s%n",
sessionType.name(),
resultContainer.firstTable(),
resultContainer.secondTable(),
resultContainer.difference());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private void writeToCsvFileInternal(Long sessionId, ResultContainer resultContainer) {
try (PrintWriter printWriter = new PrintWriter(new FileWriter(resultDirectory.resolve("result-internal-" + sessionId + "-" + Instant.now().getEpochSecond() + ".csv").toFile()))) {
printWriter.println("session_id,paymentInstruction,s_df03,diff");
printWriter.printf("%s,%s,%s,%s%n",
sessionId,
resultContainer.firstTable(),
resultContainer.secondTable(),
resultContainer.difference());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}

View file

@ -0,0 +1,7 @@
# Database settings
control-service.database.username=clearing
control-service.database.password=Aa111111
control-service.database.url=jdbc:postgresql://localhost:5433/clearing
# Result directory
control-service.result-directory=C:\\Users\\ivan\\Desktop\\result

View file

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<property name="LOG_PATH" value="./log" />
<property name="FILE_NAME" value="control-service" />
<property name="CONSOLE_LOG_PATTERN" value="%date{HH:mm:ss.SSS} [%thread] %-5level %class{0}:%line - %message%n" />
<property name="FILE_LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %class{0}:%msg%n" />
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<!-- |%X{ru.nbch.scoring.web.logging.mdc_key}-->
<Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
<charset>utf-8</charset>
</encoder>
</appender>
<!-- first FILE TEXT appender -->
<appender name="TEXT_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/${FILE_NAME}-text.log</file>
<encoder>
<!-- |%X{ru.nbch.scoring.web.logging.mdc_key}-->
<Pattern>${FILE_LOG_PATTERN}</Pattern>
<charset>utf8</charset>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/${FILE_NAME}-text.%d{yyyy-MM-dd}.%i.gz
</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<maxHistory>10</maxHistory>
</rollingPolicy>
</appender>
<!-- second FILE JSON appender -->
<appender name="JSON_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/${FILE_NAME}-json.log</file>
<encoder class="net.logstash.logback.encoder.LogstashEncoder" />
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/${FILE_NAME}-json.%d{yyyy-MM-dd}.%i.gz
</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<maxHistory>10</maxHistory>
</rollingPolicy>
</appender>
<root level="info">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="TEXT_FILE"/>
<appender-ref ref="JSON_FILE" />
</root>
<logger name="ru.spcex" level="debug" additivity="false">
<appender-ref ref="TEXT_FILE"/>
<appender-ref ref="JSON_FILE" />
<!-- <appender-ref ref="CONSOLE"/> -->
</logger>
</configuration>

View file

@ -45,6 +45,7 @@
<module>imdg-hist</module>
<module>xml-importer</module>
<module>xml-exporter</module>
<module>control-service</module>
</modules>
<properties>

View file

@ -55,6 +55,7 @@
<folder_root_gateway-api>${folder_root_clearing}/clearing-parent/gateway-api</folder_root_gateway-api>
<folder_root_xml-exporter>${folder_root_clearing}/clearing-parent/xml-exporter</folder_root_xml-exporter>
<folder_root_xml-importer>${folder_root_clearing}/clearing-parent/xml-importer</folder_root_xml-importer>
<folder_root_control-service>${folder_root_clearing}/clearing-parent/control-service</folder_root_control-service>
<!-- IMDG -->
<external_libraries.hazelcast.version>3.12.4</external_libraries.hazelcast.version>
<external_libraries.slf4j.version>1.7.33</external_libraries.slf4j.version>

View file

@ -614,6 +614,29 @@
</fileSets>
</configuration>
</execution>
<execution>
<id>copy-control-service-bin</id>
<phase>prepare-package</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<fileSets>
<fileSet>
<sourceFile>${folder_root_control-service}/target/control-service.jar</sourceFile>
<destinationFile>${folder.clearing.distr.bin}/control-service.jar</destinationFile>
</fileSet>
<fileSet>
<sourceFile>${folder_root_control-service}/target/control-service.jar</sourceFile>
<destinationFile>${folder.clearing.distr.services}/control-service/control-service.jar</destinationFile>
</fileSet>
<fileSet>
<sourceFile>${folder_root_control-service}/src/main/resources/application.properties</sourceFile>
<destinationFile>${folder.clearing.distr.settings}/control-service/application.properties</destinationFile>
</fileSet>
</fileSets>
</configuration>
</execution>
</executions>
</plugin>
<plugin>

View file

@ -0,0 +1,9 @@
#!/bin/bash
CLEARING_HOME=/opt/mfd/clearing/
cd $CLEARING_HOME/bin
CMD="java -jar control-service.jar --spring.config.location=$CLEARING_HOME/settings/control-service/"
$CMD >/dev/null 2>&1 &