diff --git a/clearing-parent/imdg/src/main/java/ru/spcex/clearing/imdg/businessevent/AccountBalanceHistoryMapStore.java b/clearing-parent/imdg/src/main/java/ru/spcex/clearing/imdg/businessevent/AccountBalanceHistoryMapStore.java index 9e081556d..0540af846 100644 --- a/clearing-parent/imdg/src/main/java/ru/spcex/clearing/imdg/businessevent/AccountBalanceHistoryMapStore.java +++ b/clearing-parent/imdg/src/main/java/ru/spcex/clearing/imdg/businessevent/AccountBalanceHistoryMapStore.java @@ -41,7 +41,7 @@ public class AccountBalanceHistoryMapStore extends TemplateEventMapStore { @@ -26,17 +27,17 @@ public class AccountRoutingHistoryMapStore extends TemplateEventMapStore { @@ -36,7 +37,7 @@ public class BankAccountHistoryMapStore extends TemplateEventMapStore { @@ -26,17 +27,17 @@ public class InformationAccountHistoryMapStore extends TemplateEventMapStore { @Override public String[] getFields() { - return new String[]{"ID","EVENT_TIME", "EVENT_USER_ID", - "USER_ID", "CREATED_AT", "UPDATED_AT", "IDENTIFIER" + return new String[]{"ID", "EVENT_TIME", "EVENT_USER_ID", + "USER_CLS_ID", "CREATED_AT", "UPDATED_AT", "IDENTIFIER" }; } @Override public Object[] objectToField(UserHistory historyLog) { - User object=historyLog.getObject(); + User object = historyLog.getObject(); Object[] args = new Object[]{ historyLog.getId(), - historyLog.getEventTime(), + TimeUtil.toDateFromInstant(historyLog.getEventTime()), historyLog.getUserId(), object.getId(), diff --git a/clearing-parent/imdg/src/test/ddl.sql b/clearing-parent/imdg/src/test/ddl.sql index 3414a840d..6a9c2eeb6 100644 --- a/clearing-parent/imdg/src/test/ddl.sql +++ b/clearing-parent/imdg/src/test/ddl.sql @@ -1111,6 +1111,47 @@ COMMENT ON COLUMN COMPANY.SHORT_NAME IS 'Краткое наименование COMMENT ON COLUMN COMPANY.FULL_NAME IS 'Полное наименование Компании'; + +-- History log of company - Участник +DROP TABLE IF EXISTS COMPANY_HISTORY; +CREATE TABLE COMPANY_HISTORY +( + COMPANY_ID BIGINT NOT NULL, + EVENT_TIME timestamp, + EVENT_USER_ID BIGINT, + ID bigint PRIMARY KEY, + CREATED_AT timestamp, + UPDATED_AT timestamp, + TRADING_CODE varchar(255), + CLEARING_CODE varchar(255), + REGISTRATION_CODE varchar(255), + WORKFLOW_STATUS char(4), + SHORT_NAME varchar(255), + FULL_NAME varchar(255) +); +COMMENT ON TABLE COMPANY_HISTORY IS 'История изменений таблицы company'; +COMMENT ON COLUMN COMPANY_HISTORY.COMPANY_ID IS 'Идентификатор записи в таблице COMPANY'; +COMMENT ON COLUMN COMPANY_HISTORY.EVENT_TIME IS 'Дата и время изменения'; +COMMENT ON COLUMN COMPANY_HISTORY.EVENT_USER_ID IS 'Инициатор изменения'; + +COMMENT ON COLUMN COMPANY_HISTORY.ID IS 'Идентификатор записи'; + +COMMENT ON COLUMN COMPANY_HISTORY.CREATED_AT IS 'Дата и время создания записи'; + +COMMENT ON COLUMN COMPANY_HISTORY.UPDATED_AT IS 'Дата и время изменения записи'; + +COMMENT ON COLUMN COMPANY_HISTORY.TRADING_CODE IS 'Код участника торгов'; + +COMMENT ON COLUMN COMPANY_HISTORY.CLEARING_CODE IS 'Код участника клиринга'; + +COMMENT ON COLUMN COMPANY_HISTORY.REGISTRATION_CODE IS 'Регистрационный код участника'; + +COMMENT ON COLUMN COMPANY_HISTORY.WORKFLOW_STATUS IS 'Статус (linked to workflowStatus)'; + +COMMENT ON COLUMN COMPANY_HISTORY.SHORT_NAME IS 'Краткое наименование Компании'; + +COMMENT ON COLUMN COMPANY_HISTORY.FULL_NAME IS 'Полное наименование Компании'; + -- companyInfo - Профиль Компании DROP TABLE IF EXISTS COMPANY_INFO; CREATE TABLE COMPANY_INFO @@ -1975,7 +2016,6 @@ COMMENT ON COLUMN MONEY_MARKET_SECURITY.SECURITY_SYMBOL IS 'Код инстру COMMENT ON COLUMN MONEY_MARKET_SECURITY.TERM_TYPE IS 'Идентификатор вида инструмента (linked to termType)'; - -- History log of moneyMarketSecurity - Инструменты Денежного рынка DROP TABLE IF EXISTS MONEY_MARKET_SECURITY_HISTORY; CREATE TABLE MONEY_MARKET_SECURITY_HISTORY @@ -1993,7 +2033,8 @@ CREATE TABLE MONEY_MARKET_SECURITY_HISTORY INSTRUMENT_TYPE char(4), FULL_NAME varchar(255), SECURITY_SYMBOL varchar(255), - TERM_TYPE char(4) + TERM_TYPE char(4), + ID numeric(72, 2) PRIMARY KEY ); COMMENT ON TABLE MONEY_MARKET_SECURITY_HISTORY IS 'История изменений таблицы moneyMarketSecurity'; COMMENT ON COLUMN MONEY_MARKET_SECURITY_HISTORY.MONEY_MARKET_SECURITY_ID IS 'Идентификатор записи в таблице MONEY_MARKET_SECURITY'; @@ -2022,6 +2063,8 @@ COMMENT ON COLUMN MONEY_MARKET_SECURITY_HISTORY.SECURITY_SYMBOL IS 'Код ин COMMENT ON COLUMN MONEY_MARKET_SECURITY_HISTORY.TERM_TYPE IS 'Идентификатор вида инструмента (linked to termType)'; +COMMENT ON COLUMN MONEY_MARKET_SECURITY_HISTORY.ID IS 'Размер лота (linked to listing)'; + -- listing - Листинг инструментов DROP TABLE IF EXISTS LISTING; CREATE TABLE LISTING diff --git a/clearing-parent/imdg/src/test/ru/spcex/clearing/imdg/AllMapStoreTest.java b/clearing-parent/imdg/src/test/ru/spcex/clearing/imdg/AllMapStoreTest.java index edf1dd52a..0d82ef029 100644 --- a/clearing-parent/imdg/src/test/ru/spcex/clearing/imdg/AllMapStoreTest.java +++ b/clearing-parent/imdg/src/test/ru/spcex/clearing/imdg/AllMapStoreTest.java @@ -10,6 +10,7 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.support.DataAccessUtils; import org.springframework.jdbc.core.BeanPropertyRowMapper; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.test.context.ContextConfiguration; @@ -22,6 +23,7 @@ import ru.spcex.clearing.imdg.base.TemplateMapStore; import ru.spcex.clearing.imdg.config.DbTestConnectionConfig; import ru.spcex.clearing.imdg.config.TestConfiguration; import ru.spcex.clearing.imdg.structure.ObjectForCheckMapStore; +import ru.spcex.clearing.imdg.utils.BusinessEventRowMapper; import ru.spcex.clearing.imdg.utils.DbDataUtils; import ru.spcex.platform.classes.base.SpcexObjectBase; @@ -81,8 +83,7 @@ public class AllMapStoreTest { HazelcastInstance hazelcastInstance = testConfig.getHazelcastInstance(); hazelcastInstance.getMap(""); for (String mapName : hazelcastInstance.getConfig().getMapConfigs().keySet()) { - MapStoreConfig mapStoreConfig = hazelcastInstance.getConfig().getMapConfig(mapName).getMapStoreConfig(); - Object mapStore = mapStoreConfig.getImplementation(); + Object mapStore = getMapStore(mapName); String tableName; if (mapStore instanceof TemplateMapStore) { tableName = ((TemplateMapStore) mapStore).getTableName(); @@ -113,8 +114,16 @@ public class AllMapStoreTest { testConfig.reinitHazlecastInstance(); for (ObjectForCheckMapStore objectForCheck : objectForCheckMapStores) { - IMap map = testConfig.getHazelcastInstance().getMap(objectForCheck.getMapName()); - SpcexObjectBase actual = map.get(ID); + String mapName = objectForCheck.getMapName(); + SpcexObjectBase actual; + if (getMapStore(mapName) instanceof TemplateEventMapStore) { + String sql = String.format("SELECT * FROM %s WHERE id = %d", getTableNameFromMapStore(objectForCheck.getMapName()), ID); + List obj = jdbcTemplate.query(sql, new BusinessEventRowMapper<>(objectForCheck.getClazz())); + actual = DataAccessUtils.singleResult(obj); + } else { + IMap map = testConfig.getHazelcastInstance().getMap(mapName); + actual = map.get(ID); + } objectForCheck.getMATCHER().assertMatch(actual, objectForCheck.getPredictableObj()); } } @@ -128,7 +137,7 @@ public class AllMapStoreTest { for (ObjectForCheckMapStore objectForCheck : objectForCheckMapStores) { IMap map = testConfig.getHazelcastInstance().getMap(objectForCheck.getMapName()); map.remove(ID); - String sql = String.format("SELECT FROM %s WHERE id = %d", getTableNameFromMapStore(objectForCheck.getMapName()), ID); + String sql = String.format("SELECT * FROM %s WHERE id = %d", getTableNameFromMapStore(objectForCheck.getMapName()), ID); List obj = jdbcTemplate.query(sql, BeanPropertyRowMapper.newInstance(objectForCheck.getClazz())); Assertions.assertNull(map.get(ID)); @@ -152,6 +161,11 @@ public class AllMapStoreTest { return tableName; } + private Object getMapStore(String mapName) { + MapStoreConfig mapStoreConfig = testConfig.getHazelcastInstance().getConfig().getMapConfig(mapName).getMapStoreConfig(); + return mapStoreConfig.getImplementation(); + } + private void saveObjectToMaps() { for (ObjectForCheckMapStore objectForCheck : objectForCheckMapStores) { String mapName = objectForCheck.getMapName(); diff --git a/clearing-parent/imdg/src/test/ru/spcex/clearing/imdg/config/DbTestConnectionConfig.java b/clearing-parent/imdg/src/test/ru/spcex/clearing/imdg/config/DbTestConnectionConfig.java index bdcaa1d6a..2709151be 100644 --- a/clearing-parent/imdg/src/test/ru/spcex/clearing/imdg/config/DbTestConnectionConfig.java +++ b/clearing-parent/imdg/src/test/ru/spcex/clearing/imdg/config/DbTestConnectionConfig.java @@ -8,6 +8,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.core.io.ClassPathResource; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.init.DatabasePopulator; +import org.springframework.jdbc.datasource.init.DatabasePopulatorUtils; import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator; import ru.spcex.clearing.imdg.error.ModuleInitializeException; @@ -52,7 +53,7 @@ public class DbTestConnectionConfig { cpds.setNumHelperThreads(configRoot.getDatabase().getNumHelperThreads()); cpds.setCheckoutTimeout(timeoutSec * 1000/*todo common 1000==ConstsCommon.SECOND*/); logTimeoutPart = String.format(" (timeout=%ds)", timeoutSec); -// DatabasePopulatorUtils.execute(createDatabasePopulator(), cpds); + DatabasePopulatorUtils.execute(createDatabasePopulator(), cpds); result = cpds; String OPERATION_DATABASE_CONNECTION_CHECK = String.format("Database [%s] connection check", dbPath); diff --git a/clearing-parent/imdg/src/test/ru/spcex/clearing/imdg/structure/GeneratorMapNamesForHazelcast.java b/clearing-parent/imdg/src/test/ru/spcex/clearing/imdg/structure/GeneratorMapNamesForHazelcast.java index ad1ef222e..5e86ffb4c 100644 --- a/clearing-parent/imdg/src/test/ru/spcex/clearing/imdg/structure/GeneratorMapNamesForHazelcast.java +++ b/clearing-parent/imdg/src/test/ru/spcex/clearing/imdg/structure/GeneratorMapNamesForHazelcast.java @@ -1,18 +1,21 @@ package ru.spcex.clearing.imdg.structure; -import ru.clearing.classes.statics.data.account.Account; -import ru.clearing.classes.statics.data.account.AccountBalance; +import ru.clearing.classes.statics.data.account.*; import ru.clearing.classes.statics.data.company.Company; import ru.clearing.classes.statics.data.company.relation.Relation; +import ru.clearing.classes.statics.data.company.relation.RelationHistory; import ru.clearing.classes.statics.data.messages.ErrorText; import ru.clearing.classes.statics.data.scheduler.Scheduler; import ru.clearing.classes.statics.data.scheduler.TaskRunner; import ru.clearing.classes.statics.data.scheduler.Timetable; import ru.clearing.classes.statics.data.scheduler.TradingCalendar; import ru.clearing.classes.statics.data.security.Security; +import ru.clearing.classes.statics.data.security.SecurityHistory; import ru.clearing.classes.statics.data.statement.Statement; import ru.clearing.classes.statics.data.user.User; import ru.clearing.classes.statics.data.user.UserConnect; +import ru.clearing.classes.statics.data.user.UserConnectHistory; +import ru.clearing.classes.statics.data.user.UserHistory; import ru.spcex.clearing.imdg.IMDGDistributedNames; import ru.spcex.clearing.imdg.structure.ObjectForCheckMapStore.SettingOperation; @@ -31,7 +34,18 @@ public class GeneratorMapNamesForHazelcast { private static void init() { objectForCheckMapStores = new LinkedList<>(); - //businessobject + //business event + objectForCheckMapStores.add(new ObjectForCheckMapStore<>(IMDGDistributedNames.Map_AccountBalanceHistory, AccountBalanceHistory.class)); + objectForCheckMapStores.add(new ObjectForCheckMapStore<>(IMDGDistributedNames.Map_AccountHistory, AccountHistory.class)); + objectForCheckMapStores.add(new ObjectForCheckMapStore<>(IMDGDistributedNames.Map_AccountRoutingHistory, AccountRoutingHistory.class)); + objectForCheckMapStores.add(new ObjectForCheckMapStore<>(IMDGDistributedNames.Map_BankAccountHistory, BankAccountHistory.class)); +// objectForCheckMapStores.add(new ObjectForCheckMapStore<>(IMDGDistributedNames.Map_CompanyHistory, CompanyHistory.class)); + objectForCheckMapStores.add(new ObjectForCheckMapStore<>(IMDGDistributedNames.Map_InformationAccountHistory, InformationAccountHistory.class)); + objectForCheckMapStores.add(new ObjectForCheckMapStore<>(IMDGDistributedNames.Map_RelationHistory, RelationHistory.class)); + objectForCheckMapStores.add(new ObjectForCheckMapStore<>(IMDGDistributedNames.Map_SecurityHistory, SecurityHistory.class)); + objectForCheckMapStores.add(new ObjectForCheckMapStore<>(IMDGDistributedNames.Map_UserConnectHistory, UserConnectHistory.class)); + objectForCheckMapStores.add(new ObjectForCheckMapStore<>(IMDGDistributedNames.Map_UserHistory, UserHistory.class)); + //business object objectForCheckMapStores.add(new ObjectForCheckMapStore<>(IMDGDistributedNames.Map_Account, Account.class)); objectForCheckMapStores.add(new ObjectForCheckMapStore<>(IMDGDistributedNames.Map_AccountBalance, AccountBalance.class)); objectForCheckMapStores.add(new ObjectForCheckMapStore<>(IMDGDistributedNames.Map_Company, Company.class)); @@ -46,7 +60,6 @@ public class GeneratorMapNamesForHazelcast { objectForCheckMapStores.add(new ObjectForCheckMapStore<>(IMDGDistributedNames.Map_TradingCalendar, TradingCalendar.class)); objectForCheckMapStores.add(new ObjectForCheckMapStore<>(IMDGDistributedNames.Map_UserConnect, UserConnect.class)); objectForCheckMapStores.add(new ObjectForCheckMapStore<>(IMDGDistributedNames.Map_User, User.class)); -// objectForCheckMapStores.add(new ObjectForCheckMapStore<>(IMDGDistributedNames.Map_UserConnectHistory, UserConnectHistory.class)); } public static Map createClassByMapStoreName() { diff --git a/clearing-parent/imdg/src/test/ru/spcex/clearing/imdg/structure/ObjectForCheckMapStore.java b/clearing-parent/imdg/src/test/ru/spcex/clearing/imdg/structure/ObjectForCheckMapStore.java index d421a901a..9f90a89b0 100644 --- a/clearing-parent/imdg/src/test/ru/spcex/clearing/imdg/structure/ObjectForCheckMapStore.java +++ b/clearing-parent/imdg/src/test/ru/spcex/clearing/imdg/structure/ObjectForCheckMapStore.java @@ -58,6 +58,7 @@ public class ObjectForCheckMapStore { private static Class[] parameterTypes; private static Object[] params; + //сеттер для полей значения которых обрезаются при вставке в БД public SettingOperation(String methodName, Class[] parameterTypes, Object[] params) { SettingOperation.methodName = methodName; SettingOperation.parameterTypes = parameterTypes; diff --git a/clearing-parent/imdg/src/test/ru/spcex/clearing/imdg/utils/BusinessEventRowMapper.java b/clearing-parent/imdg/src/test/ru/spcex/clearing/imdg/utils/BusinessEventRowMapper.java new file mode 100644 index 000000000..4e437e236 --- /dev/null +++ b/clearing-parent/imdg/src/test/ru/spcex/clearing/imdg/utils/BusinessEventRowMapper.java @@ -0,0 +1,120 @@ +package ru.spcex.clearing.imdg.utils; + +import org.springframework.beans.BeanUtils; +import org.springframework.beans.NotWritablePropertyException; +import org.springframework.beans.TypeMismatchException; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.jdbc.support.JdbcUtils; +import ru.clearing.classes.objects.BusinessEvent; +import ru.spcex.platform.classes.base.SpcexObjectBase; + +import java.lang.reflect.Field; +import java.sql.*; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.ZoneId; +import java.util.Date; + +public class BusinessEventRowMapper implements RowMapper { + + private final Class mappedClass; + + public BusinessEventRowMapper(Class mappedClass) { + this.mappedClass = mappedClass; + } + + @Override + public T mapRow(ResultSet rs, int rowNum) throws SQLException { + T historyObject = BeanUtils.instantiateClass(this.mappedClass); + SpcexObjectBase objectFild = null; + try { + objectFild = (SpcexObjectBase) BeanUtils.instantiateClass(historyObject.getClass().getDeclaredField("object").getType()); + } catch (NoSuchFieldException e) { + throw new RuntimeException(e); + } + ResultSetMetaData meta_data = rs.getMetaData(); + String tableName = rs.getMetaData().getTableName(1); + tableName.replace("_history", ""); + int columnCount = meta_data.getColumnCount(); + + for (int index = 1; index <= columnCount; index++) { + String column = JdbcUtils.lookupColumnName(meta_data, index); + String fildName = getFildName(column, tableName); + try { + Object value = JdbcUtils.getResultSetValue(rs, index, Class.forName(meta_data.getColumnClassName(index))); + setValueFildForObjectIfFildExists(historyObject, historyObject.getClass(), fildName, value); + setValueFildForObjectIfFildExists(objectFild, objectFild.getClass(), fildName, value); + } catch (TypeMismatchException | NotWritablePropertyException e) { + // Ignore + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + + } + historyObject.setObject(objectFild); + return historyObject; + } + + public void setValueFildForObjectIfFildExists(SpcexObjectBase object, Class clazz, String fildName, Object value) { + try { + Field field = clazz.getDeclaredField(fildName); + field.setAccessible(true); + Class typeField = field.getType(); + if (typeField.equals(LocalDate.class)) { + Date date = (Date) value; + field.set(object, value != null ? Instant.ofEpochMilli(date.getTime()).atZone(ZoneId.systemDefault()).toLocalDate() : null); + } else if (typeField.equals(LocalTime.class)) { + Time time = (Time) value; + field.set(object, value != null ? time.toLocalTime() : null); + } else if (typeField.getName().equals(Instant.class.getName())) { + field.set(object, value != null ? ((Timestamp) value).toInstant() : null); + } else { + field.set(object, value); + } + } catch (NoSuchFieldException e) { + if (!clazz.getSuperclass().getName().equals(Object.class.getName())) { + setValueFildForObjectIfFildExists(object, clazz.getSuperclass(), fildName, value); + } + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + } + + private String getFildName(String str, String tableName) { + if (str.startsWith(tableName)) { + return "id"; + } else if (str.equals("created_at")) { + return "created"; + } else if (str.equals("updated_at")) { + return "updated"; + } else if (str.equals("event_user_id")) { + return "userId"; + } + return snakeToCamel(str); + } + + private String snakeToCamel(String str) { + StringBuilder builder = new StringBuilder(str); + + // Traverse the string character by + // character and remove underscore + // and capitalize next letter + for (int i = 0; i < builder.length(); i++) { + + // Check char is underscore + if (builder.charAt(i) == '_') { + + builder.deleteCharAt(i); + builder.replace( + i, i + 1, + String.valueOf( + Character.toUpperCase( + builder.charAt(i)))); + } + } + + // Return in String type + return builder.toString(); + } +} \ No newline at end of file