From 031d09d7e37f8f09d3a2d75e30c4f8947aae49eb Mon Sep 17 00:00:00 2001 From: ialbert Date: Wed, 1 Jul 2026 15:31:56 +0300 Subject: [PATCH] http://jira.mfd.msk:8088/browse/CLS-1056 --- .../controller/test/EditImdgObjectAction.java | 61 ++++++++ .../controller/test/EditObjectController.java | 131 ++++++++++++++++ .../IsActiveSessionRunningService.java | 32 ++++ .../service/executors/Sdf10Executor.java | 65 ++++++-- .../TasksDelayedBySessionServiceV2.java | 143 ++++++++++++++++++ .../iml/hazelcast/adapter/ImdgHazelcast.java | 6 + .../adapter/ImdgHazelcastListener.java | 29 ++++ .../java/ru/spcex/platform/imdg/api/Imdg.java | 4 + .../platform/imdg/api/ImdgEntryListener.java | 14 ++ .../imdg/api/listener/ImdgEntryEvent.java | 25 +++ .../domain/cud/balance/StatementRequest.java | 10 ++ 11 files changed, 507 insertions(+), 13 deletions(-) create mode 100644 clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/controller/test/EditImdgObjectAction.java create mode 100644 clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/controller/test/EditObjectController.java create mode 100644 clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/IsActiveSessionRunningService.java create mode 100644 clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/schedule/TasksDelayedBySessionServiceV2.java create mode 100644 platform-parent/platform-imdg-api-hazelcast-impl/src/main/java/ru/spcex/platform/imdg/iml/hazelcast/adapter/ImdgHazelcastListener.java create mode 100644 platform-parent/platform-imdg-api/src/main/java/ru/spcex/platform/imdg/api/ImdgEntryListener.java create mode 100644 platform-parent/platform-imdg-api/src/main/java/ru/spcex/platform/imdg/api/listener/ImdgEntryEvent.java diff --git a/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/controller/test/EditImdgObjectAction.java b/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/controller/test/EditImdgObjectAction.java new file mode 100644 index 000000000..8cfa63e19 --- /dev/null +++ b/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/controller/test/EditImdgObjectAction.java @@ -0,0 +1,61 @@ +package ru.spcex.clearing.backendapi.controller.test; + +import io.swagger.annotations.ApiModelProperty; +import java.util.Map; +import com.fasterxml.jackson.annotation.JsonProperty; + +public class EditImdgObjectAction { + @ApiModelProperty( + value = "IMDG map name", + example = "Map_Registry" + ) + @JsonProperty + private String mapName; + + @ApiModelProperty( + value = "Full class name of IMDG object", + example = "ru.clearing.classes.statics.data.registry.Registry" + ) + @JsonProperty + private String fullClassName; + + @ApiModelProperty(value = "IMDG object id") + @JsonProperty + private Long objId; + + @ApiModelProperty(value = "Fields and replacement values") + @JsonProperty + private Map fields; + + public String getMapName() { + return mapName; + } + + public void setMapName(String mapName) { + this.mapName = mapName; + } + + public String getFullClassName() { + return fullClassName; + } + + public void setFullClassName(String fullClassName) { + this.fullClassName = fullClassName; + } + + public Long getObjId() { + return objId; + } + + public void setObjId(Long objId) { + this.objId = objId; + } + + public Map getFields() { + return fields; + } + + public void setFields(Map fields) { + this.fields = fields; + } +} diff --git a/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/controller/test/EditObjectController.java b/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/controller/test/EditObjectController.java new file mode 100644 index 000000000..7da8554ed --- /dev/null +++ b/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/controller/test/EditObjectController.java @@ -0,0 +1,131 @@ +package ru.spcex.clearing.backendapi.controller.test; + +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; +import java.lang.reflect.Field; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.Map; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Profile; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Controller; +import org.springframework.util.ClassUtils; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseBody; +import ru.spcex.clearing.backendapi.errors.BackEndError; +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.utils.enumeration.EnumMessage; +import ru.spcex.platform.utils.error.ValidationException; +import ru.spcex.platform.utils.text.TextUtil; + +@Profile("dev") +@Controller +@RequestMapping("/anonymous/imdg-api") +public class EditObjectController { + private final Logger log = LoggerFactory.getLogger(getClass()); + private static final ObjectMapper json = new ObjectMapper(); + private final ImdgProvider imdgProvider; + + @Autowired + public EditObjectController(ImdgProvider imdgProvider) { + this.imdgProvider = imdgProvider; + } + + @ApiOperation(value = "Edit any object.") + @ApiResponses(value = {@ApiResponse(code = 200, message = "OK", response = String.class)}) + @RequestMapping(method = RequestMethod.POST, path = "/edit/object", produces = MediaType.TEXT_PLAIN_VALUE) + @ResponseBody + public String updateObjectById(@ApiParam(value = "Command params in JSON format.", required = true) + @RequestBody EditImdgObjectAction action) throws ClassNotFoundException, ValidationException, IllegalAccessException { + log.info("Edit any object method"); + validate(action); + Class rawParameterType = ClassUtils.forName(action.getFullClassName(), ClassUtils.getDefaultClassLoader()); + if (!SpcexObjectBase.class.isAssignableFrom(rawParameterType)) { + throw new ValidationException(new EnumMessage(BackEndError.ValidationError, "fullClassName")); + } + Class parameterType = rawParameterType.asSubclass(SpcexObjectBase.class); + Long id = action.getObjId(); + String mapName = action.getMapName(); + Imdg imdg = (Imdg) imdgProvider.getImdg(mapName, parameterType); + SpcexObjectBase objectToUpdate = imdg.getSingleObjectByID(id); + if (objectToUpdate == null) { + throw new ValidationException(new EnumMessage(BackEndError.ResourceNotFound, "id")); + } + + setFields(objectToUpdate, action.getFields()); + imdg.update(objectToUpdate); + return "success: object.id = " + id; + } + + private void validate(EditImdgObjectAction action) throws ValidationException { + if (TextUtil.isEmpty(action.getMapName())) { + throw new ValidationException(new EnumMessage(BackEndError.ValidationError, "mapName")); + } + if (TextUtil.isEmpty(action.getFullClassName())) { + throw new ValidationException(new EnumMessage(BackEndError.ValidationError, "fullClassName")); + } + if (action.getObjId() == null) { + throw new ValidationException(new EnumMessage(BackEndError.ValidationError, "objId")); + } + if (action.getFields() == null || action.getFields().isEmpty()) { + throw new ValidationException(new EnumMessage(BackEndError.ValidationError, "fields")); + } + } + + private void setFields(SpcexObjectBase objectToUpdate, Map fieldsToChange) throws ValidationException, IllegalAccessException { + for (Map.Entry fieldToChange : fieldsToChange.entrySet()) { + Field field = findField(objectToUpdate.getClass(), fieldToChange.getKey()); + field.setAccessible(true); + field.set(objectToUpdate, toFieldValue(field, fieldToChange.getValue())); + } + } + + private Field findField(Class objectClass, String fieldName) throws ValidationException { + Class currentClass = objectClass; + while (currentClass != null) { + try { + return currentClass.getDeclaredField(fieldName); + } catch (NoSuchFieldException ignored) { + currentClass = currentClass.getSuperclass(); + } + } + throw new ValidationException(new EnumMessage(BackEndError.ValidationError, "fields." + fieldName)); + } + + private Object toFieldValue(Field field, Object value) throws ValidationException { + if (value == null) { + if (field.getType().isPrimitive()) { + throw new ValidationException(new EnumMessage(BackEndError.ValidationError, field.getName())); + } + return null; + } + Class fieldType = field.getType(); + if (fieldType.isAssignableFrom(value.getClass())) { + return value; + } + if (fieldType.isEnum() && value instanceof String) { + return Enum.valueOf((Class) fieldType.asSubclass(Enum.class), (String) value); + } + if (fieldType == LocalDate.class && value instanceof String) { + return LocalDate.parse((String) value); + } + if (fieldType == LocalDateTime.class && value instanceof String) { + return LocalDateTime.parse((String) value); + } + if (fieldType == LocalTime.class && value instanceof String) { + return LocalTime.parse((String) value); + } + return json.convertValue(value, json.getTypeFactory().constructType(field.getGenericType())); + } +} diff --git a/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/IsActiveSessionRunningService.java b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/IsActiveSessionRunningService.java new file mode 100644 index 000000000..87963e87a --- /dev/null +++ b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/IsActiveSessionRunningService.java @@ -0,0 +1,32 @@ +package ru.spcex.clearing.service; + +import java.util.Collection; +import java.util.Map; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; +import ru.clearing.classes.statics.data.misc.Session; +import ru.spcex.clearing.imdg.IMDGDistributedNames; +import ru.spcex.platform.enumeration.WorkflowStatus; +import ru.spcex.platform.imdg.api.Imdg; +import ru.spcex.platform.imdg.api.ImdgProvider; + +@Service +public class IsActiveSessionRunningService { + private final Logger log = LoggerFactory.getLogger(getClass()); + private final Imdg ssnImdg; + + public IsActiveSessionRunningService(ImdgProvider imdgProvider) { + this.ssnImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_Session, Session.class); + } + + public boolean isThereAnActiveSession() { + Collection activeSessions = ssnImdg.getCollectionObjectsByFieldValues(Map.of( + "workflowStatus", WorkflowStatus.Active.getKey() + )); + activeSessions.stream().findFirst().ifPresent( + session -> log.debug("found session.id {}", session.getId()) + ); + return !activeSessions.isEmpty(); + } +} diff --git a/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/executors/Sdf10Executor.java b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/executors/Sdf10Executor.java index a3d24aaea..e2df78b10 100644 --- a/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/executors/Sdf10Executor.java +++ b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/executors/Sdf10Executor.java @@ -1,5 +1,14 @@ package ru.spcex.clearing.service.executors; +import java.math.BigDecimal; +import java.time.Instant; +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Map; +import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -25,12 +34,24 @@ import ru.spcex.clearing.platform.messaging.domain.cud.gateway.AssetOperationApp import ru.spcex.clearing.platform.messaging.domain.cud.gateway.SingleAssetResponse; import ru.spcex.clearing.platform.messaging.domain.cud.importexport.SwtExporterRequest; import ru.spcex.clearing.platform.messaging.service.sender.KafkaSender; +import ru.spcex.clearing.service.IsActiveSessionRunningService; import ru.spcex.clearing.service.registry.AssetTBFProcessing; import ru.spcex.clearing.service.registry.RegistryManager; +import ru.spcex.clearing.service.schedule.TasksDelayedBySessionServiceV2; import ru.spcex.clearing.service.schedule.TradingTimeService; import ru.spcex.clearing.service.validation.ValidationStored; import ru.spcex.clearing.session.stage.util.RegistryUtil; -import ru.spcex.platform.enumeration.*; +import ru.spcex.platform.enumeration.InOutDirection; +import ru.spcex.platform.enumeration.InOutSDfType; +import ru.spcex.platform.enumeration.OperationStatus; +import ru.spcex.platform.enumeration.RegistryDesignation; +import ru.spcex.platform.enumeration.RegistryStatus; +import ru.spcex.platform.enumeration.RegistryTradingParams; +import ru.spcex.platform.enumeration.RegistryUnit; +import ru.spcex.platform.enumeration.SdfTable; +import ru.spcex.platform.enumeration.Sender; +import ru.spcex.platform.enumeration.StatementType; +import ru.spcex.platform.enumeration.WorkflowStatus; import ru.spcex.platform.imdg.api.Imdg; import ru.spcex.platform.imdg.api.ImdgId; import ru.spcex.platform.imdg.api.ImdgProvider; @@ -39,18 +60,8 @@ import ru.spcex.platform.imdg.api.predicate.specific.SecuritySelector; import ru.spcex.platform.utils.enumeration.EnumMessage; import ru.spcex.platform.utils.enumeration.IEnumId; import ru.spcex.platform.utils.enumeration.IMessageResolver; -import ru.spcex.platform.utils.validation.IValidator; - -import java.math.BigDecimal; -import java.time.Instant; -import java.time.LocalDate; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Map; -import java.util.Optional; -import java.util.function.Function; - import static ru.spcex.platform.utils.number.BigDecimalUtil.safeBD; +import ru.spcex.platform.utils.validation.IValidator; @Service public class Sdf10Executor { @@ -70,6 +81,8 @@ public class Sdf10Executor { private final KafkaSender kafkaSender; private final SecuritySelector scrtSlct; private final AssetTBFProcessing assets; + private final TasksDelayedBySessionServiceV2 delayedService; + private final IsActiveSessionRunningService sessionChecker; private final static String OK = "OK"; private final static String SYNTAX_ERROR = "Синтаксическая ошибка (файл сформирован неверно)"; @@ -90,7 +103,7 @@ public class Sdf10Executor { public Sdf10Executor(ImdgProvider imdgProvider, RegistryManager rgsMng, IMessageResolver messageResolver, @Qualifier("sdf10Validator") Function sDf10Validator, - TradingTimeService tradingTimeService, KafkaSender kafkaSender, AssetTBFProcessing assets) { + TradingTimeService tradingTimeService, KafkaSender kafkaSender, AssetTBFProcessing assets, TasksDelayedBySessionServiceV2 delayedService, IsActiveSessionRunningService sessionChecker) { this.imdgProvider = imdgProvider; this.statementImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_Statement, Statement.class); this.registryImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_Registry, Registry.class); @@ -102,9 +115,11 @@ public class Sdf10Executor { this.kafkaSender = kafkaSender; this.sdf10Imdg = imdgProvider.getImdg(IMDGDistributedNames.Map_SDf10, SDf10.class); this.sdf11Imdg = imdgProvider.getImdg(IMDGDistributedNames.Map_SDf11, SDf11.class); + this.sessionChecker = sessionChecker; this.plannerAllTodayImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_PlannerAllToday, PlannerAllToday.class); this.scrtSlct = new SecuritySelector<>(imdgProvider, Security.class); this.assets = assets; + this.delayedService = delayedService; } public void execute(BaseRequest systemRequest) { @@ -125,9 +140,21 @@ public class Sdf10Executor { sendToExporter(sdf11GroupId); return; } + if (sessionChecker.isThereAnActiveSession()) { + log.info("delaying SDF10 execution for groupId={} - session is active", groupId); + pauseAll(sdfs); + delayedService.subscribe(groupId); + return; + } Collection requests = new ArrayList<>(); boolean sdf11WasCreated = false; for (SDf10 sDf10 : sdfs) { + if (systemRequest.getRequestPayload().isDelayed() && !WorkflowStatus.Pause.equalsByKey(sDf10.getWorkflowStatus())) { + log.warn("got delayed SDF10 request groupId: {}. sdf10.id {} is not in {} status.", groupId, sDf10.getId(), WorkflowStatus.Pause.getKey()); + continue; + } + sDf10.setWorkflowStatus(WorkflowStatus.Blocked.getKey()); + sdf10Imdg.update(sDf10); IValidator validator = sDf10Validator.apply(sDf10); Optional err = validator.tillFirstError(); Security security = validator.getStored(ValidationStored.SecurityBySecurityCode); @@ -188,6 +215,18 @@ public class Sdf10Executor { } } + private void pauseAll(Collection sdfs) { + Map sdfById = sdfs.stream() + .collect(Collectors.toMap( + SDf10::getId, + sdf -> { + sdf.setWorkflowStatus(WorkflowStatus.Pause.getKey()); + return sdf; + } + )); + sdf10Imdg.putAll(sdfById); + } + private void createDs_iResponseFromGateway(Long accountId, String securitySymbol, BigDecimal summ, String outDocument) { Optional as_t = rgsMng.searchByAccSec(accountId, securitySymbol, RegistryTradingParams.AS_T); diff --git a/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/schedule/TasksDelayedBySessionServiceV2.java b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/schedule/TasksDelayedBySessionServiceV2.java new file mode 100644 index 000000000..0ff17d524 --- /dev/null +++ b/clearing-parent/clearing-service/src/main/java/ru/spcex/clearing/service/schedule/TasksDelayedBySessionServiceV2.java @@ -0,0 +1,143 @@ +package ru.spcex.clearing.service.schedule; + +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.beans.factory.InitializingBean; +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.Session; +import ru.clearing.classes.statics.data.sdf.SDf10; +import ru.spcex.clearing.imdg.IMDGDistributedNames; +import ru.spcex.clearing.platform.messaging.domain.Consts; +import ru.spcex.clearing.platform.messaging.domain.cud.balance.StatementRequest; +import ru.spcex.clearing.platform.messaging.service.sender.KafkaSender; +import ru.spcex.clearing.service.IsActiveSessionRunningService; +import ru.spcex.platform.enumeration.SdfTable; +import ru.spcex.platform.enumeration.WorkflowStatus; +import ru.spcex.platform.imdg.api.Imdg; +import ru.spcex.platform.imdg.api.ImdgEntryListener; +import ru.spcex.platform.imdg.api.ImdgProvider; +import ru.spcex.platform.imdg.api.listener.ImdgEntryEvent; +import ru.spcex.platform.imdg.api.predicate.ImdgPredicateBuilder; +import ru.spcex.platform.utils.log.ExceptionUtils; +import ru.spcex.platform.utils.time.TimeUtil; + +@Service +public class TasksDelayedBySessionServiceV2 implements ImdgEntryListener, + DisposableBean, InitializingBean { + private final Logger log = LoggerFactory.getLogger(getClass()); + private final Set groupIds; + private final ExecutorService singleThreadExecutor; + private final KafkaSender kafka; + private final Imdg sdf10Imdg; + private final ImdgProvider imdgProvider; + private final IsActiveSessionRunningService sessionChecker; + + @Autowired + public TasksDelayedBySessionServiceV2(@Qualifier("kafkaSenderWithoutRequestInfo") + KafkaSender kafka, ImdgProvider imdgProvider, IsActiveSessionRunningService sessionChecker) { + this.kafka = kafka; + this.imdgProvider = imdgProvider; + this.sdf10Imdg = imdgProvider.getImdg(IMDGDistributedNames.Map_SDf10, SDf10.class); + this.sessionChecker = sessionChecker; + this.groupIds = new LinkedHashSet<>(); + this.singleThreadExecutor = Executors.newSingleThreadExecutor(); + } + + @Override + public void afterPropertiesSet() throws Exception { + submitWrap(this::checkDelayedStatusesOnStartup); + this.imdgProvider.waitAvailable(); + Imdg ssnImdg = this.imdgProvider.getImdg(IMDGDistributedNames.Map_Session, Session.class); + ssnImdg.addListener(this); + } + + private void checkDelayedStatusesOnStartup() { + imdgProvider.waitAvailable(); + ImdgPredicateBuilder pb = sdf10Imdg.predicateBuilder(); + Collection sdfs = sdf10Imdg.getCollectionObjectsByPredicate( + pb.and( + pb.greatEqual("generationTime", TimeUtil.localDateToInstant(LocalDate.now())), + pb.equals("workflowStatus", WorkflowStatus.Pause.getKey()) + ) + ); + List groupIds = sdfs.stream() + .map(SDf10::getGenerationId) + .distinct() + .toList(); + log.info("{} sdf10 on startup; {} groups", sdfs.size(), groupIds.size()); + if (sessionChecker.isThereAnActiveSession()) { + log.info("there is an active session: sdf10 stay delayed"); + synchronized (this.groupIds) { + groupIds.forEach(this::subscribe); + } + } else { + groupIds.forEach(this::notifyEventReceiver); + } + } + + public void subscribe(Long groupId) { + synchronized (groupIds) { + groupIds.add(groupId); + } + log.info("sdf10 groupId={} delayed", groupId); + } + + @Override + public void innerEntryUpdated(ImdgEntryEvent event) { + if (notSessionEndEvent(event.getOldValue(), event.getValue())) { + log.trace("not a session end event."); + return; + } + synchronized (groupIds) { + List filtered = new ArrayList<>(groupIds); + groupIds.clear(); + submitWrap(() -> filtered.forEach(this::notifyEventReceiver)); + } + } + + private boolean notSessionEndEvent(Session sessionOldValue, Session sessionNewValue) { + if (sessionOldValue == null || sessionNewValue == null) { + log.warn("old value null {}, new value null {}", + sessionOldValue == null, sessionNewValue == null); + return true; + } + String oldWS = sessionOldValue.getWorkflowStatus(); + String newWS = sessionNewValue.getWorkflowStatus(); + log.trace("workflow statuses: {} -> {}", oldWS, newWS); + return !(WorkflowStatus.Active.equalsByKey(oldWS) && !WorkflowStatus.Active.equalsByKey(newWS)); + } + + private void notifyEventReceiver(Long groupId) { + StatementRequest request = new StatementRequest(); + request.setGroupId(groupId); + request.setTable(SdfTable.SDF_10); + request.setDelayed(true); + kafka.sendRequestToQueue(Consts.STATEMENT_PROCESS, request); + } + + private void submitWrap(Runnable task) { + singleThreadExecutor.submit(() -> { + try { + task.run(); + } catch (Exception e) { + log.error(ExceptionUtils.getStackTrace(e)); + } + }); + } + + @Override + public void destroy() { + this.singleThreadExecutor.close(); + } +} diff --git a/platform-parent/platform-imdg-api-hazelcast-impl/src/main/java/ru/spcex/platform/imdg/iml/hazelcast/adapter/ImdgHazelcast.java b/platform-parent/platform-imdg-api-hazelcast-impl/src/main/java/ru/spcex/platform/imdg/iml/hazelcast/adapter/ImdgHazelcast.java index c5acd2031..506db7a64 100644 --- a/platform-parent/platform-imdg-api-hazelcast-impl/src/main/java/ru/spcex/platform/imdg/iml/hazelcast/adapter/ImdgHazelcast.java +++ b/platform-parent/platform-imdg-api-hazelcast-impl/src/main/java/ru/spcex/platform/imdg/iml/hazelcast/adapter/ImdgHazelcast.java @@ -20,6 +20,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import ru.spcex.platform.classes.base.SpcexObjectBase; import ru.spcex.platform.imdg.api.Imdg; +import ru.spcex.platform.imdg.api.ImdgEntryListener; import ru.spcex.platform.imdg.api.predicate.ImdgPredicate; import ru.spcex.platform.imdg.api.predicate.ImdgPredicateBuilder; import ru.spcex.platform.imdg.iml.hazelcast.adapter.predicate.ImdgPredicateBuilderHazelcast; @@ -350,4 +351,9 @@ public class ImdgHazelcast implements Imdg { public ImdgPredicateBuilder predicateBuilder() { return ImdgPredicateBuilderHazelcast.instance(); } + + @Override + public void addListener(ImdgEntryListener listener) { + map.addEntryListener(new ImdgHazelcastListener<>(listener), true); + } } diff --git a/platform-parent/platform-imdg-api-hazelcast-impl/src/main/java/ru/spcex/platform/imdg/iml/hazelcast/adapter/ImdgHazelcastListener.java b/platform-parent/platform-imdg-api-hazelcast-impl/src/main/java/ru/spcex/platform/imdg/iml/hazelcast/adapter/ImdgHazelcastListener.java new file mode 100644 index 000000000..ba3293414 --- /dev/null +++ b/platform-parent/platform-imdg-api-hazelcast-impl/src/main/java/ru/spcex/platform/imdg/iml/hazelcast/adapter/ImdgHazelcastListener.java @@ -0,0 +1,29 @@ +package ru.spcex.platform.imdg.iml.hazelcast.adapter; + +import com.hazelcast.core.EntryEvent; +import com.hazelcast.map.listener.EntryAddedListener; +import com.hazelcast.map.listener.EntryUpdatedListener; +import ru.spcex.platform.imdg.api.ImdgEntryListener; +import ru.spcex.platform.imdg.api.listener.ImdgEntryEvent; + +public class ImdgHazelcastListener implements + EntryUpdatedListener, + EntryAddedListener +{ + + private final ImdgEntryListener delegate; + + public ImdgHazelcastListener(ImdgEntryListener delegate) { + this.delegate = delegate; + } + + @Override + public void entryUpdated(EntryEvent event) { + this.delegate.innerEntryUpdated(new ImdgEntryEvent<>(event.getValue(), event.getOldValue(), event.getKey())); + } + + @Override + public void entryAdded(EntryEvent event) { + this.delegate.innerEntryAdded(new ImdgEntryEvent<>(event.getValue(), event.getOldValue(), event.getKey())); + } +} diff --git a/platform-parent/platform-imdg-api/src/main/java/ru/spcex/platform/imdg/api/Imdg.java b/platform-parent/platform-imdg-api/src/main/java/ru/spcex/platform/imdg/api/Imdg.java index 996fec6e5..7b782d024 100644 --- a/platform-parent/platform-imdg-api/src/main/java/ru/spcex/platform/imdg/api/Imdg.java +++ b/platform-parent/platform-imdg-api/src/main/java/ru/spcex/platform/imdg/api/Imdg.java @@ -133,4 +133,8 @@ public interface Imdg { default ImdgPredicateBuilder predicateBuilder() { throw new UnsupportedOperationException("not implemented predicateBuilder API"); } + + default void addListener(ImdgEntryListener listener) { + throw new UnsupportedOperationException("not implemented addListener"); + } } diff --git a/platform-parent/platform-imdg-api/src/main/java/ru/spcex/platform/imdg/api/ImdgEntryListener.java b/platform-parent/platform-imdg-api/src/main/java/ru/spcex/platform/imdg/api/ImdgEntryListener.java new file mode 100644 index 000000000..3f8d4a4ef --- /dev/null +++ b/platform-parent/platform-imdg-api/src/main/java/ru/spcex/platform/imdg/api/ImdgEntryListener.java @@ -0,0 +1,14 @@ +package ru.spcex.platform.imdg.api; + +import ru.spcex.platform.imdg.api.listener.ImdgEntryEvent; + +public interface ImdgEntryListener { + default void innerEntryAdded(ImdgEntryEvent event) { + + } + + default void innerEntryUpdated(ImdgEntryEvent event) { + + } + +} diff --git a/platform-parent/platform-imdg-api/src/main/java/ru/spcex/platform/imdg/api/listener/ImdgEntryEvent.java b/platform-parent/platform-imdg-api/src/main/java/ru/spcex/platform/imdg/api/listener/ImdgEntryEvent.java new file mode 100644 index 000000000..34f73acc4 --- /dev/null +++ b/platform-parent/platform-imdg-api/src/main/java/ru/spcex/platform/imdg/api/listener/ImdgEntryEvent.java @@ -0,0 +1,25 @@ +package ru.spcex.platform.imdg.api.listener; + +public class ImdgEntryEvent { + private final T value; + private final T oldValue; + private final Long key; + + public ImdgEntryEvent(T value, T oldValue, Long key) { + this.value = value; + this.oldValue = oldValue; + this.key = key; + } + + public T getValue() { + return value; + } + + public T getOldValue() { + return oldValue; + } + + public Long getKey() { + return key; + } +} diff --git a/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/domain/cud/balance/StatementRequest.java b/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/domain/cud/balance/StatementRequest.java index 422500b72..271c97064 100644 --- a/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/domain/cud/balance/StatementRequest.java +++ b/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/domain/cud/balance/StatementRequest.java @@ -20,6 +20,8 @@ public class StatementRequest { private Long childGenerationId; @JsonProperty private boolean continueSdf = false; + @JsonProperty + private boolean delayed = false; public Long getGroupId() { return groupId; @@ -68,4 +70,12 @@ public class StatementRequest { public void setFromAccount(boolean fromAccount) { this.fromAccount = fromAccount; } + + public boolean isDelayed() { + return delayed; + } + + public void setDelayed(boolean delayed) { + this.delayed = delayed; + } } \ No newline at end of file