Merge remote-tracking branch 'origin/dev' into dev

This commit is contained in:
ialbert 2025-08-07 18:33:43 +03:00
commit 63f94d6921
11 changed files with 247 additions and 28 deletions

View file

@ -4,6 +4,8 @@ import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import java.util.Collection;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@ -14,15 +16,20 @@ import ru.spcex.clearing.backendapi.controller.queue.AbstractQueueController;
import ru.spcex.clearing.backendapi.controller.response.entity.CommonGetAllResponse;
import ru.spcex.clearing.backendapi.service.IOperator;
import ru.spcex.clearing.backendapi.service.IStateLoader;
import ru.spcex.platform.imdg.api.ImdgProvider;
@Controller
@RequestMapping("/sessionHistory")
public class SessionHistoryController extends AbstractQueueController {
private final IStateLoader stateLoader;
private final ImdgProvider imdgProvider;
@Autowired
public SessionHistoryController(IOperator operator, IStateLoader stateLoader) {
public SessionHistoryController(IOperator operator,
ImdgProvider imdgProvider,
IStateLoader stateLoader) {
super(operator);
this.imdgProvider = imdgProvider;
this.stateLoader = stateLoader;
}
@ -35,7 +42,9 @@ public class SessionHistoryController extends AbstractQueueController {
@RequestParam(name = "id")
Long id
) {
Collection<Map<String, Object>> sessionHistories = stateLoader.getSessionHistoryById(id);
CommonGetAllResponse response = new CommonGetAllResponse();
response.fromEntity(sessionHistories);
return response;
}
}

View file

@ -23,4 +23,5 @@ public interface IStateLoader {
String mapName,
Class<T> clazz,
ImdgPredicate conditions);
Collection<Map<String, Object>> getSessionHistoryById(Long sessionId);
}

View file

@ -9,12 +9,15 @@ import java.util.concurrent.ConcurrentHashMap;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import ru.clearing.classes.statics.data.misc.SessionHistory;
import ru.spcex.clearing.backendapi.meta.GetResponseFactory;
import ru.spcex.clearing.backendapi.service.IStateLoader;
import ru.spcex.clearing.imdg.IMDGDistributedNames;
import ru.spcex.platform.classes.base.SpcexObjectBase;
import ru.spcex.platform.imdg.api.Imdg;
import ru.spcex.platform.imdg.api.ImdgProvider;
import ru.spcex.platform.imdg.api.predicate.ImdgPredicate;
import ru.spcex.platform.imdg.api.predicate.ImdgPredicateBuilder;
@Service
public class StateLoaderImpl implements IStateLoader {
@ -108,4 +111,17 @@ public class StateLoaderImpl implements IStateLoader {
.map(o -> responseFactory.responseFromObject(o, clazz))
.toList();
}
@Override
public Collection<Map<String, Object>> getSessionHistoryById(Long sessionId) {
Imdg<SpcexObjectBase> imdgSearch = getHistImdg(IMDGDistributedNames.Map_SearchSessionHistory, SpcexObjectBase.class);
ImdgPredicateBuilder predicateBuilder = imdgSearch.predicateBuilder();
ImdgPredicate imdgPredicate = predicateBuilder.equals("sessionId", sessionId);
Imdg<SessionHistory> sessionHistoryImdg = getHistImdg(IMDGDistributedNames.Map_SessionHistory, SessionHistory.class);
return imdgSearch.getCollectionIdsByPredicate(imdgPredicate).stream()
.map(sessionHistoryImdg::getSingleObjectByID)
.filter(Objects::nonNull)
.map(sessionHistory -> responseFactory.responseFromObject(sessionHistory, SessionHistory.class))
.toList();
}
}

View file

