diff --git a/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/config/BeanConfiguration.java b/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/config/BeanConfiguration.java index c77296e64..cec73efc2 100644 --- a/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/config/BeanConfiguration.java +++ b/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/config/BeanConfiguration.java @@ -2,6 +2,8 @@ package ru.spcex.clearing.company.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import ru.spcex.clearing.util.security.UserRoleVerification; +import ru.spcex.platform.imdg.api.ImdgProvider; import ru.spcex.platform.utils.enumeration.IMessageResolver; import ru.spcex.platform.utils.enumeration.SimpleMessageResolver; @@ -11,4 +13,9 @@ public class BeanConfiguration { public IMessageResolver messageResolver() { return new SimpleMessageResolver(); } + + @Bean + UserRoleVerification userRoleVerification(ImdgProvider imdg) { + return new UserRoleVerification(imdg); + } } diff --git a/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/config/validation/CompanyValidationConfig.java b/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/config/validation/CompanyValidationConfig.java index 5c80d1569..2d3a63605 100644 --- a/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/config/validation/CompanyValidationConfig.java +++ b/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/config/validation/CompanyValidationConfig.java @@ -2,9 +2,19 @@ package ru.spcex.clearing.company.config.validation; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import ru.clearing.classes.statics.data.company.Company; +import ru.clearing.platform.dictionary.CompanySymbolDictionary; +import ru.clearing.platform.dictionary.WorkflowStatusDictionary; +import ru.spcex.clearing.company.error.CompanyErrors; import ru.spcex.clearing.imdg.IMDGDistributedNames; +import ru.spcex.clearing.platform.messaging.domain.cud.common.CommonDeleteRequest; +import ru.spcex.clearing.platform.messaging.domain.cud.company.ClearingMemberCategoryNewRequest; import ru.spcex.clearing.platform.messaging.domain.cud.company.CompanyNewRequest; +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.WorkflowStatus; import ru.spcex.platform.imdg.api.Imdg; import ru.spcex.platform.imdg.validation.ImdgValidationContext; import ru.spcex.platform.utils.validation.IValidator; @@ -23,24 +33,84 @@ public class CompanyValidationConfig { ImdgValidationContext context = new ImdgValidationContext<>(); context.setValidatedObject(companyNewRequest); Consumer addImdg = (s) -> context.addImdg(s, imdgForValidation.get(s)); -// addImdg.accept(IMDGDistributedNames.Map_TaskDictionary); -// addImdg.accept(IMDGDistributedNames.Map_TaskStatusDictionary); + addImdg.accept(IMDGDistributedNames.Map_CompanySymbolDictionary); + addImdg.accept(IMDGDistributedNames.Map_WorkflowStatusDictionary); addImdg.accept(IMDGDistributedNames.Map_Company); -// addImdg.accept(IMDGDistributedNames.Map_Security); return new ValidatorImpl<>(context - // , -// FieldRequiredRule.instance("shortName", CompanyNewRequest::getShortName, CompanyErrors.RequiredFieldEmpty), -// FieldRequiredRule.instance("companySymbol", CompanyNewRequest::getCompanySymbol, CompanyErrors.RequiredFieldEmpty), -// FieldRequiredRule.instance("companySymbolValue", CompanyNewRequest::getCompanySymbolValue, CompanyErrors.RequiredFieldEmpty), -// FieldRequiredRule.instance("workflowStatus", CompanyNewRequest::getWorkflowStatus, CompanyErrors.RequiredFieldEmpty), + , + FieldRequiredRule.instance("shortName", CompanyNewRequest::getShortName, CompanyErrors.RequiredFieldEmpty), + FieldRequiredRule.instance("companySymbol", CompanyNewRequest::getCompanySymbol, CompanyErrors.RequiredFieldEmpty), + FieldRequiredRule.instance("companySymbolValue", CompanyNewRequest::getCompanySymbolValue, CompanyErrors.RequiredFieldEmpty), +// FieldRequiredRule.instance("workflowStatus", CompanyNewRequest::getWorkflowStatus, CompanyErrors.RequiredFieldEmpty), -// DictionaryPresentRule.instance("workflowStatus", -// CompanyNewRequest::getWorkflowStatus, -// IMDGDistributedNames.Map_WorkflowStatus, -// WorkflowStatusDictionary.class), + DictionaryPresentRule.instance("workflowStatus", + CompanyNewRequest::getWorkflowStatus, + IMDGDistributedNames.Map_WorkflowStatusDictionary, + WorkflowStatusDictionary.class, + CompanyErrors.RequiredFieldEmpty, + CompanyErrors.WrongFieldValue), + DictionaryPresentRule.instance("companySymbol", + CompanyNewRequest::getCompanySymbol, + IMDGDistributedNames.Map_CompanySymbolDictionary, + CompanySymbolDictionary.class, + CompanyErrors.RequiredFieldEmpty, + CompanyErrors.WrongFieldValue) + ); + }; + } + + @Bean("companyUpdateRequestValidator")//todo type of request + public Function companyUpdateRequestValidator(Map> imdgForValidation) { + return companyNewRequest -> { + ImdgValidationContext context = new ImdgValidationContext<>(); + context.setValidatedObject(companyNewRequest); + Consumer addImdg = (s) -> context.addImdg(s, imdgForValidation.get(s)); + addImdg.accept(IMDGDistributedNames.Map_CompanySymbolDictionary); + addImdg.accept(IMDGDistributedNames.Map_WorkflowStatusDictionary); + addImdg.accept(IMDGDistributedNames.Map_Company); + return new ValidatorImpl<>(context, + FieldRequiredRule.instance("id", CompanyNewRequest::getId, CompanyErrors.RequiredFieldEmpty), + + FieldRequiredRule.instance("shortName", CompanyNewRequest::getShortName, CompanyErrors.RequiredFieldEmpty), +// FieldRequiredRule.instance("companySymbol", CompanyNewRequest::getCompanySymbol, CompanyErrors.RequiredFieldEmpty), +// FieldRequiredRule.instance("companySymbolValue", CompanyNewRequest::getCompanySymbolValue, CompanyErrors.RequiredFieldEmpty), + FieldRequiredRule.instance("workflowStatus", CompanyNewRequest::getWorkflowStatus, CompanyErrors.RequiredFieldEmpty), + + DictionaryPresentRule.instance("workflowStatus", + CompanyNewRequest::getWorkflowStatus, + IMDGDistributedNames.Map_WorkflowStatusDictionary, + WorkflowStatusDictionary.class, + CompanyErrors.RequiredFieldEmpty, + CompanyErrors.WrongFieldValue) +// DictionaryPresentRule.instance("companySymbol", +// CompanyNewRequest::getCompanySymbol, +// IMDGDistributedNames.Map_CompanySymbolDictionary, +// CompanySymbolDictionary.class, +// CompanyErrors.RequiredFieldEmpty, +// CompanyErrors.WrongFieldValue) ); }; } + @Bean("CompanyDeleteRequestValidator") + public Function companyDeleteRequestValidator(Map> imdgForValidation) { + return companyDeleteRequest -> { + ImdgValidationContext context = new ImdgValidationContext<>(); + context.setValidatedObject(companyDeleteRequest); + Consumer addImdg = (s) -> context.addImdg(s, imdgForValidation.get(s)); + addImdg.accept(IMDGDistributedNames.Map_Company); + return new ValidatorImpl<>(context, +// FieldRequiredRule.instance("id", CommonDeleteRequest::getId, CompanyErrors.RequiredFieldEmpty), + IdPresentRule.instance("id", + CommonDeleteRequest::getId, + IMDGDistributedNames.Map_Company, + Company.class, + CompanyErrors.RequiredFieldEmpty, + CompanyErrors.CompanyNotFound, + company -> WorkflowStatus.Active.getKey().equals(company.getWorkflowStatus()) ? null : CompanyErrors.CompanyDisabled) + ); + }; + } + } diff --git a/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/config/validation/ValidationConfig.java b/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/config/validation/ValidationConfig.java index 9ce392c5d..20ce51e01 100644 --- a/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/config/validation/ValidationConfig.java +++ b/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/config/validation/ValidationConfig.java @@ -32,6 +32,10 @@ public class ValidationConfig { addImdg.accept(IMDGDistributedNames.Map_ProfileDocument, ProfileDocument.class); addImdg.accept(IMDGDistributedNames.Map_DocumentTypeDictionary, DocumentTypeDictionary.class); + addImdg.accept(IMDGDistributedNames.Map_WorkflowStatusDictionary, DocumentTypeDictionary.class); + addImdg.accept(IMDGDistributedNames.Map_CompanySymbolDictionary, DocumentTypeDictionary.class); + + return imdg; } diff --git a/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/service/CompanyService.java b/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/service/CompanyService.java index 9a38c614d..69af48acd 100644 --- a/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/service/CompanyService.java +++ b/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/service/CompanyService.java @@ -9,6 +9,9 @@ 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.company.Company; +import ru.clearing.classes.statics.data.company.CompanySymbols; +import ru.clearing.classes.statics.data.company.relation.Relation; +import ru.spcex.clearing.company.error.CompanyErrors; import ru.spcex.clearing.company.util.RequestHelper; import ru.spcex.clearing.imdg.IMDGDistributedNames; import ru.spcex.clearing.platform.messaging.domain.BaseRequest; @@ -16,116 +19,101 @@ import ru.spcex.clearing.platform.messaging.domain.Consts; import ru.spcex.clearing.platform.messaging.domain.cud.common.CommonDeleteRequest; import ru.spcex.clearing.platform.messaging.domain.cud.company.CompanyNewRequest; import ru.spcex.clearing.platform.messaging.service.QueueConsumer; -import ru.spcex.platform.enumeration.WorkflowStatus; +import ru.spcex.clearing.platform.messaging.service.RequestInfoUpdate; +import ru.spcex.clearing.util.security.UserRoleVerification; +import ru.spcex.clearing.validation.common.ValidationHelper; +import ru.spcex.platform.enumeration.*; import ru.spcex.platform.imdg.api.Imdg; import ru.spcex.platform.imdg.api.ImdgId; import ru.spcex.platform.imdg.api.ImdgProvider; -import ru.spcex.platform.utils.enumeration.EnumMessage; import ru.spcex.platform.utils.enumeration.IMessageResolver; import ru.spcex.platform.utils.error.ValidationException; import ru.spcex.platform.utils.validation.IValidator; import java.time.Instant; -import java.util.Optional; +import java.util.Collection; +import java.util.Map; +import java.util.Objects; import java.util.function.Function; @Service public class CompanyService extends QueueConsumer implements InitializingBean { private final Logger log = LoggerFactory.getLogger(getClass()); + private final RequestHelper requestHelper; private final Imdg companyMap; + private final Imdg companySymnbolsMap; + private final Imdg relationMap; private final ImdgId idSequence; -// protected UserRoleVerification userRoleVerification; - protected IMessageResolver messageResolver; + protected UserRoleVerification userRoleVerification; + protected IMessageResolver messageResolver; + private ValidationHelper validationHelper; private Function companyNewRequestValidator; + private Function companyUpdateRequestValidator; + private Function companyDeleteRequestValidator; + + // Синхронизация обновлений + public final Object lockCompany = new Object(); // Company, CompanySymbol, @Autowired public CompanyService(Consumer kafkaQueue, Producer kafkaProducer, ImdgProvider imdgProvider, - IMessageResolver messageResolver, -// , UserRoleVerification userRoleVerification + IMessageResolver messageResolver, + UserRoleVerification userRoleVerification, + ValidationHelper validationHelper, @Qualifier("companyNewRequestValidator") - Function companyNewRequestValidator + Function companyNewRequestValidator, + @Qualifier("companyUpdateRequestValidator") + Function companyUpdateRequestValidator, + @Qualifier("CompanyDeleteRequestValidator") + Function companyDeleteRequestValidator ) { super(kafkaQueue, kafkaProducer); this.messageResolver = messageResolver; + this.requestHelper = new RequestHelper(log, messageResolver); this.companyMap = imdgProvider.getImdg(IMDGDistributedNames.Map_Company, Company.class); + this.relationMap = imdgProvider.getImdg(IMDGDistributedNames.Map_Relation, Relation.class); + this.companySymnbolsMap = imdgProvider.getImdg(IMDGDistributedNames.Map_CompanySymbols, CompanySymbols.class); this.idSequence = imdgProvider.getImdgIdGenerator(); -// this.userRoleVerification = userRoleVerification; + this.userRoleVerification = userRoleVerification; + this.validationHelper = validationHelper; this.companyNewRequestValidator = companyNewRequestValidator; + this.companyUpdateRequestValidator = companyUpdateRequestValidator; + this.companyDeleteRequestValidator = companyDeleteRequestValidator; } @Override public void afterPropertiesSet() { - RequestHelper requestHelper = new RequestHelper(log, messageResolver); callback(CommonDeleteRequest.class) - .setConsumer(this::deleteCompany) + .setConsumer(request -> requestHelper.requestFunction(this::deleteCompany, request)) .forDestination(Consts.DESTINATION_COMPANY_DELETE, callbacks::put); callback(CompanyNewRequest.class) - .setFunction( request -> requestHelper.requestFunction(this::createCompany, request)) + .setFunction(request -> requestHelper.requestFunction(this::createCompany, request)) .forDestination(Consts.DESTINATION_COMPANY_NEW, callbacks::put); callback(CompanyNewRequest.class) - .setConsumer(this::updateCompany) + .setConsumer(request -> requestHelper.requestFunction(this::updateCompany, request)) .forDestination(Consts.DESTINATION_COMPANY_UPDATE, callbacks::put); - //todo IV - сообщения на добавления документа расторжения договора - см. Документ расторжения договора. profileDocument): необходимо обновить запись в company согласно описанию с тэгом "При обработке расторжения". -// callback(CompanyNewRequest.class) -// .setConsumer(this::updateCompany) -// .forDestination(Consts.DESTINATION_COMPANY_UPDATE, callbacks::put); init(); } -// -// interface RequestCommand { -// Long command(T arg) throws ClearingBaseException; -// } -// -// protected > Object requestFunction(RequestCommand command, T userRequest) { -// try { -// Long itemAffected = command.command(userRequest); -// log.trace("Item affected: {}", itemAffected); -// return null; -// } catch (ClearingBaseException expectedE) { -// log.trace("{} return expected exception: {}", command, expectedE.toString()); -// return new RequestInfoUpdate() -// .setId(userRequest.getId()) -// .setStatus(Status.Error) -// .setMessage(expectedE.getMessage()); -// } catch (Exception unexpectedE) { -// log.error("Error at {}: {} ", command, ExceptionUtils.getStackTrace(unexpectedE)); -// String message = messageResolver.resolve(new EnumMessage(CompanyErrors.GeneralError)); -// return new RequestInfoUpdate() -// .setId(userRequest.getId()) -// .setStatus(Status.Error) -// .setMessage(message); -// } -// } - protected void validateRole(BaseRequest req) throws ValidationException { - Long requestor = null; // todo требуется передавать это значение из frontendapi! req.getUserId(); -// if (!userRoleVerification.userHasRole(requestor, UserRole.Admin)) { -// -// }; - } - - void validate(CompanyNewRequest object) throws ValidationException { - IValidator validator = companyNewRequestValidator.apply(object); - Optional error = validator.tillFirstError(); - if (error.isPresent()) { - log.warn("CompanyNewRequest validation error: {}", error.get()); - throw new ValidationException(error.get()); -// String errorMsg = messageResolver.resolve(error.get()); -// return new RequestInfoUpdate() // todo reply message -// .setId(userRequest.getId()) -// .setStatus(Status.Error) -// .setMessage(errorMsg); + protected void validateRole(BaseRequest req) throws ValidationException { + Long requestor = req.getUserId(); + if (requestor != null && !userRoleVerification.userHasRole(requestor, UserRole.Admin)) { + log.trace("User {} has no role to allow this action", requestor); + throw new ValidationException(CompanyErrors.UserVerifyDenial); + } else { + log.trace("Request without userId"); } } - private Long createCompany(BaseRequest companyNewRequestBaseRequest) throws ValidationException { + + private synchronized RequestInfoUpdate createCompany(BaseRequest companyNewRequestBaseRequest) throws ValidationException { CompanyNewRequest req = companyNewRequestBaseRequest.getRequestPayload(); log.debug("company-new request received, BaseRequest.id = {}", companyNewRequestBaseRequest.getId()); { // Валидация, ValidationException validateRole(companyNewRequestBaseRequest); - validate(req); + RequestInfoUpdate requestInfoUpdate = validationHelper.validateTillFirstError(companyNewRequestBaseRequest, companyNewRequestValidator); + if (requestInfoUpdate != null) return requestInfoUpdate; } Company company = new Company(); company.setId(idSequence.nextId()); @@ -135,46 +123,223 @@ public class CompanyService extends QueueConsumer implements InitializingBean { company.setShortName(req.getShortName()); company.setFullName(req.getFullName()); - company.setTradingCode(req.getTradingCode()); - company.setClearingCode(req.getClearingCode()); - company.setRegistrationCode(req.getRegistrationCode()); + + if (req.getCompanySymbol() != null) { + CompanySymbols newSymbol = createCompanySymbol(company, req); + updateCompanyBySymbol(company, newSymbol); + log.debug("Created new companySymbols.id={} for company {}", newSymbol.getId(), company.getId()); + companySymnbolsMap.insert(newSymbol); + } company.setWorkflowStatus(WorkflowStatus.Active.getKey()); -// if (WorkflowStatus.Active.equalsByKey(company.getWorkflowStatus())) { -// -// } else if (WorkflowStatus.Blocked.equalsByKey(company.getWorkflowStatus())) { -// -// } companyMap.insert(company); log.debug("company-new request processed, BaseRequest.id = {}, company.id={}", companyNewRequestBaseRequest.getId(), company.getId()); + createNewRelation(company); - return company.getId(); + return null; + } + + protected CompanySymbols createCompanySymbol(Company forCompany, CompanyNewRequest request) { + //todo see CompanySymbolService + CompanySymbols newSymbol = new CompanySymbols(); + newSymbol.setId(idSequence.nextId()); + newSymbol.setCompanyId(forCompany.getId()); + newSymbol.setCompanySymbol(request.getCompanySymbol()); + newSymbol.setCompanySymbolValue(request.getCompanySymbolValue()); + return newSymbol; } - private void updateCompany(BaseRequest companyNewRequestBaseRequest) { - //todo update -// CompanyUpdateRequest req = companyUpdateRequestBaseRequest.getRequestPayload(); -// log.debug("company-update request received, BaseRequest.id = {}", companyNewRequestBaseRequest.getId()); -// Company company = new Company(); -// company.setShortName(req.getShortName()); -// company.setFullName(req.getFullName()); -// company.setTradingCode(req.getTradingCode()); -// company.setClearingCode(req.getClearingCode()); -// company.setRegistrationCode(req.getRegistrationCode()); -// companyMap.insert(company); -// log.debug("company-update request processed, BaseRequest.id = {}, company.id={}", -// companyNewRequestBaseRequest.getId(), company.getId()); + private synchronized RequestInfoUpdate updateCompany(BaseRequest companyUpdateRequestBaseRequest) throws ValidationException { + CompanyNewRequest updateRequest = companyUpdateRequestBaseRequest.getRequestPayload(); + { // Валидация, ValidationException + validateRole(companyUpdateRequestBaseRequest); + RequestInfoUpdate requestInfoUpdate = validationHelper.validateTillFirstError(companyUpdateRequestBaseRequest, companyUpdateRequestValidator); + if (requestInfoUpdate != null) return requestInfoUpdate; + } + Company company = companyMap.getSingleObjectByID(updateRequest.getId()); // todo use validationContext. + if (company == null) { + log.trace("Company {} not found", updateRequest.getId()); + return requestHelper.makeErrorResponse(companyUpdateRequestBaseRequest, CompanyErrors.CompanyNotFound, updateRequest.getId()); + } + if (!WorkflowStatus.Active.equalsByKey(company.getWorkflowStatus())) { + log.trace("Company {} not active: {}", company.getId(), company.getWorkflowStatus()); + return requestHelper.makeErrorResponse(companyUpdateRequestBaseRequest, CompanyErrors.CompanyDisabled, updateRequest.getId()); + } + + company.setUpdated(Instant.now()); + company.setShortName(updateRequest.getShortName()); + company.setFullName(updateRequest.getFullName()); + + if (updateRequest.getCompanySymbol() != null || updateRequest.getCompanySymbolValue() != null) { + log.trace("Request field CompanySymbol, CompanySymbolValue ignore for update company request."); + } + CompanySymbols companySymbol = companySymnbolsMap.getSingleObjectByFieldValues(Map.of("companyId", company.getId())); + if (companySymbol == null) { + log.warn("CompanySymbols not found for companyId={}", company.getId()); + } else { + log.trace("Found CompanySymbols.id={} for companyId={}", companySymbol.getId(), company.getId()); + updateCompanyBySymbol(company, companySymbol); + } + + +// String prevStatus = company.getWorkflowStatus(); +// if (updateRequest.getWorkflowStatus() != null) { +// company.setWorkflowStatus(updateRequest.getWorkflowStatus()); +// if (!Objects.equals(prevStatus, company.getWorkflowStatus())) { +// onChangeWorkflowStatus(company, prevStatus, company.getWorkflowStatus()); +// } else { +// log.trace("Status was not changed"); +// } +// } else { +// log.trace("Null new WorkflowStatus"); +// } + return null; + } + + protected void updateCompanyBySymbol(Company company, CompanySymbols companySymbol) { + assert company.getId().equals(companySymbol.getCompanyId()); + if (CompanySymbol.TRDC.equalsByKey(companySymbol.getCompanySymbol())) { + company.setTradingCode(companySymbol.getCompanySymbolValue()); + } else { + company.setTradingCode(null); + } + if (CompanySymbol.CLRC.equalsByKey(companySymbol.getCompanySymbol())) { + company.setClearingCode(companySymbol.getCompanySymbolValue()); + } else { + company.setClearingCode(null); + } + if (CompanySymbol.RGRC.equalsByKey(companySymbol.getCompanySymbol())) { + company.setRegistrationCode(companySymbol.getCompanySymbolValue()); + } else { + company.setRegistrationCode(null); + } } + protected static final Long SPVB_ID = 1L; // СПВБ - private void deleteCompany(BaseRequest userRequest) { - CommonDeleteRequest req = userRequest.getRequestPayload(); - log.debug("CommonDeleteRequest received id = {}", req.getId()); - Company company = companyMap.getSingleObjectByID(req.getId()); - companyMap.delete(company); + private void createNewRelation(Company company) { + Relation relation = new Relation(); + relation.setId(idSequence.nextId()); + relation.setCreated(Instant.now()); + relation.setUpdated(relation.getCreated()); + + relation.setConsumerId(company.getId()); + relation.setSupplierId(SPVB_ID); // 1 СПВБ + relation.setServiceStatus(WorkflowStatus.Active.getKey()); + relation.setService(ru.spcex.platform.enumeration.Service.MKR.getKey()); // MKR + relation.setServiceProduct(ServiceProduct.ZERO.getKey()); + log.debug("New Relation[{}] created.", relation.getId()); + relationMap.insert(relation); // todo use transaction! + } + + /** + * @param company + * @param oldStatus + * @param newStatus WorkflowStatus.Active - при возобналвении; WorkflowStatus.Blocked - при блокировки/расторжении + */ + private void onChangeWorkflowStatus(Company company, String oldStatus, String newStatus) { + //todo в ТЗ упоминается: ри изменении (relation/put): //из соответствующего параметра полученного метода, если значение отличается от сохраненного ране + log.debug("Company id={} status changed from {} to {}", + company.getId(), oldStatus, company.getWorkflowStatus()); + assert Objects.equals(newStatus, company.getWorkflowStatus()); + String query = String.format("consumerId=%s", company.getId()); + Collection relations = relationMap.getCollectionObjectsBySQL(query); + log.trace("Selected {} Relation by query: {}", relations.size(), query); + Instant now = Instant.now(); + for (Relation relation : relations) { + boolean modified = false; + if (WorkflowStatus.Active.equalsByKey(company.getWorkflowStatus())) { + if (!ServiceStatus.Reopened.equalsByKey(relation.getServiceStatus())) { + relation.setServiceStatus(ServiceStatus.Reopened.getKey()); + modified = true; + } + } else if (WorkflowStatus.Blocked.equalsByKey(company.getWorkflowStatus())) { + if (!ServiceStatus.Closed.equalsByKey(relation.getServiceStatus())) { + relation.setServiceStatus(ServiceStatus.Closed.getKey()); + modified = true; + } + } + if (modified) { + relation.setUpdated(now); + log.debug("Relation id={} updated", relation.getId()); + relationMap.update(relation); + } + } + + } + + + /** + * Блокировка компании + * + * @param companyBlockRequestBaseRequest + * @return + */ + private synchronized RequestInfoUpdate deleteCompany(BaseRequest companyBlockRequestBaseRequest) throws ValidationException { + CommonDeleteRequest request = companyBlockRequestBaseRequest.getRequestPayload(); + log.debug("CommonDeleteRequest (block request) received id = {}", request.getId()); + { // Валидация, ValidationException + validateRole(companyBlockRequestBaseRequest); + RequestInfoUpdate requestInfoUpdate = validationHelper.validateTillFirstError(companyBlockRequestBaseRequest, companyDeleteRequestValidator); + if (requestInfoUpdate != null) return requestInfoUpdate; + } + Company company = companyMap.getSingleObjectByID(request.getId()); + if (company == null) { + log.warn("Company {} not found", request.getId()); + return requestHelper.makeErrorResponse(companyBlockRequestBaseRequest, CompanyErrors.CompanyNotFound, request.getId()); + } +// if (!WorkflowStatus.Active.equalsByKey(company.getWorkflowStatus())) { +// log.trace("Company {} not active: {}", company.getId(), company.getWorkflowStatus()); +// return requestHelper.makeErrorResponse(companyBlockRequestBaseRequest, CompanyErrors.CompanyDisabled, request.getId()); +// } + + company.setUpdated(Instant.now()); + + CompanySymbols companySymbol = companySymnbolsMap.getSingleObjectByFieldValues(Map.of("companyId", company.getId())); + if (companySymbol == null) { + log.warn("CompanySymbols not found for companyId={}", company.getId()); + } else { + log.trace("Found CompanySymbols.id={} for companyId={}", companySymbol.getId(), company.getId()); + updateCompanyBySymbol(company, companySymbol); + } + + String prevStatus = company.getWorkflowStatus(); + company.setWorkflowStatus(WorkflowStatus.Blocked.getKey()); + onChangeWorkflowStatus(company, prevStatus, company.getWorkflowStatus()); + log.debug("Update company.id={}", company.getId()); + companyMap.update(company); + + // todo kafka send !!! Account. + return null; + +// Company company = companyMap.getSingleObjectByID(req.getId()); +// companyMap.delete(company); + } + + + /** + * IV - сообщения на добавления документа расторжения договора + * + * @param companyId + */ + public synchronized void onRastorjeniye(Long companyId) { + Company company = companyMap.getSingleObjectByID(companyId); + if (company == null) { + log.warn("Company {} not found", companyId); + return; + } + + if (!WorkflowStatus.Blocked.equalsByKey(company.getWorkflowStatus())) { + log.debug("Update company.id={}", company.getId()); + company.setWorkflowStatus(WorkflowStatus.Blocked.getKey()); + company.setUpdated(Instant.now()); + companyMap.update(company); + } else { + log.trace("Company {} already has blocked.", companyId); + } } } diff --git a/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/util/RequestHelper.java b/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/util/RequestHelper.java index 5cbb5d6da..1b810e700 100644 --- a/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/util/RequestHelper.java +++ b/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/util/RequestHelper.java @@ -6,6 +6,7 @@ import ru.spcex.clearing.platform.messaging.domain.BaseRequest; import ru.spcex.clearing.platform.messaging.service.RequestInfoUpdate; import ru.spcex.clearing.platform.messaging.service.Status; 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.error.ClearingBaseException; import ru.spcex.platform.utils.log.ExceptionUtils; @@ -42,9 +43,10 @@ public class RequestHelper { */ public > RequestInfoUpdate requestFunction(RequestCommand command, T userRequest) { try { - Long itemAffected = command.command(userRequest); - log.trace("Item affected: {}", itemAffected); - return null; + log.debug("{} request received, BaseRequest.id = {}", userRequest.getClass().getSimpleName(), userRequest.getId()); + RequestInfoUpdate reply = command.command(userRequest); + log.trace("Reply on request {}: {}" , userRequest.getId(), reply); + return reply; } catch (ClearingBaseException expectedE) { log.trace("{} return expected exception: {}", command, expectedE.toString()); String message = expectedE.getEnumMsg() == null @@ -64,7 +66,21 @@ public class RequestHelper { } } + public RequestInfoUpdate makeErrorResponse(BaseRequest request, EnumMessage msg) { + String errorMsg = msg == null? "" : messageResolver.resolve(msg); + return new RequestInfoUpdate() + .setId(request.getId()) + .setStatus(Status.Error) + .setMessage(errorMsg); + } + + + public RequestInfoUpdate makeErrorResponse(BaseRequest request, IEnumId error, Object ... args) { + //todo должны уточнить интерфейс IEnumId в CompanyErrors, вечером проверить код. + return makeErrorResponse(request, new EnumMessage(error, args)); + } + public interface RequestCommand { - Long command(T arg) throws ClearingBaseException; + RequestInfoUpdate command(T arg) throws ClearingBaseException; } } diff --git a/clearing-parent/company-service/src/test/java/ru/spcex/clearing/company/config/BeanConfiguration.java b/clearing-parent/company-service/src/test/java/ru/spcex/clearing/company/config/BeanConfiguration.java new file mode 100644 index 000000000..cec73efc2 --- /dev/null +++ b/clearing-parent/company-service/src/test/java/ru/spcex/clearing/company/config/BeanConfiguration.java @@ -0,0 +1,21 @@ +package ru.spcex.clearing.company.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import ru.spcex.clearing.util.security.UserRoleVerification; +import ru.spcex.platform.imdg.api.ImdgProvider; +import ru.spcex.platform.utils.enumeration.IMessageResolver; +import ru.spcex.platform.utils.enumeration.SimpleMessageResolver; + +@Configuration +public class BeanConfiguration { + @Bean + public IMessageResolver messageResolver() { + return new SimpleMessageResolver(); + } + + @Bean + UserRoleVerification userRoleVerification(ImdgProvider imdg) { + return new UserRoleVerification(imdg); + } +} diff --git a/clearing-parent/company-service/src/test/java/ru/spcex/clearing/company/service/CompanyServiceTest.java b/clearing-parent/company-service/src/test/java/ru/spcex/clearing/company/service/CompanyServiceTest.java index b8cd3e7e2..84ce45c8a 100644 --- a/clearing-parent/company-service/src/test/java/ru/spcex/clearing/company/service/CompanyServiceTest.java +++ b/clearing-parent/company-service/src/test/java/ru/spcex/clearing/company/service/CompanyServiceTest.java @@ -14,33 +14,47 @@ import org.springframework.boot.test.mock.mockito.SpyBean; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; import ru.clearing.classes.statics.data.company.Company; +import ru.clearing.classes.statics.data.company.CompanySymbols; import ru.clearing.classes.statics.data.generated.ClearingMemberCategory; +import ru.clearing.platform.dictionary.CompanySymbolDictionary; +import ru.clearing.platform.dictionary.WorkflowStatusDictionary; +import ru.spcex.clearing.company.config.BeanConfiguration; import ru.spcex.clearing.company.config.HazelcastServiceTestConfiguration; import ru.spcex.clearing.company.config.KafkaConfigTest; +import ru.spcex.clearing.company.config.validation.CompanyValidationConfig; +import ru.spcex.clearing.company.config.validation.ValidationConfig; import ru.spcex.clearing.company.utils.MatcherFactory; 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.common.CommonDeleteRequest; +import ru.spcex.clearing.platform.messaging.domain.cud.company.CompanyNewRequest; +import ru.spcex.platform.enumeration.CompanySymbol; +import ru.spcex.platform.enumeration.WorkflowStatus; import ru.spcex.platform.imdg.api.Imdg; import ru.spcex.platform.imdg.iml.hazelcast.service.HazelcastService; import javax.annotation.PostConstruct; +import java.util.Map; + import static ru.spcex.clearing.company.utils.MatcherFactory.usingIgnoringFieldsComparator; import static ru.spcex.clearing.company.utils.TestUtils.*; @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = { CompanyService.class, + CompanyValidationConfig.class, + ValidationConfig.class, + KafkaConfigTest.class, - HazelcastServiceTestConfiguration.class}) + HazelcastServiceTestConfiguration.class, + BeanConfiguration.class}) class CompanyServiceTest { public static final MatcherFactory.Matcher COMPANY_MATCHER = usingIgnoringFieldsComparator(); private static final int PARTITION = 0; private static final String TOPIC_COMPANY_DELETE = Consts.DESTINATION_COMPANY_DELETE; - private static final Long ID = 0L; @Autowired CompanyService companyService; @@ -48,6 +62,7 @@ class CompanyServiceTest { @Qualifier("hazelcastServiceTest") private HazelcastService hazelcastServiceTest; private Imdg companyImdg; + private Imdg workflowStatusDictionaryImdg; @Captor private ArgumentCaptor producerRecord; @@ -58,6 +73,28 @@ class CompanyServiceTest { private void init() { hazelcastServiceTest.waitAvailable(); companyImdg = hazelcastServiceTest.getImdg(IMDGDistributedNames.Map_Company, Company.class); + + workflowStatusDictionaryImdg = hazelcastServiceTest.getImdg(IMDGDistributedNames.Map_WorkflowStatusDictionary, WorkflowStatusDictionary.class); + WorkflowStatusDictionary ws1 = new WorkflowStatusDictionary(); + ws1.setId(1L); + ws1.setCode(WorkflowStatus.Active.getKey()); + ws1.setName("Active"); + workflowStatusDictionaryImdg.insert(ws1); + WorkflowStatusDictionary ws2 = new WorkflowStatusDictionary(); + ws2.setId(2L); + ws2.setCode(WorkflowStatus.Blocked.getKey()); + ws2.setName("Blocked"); + workflowStatusDictionaryImdg.insert(ws2); + + Imdg companySymbolDictionaryImdg = hazelcastServiceTest.getImdg(IMDGDistributedNames.Map_CompanySymbolDictionary, CompanySymbolDictionary.class); + { + CompanySymbolDictionary cSymbol = new CompanySymbolDictionary(); + cSymbol.setId(11L); + cSymbol.setCode(CompanySymbol.CLRC.getKey()); + cSymbol.setName(CompanySymbol.CLRC.getKey()); + cSymbol.setShortname(" CLRC key"); + companySymbolDictionaryImdg.insert(cSymbol); + } } /** @@ -69,8 +106,10 @@ class CompanyServiceTest { @Test void deleteCompany() throws InterruptedException { //ARRANGE + final Long ID = 101L; Company existsCompany = new Company(); existsCompany.setId(ID); + existsCompany.setWorkflowStatus(WorkflowStatus.Active.getKey()); companyImdg.insert(existsCompany); CommonDeleteRequest commonDeleteRequest = new CommonDeleteRequest(); @@ -84,6 +123,42 @@ class CompanyServiceTest { waitingWhenAddedRecordAndCheckIt(ID, mockProducer, producerRecord); Company resultDeleting = companyImdg.getSingleObjectByID(ID); - Assertions.assertNull(resultDeleting); + Assertions.assertEquals(WorkflowStatus.Blocked.getKey(), resultDeleting.getWorkflowStatus()); + //Assertions.assertNull(resultDeleting); + } + + + /** + * {@link CompanyService#createCompany(BaseRequest)}
+ * Тест проверяет удаление сущности {@link Company} в Hazelcast при передаче из Apache Kafka.
+ * Входной запрос {@link CommonDeleteRequest}:
+ * {@link CommonDeleteRequest#id} - Идентификатор записи
+ */ + @Test + void newCompany() throws InterruptedException { + //ARRANGE + final Long ID = 102L; + final String DESTINATION_COMPANY_NEW = Consts.DESTINATION_COMPANY_NEW; + companyImdg.size(); + + CompanyNewRequest companyNewRequest = new CompanyNewRequest(); +// companyNewRequest.setId(ID); + companyNewRequest.setCompanySymbol(CompanySymbol.CLRC.getKey()); + companyNewRequest.setCompanySymbolValue("CLR-symbol ipo"); + companyNewRequest.setShortName("ClrIPO"); + companyNewRequest.setFullName("Clearing IPO"); + companyNewRequest.setWorkflowStatus(WorkflowStatus.Active.getKey()); + + //ACT + String jsonString = getJsonStringForNew(companyNewRequest, ID); + addRecordToKafka((MockConsumer) companyService.getConsumer(), DESTINATION_COMPANY_NEW, PARTITION, 0, jsonString); + + //ASSERT + waitingWhenAddedRecordAndCheckIt(ID, mockProducer, producerRecord); + + Company resultNew = companyImdg.getSingleObjectByFieldValues(Map.of("ShortName", "ClrIPO")); + Assertions.assertNotNull(resultNew); + Assertions.assertEquals("ClrIPO", resultNew.getShortName()); + Assertions.assertEquals(WorkflowStatus.Active.getKey(), resultNew.getWorkflowStatus()); } } \ No newline at end of file