From 00c2ab02c97abdc91892918423a65b797c7ab006 Mon Sep 17 00:00:00 2001 From: ialbert Date: Thu, 9 Apr 2026 17:57:35 +0300 Subject: [PATCH] http://jira.mfd.msk:8088/browse/CLS-952 --- .../OrderCurrencyValidationStored.java | 5 + .../QuantityLotGreaterThanZero.java | 26 ++++ .../management/config/ValidationConfig.java | 97 +++++++++++++++ .../error/DefaultManagementErrors.java | 3 +- .../listener/OrderCurrencyListener.java | 103 ++++++++++++++++ .../predicate/specific/IdEqualPredicate.java | 28 +++++ .../validation/rule/PresentByPredicate.java | 112 ++++++++++++++++++ .../cud/order/OrderCompanyNewRequest.java | 3 +- .../cud/order/OrderCurrencyUpdateRequest.java | 5 +- 9 files changed, 378 insertions(+), 4 deletions(-) create mode 100644 clearing-parent/default-management/src/main/java/ru/spcex/clearing/dflt/management/component/validation/OrderCurrencyValidationStored.java create mode 100644 clearing-parent/default-management/src/main/java/ru/spcex/clearing/dflt/management/component/validation/QuantityLotGreaterThanZero.java create mode 100644 clearing-parent/default-management/src/main/java/ru/spcex/clearing/dflt/management/listener/OrderCurrencyListener.java create mode 100644 platform-parent/platform-imdg-api/src/main/java/ru/spcex/platform/imdg/api/predicate/specific/IdEqualPredicate.java create mode 100644 platform-parent/platform-imdg-api/src/main/java/ru/spcex/platform/imdg/validation/rule/PresentByPredicate.java diff --git a/clearing-parent/default-management/src/main/java/ru/spcex/clearing/dflt/management/component/validation/OrderCurrencyValidationStored.java b/clearing-parent/default-management/src/main/java/ru/spcex/clearing/dflt/management/component/validation/OrderCurrencyValidationStored.java new file mode 100644 index 000000000..6661ad587 --- /dev/null +++ b/clearing-parent/default-management/src/main/java/ru/spcex/clearing/dflt/management/component/validation/OrderCurrencyValidationStored.java @@ -0,0 +1,5 @@ +package ru.spcex.clearing.dflt.management.component.validation; + +public enum OrderCurrencyValidationStored { + OrderCurrency, Company, TCR +} diff --git a/clearing-parent/default-management/src/main/java/ru/spcex/clearing/dflt/management/component/validation/QuantityLotGreaterThanZero.java b/clearing-parent/default-management/src/main/java/ru/spcex/clearing/dflt/management/component/validation/QuantityLotGreaterThanZero.java new file mode 100644 index 000000000..e79b3d26d --- /dev/null +++ b/clearing-parent/default-management/src/main/java/ru/spcex/clearing/dflt/management/component/validation/QuantityLotGreaterThanZero.java @@ -0,0 +1,26 @@ +package ru.spcex.clearing.dflt.management.component.validation; + +import java.util.Optional; +import ru.spcex.clearing.dflt.management.error.DefaultManagementErrors; +import ru.spcex.clearing.platform.messaging.domain.cud.order.OrderCompanyNewRequest; +import ru.spcex.platform.imdg.validation.ImdgValidationContext; +import ru.spcex.platform.utils.enumeration.EnumMessage; +import ru.spcex.platform.utils.validation.IValidationRule; + +public class QuantityLotGreaterThanZero implements IValidationRule> { + public static final QuantityLotGreaterThanZero INSTANCE = new QuantityLotGreaterThanZero(); + + @Override + public Optional validate(ImdgValidationContext ctx) { + OrderCompanyNewRequest validatedObject = ctx.getValidatedObject(); + if (validatedObject.getQuantityLot() == null || validatedObject.getQuantityLot() < 0) { + return of(DefaultManagementErrors.RequiredFieldIsEmpty, "quantityLot"); + } + return Optional.empty(); + } + + @Override + public String ruleName() { + return "QuantityLotGreaterThanZero"; + } +} diff --git a/clearing-parent/default-management/src/main/java/ru/spcex/clearing/dflt/management/config/ValidationConfig.java b/clearing-parent/default-management/src/main/java/ru/spcex/clearing/dflt/management/config/ValidationConfig.java index 4179ea94a..4bb254233 100644 --- a/clearing-parent/default-management/src/main/java/ru/spcex/clearing/dflt/management/config/ValidationConfig.java +++ b/clearing-parent/default-management/src/main/java/ru/spcex/clearing/dflt/management/config/ValidationConfig.java @@ -6,19 +6,30 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import ru.clearing.classes.statics.data.company.Company; import ru.clearing.classes.statics.data.misc.Market; +import ru.clearing.classes.statics.data.misc.OrderCurrency; import ru.clearing.classes.statics.data.misc.OvernightSettings; +import ru.clearing.classes.statics.data.registry.TradingClearingRegistry; import ru.clearing.platform.dictionary.OvernightTypeDictionary; +import ru.spcex.clearing.dflt.management.component.validation.OrderCurrencyValidationStored; +import ru.spcex.clearing.dflt.management.component.validation.QuantityLotGreaterThanZero; import ru.spcex.clearing.dflt.management.error.DefaultManagementErrors; import ru.spcex.clearing.imdg.IMDGDistributedNames; import ru.spcex.clearing.platform.messaging.domain.cud.misc.OvernightSettingsNewRequest; import ru.spcex.clearing.platform.messaging.domain.cud.misc.OvernightSettingsUpdateRequest; +import ru.spcex.clearing.platform.messaging.domain.cud.order.OrderCompanyNewRequest; +import ru.spcex.clearing.platform.messaging.domain.cud.order.OrderCurrencyUpdateRequest; import ru.spcex.clearing.validation.common.rules.DictionaryPresentRule; import ru.spcex.clearing.validation.common.rules.IdPresentRule; import ru.spcex.clearing.validation.common.rules.SpecialIdPresentRule; import ru.spcex.platform.imdg.api.Imdg; import ru.spcex.platform.imdg.api.ImdgProvider; +import ru.spcex.platform.imdg.api.predicate.specific.IdEqualPredicate; +import static ru.spcex.platform.imdg.api.predicate.specific.IdEqualPredicate.byId; import ru.spcex.platform.imdg.validation.ImdgValidationContext; +import ru.spcex.platform.imdg.validation.LogPrefixId; import ru.spcex.platform.imdg.validation.rule.PresentById; +import ru.spcex.platform.imdg.validation.rule.PresentByPredicate; +import ru.spcex.platform.utils.enumeration.EnumMessage; import ru.spcex.platform.utils.validation.IValidator; import ru.spcex.platform.utils.validation.ValidatorImpl; @@ -27,8 +38,10 @@ public class ValidationConfig { private final Imdg overnightSettingsImdg; private final Imdg companyImdg; + private final Imdg tcrImdg; private final Imdg marketImdg; private final Imdg osTypeDictionaryImdg; + private final Imdg orderCurrencyImdg; @Autowired public ValidationConfig(ImdgProvider imdgProvider) { @@ -36,6 +49,8 @@ public class ValidationConfig { this.companyImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_Company, Company.class); this.marketImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_Market, Market.class); this.osTypeDictionaryImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_OvernightTypeDictionary, OvernightTypeDictionary.class); + this.orderCurrencyImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_OrderCurrency, OrderCurrency.class); + this.tcrImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_TradingClearingRegistry, TradingClearingRegistry.class); } public enum OvernightSettingsValidationStorage { @@ -103,4 +118,86 @@ public class ValidationConfig { ); }; } + + @Bean + public Function orderCurrencyUpdateOvernightTypeValidator() { + return req -> { + ImdgValidationContext ctx = new ImdgValidationContext<>(); + ctx.setValidatedObject(req); + ctx.setLogPrefix(LogPrefixId.INSTANCE); + ctx.addImdg(IMDGDistributedNames.Map_OrderCurrency, orderCurrencyImdg); + return new ValidatorImpl<>(ctx, + new PresentById(IMDGDistributedNames.Map_OrderCurrency, DefaultManagementErrors.DictionaryNotFound, true), + DictionaryPresentRule.instance( + "overnightType", + OvernightSettingsNewRequest::getOvernightType, + IMDGDistributedNames.Map_OvernightTypeDictionary, + OvernightTypeDictionary.class, + DefaultManagementErrors.RequiredFieldIsEmpty, + DefaultManagementErrors.DictionaryNotFound, + true) + ); + }; + } + + private EnumMessage dictNotFound(String dictName, String code) { + return new EnumMessage(DefaultManagementErrors.DictionaryNotFound, dictName, code); + } + + private EnumMessage recordNotFound(Long description) { + String descriptionString = description != null ? description.toString() : null; + return new EnumMessage(DefaultManagementErrors.RecordNotFound, descriptionString); + } + + private EnumMessage recordNotFound(String description) { + return new EnumMessage(DefaultManagementErrors.RecordNotFound, description); + } + + @Bean + public Function orderCurrencyNewCompany() { + return req -> { + ImdgValidationContext ctx = new ImdgValidationContext<>(); + ctx.setValidatedObject(req); + ctx.setLogPrefix(LogPrefixId.INSTANCE); + ctx.addImdg(IMDGDistributedNames.Map_OrderCurrency, orderCurrencyImdg); + ctx.addImdg(IMDGDistributedNames.Map_Company, companyImdg); + ctx.addImdg(IMDGDistributedNames.Map_TradingClearingRegistry, tcrImdg); + var orderCurrencyPresent = PresentByPredicate + .builder(byId(orderCurrencyImdg.predicateBuilder(), req)) + .mapName(IMDGDistributedNames.Map_OrderCurrency) + .errorProvider(r -> recordNotFound(r.getId())) + .store(OrderCurrencyValidationStored.OrderCurrency) + .build(); + + var companyPresentRule = PresentByPredicate + .builder(new IdEqualPredicate<>( + companyImdg.predicateBuilder(), + OrderCompanyNewRequest::getCompanyId) + ) + .mapName(IMDGDistributedNames.Map_Company) + .errorProvider(r -> recordNotFound("company.id=" + r.getCompanyId())) + .store(OrderCurrencyValidationStored.Company) + .build(); + + var tcrPresent = PresentByPredicate + .builder(new IdEqualPredicate<>( + tcrImdg.predicateBuilder(), + OrderCompanyNewRequest::getTradingClearingRegistryId) + ) + .mapName(IMDGDistributedNames.Map_TradingClearingRegistry) + .errorProvider(r -> recordNotFound("tcr.id=" + r.getTradingClearingRegistryId())) + .store(OrderCurrencyValidationStored.TCR) + .build(); + + + //fixme проверить что tcr.getCompanyId() = companyId из запроса? + return new ValidatorImpl<>(ctx, + orderCurrencyPresent, + companyPresentRule, + tcrPresent, + QuantityLotGreaterThanZero.INSTANCE); + }; + } + + } diff --git a/clearing-parent/default-management/src/main/java/ru/spcex/clearing/dflt/management/error/DefaultManagementErrors.java b/clearing-parent/default-management/src/main/java/ru/spcex/clearing/dflt/management/error/DefaultManagementErrors.java index e9ea0423c..556adfeac 100644 --- a/clearing-parent/default-management/src/main/java/ru/spcex/clearing/dflt/management/error/DefaultManagementErrors.java +++ b/clearing-parent/default-management/src/main/java/ru/spcex/clearing/dflt/management/error/DefaultManagementErrors.java @@ -8,7 +8,8 @@ public enum DefaultManagementErrors implements IErrorEnumId { GeneralError(3000L), // Общая ошибка модуля UserVerifyDenial(3001L), // 'Нет прав на проведение данной операции RequiredFieldIsEmpty(3002L), - DictionaryNotFound(3003L) + DictionaryNotFound(3003L), + RecordNotFound(3006L), ; private final Long id; diff --git a/clearing-parent/default-management/src/main/java/ru/spcex/clearing/dflt/management/listener/OrderCurrencyListener.java b/clearing-parent/default-management/src/main/java/ru/spcex/clearing/dflt/management/listener/OrderCurrencyListener.java new file mode 100644 index 000000000..564c3d533 --- /dev/null +++ b/clearing-parent/default-management/src/main/java/ru/spcex/clearing/dflt/management/listener/OrderCurrencyListener.java @@ -0,0 +1,103 @@ +package ru.spcex.clearing.dflt.management.listener; + +import java.math.BigDecimal; +import java.util.Optional; +import java.util.function.Function; +import org.apache.kafka.clients.consumer.Consumer; +import org.apache.kafka.clients.producer.Producer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import ru.clearing.classes.statics.data.misc.OrderCurrency; +import ru.clearing.classes.statics.data.misc.OvernightSettings; +import ru.clearing.classes.statics.data.registry.Registry; +import ru.spcex.clearing.dflt.management.component.validation.OrderCurrencyValidationStored; +import ru.spcex.clearing.dflt.management.service.OrderCurrencyService; +import ru.spcex.clearing.imdg.IMDGDistributedNames; +import ru.spcex.clearing.platform.messaging.domain.BaseRequest; +import ru.spcex.clearing.platform.messaging.domain.Consts; +import ru.spcex.clearing.platform.messaging.domain.cud.order.OrderCompanyNewRequest; +import ru.spcex.clearing.platform.messaging.domain.cud.order.OrderCurrencyUpdateRequest; +import ru.spcex.clearing.platform.messaging.service.QueueConsumer; +import ru.spcex.clearing.platform.messaging.service.RequestInfoUpdate; +import ru.spcex.clearing.platform.messaging.service.Status; +import ru.spcex.clearing.util.security.UserRoleVerification; +import ru.spcex.platform.imdg.api.Imdg; +import ru.spcex.platform.imdg.api.ImdgProvider; +import ru.spcex.platform.imdg.validation.Stored; +import ru.spcex.platform.utils.enumeration.EnumMessage; +import ru.spcex.platform.utils.enumeration.IMessageResolver; +import ru.spcex.platform.utils.validation.IValidator; + +@Service +public class OrderCurrencyListener extends QueueConsumer implements InitializingBean { + protected final Logger log = LoggerFactory.getLogger(getClass()); + private final Imdg registryImdg; + private final Imdg overnightSettingsImdg; + private final Imdg orderCurrencyImdg; + private final Function overnightTypeUpdateValidator; + private final Function orderCompanyNewValidator; + private final IMessageResolver msgResolver; + + @Autowired + public OrderCurrencyListener(Consumer kafkaQueue, + Producer kafkaProducer, + ImdgProvider imdgProvider, + UserRoleVerification userRoleVerification, + OrderCurrencyService orderCurrencyService, + Function overnightTypeUpdateValidator, + Function orderCompanyNewValidator, + IMessageResolver msgResolver) { + super(kafkaQueue, kafkaProducer); + this.registryImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_Registry, Registry.class); + this.overnightSettingsImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_OvernightSettings, OvernightSettings.class); + this.overnightTypeUpdateValidator = overnightTypeUpdateValidator; + this.orderCompanyNewValidator = orderCompanyNewValidator; + this.msgResolver = msgResolver; + this.orderCurrencyImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_OrderCurrency, OrderCurrency.class); + } + + @Override + public void afterPropertiesSet() { + callback(OrderCurrencyUpdateRequest.class) + .setFunction(this::updateOvernightType) + .forDestination(Consts.ORDER_OVERNIGHT_TYPE_UPDATE, callbacks::put); + callback(OrderCompanyNewRequest.class) + .setFunction(this::updateCompany) + .forDestination(Consts.ORDER_CURRENCY_COMPANY_NEW, callbacks::put); + init(); + } + + private RequestInfoUpdate updateOvernightType(BaseRequest req) { + OrderCurrencyUpdateRequest payload = req.getRequestPayload(); + IValidator validator = overnightTypeUpdateValidator.apply(payload); + Optional error = validator.tillFirstError(); + if (error.isPresent()) { + return new RequestInfoUpdate(req.getId(), Status.Error, msgResolver.resolve(error.get())); + } + OrderCurrency orderCurrency = validator.getStored(Stored.PresentById); + orderCurrency.setOvernightType(payload.getOvernightType()); + log.info("Updating overnight type for orderCurrency.id={} to {}", + orderCurrency.getId(), + orderCurrency.getOvernightType()); + orderCurrencyImdg.update(orderCurrency); + return null; + } + + private RequestInfoUpdate updateCompany(BaseRequest req) { + OrderCompanyNewRequest payload = req.getRequestPayload(); + IValidator validator = orderCompanyNewValidator.apply(payload); + Optional error = validator.tillFirstError(); + if (error.isPresent()) { + return new RequestInfoUpdate(req.getId(), Status.Error, msgResolver.resolve(error.get())); + } + OrderCurrency oc = validator.getStored(OrderCurrencyValidationStored.OrderCurrency); + oc.setCompanyId(payload.getCompanyId()); + oc.setTradingClearingRegistryId(payload.getTradingClearingRegistryId()); + oc.setQuantityLot(new BigDecimal(payload.getQuantityLot())); + orderCurrencyImdg.update(oc); + return null; + } +} diff --git a/platform-parent/platform-imdg-api/src/main/java/ru/spcex/platform/imdg/api/predicate/specific/IdEqualPredicate.java b/platform-parent/platform-imdg-api/src/main/java/ru/spcex/platform/imdg/api/predicate/specific/IdEqualPredicate.java new file mode 100644 index 000000000..5e76160c5 --- /dev/null +++ b/platform-parent/platform-imdg-api/src/main/java/ru/spcex/platform/imdg/api/predicate/specific/IdEqualPredicate.java @@ -0,0 +1,28 @@ +package ru.spcex.platform.imdg.api.predicate.specific; + +import java.util.function.Function; +import ru.spcex.platform.classes.base.interfaces.WithId; +import ru.spcex.platform.imdg.api.predicate.ImdgPredicate; +import ru.spcex.platform.imdg.api.predicate.ImdgPredicateBuilder; + +public class IdEqualPredicate implements Function { + private final Function idGetter; + private final ImdgPredicateBuilder pb; + + public IdEqualPredicate(ImdgPredicateBuilder pb, Function idGetter) { + this.pb = pb; + this.idGetter = idGetter; + } + + public static IdEqualPredicate byId(ImdgPredicateBuilder pb, T obj) { + return new IdEqualPredicate<>(pb, WithId::getId); + } + + @Override + public ImdgPredicate apply(T t) { + Long entityId = idGetter.apply(t); + return pb.equals( + "id", entityId + ); + } +} diff --git a/platform-parent/platform-imdg-api/src/main/java/ru/spcex/platform/imdg/validation/rule/PresentByPredicate.java b/platform-parent/platform-imdg-api/src/main/java/ru/spcex/platform/imdg/validation/rule/PresentByPredicate.java new file mode 100644 index 000000000..5269785bc --- /dev/null +++ b/platform-parent/platform-imdg-api/src/main/java/ru/spcex/platform/imdg/validation/rule/PresentByPredicate.java @@ -0,0 +1,112 @@ +package ru.spcex.platform.imdg.validation.rule; + +import java.util.Optional; +import java.util.function.Function; +import ru.spcex.platform.classes.base.SpcexObjectBase; +import ru.spcex.platform.imdg.api.Imdg; +import ru.spcex.platform.imdg.api.predicate.ImdgPredicate; +import ru.spcex.platform.imdg.validation.ImdgValidationContext; +import ru.spcex.platform.utils.enumeration.EnumMessage; +import ru.spcex.platform.utils.validation.IValidationRule; + +public class PresentByPredicate implements IValidationRule> { + + private final String mapName; + private final Function errorProvider; + private final Function predicateProvider; + private final Enum storeEnabled; + + private PresentByPredicate( + String mapName, + Function predicateProvider, + Function errorProvider, + Enum storeEnabled + ) { + this.mapName = mapName; + this.errorProvider = errorProvider; + this.storeEnabled = storeEnabled; + this.predicateProvider = predicateProvider; + } + + public static PredicateStep builder(Function predicateProvider) { + return new Builder<>(predicateProvider); + } + + @Override + public Optional validate(ImdgValidationContext context) { + T validatedObject = context.getValidatedObject(); + Imdg targetMap = context.obtainMap(mapName, SpcexObjectBase.class); + + ImdgPredicate predicate = predicateProvider.apply(validatedObject); + SpcexObjectBase obj = targetMap.getSingleObjectByPredicate(predicate); + + if (obj == null) { + EnumMessage errMsg = errorProvider.apply(validatedObject); + return Optional.of(errMsg); + } + + if (storeEnabled != null) { + context.storeObject(storeEnabled, obj); + } + + return Optional.empty(); + } + + public interface PredicateStep { + MapStep mapName(String mapName); + } + + public interface MapStep { + ErrorStep errorProvider(Function errorProvider); + } + + public interface ErrorStep { + OptionalStep store(Enum storeEnabled); + PresentByPredicate build(); + } + + public interface OptionalStep { + PresentByPredicate build(); + } + + private static class Builder + implements PredicateStep, MapStep, ErrorStep, OptionalStep { + + private final Function predicateProvider; + private String mapName; + private Function errorProvider; + private Enum storeEnabled; + + private Builder(Function predicateProvider) { + this.predicateProvider = predicateProvider; + } + + @Override + public MapStep mapName(String mapName) { + this.mapName = mapName; + return this; + } + + @Override + public ErrorStep errorProvider(Function errorProvider) { + this.errorProvider = errorProvider; + return this; + } + + @Override + public OptionalStep store(Enum storeEnabled) { + this.storeEnabled = storeEnabled; + return this; + } + + @Override + public PresentByPredicate build() { + return new PresentByPredicate<>( + mapName, + predicateProvider, + errorProvider, + storeEnabled + ); + } + } +} \ No newline at end of file diff --git a/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/domain/cud/order/OrderCompanyNewRequest.java b/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/domain/cud/order/OrderCompanyNewRequest.java index e3339c4aa..a4e28018e 100644 --- a/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/domain/cud/order/OrderCompanyNewRequest.java +++ b/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/domain/cud/order/OrderCompanyNewRequest.java @@ -1,8 +1,9 @@ package ru.spcex.clearing.platform.messaging.domain.cud.order; import com.fasterxml.jackson.annotation.JsonProperty; +import ru.spcex.platform.classes.base.interfaces.WithId; -public class OrderCompanyNewRequest { +public class OrderCompanyNewRequest implements WithId { @JsonProperty private Long id; @JsonProperty diff --git a/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/domain/cud/order/OrderCurrencyUpdateRequest.java b/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/domain/cud/order/OrderCurrencyUpdateRequest.java index aa8ebf790..809a63df0 100644 --- a/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/domain/cud/order/OrderCurrencyUpdateRequest.java +++ b/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/domain/cud/order/OrderCurrencyUpdateRequest.java @@ -1,9 +1,10 @@ package ru.spcex.clearing.platform.messaging.domain.cud.order; -import com.fasterxml.jackson.annotation.JsonProperty; import java.math.BigDecimal; +import com.fasterxml.jackson.annotation.JsonProperty; +import ru.spcex.platform.classes.base.interfaces.WithId; -public class OrderCurrencyUpdateRequest { +public class OrderCurrencyUpdateRequest implements WithId { @JsonProperty private Long id; @JsonProperty