@ -6274,55 +6274,55 @@
}
,
{"code": "sessionId",
"type": 1,"name": "Идентификатор записи","shortname": "ID","searchable": true,"sortable": true
"field": "object.id","type": 1,"name": "Идентификатор сессии","shortname": "ID сессии","searchable": true,"sortable": true
}
,
{"code": "eventTime",
"type": 1,"name": "Идентификатор записи","shortname": "ID","searchable": true,"sortable": true
"type": 4,"name": "Дата и время изменения","shortname": "Дата и время изменения","searchable": true,"sortable": true
}
,
{"code": "eventType",
"type": 1,"name": "Идентификатор записи","shortname": "ID","searchable": true,"sortable": true
"type": 12,"name": "Тип изменения","shortname": "Тип изменения","searchable": true,"sortable": true
}
,
{"code": "createdAt",
"field": "created","type": 4,"webtype": "5","dbname": "Дата-время создания записи","name": "Время создания записи","shortname": "Создано","searchable": true,"sortable": true
"field": "object.created","type": 4,"webtype": "5","dbname": "Дата-время создания записи","name": "Время создания записи","shortname": "Создано","searchable": true,"sortable": true
}
,
{"code": "updatedAt",
"field": "updated","type": 4,"webtype": "5","dbname": "Дата-время изменения записи","name": "Время изменения записи","shortname": "Завершено","searchable": true,"sortable": true
"field": "object.updated","type": 4,"webtype": "5","dbname": "Дата-время изменения записи","name": "Время изменения записи","shortname": "Завершено","searchable": true,"sortable": true
}
,
{"code": "clearingDate",
"type": 6,"name": "Дата","shortname": "Дата","searchable": true,"sortable": true,"visible": true
"field": "object.clearingDate","type": 6,"name": "Дата","shortname": "Дата","searchable": true,"sortable": true,"visible": true
}
,
{"code": "sessionStatus",
"type": 12,"dbname": "Код статуса клиринговой сессии","name": "Статус клиринговой сессии","shortname": "Шаг","searchable": true,"sortable": true,"visible": true,"link": "sessionStatus"
"field": "object.sessionStatus","type": 12,"dbname": "Код статуса клиринговой сессии","name": "Статус клиринговой сессии","shortname": "Шаг","searchable": true,"sortable": true,"visible": true,"link": "sessionStatus"
}
,
{"code": "companyId",
"type": 1,"dbname": "Идентификатор инициатора торгов","name": "Наименование инициатора торгов","shortname": "Инициатор","visible": false,"searchable": true,"sortable": true,"link": "company","linkCode": "shortName"
"field": "object.companyId","type": 1,"dbname": "Идентификатор инициатора торгов","name": "Наименование инициатора торгов","shortname": "Инициатор","visible": false,"searchable": true,"sortable": true,"link": "company","linkCode": "shortName"
}
,
{"code": "securityId",
"type": 1,"dbname": "Идентификатор инструмента","name": "Наименование инструмента","shortname": "Инструмент","searchable": false,"sortable": true,"visible": true,"link": "security","linkCode": "shortName"
"field": "object.securityId","type": 1,"dbname": "Идентификатор инструмента","name": "Наименование инструмента","shortname": "Инструмент","searchable": false,"sortable": true,"visible": true,"link": "security","linkCode": "shortName"
}
,
{"code": "userId",
"type": 1,"dbname": "Идентификатор пользователя","name": "Наименование пользователя","shortname": "Пользователь","searchable": true,"sortable": true,"visible": true,"link": "userCls","linkCode": "identifier"
"field": "object.userId","type": 1,"dbname": "Идентификатор пользователя","name": "Наименование пользователя","shortname": "Пользователь","searchable": true,"sortable": true,"visible": true,"link": "userCls","linkCode": "identifier"
}
,
{"code": "section",
"type": 12,"dbname": "Код наименования секции","name": "Наименование секции","shortname": "Секция","searchable": true,"sortable": true,"visible": true,"link": "section"
"field": "object.section","type": 12,"dbname": "Код наименования секции","name": "Наименование секции","shortname": "Секция","searchable": true,"sortable": true,"visible": true,"link": "section"
}
,
{"code": "sessionType",
"type": 12,"dbname": "Код типа клиринговой сессии","name": "Тип клиринговой сессии","shortname": "Тип","searchable": true,"sortable": true,"visible": true,"link": "sessionType"
"field": "object.sessionType","type": 12,"dbname": "Код типа клиринговой сессии","name": "Тип клиринговой сессии","shortname": "Тип","searchable": true,"sortable": true,"visible": true,"link": "sessionType"
}
,
{"code": "workflowStatus",
"type": 12,"dbname": "Код статуса","name": "Наименование статуса","shortname": "Статус","searchable": true,"sortable": true,"visible": true,"link": "workflowStatus"
"field": "object.workflowStatus","type": 12,"dbname": "Код статуса","name": "Наименование статуса","shortname": "Статус","searchable": true,"sortable": true,"visible": true,"link": "workflowStatus"
}
]

