ialbert 2026-04-09 17:57:35 +03:00
parent f6096bae03
commit 00c2ab02c9
9 changed files with 378 additions and 4 deletions

View file

@ -0,0 +1,5 @@
package ru.spcex.clearing.dflt.management.component.validation;
public enum OrderCurrencyValidationStored {
OrderCurrency, Company, TCR
}

View file

@ -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<ImdgValidationContext<OrderCompanyNewRequest>> {
public static final QuantityLotGreaterThanZero INSTANCE = new QuantityLotGreaterThanZero();
@Override
public Optional<EnumMessage> validate(ImdgValidationContext<OrderCompanyNewRequest> 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";
}
}

View file

@ -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<OvernightSettings> overnightSettingsImdg;
private final Imdg<Company> companyImdg;
private final Imdg<TradingClearingRegistry> tcrImdg;
private final Imdg<Market> marketImdg;
private final Imdg<OvernightTypeDictionary> osTypeDictionaryImdg;
private final Imdg<OrderCurrency> 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<OrderCurrencyUpdateRequest, IValidator> orderCurrencyUpdateOvernightTypeValidator() {
return req -> {
ImdgValidationContext<OrderCurrencyUpdateRequest> 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<OrderCompanyNewRequest, IValidator> orderCurrencyNewCompany() {
return req -> {
ImdgValidationContext<OrderCompanyNewRequest> 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);
};
}
}

View file

@ -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;

View file

@ -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<Registry> registryImdg;
private final Imdg<OvernightSettings> overnightSettingsImdg;
private final Imdg<OrderCurrency> orderCurrencyImdg;
private final Function<OrderCurrencyUpdateRequest, IValidator> overnightTypeUpdateValidator;
private final Function<OrderCompanyNewRequest, IValidator> orderCompanyNewValidator;
private final IMessageResolver msgResolver;
@Autowired
public OrderCurrencyListener(Consumer<String, Object> kafkaQueue,
Producer<String, Object> kafkaProducer,
ImdgProvider imdgProvider,
UserRoleVerification userRoleVerification,
OrderCurrencyService orderCurrencyService,
Function<OrderCurrencyUpdateRequest, IValidator> overnightTypeUpdateValidator,
Function<OrderCompanyNewRequest, IValidator> 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<OrderCurrencyUpdateRequest> req) {
OrderCurrencyUpdateRequest payload = req.getRequestPayload();
IValidator validator = overnightTypeUpdateValidator.apply(payload);
Optional<EnumMessage> 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<OrderCompanyNewRequest> req) {
OrderCompanyNewRequest payload = req.getRequestPayload();
IValidator validator = orderCompanyNewValidator.apply(payload);
Optional<EnumMessage> 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;
}
}

View file

@ -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<T> implements Function<T, ImdgPredicate> {
private final Function<T, Long> idGetter;
private final ImdgPredicateBuilder pb;
public IdEqualPredicate(ImdgPredicateBuilder pb, Function<T, Long> idGetter) {
this.pb = pb;
this.idGetter = idGetter;
}
public static <T extends WithId> IdEqualPredicate<T> 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
);
}
}

View file

@ -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<T> implements IValidationRule<ImdgValidationContext<T>> {
private final String mapName;
private final Function<T, EnumMessage> errorProvider;
private final Function<T, ImdgPredicate> predicateProvider;
private final Enum<?> storeEnabled;
private PresentByPredicate(
String mapName,
Function<T, ImdgPredicate> predicateProvider,
Function<T, EnumMessage> errorProvider,
Enum<?> storeEnabled
) {
this.mapName = mapName;
this.errorProvider = errorProvider;
this.storeEnabled = storeEnabled;
this.predicateProvider = predicateProvider;
}
public static <T> PredicateStep<T> builder(Function<T, ImdgPredicate> predicateProvider) {
return new Builder<>(predicateProvider);
}
@Override
public Optional<EnumMessage> validate(ImdgValidationContext<T> context) {
T validatedObject = context.getValidatedObject();
Imdg<SpcexObjectBase> 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<T> {
MapStep<T> mapName(String mapName);
}
public interface MapStep<T> {
ErrorStep<T> errorProvider(Function<T, EnumMessage> errorProvider);
}
public interface ErrorStep<T> {
OptionalStep<T> store(Enum<?> storeEnabled);
PresentByPredicate<T> build();
}
public interface OptionalStep<T> {
PresentByPredicate<T> build();
}
private static class Builder<T>
implements PredicateStep<T>, MapStep<T>, ErrorStep<T>, OptionalStep<T> {
private final Function<T, ImdgPredicate> predicateProvider;
private String mapName;
private Function<T, EnumMessage> errorProvider;
private Enum<?> storeEnabled;
private Builder(Function<T, ImdgPredicate> predicateProvider) {
this.predicateProvider = predicateProvider;
}
@Override
public MapStep<T> mapName(String mapName) {
this.mapName = mapName;
return this;
}
@Override
public ErrorStep<T> errorProvider(Function<T, EnumMessage> errorProvider) {
this.errorProvider = errorProvider;
return this;
}
@Override
public OptionalStep<T> store(Enum<?> storeEnabled) {
this.storeEnabled = storeEnabled;
return this;
}
@Override
public PresentByPredicate<T> build() {
return new PresentByPredicate<>(
mapName,
predicateProvider,
errorProvider,
storeEnabled
);
}
}
}

View file

@ -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

View file

@ -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