etreschenkov 2024-06-07 10:52:45 +03:00
parent 2289d1ed6c
commit 0ef3aca8b0
8 changed files with 231 additions and 35 deletions

View file

@ -1,33 +1,40 @@
package ru.spcex.clearing.account.config.validation;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import ru.clearing.classes.statics.data.account.Account;
import ru.clearing.classes.statics.data.account.ClientCode;
import ru.clearing.classes.statics.data.company.Company;
import ru.clearing.platform.dictionary.ServiceStatusDictionary;
import ru.spcex.clearing.account.errors.AccountError;
import ru.spcex.clearing.imdg.IMDGDistributedNames;
import ru.spcex.clearing.platform.messaging.domain.cud.account.ClientCodeNewRequest;
import ru.spcex.clearing.platform.messaging.domain.cud.account.ClientCodeUpdateRequest;
import ru.spcex.clearing.platform.messaging.domain.cud.common.CommonDeleteRequest;
import ru.spcex.clearing.validation.common.rules.DictionaryPresentRule;
import ru.spcex.clearing.validation.common.rules.FieldRequiredRule;
import ru.spcex.clearing.validation.common.rules.IdPresentRule;
import ru.spcex.platform.classes.base.SpcexObjectBase;
import ru.spcex.platform.imdg.api.Imdg;
import ru.spcex.platform.imdg.validation.ImdgValidationContext;
import ru.spcex.platform.utils.enumeration.EnumMessage;
import ru.spcex.platform.utils.validation.IValidationRule;
import ru.spcex.platform.utils.validation.IValidator;
import ru.spcex.platform.utils.validation.ValidatorImpl;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;
import ru.clearing.classes.statics.data.account.Account;
import ru.clearing.classes.statics.data.account.ClientCode;
import ru.clearing.classes.statics.data.company.Company;
import ru.clearing.classes.statics.data.company.CompanySymbols;
import ru.clearing.platform.dictionary.ServiceStatusDictionary;
import ru.spcex.clearing.account.errors.AccountError;
import ru.spcex.clearing.imdg.IMDGDistributedNames;
import ru.spcex.clearing.platform.messaging.domain.cud.account.ClientCodeNewRequest;
import ru.spcex.clearing.platform.messaging.domain.cud.account.ClientCodeUpdateRequest;
import ru.spcex.clearing.platform.messaging.domain.cud.account.TkrAccount;
import ru.spcex.clearing.platform.messaging.domain.cud.common.CommonDeleteRequest;
import ru.spcex.clearing.platform.messaging.domain.cud.gateway.MoneyAccountMsgRequest;
import ru.spcex.clearing.validation.common.rules.DictionaryPresentRule;
import ru.spcex.clearing.validation.common.rules.FieldRequiredRule;
import ru.spcex.clearing.validation.common.rules.IdPresentRule;
import ru.spcex.platform.classes.base.SpcexObjectBase;
import ru.spcex.platform.enumeration.CompanySymbol;
import ru.spcex.platform.enumeration.CurrencyCode;
import ru.spcex.platform.imdg.api.Imdg;
import ru.spcex.platform.imdg.api.predicate.ImdgPredicate;
import ru.spcex.platform.imdg.api.predicate.ImdgPredicateBuilder;
import ru.spcex.platform.imdg.validation.ImdgValidationContext;
import ru.spcex.platform.utils.enumeration.EnumMessage;
import ru.spcex.platform.utils.validation.IValidationRule;
import ru.spcex.platform.utils.validation.IValidator;
import ru.spcex.platform.utils.validation.ValidatorImpl;
@Configuration
public class ClientCodeValidationConfig {
@ -121,6 +128,86 @@ public class ClientCodeValidationConfig {
}
}
public static class ExistCompanyByUuid<R> implements IValidationRule<ImdgValidationContext<R>> {
String fieldName;
Function<R, String> symbolCodeGetter;
public ExistCompanyByUuid(String fieldName, Function<R, String> symbolCodeGetter) {
this.fieldName = fieldName;
this.symbolCodeGetter = symbolCodeGetter;
}
@Override
public Optional<EnumMessage> validate(ImdgValidationContext<R> context) {
R validatedObject = context.getValidatedObject();
String symbolCodeValue = symbolCodeGetter.apply(validatedObject);
if (!StringUtils.hasText(symbolCodeValue)) {
return of(AccountError.RequiredFieldEmpty, fieldName);
}
Imdg<CompanySymbols> imdg = context.obtainMap(IMDGDistributedNames.Map_CompanySymbols, CompanySymbols.class);
CompanySymbols companySymbols = imdg.getFirstObjectByFieldValues(
Map.of(
"companySymbol", CompanySymbol.UUID.getKey(),
"companySymbolValue", symbolCodeValue
)
);
if (companySymbols == null) { // null значения в массиве не ожидаются
return of(AccountError.COMPANY_NOT_FOUND_GTW, fieldName);
}
return Optional.empty();
}
@Override
public String ruleName() {
return getClass().getSimpleName() + "{" + fieldName + "}";
}
}
public static class ExistAccountByValue<R> implements IValidationRule<ImdgValidationContext<R>> {
String fieldName;
Function<R, List<MoneyAccountMsgRequest>> accountsListValueGetter;
public ExistAccountByValue(String fieldName, Function<R, List<MoneyAccountMsgRequest>> accountsListValueGetter) {
this.fieldName = fieldName;
this.accountsListValueGetter = accountsListValueGetter;
}
@Override
public Optional<EnumMessage> validate(ImdgValidationContext<R> context) {
R validatedObject = context.getValidatedObject();
List<MoneyAccountMsgRequest> accountList = accountsListValueGetter.apply(validatedObject);
if (accountList == null || accountList.isEmpty()) {
return of(AccountError.RequiredFieldEmpty, fieldName);
}
Imdg<Account> imdg = context.obtainMap(IMDGDistributedNames.Map_Account, Account.class);
for (MoneyAccountMsgRequest moneyAccountMsg : accountList) {
ImdgPredicateBuilder predicateBuilder = imdg.predicateBuilder();
ImdgPredicate currencyPredicate;
ImdgPredicate accountPredicate = predicateBuilder.equals("account", moneyAccountMsg.getAccount());
if (moneyAccountMsg.getCurrCode().equals(CurrencyCode.RUB.getKey())) {
currencyPredicate = predicateBuilder.or(
predicateBuilder.equals("currency", CurrencyCode.RUB.getKey()),
predicateBuilder.isNull("currency")
);
} else {
currencyPredicate = predicateBuilder.equals("currency", moneyAccountMsg.getCurrCode());
}
Account account = imdg.getFirstObjectByPredicate(
predicateBuilder.and(accountPredicate, currencyPredicate)
);
if (account == null) {
return of(AccountError.ACCOUNT_NOT_FOUND_GTW, moneyAccountMsg.getAccount(), fieldName);
}
}
return Optional.empty();
}
@Override
public String ruleName() {
return getClass().getSimpleName() + "{" + fieldName + "}";
}
}
@Bean("clientCodeUpdateRequestValidator")
public Function<ClientCodeUpdateRequest, IValidator> clientCodeUpdateRequestValidator(Map<String, Imdg<? extends SpcexObjectBase>> imdgForValidation) {
return clientCodeUpdateRequest -> {
@ -155,7 +242,7 @@ public class ClientCodeValidationConfig {
Account.class,
AccountError.RequiredFieldEmpty,
AccountError.AccountNotFound,
false,
true,
acc -> Objects.equals(context.getValidatedObject().getCompanyId(), acc.getCompanyId())
? null : AccountError.AccountNotFound
),
@ -202,4 +289,22 @@ public class ClientCodeValidationConfig {
);
};
}
@Bean("tkrAccountsGatewayValidator")
public Function<TkrAccount, IValidator> tkrAccountsGatewayValidator(Map<String, Imdg<? extends SpcexObjectBase>> imdgForValidation) {
return tkrGatewayRequest -> {
ImdgValidationContext<TkrAccount> context = new ImdgValidationContext<>();
context.setValidatedObject(tkrGatewayRequest);
Consumer<String> addImdg = (s) -> context.addImdg(s, imdgForValidation.get(s));
addImdg.accept(IMDGDistributedNames.Map_CompanySymbols);
addImdg.accept(IMDGDistributedNames.Map_Account);
addImdg.accept(IMDGDistributedNames.Map_TradingClearingRegistry);
return new ValidatorImpl<>(context,
new ExistCompanyByUuid<>("companySymbolValue",
TkrAccount::getCompanyId),
new ExistAccountByValue<>("account",
TkrAccount::getMoneyAccounts)
);
};
}
}