View file

@ -1453,19 +1453,19 @@
</session>
<sessionHistory name="Клиринговые сессии" destinationById="sessionHistory" class="ru.clearing.classes.statics.data.misc.SessionHistory" table="sessionHistory">
<id type="1" name="Идентификатор записи" shortname="ID" searchable="true" sortable="true"/>
<sessionId type="1" name="Идентификатор записи" shortname="ID" searchable="true" sortable="true"/>
<eventTime type="1" name="Идентификатор записи" shortname="ID" searchable="true" sortable="true"/>
<eventType type="1" name="Идентификатор записи" shortname="ID" searchable="true" sortable="true"/>
<createdAt field="created" type="4" webtype="5" dbname="Дата-время создания записи" name="Время создания записи" shortname="Создано" searchable="true" sortable="true"/>
<updatedAt field="updated" type="4" webtype="5" dbname="Дата-время изменения записи" name="Время изменения записи" shortname="Завершено" searchable="true" sortable="true"/>
<clearingDate type="6" name="Дата" shortname="Дата" searchable="true" sortable="true" visible="true"/>
<sessionStatus type="12" dbname="Код статуса клиринговой сессии" name="Статус клиринговой сессии" shortname="Шаг" searchable="true" sortable="true" visible="true" link="sessionStatus"/>
<companyId type="1" dbname="Идентификатор инициатора торгов" name="Наименование инициатора торгов" shortname="Инициатор" visible="false" searchable="true" sortable="true" link="company" linkCode="shortName"/>
<securityId type="1" dbname="Идентификатор инструмента" name="Наименование инструмента" shortname="Инструмент" searchable="false" sortable="true" visible="true" link="security" linkCode="shortName"/>
<userId type="1" dbname="Идентификатор пользователя" name="Наименование пользователя" shortname="Пользователь" searchable="true" sortable="true" visible="true" link="userCls" linkCode="identifier"/>
<section type="12" dbname="Код наименования секции" name="Наименование секции" shortname="Секция" searchable="true" sortable="true" visible="true" link="section"/>
<sessionType type="12" dbname="Код типа клиринговой сессии" name="Тип клиринговой сессии" shortname="Тип" searchable="true" sortable="true" visible="true" link="sessionType"/>
<workflowStatus type="12" dbname="Код статуса" name="Наименование статуса" shortname="Статус" searchable="true" sortable="true" visible="true" link="workflowStatus"/>
<sessionId field="object.id" type="1" name="Идентификатор сессии" shortname="ID сессии" searchable="true" sortable="true"/>
<eventTime type="4" name="Дата и время изменения" shortname="Дата и время изменения" searchable="true" sortable="true"/>
<eventType type="12" name="Тип изменения" shortname="Тип изменения" searchable="true" sortable="true"/>
<createdAt field="object.created" type="4" webtype="5" dbname="Дата-время создания записи" name="Время создания записи" shortname="Создано" searchable="true" sortable="true"/>
<updatedAt field="object.updated" type="4" webtype="5" dbname="Дата-время изменения записи" name="Время изменения записи" shortname="Завершено" searchable="true" sortable="true"/>
<clearingDate field="object.clearingDate" type="6" name="Дата" shortname="Дата" searchable="true" sortable="true" visible="true"/>
<sessionStatus field="object.sessionStatus" type="12" dbname="Код статуса клиринговой сессии" name="Статус клиринговой сессии" shortname="Шаг" searchable="true" sortable="true" visible="true" link="sessionStatus"/>
<companyId field="object.companyId" type="1" dbname="Идентификатор инициатора торгов" name="Наименование инициатора торгов" shortname="Инициатор" visible="false" searchable="true" sortable="true" link="company" linkCode="shortName"/>
<securityId field="object.securityId" type="1" dbname="Идентификатор инструмента" name="Наименование инструмента" shortname="Инструмент" searchable="false" sortable="true" visible="true" link="security" linkCode="shortName"/>
<userId field="object.userId" type="1" dbname="Идентификатор пользователя" name="Наименование пользователя" shortname="Пользователь" searchable="true" sortable="true" visible="true" link="userCls" linkCode="identifier"/>
<section field="object.section" type="12" dbname="Код наименования секции" name="Наименование секции" shortname="Секция" searchable="true" sortable="true" visible="true" link="section"/>
<sessionType field="object.sessionType" type="12" dbname="Код типа клиринговой сессии" name="Тип клиринговой сессии" shortname="Тип" searchable="true" sortable="true" visible="true" link="sessionType"/>
<workflowStatus field="object.workflowStatus" type="12" dbname="Код статуса" name="Наименование статуса" shortname="Статус" searchable="true" sortable="true" visible="true" link="workflowStatus"/>
</sessionHistory>
<sTrades name="Сделки из Торговой системы" class="ru.clearing.classes.statics.data.misc.STrades" table="s_trades">
<id type="1" name="Идентификатор записи" shortname="ID" searchable="true" sortable="true"/>

