diff --git a/clearing-parent/control-service/pom.xml b/clearing-parent/control-service/pom.xml
new file mode 100644
index 000000000..3fed973f9
--- /dev/null
+++ b/clearing-parent/control-service/pom.xml
@@ -0,0 +1,95 @@
+
+
+ 4.0.0
+
+ control-service
+ control-service
+ Control service
+ SPCEX-3.15.207
+
+
+ clearing-parent
+ ru.spcex.clearing
+ SPCEX-3.15.207
+
+
+
+ 7.0.1
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-jdbc
+
+
+ org.postgresql
+ postgresql
+ runtime
+
+
+ com.mchange
+ c3p0
+
+
+
+
+ net.logstash.logback
+ logstash-logback-encoder
+ ${logstash-logback-encoder.version}
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+
+
+
+ ru.spcex.platform
+ platform-enum
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
+
+
+ src/main/resources
+
+ application.properties
+
+ false
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+ repackage
+
+
+
+
+ ${project.artifactId}
+
+
+
+
+
+
\ No newline at end of file
diff --git a/clearing-parent/control-service/src/main/java/ru/spcex/clearing/control/config/DataSourceConfiguration.java b/clearing-parent/control-service/src/main/java/ru/spcex/clearing/control/config/DataSourceConfiguration.java
new file mode 100644
index 000000000..7fe03204a
--- /dev/null
+++ b/clearing-parent/control-service/src/main/java/ru/spcex/clearing/control/config/DataSourceConfiguration.java
@@ -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());
+ }
+}
diff --git a/clearing-parent/control-service/src/main/java/ru/spcex/clearing/control/config/settings/ControlServiceSettings.java b/clearing-parent/control-service/src/main/java/ru/spcex/clearing/control/config/settings/ControlServiceSettings.java
new file mode 100644
index 000000000..6a3e73ceb
--- /dev/null
+++ b/clearing-parent/control-service/src/main/java/ru/spcex/clearing/control/config/settings/ControlServiceSettings.java
@@ -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;
+ }
+}
diff --git a/clearing-parent/control-service/src/main/java/ru/spcex/clearing/control/config/settings/Database.java b/clearing-parent/control-service/src/main/java/ru/spcex/clearing/control/config/settings/Database.java
new file mode 100644
index 000000000..0ae47c5a1
--- /dev/null
+++ b/clearing-parent/control-service/src/main/java/ru/spcex/clearing/control/config/settings/Database.java
@@ -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;
+ }
+}
diff --git a/clearing-parent/control-service/src/main/java/ru/spcex/clearing/control/controller/ControlController.java b/clearing-parent/control-service/src/main/java/ru/spcex/clearing/control/controller/ControlController.java
new file mode 100644
index 000000000..72ee5e8aa
--- /dev/null
+++ b/clearing-parent/control-service/src/main/java/ru/spcex/clearing/control/controller/ControlController.java
@@ -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);
+ }
+}
diff --git a/clearing-parent/control-service/src/main/java/ru/spcex/clearing/control/mapper/ResultContainerMapper.java b/clearing-parent/control-service/src/main/java/ru/spcex/clearing/control/mapper/ResultContainerMapper.java
new file mode 100644
index 000000000..ffc9f91d5
--- /dev/null
+++ b/clearing-parent/control-service/src/main/java/ru/spcex/clearing/control/mapper/ResultContainerMapper.java
@@ -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 {
+ @Override
+ public ResultContainer mapRow(ResultSet rs, int rowNum) throws SQLException {
+ return new ResultContainer(
+ rs.getLong("first_table"),
+ rs.getLong("second_table"),
+ rs.getLong("diff")
+ );
+ }
+}
diff --git a/clearing-parent/control-service/src/main/java/ru/spcex/clearing/control/model/ResultContainer.java b/clearing-parent/control-service/src/main/java/ru/spcex/clearing/control/model/ResultContainer.java
new file mode 100644
index 000000000..63894e134
--- /dev/null
+++ b/clearing-parent/control-service/src/main/java/ru/spcex/clearing/control/model/ResultContainer.java
@@ -0,0 +1,4 @@
+package ru.spcex.clearing.control.model;
+
+public record ResultContainer(Long firstTable, Long secondTable, Long difference) {
+}
diff --git a/clearing-parent/control-service/src/main/java/ru/spcex/clearing/control/repository/ControlRepository.java b/clearing-parent/control-service/src/main/java/ru/spcex/clearing/control/repository/ControlRepository.java
new file mode 100644
index 000000000..990be1513
--- /dev/null
+++ b/clearing-parent/control-service/src/main/java/ru/spcex/clearing/control/repository/ControlRepository.java
@@ -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);
+ }
+}
diff --git a/clearing-parent/control-service/src/main/java/ru/spcex/clearing/control/service/ControlService.java b/clearing-parent/control-service/src/main/java/ru/spcex/clearing/control/service/ControlService.java
new file mode 100644
index 000000000..a16223bcb
--- /dev/null
+++ b/clearing-parent/control-service/src/main/java/ru/spcex/clearing/control/service/ControlService.java
@@ -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 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 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);
+ }
+ }
+}
diff --git a/clearing-parent/control-service/src/main/resources/application.properties b/clearing-parent/control-service/src/main/resources/application.properties
new file mode 100644
index 000000000..7e62ee8b0
--- /dev/null
+++ b/clearing-parent/control-service/src/main/resources/application.properties
@@ -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
diff --git a/clearing-parent/control-service/src/main/resources/logback.xml b/clearing-parent/control-service/src/main/resources/logback.xml
new file mode 100644
index 000000000..84be3d48c
--- /dev/null
+++ b/clearing-parent/control-service/src/main/resources/logback.xml
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+
+
+
+ ${CONSOLE_LOG_PATTERN}
+ utf-8
+
+
+
+
+ ${LOG_PATH}/${FILE_NAME}-text.log
+
+
+ ${FILE_LOG_PATTERN}
+ utf8
+
+
+ ${LOG_PATH}/${FILE_NAME}-text.%d{yyyy-MM-dd}.%i.gz
+
+
+ 100MB
+
+ 10
+
+
+
+
+ ${LOG_PATH}/${FILE_NAME}-json.log
+
+
+ ${LOG_PATH}/${FILE_NAME}-json.%d{yyyy-MM-dd}.%i.gz
+
+
+ 100MB
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/clearing-parent/pom.xml b/clearing-parent/pom.xml
index 1537daa9f..899a8047d 100644
--- a/clearing-parent/pom.xml
+++ b/clearing-parent/pom.xml
@@ -45,6 +45,7 @@
imdg-hist
xml-importer
xml-exporter
+ control-service
diff --git a/pom.xml b/pom.xml
index 2ad22450c..afabeeab2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -55,6 +55,7 @@
${folder_root_clearing}/clearing-parent/gateway-api
${folder_root_clearing}/clearing-parent/xml-exporter
${folder_root_clearing}/clearing-parent/xml-importer
+ ${folder_root_clearing}/clearing-parent/control-service
3.12.4
1.7.33
diff --git a/z-distr/pom.xml b/z-distr/pom.xml
index e6234289d..f2b7581b1 100644
--- a/z-distr/pom.xml
+++ b/z-distr/pom.xml
@@ -614,6 +614,29 @@
+
+ copy-control-service-bin
+ prepare-package
+
+ copy
+
+
+
+
+ ${folder_root_control-service}/target/control-service.jar
+ ${folder.clearing.distr.bin}/control-service.jar
+
+
+ ${folder_root_control-service}/target/control-service.jar
+ ${folder.clearing.distr.services}/control-service/control-service.jar
+
+
+ ${folder_root_control-service}/src/main/resources/application.properties
+ ${folder.clearing.distr.settings}/control-service/application.properties
+
+
+
+
diff --git a/z-distr/src/main/resources/distr/sh/control-service.sh b/z-distr/src/main/resources/distr/sh/control-service.sh
new file mode 100644
index 000000000..51e19ca41
--- /dev/null
+++ b/z-distr/src/main/resources/distr/sh/control-service.sh
@@ -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 &
+