diff --git a/clearing-parent/backend-api/src/test/java/ru/spcex/clearing/backendapi/controller/queue/AbstractControllerTest.java b/clearing-parent/backend-api/src/test/java/ru/spcex/clearing/backendapi/controller/queue/AbstractControllerTest.java index 6f6e57a71..d85555b9d 100644 --- a/clearing-parent/backend-api/src/test/java/ru/spcex/clearing/backendapi/controller/queue/AbstractControllerTest.java +++ b/clearing-parent/backend-api/src/test/java/ru/spcex/clearing/backendapi/controller/queue/AbstractControllerTest.java @@ -92,7 +92,7 @@ import static ru.spcex.clearing.backendapi.controller.utils.MatcherFactory.using BankAccountController.class, //company CompanyRoleSetController.class, -//fixme на 31.03.2023 нет такого класса DeleteCompanyController.class, + CompanyController.class, EditClearingMemberCategoryController.class, EditCompanyInfoController.class, EditCompanySymbolController.class, diff --git a/clearing-parent/clearing-utils/src/main/java/ru/spcex/clearing/validation/common/EnumPresentRule.java b/clearing-parent/clearing-utils/src/main/java/ru/spcex/clearing/validation/common/EnumPresentRule.java new file mode 100644 index 000000000..ae05b8f70 --- /dev/null +++ b/clearing-parent/clearing-utils/src/main/java/ru/spcex/clearing/validation/common/EnumPresentRule.java @@ -0,0 +1,68 @@ +package ru.spcex.clearing.validation.common; + +import ru.spcex.platform.imdg.validation.ImdgValidationContext; +import ru.spcex.platform.utils.enumeration.EnumMessage; +import ru.spcex.platform.utils.enumeration.IEnumId; +import ru.spcex.platform.utils.enumeration.IEnumKey; +import ru.spcex.platform.utils.validation.IValidationRule; + +import java.util.Optional; +import java.util.function.Function; + +/** + * Проверка поля со значением из множества (enum) + * @param Класс проверяемого объекта + * @param Enum + */ +public record EnumPresentRule(String fieldName, + Function getter, + E[] enumValues, + boolean required, + IEnumId errorWrongEnumValue, + IEnumId errorEmptyRequiredValue) implements IValidationRule> { + + /** + * @param fieldName Название поля + * @param getter Метод получения проверяемого значения + * @param enumValues Значения из проверяемого множества (Enum.values()) + * @param required Флаг обязательности + * @param Класс проверяемого объекта + * @param Enum + */ + public static EnumPresentRule instance(String fieldName, + Function getter, + E[] enumValues, + boolean required, + IEnumId errorWrongEnumValue, + IEnumId errorEmptyRequiredValue) { + if (errorWrongEnumValue == null) throw new IllegalArgumentException("Empty errorWrongEnumValue"); + if (required && errorEmptyRequiredValue == null) throw new IllegalArgumentException("Empty errorEmptyRequiredValue"); + return new EnumPresentRule<>(fieldName, getter, enumValues, required, errorWrongEnumValue, errorEmptyRequiredValue); + } + + /** + * @param fieldName Название поля + * @param getter Метод получения проверяемого значения + * @param enumValues Значения из проверяемого множества (Enum.values()) + * @param Класс проверяемого объекта + * @param Enum + */ + public static EnumPresentRule instance(String fieldName, + Function getter, + E[] enumValues, + IEnumId errorWrongEnumValue, + IEnumId errorEmptyRequiredValue) { + return new EnumPresentRule<>(fieldName, getter, enumValues, true, errorWrongEnumValue, errorEmptyRequiredValue); + } + + @Override + public Optional validate(ImdgValidationContext context) { + R validatedObject = context.getValidatedObject(); + String enumCode = getter.apply(validatedObject); + if (enumCode == null) return required ? of(errorEmptyRequiredValue, fieldName) : empty(); + for (E enumValue : enumValues) { + if (enumValue.getKey().equals(enumCode)) return empty(); + } + return of(errorWrongEnumValue, fieldName); + } +} diff --git a/clearing-parent/clearing-utils/src/main/java/ru/spcex/clearing/validation/common/FieldRequiredRule.java b/clearing-parent/clearing-utils/src/main/java/ru/spcex/clearing/validation/common/FieldRequiredRule.java new file mode 100644 index 000000000..358df8caa --- /dev/null +++ b/clearing-parent/clearing-utils/src/main/java/ru/spcex/clearing/validation/common/FieldRequiredRule.java @@ -0,0 +1,37 @@ +package ru.spcex.clearing.validation.common; + + +import ru.spcex.platform.imdg.validation.ImdgValidationContext; +import ru.spcex.platform.utils.enumeration.EnumMessage; +import ru.spcex.platform.utils.enumeration.IEnumId; +import ru.spcex.platform.utils.validation.IValidationRule; + +import java.util.Optional; +import java.util.function.Function; + +/** + * Проверка обязательности поля (not null) + * @param Класс проверяемого объекта + * @param Класс проверяемого значения + */ +public record FieldRequiredRule(String fieldName, Function getter, IEnumId errorEmptyRequiredValue) implements IValidationRule> { + + /** + * @param fieldName Название поля + * @param getter Метод получения значения + * @param Класс проверяемого объекта + * @param Класс проверяемого значения + */ + public static FieldRequiredRule instance(String fieldName, Function getter, IEnumId errorEmptyRequiredValue) { + if (errorEmptyRequiredValue == null) throw new IllegalArgumentException("Empty errorEmptyRequiredValue"); + return new FieldRequiredRule<>(fieldName, getter, errorEmptyRequiredValue); + } + + @Override + public Optional validate(ImdgValidationContext context) { + R validatedObject = context.getValidatedObject(); + V value = getter.apply(validatedObject); + if (value == null) return of(errorEmptyRequiredValue, fieldName); + return empty(); + } +} 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 new file mode 100644 index 000000000..c77296e64 --- /dev/null +++ b/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/config/BeanConfiguration.java @@ -0,0 +1,14 @@ +package ru.spcex.clearing.company.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +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(); + } +} diff --git a/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/config/CompanyValidationConfig.java b/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/config/CompanyValidationConfig.java new file mode 100644 index 000000000..09e6dc2a7 --- /dev/null +++ b/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/config/CompanyValidationConfig.java @@ -0,0 +1,48 @@ +package ru.spcex.clearing.company.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import ru.spcex.clearing.company.error.CompanyErrors; +import ru.spcex.clearing.imdg.IMDGDistributedNames; +import ru.spcex.clearing.platform.messaging.domain.cud.company.CompanyNewRequest; +//import ru.spcex.clearing.validation.common.FieldRequiredRule; +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.validation.IValidator; +import ru.spcex.platform.utils.validation.ValidatorImpl; + +import java.util.Map; +import java.util.function.Consumer; +import java.util.function.Function; + +@Configuration +public class CompanyValidationConfig { + + @Bean("companyNewRequestValidator") + public Function companyNewRequestValidator(Map> imdgForValidation) { + return companyNewRequest -> { + 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_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), + +// DictionaryPresentRule.instance("workflowStatus", +// CompanyNewRequest::getWorkflowStatus, +// IMDGDistributedNames.Map_WorkflowStatus, +// WorkflowStatusDictionary.class), + ); + }; + } + + +} diff --git a/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/error/CompanyErrors.java b/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/error/CompanyErrors.java new file mode 100644 index 000000000..1784b0f5c --- /dev/null +++ b/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/error/CompanyErrors.java @@ -0,0 +1,35 @@ +package ru.spcex.clearing.company.error; + +import ru.spcex.platform.utils.enumeration.IEnumId; + +public enum CompanyErrors implements IEnumId { + GeneralError(3000L), // Общая ошибка модуля company-service + UserVerifyDenial(3001L), // 'Нет прав на проведение данной операции + + RequiredFieldEmpty(3002L), // Не заданы обязательные поля %s. + DictionaryNotFound(3003L), // Запись с идентификатором %s в справочнике %s не найдена. + VrongFieldValue(3004L), // Неверное значение поля %s. + RecordAlreadyExist(3005L), // Запись с идентификатором %s уже существует. + RecordNotFound(3006L), // Запись с идентификатором %s не найдена. + CompanyALreadyExist(3010L), // Компания %s уже существует + CompanyNotFound(3011L), // Компания %s не найдена. + CompanyDisabled(3012L), // Компания %s неактивна. + ProfileNotFound(3013L), // Профиль компании %s не найден. + CompanyInfoNotFound(3014L), // Реквизит компании %s не найден. + ContactNotFound(3015L), // Контакт компании %s не найден. + CategoryNotFound(3016L), // Категория компании %s не найдена. + CategoryAlreadySet(3017L), // Компании %s уже присвоена категория %s. + CompanyAlreadyHas_(3018L), // У компании %s присутствуют обязательства. + + ; + private final Long id; + + CompanyErrors(Long id) { + this.id = id; + } + + @Override + public Long getId() { + return id; + } +} 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 9fa18f424..007058195 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 @@ -6,6 +6,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.InitializingBean; 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.spcex.clearing.imdg.IMDGDistributedNames; @@ -14,19 +15,44 @@ 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.clearing.util.security.UserRoleVerification; +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.function.Function; @Service public class CompanyService extends QueueConsumer implements InitializingBean { private final Logger log = LoggerFactory.getLogger(getClass()); private final Imdg companyMap; + private final ImdgId idSequence; +// protected UserRoleVerification userRoleVerification; + protected IMessageResolver messageResolver; + + private Function companyNewRequestValidator; @Autowired public CompanyService(Consumer kafkaQueue, Producer kafkaProducer, - ImdgProvider imdgProvider) { + ImdgProvider imdgProvider, + IMessageResolver messageResolver, +// , UserRoleVerification userRoleVerification + @Qualifier("companyNewRequestValidator") + Function companyNewRequestValidator + ) { super(kafkaQueue, kafkaProducer); + this.messageResolver = messageResolver; this.companyMap = imdgProvider.getImdg(IMDGDistributedNames.Map_Company, Company.class); + this.idSequence = imdgProvider.getImdgIdGenerator(); +// this.userRoleVerification = userRoleVerification; + this.companyNewRequestValidator = companyNewRequestValidator; } @Override @@ -37,23 +63,87 @@ public class CompanyService extends QueueConsumer implements InitializingBean { callback(CompanyNewRequest.class) .setConsumer(this::createCompany) .forDestination(Consts.DESTINATION_COMPANY_NEW, callbacks::put); + callback(CompanyNewRequest.class) + .setConsumer(this::updateCompany) + .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(); } + 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()); + String errorMsg = messageResolver.resolve(error.get()); +// return new RequestInfoUpdate() // todo reply message +// .setId(userRequest.getId()) +// .setStatus(Status.Error) +// .setMessage(errorMsg); + } + } + private void createCompany(BaseRequest companyNewRequestBaseRequest) { CompanyNewRequest req = companyNewRequestBaseRequest.getRequestPayload(); log.debug("company-new request received, BaseRequest.id = {}", companyNewRequestBaseRequest.getId()); + try { + validateRole(companyNewRequestBaseRequest); + validate(req); + } catch (ValidationException e) { + throw new RuntimeException(e); // todo true return error + } Company company = new Company(); + company.setId(idSequence.nextId()); + Instant now = Instant.now(); + company.setCreated(now); + company.setUpdated(now); + company.setShortName(req.getShortName()); company.setFullName(req.getFullName()); company.setTradingCode(req.getTradingCode()); company.setClearingCode(req.getClearingCode()); company.setRegistrationCode(req.getRegistrationCode()); + + 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()); } + + 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 void deleteCompany(BaseRequest userRequest) { CommonDeleteRequest req = userRequest.getRequestPayload(); log.debug("CommonDeleteRequest received id = {}", req.getId()); 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 cf5043740..44d11db48 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 @@ -23,6 +23,7 @@ public interface Consts { String DESTINATION_COMPANY_NEW = "company-new"; String DESTINATION_COMPANY_DELETE = "company-delete"; + String DESTINATION_COMPANY_UPDATE = "company-update"; String DESTINATION_COMPANY_INFO_UPDATE = "company-info-update"; String DESTINATION_COMPANY_SYMBOL_UPDATE = "company-symbol-update"; String DESTINATION_CONTACT_UPDATE = "contact-update"; diff --git a/platform-parent/platform-utils/src/main/java/ru/spcex/platform/utils/error/ClearingBaseException.java b/platform-parent/platform-utils/src/main/java/ru/spcex/platform/utils/error/ClearingBaseException.java new file mode 100644 index 000000000..6a5c0b4e0 --- /dev/null +++ b/platform-parent/platform-utils/src/main/java/ru/spcex/platform/utils/error/ClearingBaseException.java @@ -0,0 +1,39 @@ +package ru.spcex.platform.utils.error; + +import ru.spcex.platform.utils.enumeration.EnumMessage; +import ru.spcex.platform.utils.enumeration.IEnumId; + +import java.util.Arrays; + +public class ClearingBaseException extends Exception { + final EnumMessage enumMsg; + + public ClearingBaseException(EnumMessage msg) { + super(msg == null ? "" : msg.getSubject().getId() + " " + (msg.getArgs() == null ? "" : Arrays.toString(msg.getArgs()))); + this.enumMsg = msg; + } + + public ClearingBaseException(ErrorEnum code) { + this(new EnumMessage(code)); + } + + public ClearingBaseException(ErrorEnum code, String message) { + super(code == null ? message : code.getId() + " " + message); + this.enumMsg = new EnumMessage(code); + } + + public ClearingBaseException(ErrorEnum code, String message, Throwable cause) { + super(code == null ? message : code.getId() + " " + message, cause); + this.enumMsg = new EnumMessage(code); + } + +// public ClearingBaseException(String message, Throwable cause) { +// super(message, cause); +// this.enumMsg = new EnumMessage(ClearingError.GeneralError); +// } + + public EnumMessage getEnumMsg() { + return enumMsg; + } + +} diff --git a/platform-parent/platform-utils/src/main/java/ru/spcex/platform/utils/error/ValidationException.java b/platform-parent/platform-utils/src/main/java/ru/spcex/platform/utils/error/ValidationException.java new file mode 100644 index 000000000..06cf2901f --- /dev/null +++ b/platform-parent/platform-utils/src/main/java/ru/spcex/platform/utils/error/ValidationException.java @@ -0,0 +1,20 @@ +package ru.spcex.platform.utils.error; + +import ru.spcex.platform.utils.enumeration.EnumMessage; +import ru.spcex.platform.utils.enumeration.IEnumId; + +public class ValidationException extends ClearingBaseException { + + public ValidationException(EnumMessage msg) { + super(msg); + } + + public ValidationException(ErrorEnum code, String... args) { + super(new EnumMessage(code, args)); + } + + public ValidationException(ErrorEnum code, String message) { + super(code, message); + } + +}