View file

@ -0,0 +1,18 @@
package ru.spcex.clearing.historyimdg.index;
import ru.spcex.clearing.historyimdg.index.field.SearchWithSessionId;
public class SearchProxySessionHistory extends SearchProxy implements SearchWithSessionId {
private Long sessionId;
@Override
public Long getSessionId() {
return sessionId;
}
@Override
public void setSessionId(Long sessionId) {
this.sessionId = sessionId;
}
}

View file

@ -0,0 +1,7 @@
package ru.spcex.clearing.historyimdg.index.field;
public interface SearchWithSessionId {
Long getSessionId();
void setSessionId(Long sessionId);
}

View file

@ -0,0 +1,48 @@
package ru.spcex.clearing.historyimdg.mapstores;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import ru.spcex.clearing.historyimdg.index.SearchProxySessionHistory;
import ru.spcex.clearing.imdg.IMDGDistributedNames;
@Component
public class SearchSessionHistoryMapStore extends AbstractSliceMapLoader<SearchProxySessionHistory> {
public SearchSessionHistoryMapStore(JdbcTemplate jdbcTemplate) {
super(jdbcTemplate);
}
@Override
protected String getTableName() {
return "session_history";
}
@Override
protected String fieldOfDay() {
return "session_id";
}
@Override
public Collection<SearchProxySessionHistory> load(Collection<Long> keys) {
Map<String, Collection<Long>> paramMap = Collections.singletonMap("ids", keys);
return namedParameterJdbcTemplate.query("select * from " + getTableName() + " where id in (:ids)", paramMap,
(rs, rowNum) -> {
SearchProxySessionHistory proxySessionHistory = new SearchProxySessionHistory();
proxySessionHistory.setId(rs.getObject("id", Long.class));
proxySessionHistory.setSessionId(rs.getObject("session_id", Long.class));
return proxySessionHistory;
});
}
@Override
public String getMapName() {
return IMDGDistributedNames.Map_SearchSessionHistory;
}
@Override
public String[] getIndexingField() {
return new String[]{"sessionId"};
}
}

View file