View file

@ -25,6 +25,10 @@ public enum AccountError implements IErrorEnumId {
AccountDepoTypeRequired(5025L),
AccountIsNotACurrency(5026L), // Счет %S не валютный
CompanyHasNotClearingMemberCategory(5027L),
//ошибки для трансляции в модуль gateway
TCR_NOT_FOUND_GTW(5028L),
ACCOUNT_NOT_FOUND_GTW(5029L),
COMPANY_NOT_FOUND_GTW(5030L),
TradingClearingRegistryNotFound(3022L),
CurrencyNotFound(1016L),
;

View file

@ -5,9 +5,11 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.apache.kafka.clients.consumer.Consumer;
import org.apache.kafka.clients.producer.Producer;
import org.apache.kafka.clients.producer.ProducerRecord;
@ -35,6 +37,9 @@ import ru.spcex.clearing.platform.messaging.domain.cud.account.TkrAccount;
import ru.spcex.clearing.platform.messaging.domain.cud.account.TkrAccountsGatewayRequest;
import ru.spcex.clearing.platform.messaging.domain.cud.common.CommonDeleteRequest;
import ru.spcex.clearing.platform.messaging.domain.cud.gateway.MoneyAccountMsgRequest;
import ru.spcex.clearing.platform.messaging.domain.cud.gateway.MoneyAccountMsgResponse;
import ru.spcex.clearing.platform.messaging.domain.cud.gateway.SendTkrRequest;
import ru.spcex.clearing.platform.messaging.domain.cud.gateway.Tkr;
import ru.spcex.clearing.platform.messaging.domain.cud.registry.TradingClearingRegistryListNewRequest;
import ru.spcex.clearing.platform.messaging.domain.cud.registry.TradingClearingRegistryListUpdateRequest;
import ru.spcex.clearing.platform.messaging.domain.cud.registry.TradingClearingRegistryNewRequest;
@ -43,6 +48,7 @@ import ru.spcex.clearing.platform.messaging.serialization.LogFormatter;
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.platform.messaging.service.sender.KafkaSender;
import ru.spcex.clearing.util.security.UserRoleVerification;
import ru.spcex.clearing.util.services.RequestHelper;
import ru.spcex.clearing.validation.common.ValidationHelper;
@ -74,6 +80,7 @@ public class ClientCodeService extends QueueConsumer implements InitializingBean
private final Function<ClientCodeNewRequest, IValidator> clientCodeNewRequestValidator;
private final Function<ClientCodeUpdateRequest, IValidator> clientCodeUpdateRequestValidator;
private final Function<CommonDeleteRequest, IValidator> clientCodeDeleteRequestValidator;
private final Function<TkrAccount, IValidator> tkrAccountsGatewayValidator;
private final ValidationHelper validationHelper;
private final UserRoleVerification userRoleVerification;
private final IMessageResolver messageResolver;
@ -81,11 +88,13 @@ public class ClientCodeService extends QueueConsumer implements InitializingBean
protected TradingClearingRegistryService tradingClearingRegistryService;
protected ConfigurableApplicationContext context;
private final KafkaSender kafkaSender;
//protected TradingClearingRegistryListService tradingClearingRegistryListService;
@Autowired
public ClientCodeService(Consumer<String, Object> kafkaQueue,
Producer<String, Object> kafkaProducer,
KafkaSender kafkaSender,
ImdgProvider imdgProvider,
ValidationHelper validationHelper,
IMessageResolver messageResolver,
@ -95,9 +104,11 @@ public class ClientCodeService extends QueueConsumer implements InitializingBean
UserRoleVerification userRoleVerification,
@Qualifier("clientCodeNewRequestValidator") Function<ClientCodeNewRequest, IValidator> clientCodeNewRequestValidator,
@Qualifier("clientCodeUpdateRequestValidator") Function<ClientCodeUpdateRequest, IValidator> clientCodeUpdateRequestValidator,
@Qualifier("clientCodeDeleteRequestValidator") Function<CommonDeleteRequest, IValidator> clientCodeDeleteRequestValidator) {
@Qualifier("clientCodeDeleteRequestValidator") Function<CommonDeleteRequest, IValidator> clientCodeDeleteRequestValidator,
@Qualifier("tkrAccountsGatewayValidator") Function<TkrAccount, IValidator> tkrAccountsGatewayValidator) {
super(kafkaQueue, kafkaProducer);
this.kafkaProducer = kafkaProducer;
this.kafkaSender = kafkaSender;
this.imdgProvider = imdgProvider;
this.idGenerator = imdgProvider.getImdgIdGenerator();
this.clientCodeMap = imdgProvider.getImdg(IMDGDistributedNames.Map_ClientCode, ClientCode.class);
@ -107,6 +118,7 @@ public class ClientCodeService extends QueueConsumer implements InitializingBean
this.clientCodeNewRequestValidator = clientCodeNewRequestValidator;
this.clientCodeUpdateRequestValidator = clientCodeUpdateRequestValidator;
this.clientCodeDeleteRequestValidator = clientCodeDeleteRequestValidator;
this.tkrAccountsGatewayValidator = tkrAccountsGatewayValidator;
this.validationHelper = validationHelper;
this.requestHelper = requestHelper;
this.messageResolver = messageResolver;
@ -174,6 +186,38 @@ public class ClientCodeService extends QueueConsumer implements InitializingBean
protected RequestInfoUpdate clientCodeNewFromGateway(BaseRequest<TkrAccountsGatewayRequest> tkrRequest) {
TkrAccountsGatewayRequest tkr = tkrRequest.getRequestPayload();
RequestInfoUpdate requestInfoUpdate = null;
Tkr errorTkr = null;
List<Tkr> successTkr = new ArrayList<>();
//Сначала нужно проверить на валидности все присланные счета на добавление
for (TkrAccount tkrAccount : tkr.getAccounts()) {
BaseRequest requestForValidation = new BaseRequest();
requestForValidation.setRequestPayload(tkrAccount);
requestInfoUpdate = validationHelper.validateTillFirstError(requestForValidation,
tkrAccountsGatewayValidator);
if (requestInfoUpdate != null) {
errorTkr = crateErrorTkrToGateway(tkrAccount, Optional.of(requestInfoUpdate.getMessage()));
errorTkr.setMoneyAccounts(tkrAccount.getMoneyAccounts().stream()
.map(this::convertAccountToResponse)
.collect(Collectors.toList()));
break;
}
Tkr tkrToGateway = crateErrorTkrToGateway(tkrAccount, Optional.empty());
tkrToGateway.setMoneyAccounts(tkrAccount.getMoneyAccounts().stream()
.map(this::convertAccountToResponse)
.collect(Collectors.toList()));
}
//Если хоть в одном была ошибка, берем ее отправляем
if (errorTkr != null) {
log.debug("Send request to gateway with error tkr cause: {}", requestInfoUpdate.getMessage());
SendTkrRequest sendTkrRequest = new SendTkrRequest();
sendTkrRequest.getTkrs().add(errorTkr);
sendTkrRequest.getTkrs().addAll(successTkr);
kafkaSender.sendRequestToQueue(Consts.ACCOUNTS_TO_GATEWAY, sendTkrRequest);
return null;
}
for (TkrAccount tkrAccount : tkr.getAccounts()) {
ClientCodeNewRequest clientCodeNewRequest = new ClientCodeNewRequest();
clientCodeNewRequest.setCode(tkrAccount.getClientCode());
@ -231,7 +275,7 @@ public class ClientCodeService extends QueueConsumer implements InitializingBean
}
BaseRequest<ClientCodeNewRequest> request = new BaseRequest<>();
request.setRequestPayload(clientCodeNewRequest);
RequestInfoUpdate requestInfoUpdate = clientCodeNew0(request, false);
requestInfoUpdate = clientCodeNew0(request, false);
if (requestInfoUpdate != null) {
log.debug("processing result is: {}", requestInfoUpdate.getMessage());
}
@ -614,4 +658,21 @@ public class ClientCodeService extends QueueConsumer implements InitializingBean
clientCode.setUpdated(Instant.now());
}
private Tkr crateErrorTkrToGateway(TkrAccount tkrAccount, Optional<String> errorMsg) {
Tkr tkr = new Tkr();
tkr.setTkrCode(tkrAccount.getTkrCode());
tkr.setClientCode(tkrAccount.getClientCode());
tkr.setCompanyId(tkrAccount.getCompanyId());
tkr.setDepoAccount(tkrAccount.getDepoAccount());
errorMsg.ifPresent(tkr::setErrorMessage);
return tkr;
}
private MoneyAccountMsgResponse convertAccountToResponse(MoneyAccountMsgRequest moneyAccountMsgRequest) {
MoneyAccountMsgResponse msgResponse = new MoneyAccountMsgResponse();
msgResponse.setAccount(moneyAccountMsgRequest.getAccount());
msgResponse.setCurrCode(moneyAccountMsgRequest.getCurrCode());
return msgResponse;
}
}

View file

@ -549,7 +549,7 @@ public class TradingClearingRegistryService extends QueueConsumer implements Ini
"companyId", company.getId()
)
);
if (companySymbols == null){
if (companySymbols == null) {
log.warn("CompanySymbols is null, searched by company.id: {}, skip this tkr", company.getId());
return Optional.empty();
}
@ -575,11 +575,9 @@ public class TradingClearingRegistryService extends QueueConsumer implements Ini
}
tkr.setAccountTypeName(accountType);
if (tradingClearingRegistry.getDepoAccountId() != null) {
DepoAccount depoAccount = depoAccountImdg.getSingleObjectByID(tradingClearingRegistry.getDepoAccountId());
Account account = null;
Account depoAccount = accountImdg.getSingleObjectByID(tradingClearingRegistry.getDepoAccountId());
if (depoAccount != null) {
account = accountImdg.getSingleObjectByID(depoAccount.getAccountId());
tkr.setDepoAccount(account.getAccount());
tkr.setDepoAccount(depoAccount.getAccount());
}
}
{

View file

@ -1,5 +1,8 @@
package ru.spcex.clearing.validation.common;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ru.spcex.clearing.platform.messaging.domain.BaseRequest;
@ -10,10 +13,6 @@ import ru.spcex.platform.utils.enumeration.IMessageResolver;
import ru.spcex.platform.utils.error.ValidationException;
import ru.spcex.platform.utils.validation.IValidator;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
public class ValidationHelper {
private final static Logger log = LoggerFactory.getLogger(ValidationHelper.class);
@ -42,7 +41,8 @@ public class ValidationHelper {
return new RequestInfoUpdate()
.setId(userRequest.getId())
.setStatus(Status.Error)
.setMessage(errorMsg);
.setMessage(errorMsg)
.setEnumMessage(validationError.get());
}
}
return null;

View file

@ -884,6 +884,12 @@ INSERT INTO ERROR_CODE_DICTIONARY(ID, CODE, NAME) values (5026, 'ACNT', 'Сче
INSERT INTO ERROR_CODE_DICTIONARY(ID, CODE, NAME) values (5027, 'ACNT', 'У компании %s отсутствует категория.') ON CONFLICT (ID) DO UPDATE SET CODE = EXCLUDED.CODE, NAME = EXCLUDED.NAME;
INSERT INTO ERROR_CODE_DICTIONARY(ID, CODE, NAME) values (5028, 'ACNT', 'Указанный ТКР не найден в клиринговой системе.') ON CONFLICT (ID) DO UPDATE SET CODE = EXCLUDED.CODE, NAME = EXCLUDED.NAME;
INSERT INTO ERROR_CODE_DICTIONARY(ID, CODE, NAME) values (5029, 'ACNT', 'Счет %s не найден в клиринговой системе.') ON CONFLICT (ID) DO UPDATE SET CODE = EXCLUDED.CODE, NAME = EXCLUDED.NAME;
INSERT INTO ERROR_CODE_DICTIONARY(ID, CODE, NAME) values (5030, 'ACNT', 'Компания %s не найдена в клиринговой системе.') ON CONFLICT (ID) DO UPDATE SET CODE = EXCLUDED.CODE, NAME = EXCLUDED.NAME;
INSERT INTO ERROR_CODE_DICTIONARY(ID, CODE, NAME) values (5200, 'BLNC', 'Общая ошибка модуля balance-service.') ON CONFLICT (ID) DO UPDATE SET CODE = EXCLUDED.CODE, NAME = EXCLUDED.NAME;
INSERT INTO ERROR_CODE_DICTIONARY(ID, CODE, NAME) values (5210, 'BLNC', 'Клиринговая сессия неактивна.') ON CONFLICT (ID) DO UPDATE SET CODE = EXCLUDED.CODE, NAME = EXCLUDED.NAME;

View file

@ -21,6 +21,8 @@ public class Tkr {
private String depoAccount;
@JsonProperty("is_active")
private Boolean isActive;
@JsonProperty("error_message")
private String errorMessage;
@JsonProperty("money_account")
private List<MoneyAccountMsgResponse> moneyAccounts = new ArrayList<>();
@ -95,4 +97,12 @@ public class Tkr {
public void setActive(Boolean active) {
isActive = active;
}
public String getErrorMessage() {
return errorMessage;
}
public void setErrorMessage(String errorMessage) {
this.errorMessage = errorMessage;
}
}

View file

@ -1,9 +1,12 @@
package ru.spcex.clearing.platform.messaging.service;
import ru.spcex.platform.utils.enumeration.EnumMessage;
public class RequestInfoUpdate {
private Long id;
private Status status;
private String message;
private EnumMessage enumMessage;
public RequestInfoUpdate() {}
@ -40,6 +43,15 @@ public class RequestInfoUpdate {
return this;
}
public EnumMessage getEnumMessage() {
return enumMessage;
}
public RequestInfoUpdate setEnumMessage(EnumMessage enumMessage) {
this.enumMessage = enumMessage;
return this;
}
@Override
public String toString() {
return "RequestInfoUpdate{" +