From a2dcd218672f6449fa4cbab2e4e887b19f4d52f1 Mon Sep 17 00:00:00 2001 From: AKurakin Date: Tue, 31 Oct 2023 17:23:03 +0300 Subject: [PATCH] =?UTF-8?q?account-service=20http://jira.mfd.msk:8088/brow?= =?UTF-8?q?se/CLS-577,=20imdg=20http://jira.mfd.msk:8088/browse/CLS-575=20?= =?UTF-8?q?=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8=D0=BB=20AccountSymbolsUpda?= =?UTF-8?q?te=20=D0=B2=20UpdateMapService.java?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AccountSymbolsValidationConfig.java | 70 ++++++++ .../config/validation/ValidationConfig.java | 1 + .../service/AccountSymbolsService.java | 122 +++++++++++++ .../service/AccountSymbolsServiceTest.java | 164 ++++++++++++++++++ .../account/AccountSymbolsController.java | 2 +- .../imdg/services/UpdateMapService.java | 6 + .../platform/messaging/domain/Consts.java | 2 +- 7 files changed, 365 insertions(+), 2 deletions(-) create mode 100644 clearing-parent/account-service/src/main/java/ru/spcex/clearing/account/config/validation/AccountSymbolsValidationConfig.java create mode 100644 clearing-parent/account-service/src/main/java/ru/spcex/clearing/account/service/AccountSymbolsService.java create mode 100644 clearing-parent/account-service/src/test/java/ru/spcex/clearing/account/service/AccountSymbolsServiceTest.java diff --git a/clearing-parent/account-service/src/main/java/ru/spcex/clearing/account/config/validation/AccountSymbolsValidationConfig.java b/clearing-parent/account-service/src/main/java/ru/spcex/clearing/account/config/validation/AccountSymbolsValidationConfig.java new file mode 100644 index 000000000..ca397cb7a --- /dev/null +++ b/clearing-parent/account-service/src/main/java/ru/spcex/clearing/account/config/validation/AccountSymbolsValidationConfig.java @@ -0,0 +1,70 @@ +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.AccountSymbols; +import ru.spcex.clearing.account.errors.AccountError; +import ru.spcex.clearing.imdg.IMDGDistributedNames; +import ru.spcex.clearing.platform.messaging.domain.cud.account.AccountSymbolsNewRequest; +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.*; +import java.util.function.Consumer; +import java.util.function.Function; + +@Configuration +public class AccountSymbolsValidationConfig { + + @Bean("accountSymbolsNewRequest") + public Function correspondentAccountNewRequestValidator( + Map> imdgForValidation + ) { + return accountSymbolsNewRequest -> { + ImdgValidationContext context = new ImdgValidationContext<>(); + context.setValidatedObject(accountSymbolsNewRequest); + Consumer addImdg = (s) -> context.addImdg(s, imdgForValidation.get(s)); + addImdg.accept(IMDGDistributedNames.Map_Account); + addImdg.accept(IMDGDistributedNames.Map_AccountSymbols); + return new ValidatorImpl<>(context, + IdPresentRule.instance("accountId", + AccountSymbolsNewRequest::getAccountId, + IMDGDistributedNames.Map_Account, + Account.class, + AccountError.RequiredFieldEmpty, + AccountError.CompanyNotFound, + false + ), + new DuplicateAccountSymbolsRule() + ); + }; + } + + static class DuplicateAccountSymbolsRule implements IValidationRule> { + @Override + public Optional validate(ImdgValidationContext context) { + AccountSymbolsNewRequest validatedObject = context.getValidatedObject(); + Imdg accountSymbolsImdg = context.obtainMap(IMDGDistributedNames.Map_AccountSymbols, AccountSymbols.class); + + Map> query = new HashMap<>(); + query.put("accountId", validatedObject.getAccountId()); + query.put("accountSymbolValue", validatedObject.getAccountSymbolValue()); + + Collection existAccSymbols = accountSymbolsImdg.getCollectionObjectsByFieldValues(query); + + if (existAccSymbols.isEmpty()) { + return empty(); + } else { + AccountSymbols existAS = existAccSymbols.iterator().next(); + return of(AccountError.AccountAlreadyExist, existAS.getAccountId()); + } + } + } +} diff --git a/clearing-parent/account-service/src/main/java/ru/spcex/clearing/account/config/validation/ValidationConfig.java b/clearing-parent/account-service/src/main/java/ru/spcex/clearing/account/config/validation/ValidationConfig.java index 1a542396e..f170eef5d 100644 --- a/clearing-parent/account-service/src/main/java/ru/spcex/clearing/account/config/validation/ValidationConfig.java +++ b/clearing-parent/account-service/src/main/java/ru/spcex/clearing/account/config/validation/ValidationConfig.java @@ -41,6 +41,7 @@ public class ValidationConfig { addImdg.accept(IMDGDistributedNames.Map_DepoAccount, DepoAccount.class); addImdg.accept(IMDGDistributedNames.Map_ServiceStatusDictionary, ServiceStatusDictionary.class); addImdg.accept(IMDGDistributedNames.Map_TradingClearingRegistry, TradingClearingRegistry.class); + addImdg.accept(IMDGDistributedNames.Map_AccountSymbols, AccountSymbols.class); //for ClientCodeValidationConfig addImdg.accept(IMDGDistributedNames.Map_ClientCode, ClientCode.class); diff --git a/clearing-parent/account-service/src/main/java/ru/spcex/clearing/account/service/AccountSymbolsService.java b/clearing-parent/account-service/src/main/java/ru/spcex/clearing/account/service/AccountSymbolsService.java new file mode 100644 index 000000000..0f139dc29 --- /dev/null +++ b/clearing-parent/account-service/src/main/java/ru/spcex/clearing/account/service/AccountSymbolsService.java @@ -0,0 +1,122 @@ +package ru.spcex.clearing.account.service; + +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.Qualifier; +import org.springframework.stereotype.Service; +import ru.clearing.classes.statics.data.account.*; +import ru.spcex.clearing.account.errors.AccountError; +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.account.AccountSymbolsNewRequest; +import ru.spcex.clearing.platform.messaging.domain.cud.common.CommonIdRequest; +import ru.spcex.clearing.platform.messaging.service.QueueConsumer; +import ru.spcex.clearing.platform.messaging.service.RequestInfoUpdate; +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; +import ru.spcex.platform.imdg.api.Imdg; +import ru.spcex.platform.imdg.api.ImdgProvider; +import ru.spcex.platform.utils.enumeration.IMessageResolver; +import ru.spcex.platform.utils.validation.IValidator; + +import java.util.function.Function; + +@Service +public class AccountSymbolsService extends QueueConsumer implements InitializingBean { + private final Logger log = LoggerFactory.getLogger(getClass()); + + private final ValidationHelper validationHelper; + private final UserRoleVerification userRoleVerification; + + private final ImdgProvider imdgProvider; + private final Imdg accountSymbolsImdg; + + private final RequestHelper requestHelper; + private final Function accountSymbolsNewRequestValidator; + + private final IMessageResolver messageResolver; + private final Producer kafkaProducer; + private final KafkaSender kafkaSender; + + public AccountSymbolsService(Consumer kafkaQueue, + Producer kafkaProducer, + KafkaSender kafkaSender, + ImdgProvider imdgProvider, + ValidationHelper validationHelper, + UserRoleVerification userRoleVerification, + IMessageResolver messageResolver, + RequestHelper requestHelper, + @Qualifier("accountSymbolsNewRequest") + Function accountSymbolsNewRequestValidator + ) { + super(kafkaQueue, kafkaProducer); + this.kafkaProducer = kafkaProducer; + this.kafkaSender = kafkaSender; + this.validationHelper = validationHelper; + this.userRoleVerification = userRoleVerification; + this.requestHelper = requestHelper; + this.imdgProvider = imdgProvider; + this.accountSymbolsImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_AccountSymbols, AccountSymbols.class); + this.accountSymbolsNewRequestValidator = accountSymbolsNewRequestValidator; + this.messageResolver = messageResolver; + } + + @Override + public void afterPropertiesSet() throws Exception { + imdgProvider.waitAvailable(); + callback(AccountSymbolsNewRequest.class) + .setFunction(this::accountSymbolsNew) + .forDestination(Consts.DESTINATION_DEPO_ACCOUNT_SYMBOLS_NEW, callbacks::put); + callback(CommonIdRequest.class) + .setFunction(this::accountSymbolsDelete) + .forDestination(Consts.DESTINATION_DEPO_ACCOUNT_SYMBOLS_DELETE, callbacks::put); + init(); + } + + protected RequestInfoUpdate accountSymbolsNew(BaseRequest userRequest) { + log.debug("AccountSymbolsNewRequest received, id={}", userRequest.getId()); + RequestInfoUpdate requestInfoUpdate = userRoleVerification.validateRoleAndGetResult(userRequest); + if (requestInfoUpdate != null) return requestInfoUpdate; + + requestInfoUpdate = validationHelper.validateTillFirstError(userRequest, accountSymbolsNewRequestValidator); + if (requestInfoUpdate != null) return requestInfoUpdate; + + AccountSymbolsNewRequest req = userRequest.getRequestPayload(); + + Long id = accountSymbolsImdg.nextIDSequenceFor(); + AccountSymbols accountSymbols = new AccountSymbols(); + accountSymbols.setId(id); + accountSymbols.setAccountId(req.getAccountId()); + accountSymbols.setAccountSymbolValue(req.getAccountSymbolValue()); + + accountSymbolsImdg.insert(accountSymbols); + + log.debug("successfully processed, id {}. New accountSymbols.id={} was created", id, accountSymbols.getId()); + return null; + } + + protected RequestInfoUpdate accountSymbolsDelete(BaseRequest userRequest) { + log.debug("AccountSymbolsDeleteRequest(CommonIdRequest) received, id={}", userRequest.getId()); + RequestInfoUpdate requestInfoUpdate = userRoleVerification.validateRoleAndGetResult(userRequest); + if (requestInfoUpdate != null) return requestInfoUpdate; + + CommonIdRequest req = userRequest.getRequestPayload(); + + AccountSymbols accountSymbols = accountSymbolsImdg.getSingleObjectByID(req.getId()); + if (accountSymbols == null) { + return requestHelper.makeErrorResponse(userRequest, AccountError.AccountNotFound, req.getId()); + } + + accountSymbolsImdg.delete(accountSymbols); + + log.debug("successfully processed, id {}. New accountSymbols.id={} was deleted", userRequest.getId(), accountSymbols.getId()); + return null; + } + +} diff --git a/clearing-parent/account-service/src/test/java/ru/spcex/clearing/account/service/AccountSymbolsServiceTest.java b/clearing-parent/account-service/src/test/java/ru/spcex/clearing/account/service/AccountSymbolsServiceTest.java new file mode 100644 index 000000000..054d8c139 --- /dev/null +++ b/clearing-parent/account-service/src/test/java/ru/spcex/clearing/account/service/AccountSymbolsServiceTest.java @@ -0,0 +1,164 @@ +package ru.spcex.clearing.account.service; + +import org.apache.kafka.clients.consumer.MockConsumer; +import org.apache.kafka.clients.producer.Producer; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.kafka.core.KafkaTemplate; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import ru.clearing.classes.statics.data.account.*; +import ru.spcex.clearing.account.config.BeanConfiguration; +import ru.spcex.clearing.account.config.validation.AccountSymbolsValidationConfig; +import ru.spcex.clearing.account.config.validation.ValidationConfig; +import ru.spcex.clearing.imdg.IMDGDistributedNames; +import ru.spcex.clearing.platform.messaging.domain.Consts; +import ru.spcex.clearing.platform.messaging.domain.cud.account.AccountSymbolsNewRequest; +import ru.spcex.clearing.platform.messaging.domain.cud.common.CommonDeleteRequest; +import ru.spcex.clearing.test.MatcherFactory; +import ru.spcex.clearing.test.TestObjectCreator; +import ru.spcex.clearing.test.config.ImdgTestConfig; +import ru.spcex.clearing.test.config.KafkaTestConfig; +import ru.spcex.platform.enumeration.*; +import ru.spcex.platform.imdg.api.Imdg; +import ru.spcex.platform.imdg.api.ImdgProvider; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; + +import static ru.spcex.clearing.test.MatcherFactory.usingIgnoringFieldsComparator; +import static ru.spcex.clearing.test.TestUtils.*; +import static ru.spcex.clearing.test.config.KafkaTestConfig.setMockFuture; + +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = { + BeanConfiguration.class, + ValidationConfig.class, + AccountSymbolsValidationConfig.class, + AccountSymbolsService.class, + ImdgTestConfig.class, + KafkaTestConfig.class}) +class AccountSymbolsServiceTest { + public static final MatcherFactory.Matcher ACCOUNT_SYMBOL_MATCHER = usingIgnoringFieldsComparator(); + private static final int PARTITION = 0; + + @Autowired + AccountSymbolsService accountSymbolsService; + @Autowired + @Qualifier("hazelcastServiceTest") + private ImdgProvider hazelcastServiceTest; + + @Autowired + @Qualifier("mockProducer") + protected Producer mockProducer; + + @Autowired + @Qualifier("kafkaTestTemplate") + protected KafkaTemplate kafkaTemplate; + + private Imdg accountSymbolsImdg; + + private Imdg accountImdg; + private Long accountId; + private Imdg clearingAccountImdg; + private Long clearingAccountId; + + static int newRequestCnt = 0; + + @PostConstruct + private void init() { + hazelcastServiceTest.waitAvailable(); + accountSymbolsImdg = hazelcastServiceTest.getImdg( + IMDGDistributedNames.Map_AccountSymbols, AccountSymbols.class + ); + accountImdg = hazelcastServiceTest.getImdg( + IMDGDistributedNames.Map_Account, Account.class + ); + clearingAccountImdg = hazelcastServiceTest.getImdg( + IMDGDistributedNames.Map_ClearingAccount, ClearingAccount.class + ); + + Account account = new Account(); + account.setStatus(ServiceStatus.Active.getKey()); + account.setAccountType(AccountType.Clrn.getKey()); + accountId = accountImdg.insert(account); + + ClearingAccount clearingAccount = new ClearingAccount(); + clearingAccount.setAccountId(accountId); + clearingAccount.setClearingAccountType("CAT"); + clearingAccountId = clearingAccountImdg.insert(clearingAccount); + + new TestObjectCreator(hazelcastServiceTest).createUserAdmin(1000L); + } + + @PreDestroy + private void destroyTest() { + //clean test data + Account account = accountImdg.getSingleObjectByID(accountId); + if (account != null) + accountImdg.delete(account); + ClearingAccount clearingAccount = clearingAccountImdg.getSingleObjectByID(clearingAccountId); + if (clearingAccount != null) + clearingAccountImdg.delete(clearingAccount); + } + + @Test + void tradingClearingRegistryNew_moneyAccount_clearingAccount() { + setMockFuture(kafkaTemplate); + AccountSymbolsNewRequest accountSymbolsNewRequest = new AccountSymbolsNewRequest(); + accountSymbolsNewRequest.setAccountId(accountId); + accountSymbolsNewRequest.setAccountSymbolValue("SYMBOL1"); + + AccountSymbols predictableAccountSymbols = new AccountSymbols(); + predictableAccountSymbols.setAccountId(accountId); + predictableAccountSymbols.setAccountSymbolValue("SYMBOL1"); + + String jsonString = getJsonStringForNew(accountSymbolsNewRequest, 0L); + + addRecordToKafka((MockConsumer) accountSymbolsService.getConsumer(), + Consts.DESTINATION_DEPO_ACCOUNT_SYMBOLS_NEW, + newRequestCnt, + 0, + jsonString); + + waitingSendAndCheckRecord(0L, mockProducer); + + AccountSymbols resultNew = accountSymbolsImdg.getAllValues().iterator().next(); + predictableAccountSymbols.setId(resultNew.getId()); + + ACCOUNT_SYMBOL_MATCHER.assertMatch(resultNew, predictableAccountSymbols); + accountSymbolsImdg.delete(resultNew); // cleanup test + + newRequestCnt++; + } + + @Test + void tradingClearingRegistryDelete() { + AccountSymbols existAccountSymbols = new AccountSymbols(); + existAccountSymbols.setAccountId(accountId); + existAccountSymbols.setAccountSymbolValue("SYMBOL 2"); + Long accountSymbolId = accountSymbolsImdg.insert(existAccountSymbols); + + CommonDeleteRequest accountSymbolsDeleteRequest = new CommonDeleteRequest(); + accountSymbolsDeleteRequest.setId(accountSymbolId); + + String jsonString = getJsonStringForDelete(accountSymbolsDeleteRequest, 0); + + //ACT + addRecordToKafka((MockConsumer) accountSymbolsService.getConsumer(), + Consts.DESTINATION_DEPO_ACCOUNT_SYMBOLS_DELETE, + PARTITION, + 0, + jsonString); + + //ASSERT + waitingSendAndCheckRecord(0L, mockProducer); + + AccountSymbols resultUpdating = accountSymbolsImdg.getSingleObjectByID(accountSymbolId); + Assertions.assertNull(resultUpdating, "Должен был удалиться"); + } + +} \ No newline at end of file diff --git a/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/controller/queue/account/AccountSymbolsController.java b/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/controller/queue/account/AccountSymbolsController.java index d88cdc09e..f4b087774 100644 --- a/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/controller/queue/account/AccountSymbolsController.java +++ b/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/controller/queue/account/AccountSymbolsController.java @@ -65,7 +65,7 @@ public class AccountSymbolsController extends AbstractQueueController { @PathVariable("id") Long id) throws ExecutionException, InterruptedException { CommonDeleteAction deleteAction = new CommonDeleteAction(); deleteAction.setId(id); - return processRequest(Consts.DESTINATION_DEPO_ACCOUNT_SYMBOLS_BLOCK, deleteAction); + return processRequest(Consts.DESTINATION_DEPO_ACCOUNT_SYMBOLS_DELETE, deleteAction); } } diff --git a/clearing-parent/imdg/src/main/java/ru/spcex/clearing/imdg/services/UpdateMapService.java b/clearing-parent/imdg/src/main/java/ru/spcex/clearing/imdg/services/UpdateMapService.java index b1c663d90..4b1feb44a 100644 --- a/clearing-parent/imdg/src/main/java/ru/spcex/clearing/imdg/services/UpdateMapService.java +++ b/clearing-parent/imdg/src/main/java/ru/spcex/clearing/imdg/services/UpdateMapService.java @@ -73,6 +73,7 @@ public class UpdateMapService extends AbstractUpdateMapService { hazelcastServerInstance.getMap(IMDGDistributedNames.Map_Registry).addLocalEntryListener(this, Predicates.alwaysTrue(), true); hazelcastServerInstance.getMap(IMDGDistributedNames.Map_Session).addLocalEntryListener(this, Predicates.alwaysTrue(), true); hazelcastServerInstance.getMap(IMDGDistributedNames.Map_TradingClearingRegistry).addLocalEntryListener(this, Predicates.alwaysTrue(), true); + hazelcastServerInstance.getMap(IMDGDistributedNames.Map_AccountSymbols).addLocalEntryListener(this, Predicates.alwaysTrue(), true); } @Override @@ -214,6 +215,11 @@ public class UpdateMapService extends AbstractUpdateMapService { createBusinessEvent(tradingClearingRegistryHistory, eventType); tradingClearingRegistryHistory.setObject((TradingClearingRegistry) value); hazelcastServerInstance.getMap(IMDGDistributedNames.Map_TradingClearingRegistryHistory).put(tradingClearingRegistryHistory.getId(), tradingClearingRegistryHistory); + } else if (value instanceof AccountSymbols) { + AccountSymbolsHistory accountSymbolsHistory = new AccountSymbolsHistory(); + createBusinessEvent(accountSymbolsHistory, eventType); + accountSymbolsHistory.setObject((AccountSymbols) value); + hazelcastServerInstance.getMap(IMDGDistributedNames.Map_TradingClearingRegistryHistory).put(accountSymbolsHistory.getId(), accountSymbolsHistory); } else if (value instanceof Security) { // Существуют наследники этой таблицы, по этому в последнюю очередь делать эту проверку. diff --git a/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/domain/Consts.java b/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/domain/Consts.java index 4f00377fe..c55d21d98 100644 --- a/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/domain/Consts.java +++ b/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/domain/Consts.java @@ -156,7 +156,7 @@ public interface Consts { String LAUNCHER_NEW = "launcher-new"; String DESTINATION_DEPO_ACCOUNT_SYMBOLS_NEW = "depo-accounts-symbols-new"; - String DESTINATION_DEPO_ACCOUNT_SYMBOLS_BLOCK = "depo-accounts-symbols-block"; + String DESTINATION_DEPO_ACCOUNT_SYMBOLS_DELETE = "depo-accounts-symbols-delete"; String REQUEST_INFO_UPDATE = "request-info-update";