@ -0,0 +1,119 @@
package ru.spcex.clearing.historyimdg.mapstores;
import com.hazelcast.config.EvictionConfig;
import com.hazelcast.config.MaxSizeConfig;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.springframework.dao.support.DataAccessUtils;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import ru.clearing.classes.statics.data.misc.Session;
import ru.clearing.classes.statics.data.misc.SessionHistory;
import ru.spcex.clearing.imdg.businessevent.SessionHistoryMapStore;
import ru.spcex.clearing.imdg.config.element.ImdgHzEvictionConfig;
@Component
public class SessionHistoryOnlyLoadMapStore extends SessionHistoryMapStore {
public SessionHistoryOnlyLoadMapStore(JdbcTemplate jdbcTemplate) {
super(jdbcTemplate);
}
@Override
public Optional<ImdgHzEvictionConfig> evictionConfig() {
ImdgHzEvictionConfig evctCfg = new ImdgHzEvictionConfig();
evctCfg.setSize(5000);
evctCfg.setMaxSizePolicy(MaxSizeConfig.MaxSizePolicy.PER_NODE);
evctCfg.setEvictionPolicy(EvictionConfig.DEFAULT_EVICTION_POLICY);
evctCfg.setTimeToLiveSeconds(1800);
return Optional.of(evctCfg);
}
@Override
public SessionHistory load(Long id) {
if (!isLoadable(id)) return null;
Collection<SessionHistory> rows;
List<Long> list = Collections.singletonList(id);
try {
rows = load(list);
} catch (Throwable e) { // one retry
rows = load(list);
}
SessionHistory obj = DataAccessUtils.singleResult(rows);
if (obj != null && isLoadable(obj))
return obj;
else
return null;
}
@Override
public Map<Long, SessionHistory> loadAll(Collection<Long> keys) {
log.debug("loadAll from " + getTableName() + " " + keys.size() + " keys");
Map<Long, SessionHistory> result = new HashMap<>();
long start = System.currentTimeMillis();
// загрузить данные по ключам частями, чтобы не выйти за ограничения базы по кол-ву элементов в in clause
List<Long> keysSubList = new ArrayList<>(MAX_IN_CLAUSE_SIZE);
for (Iterator<Long> iterator = keys.iterator(); iterator.hasNext(); ) {
Long key = iterator.next();
keysSubList.add(key);
if (keysSubList.size() == MAX_IN_CLAUSE_SIZE || !iterator.hasNext()) {
Collection<SessionHistory> rows = load(keysSubList);
for (SessionHistory row : rows) {
result.put(row.getId(), row);
}
keysSubList.clear();
}
}
log.debug("loadAll from " + getTableName() + " " + keys.size() + " keys done in " + (System.currentTimeMillis() - start) + "ms");
return result;
}
@Override
public Collection<SessionHistory> load(Collection<Long> keys) {
Map<String, Collection<Long>> paramMap = Collections.singletonMap("ids", keys);
return namedParameterJdbcTemplate.query("select * from " + getTableName() + " where id in (:ids)", paramMap,
(resultSet, i) -> objectReader(resultSet));
}
protected SessionHistory objectReader(ResultSet resultSet) throws SQLException {
SessionHistory sessionHistory = new SessionHistory();
Session object = new Session();
sessionHistory.setId(resultSet.getObject("ID", Long.class));
sessionHistory.setEventTime(getInstantFromTimestamp(resultSet, "EVENT_TIME"));
sessionHistory.setUserId(resultSet.getObject("EVENT_USER_ID", Long.class));
sessionHistory.setEventType(resultSet.getObject("EVENT_TYPE", String.class));
object.setId(resultSet.getObject("SESSION_ID", Long.class));
object.setCreated(getInstantFromTimestamp(resultSet, "CREATED_AT"));
object.setUpdated(getInstantFromTimestamp(resultSet, "UPDATED_AT"));
object.setClearingDate(getLocalDateFromSqlDate(resultSet, "CLEARING_DATE"));
object.setSessionStatus(resultSet.getObject("SESSION_STATUS", String.class));
object.setCompanyId(resultSet.getObject("COMPANY_ID", Long.class));
object.setSecurityId(resultSet.getObject("SECURITY_ID", Long.class));
object.setUserId(resultSet.getObject("USER_ID", Long.class));
object.setSection(resultSet.getObject("SECTION", String.class));
object.setSessionType(resultSet.getObject("SESSION_TYPE", String.class));
object.setWorkflowStatus(resultSet.getObject("WORKFLOW_STATUS", String.class));
sessionHistory.setObject(object);
return sessionHistory;
}
@Override
public void store(Map<Long, SessionHistory> map) {
//do nothing
}
}

View file

@ -2,9 +2,9 @@ package ru.spcex.clearing.imdg.businessevent;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import ru.spcex.clearing.imdg.IMDGDistributedNames;
import ru.clearing.classes.statics.data.misc.Session;
import ru.clearing.classes.statics.data.misc.SessionHistory;
import ru.spcex.clearing.imdg.IMDGDistributedNames;
import ru.spcex.clearing.imdg.base.TemplateEventMapStore;
import ru.spcex.platform.utils.time.TimeUtil;

View file

@ -241,6 +241,7 @@ public final class IMDGDistributedNames {
public static final String Map_SearchLiabilitiesRegister = "Map_SearchLiabilitiesRegister";
public static final String Map_SearchExecutionRegister = "Map_SearchExecutionRegister";
public static final String Map_SearchSession = "Map_SearchSession";
public static final String Map_SearchSessionHistory = "Map_SearchSessionHistory";
public static final String MAP_SEQUENCE_NAME = "MAP_SEQUENCE_NAME";
private IMDGDistributedNames() {