From 6f6ace106d9e048497868b5e488fb3e558308d72 Mon Sep 17 00:00:00 2001 From: ialbert Date: Wed, 17 Aug 2022 16:18:01 +0300 Subject: [PATCH] http://jira.mfd.msk:8088/browse/CLS-16 --- .../backendapi/config/MessagesConfig.java | 5 +- .../controller/cud/CudController.java | 101 ++++++++---------- .../controller/cud/CudExceptionHandler.java | 43 ++++++++ .../cud/MoneyMarketSecurityNewAction.java | 12 +++ .../response/BasicSpcexResponse.java | 6 +- .../controller/response/cud/CudResponse.java | 19 ++++ .../response/cud/QueueSuccessResponse.java | 27 +++++ .../backendapi/domain/actions/IAction.java | 11 +- .../errors/ActionValidationException.java | 22 ++++ .../backendapi/errors/BackEndError.java | 17 +++ .../backendapi/errors/ErrorResolver.java | 28 +++++ .../backendapi/service/IOperator.java | 10 ++ .../backendapi/service/impl/OperatorImpl.java | 46 ++++++++ .../main/resources/messages/error.properties | 2 + .../resources/messages/error_ru.properties | 2 + .../resources/messages/response.properties | 1 - .../resources/messages/response_ru.properties | 1 - .../adapter/ImdgIdGeneratorHazelcast.java | 21 ++++ .../service/HazelcastServiceBase.java | 19 ++++ .../ru/spcex/platform/imdg/api/ImdgId.java | 5 + .../spcex/platform/imdg/api/ImdgProvider.java | 2 + platform-parent/platform-messaging/pom.xml | 5 + .../platform/messaging/domain/ActionType.java | 5 + .../messaging/domain/BaseRequest.java | 8 ++ .../MoneyMarketSecurityNewRequest.java | 21 +++- .../utils/enumeration/EnumMessage.java | 35 ++++++ .../platform/utils/enumeration/IEnumId.java | 64 +++++++++++ .../utils/enumeration/IMessageResolver.java | 5 + .../resources/distr/bin/securities-service.sh | 2 +- 29 files changed, 479 insertions(+), 66 deletions(-) create mode 100644 clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/controller/cud/CudExceptionHandler.java create mode 100644 clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/controller/response/cud/CudResponse.java create mode 100644 clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/controller/response/cud/QueueSuccessResponse.java create mode 100644 clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/errors/ActionValidationException.java create mode 100644 clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/errors/BackEndError.java create mode 100644 clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/errors/ErrorResolver.java create mode 100644 clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/service/IOperator.java create mode 100644 clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/service/impl/OperatorImpl.java create mode 100644 clearing-parent/backend-api/src/main/resources/messages/error.properties create mode 100644 clearing-parent/backend-api/src/main/resources/messages/error_ru.properties delete mode 100644 clearing-parent/backend-api/src/main/resources/messages/response.properties delete mode 100644 clearing-parent/backend-api/src/main/resources/messages/response_ru.properties create mode 100644 platform-parent/platform-imdg-api-hazelcast-impl/src/main/java/ru/spcex/platform/imdg/iml/hazelcast/adapter/ImdgIdGeneratorHazelcast.java create mode 100644 platform-parent/platform-imdg-api/src/main/java/ru/spcex/platform/imdg/api/ImdgId.java create mode 100644 platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/domain/ActionType.java create mode 100644 platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/domain/BaseRequest.java create mode 100644 platform-parent/platform-utils/src/main/java/ru/spcex/platform/utils/enumeration/EnumMessage.java create mode 100644 platform-parent/platform-utils/src/main/java/ru/spcex/platform/utils/enumeration/IEnumId.java create mode 100644 platform-parent/platform-utils/src/main/java/ru/spcex/platform/utils/enumeration/IMessageResolver.java diff --git a/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/config/MessagesConfig.java b/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/config/MessagesConfig.java index bed0c3aa7..6e2ab3e55 100644 --- a/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/config/MessagesConfig.java +++ b/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/config/MessagesConfig.java @@ -8,14 +8,13 @@ import java.util.Locale; @Configuration public class MessagesConfig { - @Bean + @Bean("validation-error-messages") public ResourceBundleMessageSource messages() { ResourceBundleMessageSource source = new ResourceBundleMessageSource(); - source.setBasenames("messages/response"); + source.setBasenames("messages/error"); source.setUseCodeAsDefaultMessage(true); source.setDefaultEncoding("utf8"); source.setDefaultLocale(Locale.ROOT); - return source; } } diff --git a/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/controller/cud/CudController.java b/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/controller/cud/CudController.java index 6763b14bf..cea2a05e7 100644 --- a/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/controller/cud/CudController.java +++ b/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/controller/cud/CudController.java @@ -1,95 +1,88 @@ package ru.spcex.clearing.backendapi.controller.cud; -import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; -import io.swagger.annotations.*; -import org.apache.kafka.clients.producer.Producer; -import org.apache.kafka.clients.producer.ProducerRecord; -import org.apache.kafka.clients.producer.RecordMetadata; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import io.swagger.annotations.ApiResponse; +import io.swagger.annotations.ApiResponses; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; import ru.spcex.clearing.backendapi.controller.response.BasicSpcexResponse; +import ru.spcex.clearing.backendapi.controller.response.cud.CudResponse; import ru.spcex.clearing.backendapi.domain.actions.IAction; import ru.spcex.clearing.backendapi.meta.CudMetaService; +import ru.spcex.clearing.backendapi.service.IOperator; import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; @Controller @RequestMapping("/cud") public class CudController { - private final Producer kafka; + private final IOperator operator; private final ObjectMapper json; private final CudMetaService meta; @Autowired - public CudController(Producer kafka, CudMetaService meta) { - this.kafka = kafka; + public CudController(IOperator operator, CudMetaService meta) { this.meta = meta; this.json = new ObjectMapper(); + this.operator = operator; } @ApiOperation(value = "create/update/delete business objects. See meta.xml for field descriptions.") - @ApiResponses(value = {@ApiResponse(code = 200, message = "OK", response = MetaDataResponse.class)}) + @ApiResponses(value = {@ApiResponse(code = 200, message = "OK", response = CudResponse.class), @ApiResponse(code = 400, message = "Ошибка валидации", response = BasicSpcexResponse.class)}) @RequestMapping(value = "/{destination}", method = RequestMethod.POST) @ResponseBody - public MetaDataResponse add( + public CudResponse add( @ApiParam(value = "Последняя часть URL определяет 'направление', по которому пойдет запрос. " + "Должно биться с форматом запроса.", required = true, example = "money-market-security-new") @PathVariable("destination") String destination, @ApiParam(value = "Параметры команды в JSON формате, поля см. в meta.xml.", required = true) @RequestBody String body) throws JsonProcessingException, ExecutionException, InterruptedException { + return processRequest(destination, body); + } + + @ApiOperation(value = "create/update/delete business objects. See meta.xml for field descriptions.") + @ApiResponses(value = {@ApiResponse(code = 200, message = "OK", response = CudResponse.class), @ApiResponse(code = 400, message = "Ошибка валидации", response = BasicSpcexResponse.class)}) + @RequestMapping(value = "/{destination}", method = RequestMethod.PUT) + @ResponseBody + public CudResponse update( + @ApiParam(value = "Последняя часть URL определяет 'направление', по которому пойдет запрос. " + + "Должно биться с форматом запроса.", required = true, example = "money-market-security-new") + @PathVariable("destination") + String destination, + @ApiParam(value = "Параметры команды в JSON формате, поля см. в meta.xml.", required = true) + @RequestBody String body) throws JsonProcessingException, ExecutionException, InterruptedException { + return processRequest(destination, body); + } + + @ApiOperation(value = "delete business objects. See meta.xml for field descriptions.") + @ApiResponses(value = {@ApiResponse(code = 200, message = "OK", response = CudResponse.class), @ApiResponse(code = 400, message = "Ошибка валидации", response = BasicSpcexResponse.class)}) + @RequestMapping(value = "/{destination}", method = RequestMethod.DELETE) + @ResponseBody + public CudResponse delete( + @ApiParam(value = "Последняя часть URL определяет 'направление', по которому пойдет запрос. " + + "Должно биться с форматом запроса.", required = true, example = "money-market-security-new") + @PathVariable("destination") + String destination, + @ApiParam(value = "Параметры команды в JSON формате, поля см. в meta.xml.", required = true) + @RequestBody String body) throws JsonProcessingException, ExecutionException, InterruptedException { + return processRequest(destination, body); + } + + private CudResponse processRequest(String destination, String body) throws ExecutionException, InterruptedException, JsonProcessingException { Class> actionClazz = meta.byDestination(destination); if (actionClazz == null) { throw new UnsupportedOperationException("unsupported destination " + destination); } IAction iAction = json.readValue(body, actionClazz); - Future send = kafka.send(new ProducerRecord<>(destination, iAction.toRequest())); - RecordMetadata kafkaMetaData = send.get(); - MetaDataResponse responseToClient = new MetaDataResponse(); - responseToClient.setOffset(kafkaMetaData.offset()); - responseToClient.setPartition(kafkaMetaData.partition()); - responseToClient.setTopic(kafkaMetaData.topic()); + CudResponse responseToClient = new CudResponse(); + responseToClient.setResponse(operator.sendRequestToQueue(destination, iAction)); + responseToClient.setCode(0); + responseToClient.setMessage("success"); return responseToClient; } - - @ApiModel(description="Ответ в результате отправки операции в топик Kafka.") - private static class MetaDataResponse extends BasicSpcexResponse { - @ApiModelProperty(value="Offset положенного сообщения") - @JsonProperty - private Long offset; - @ApiModelProperty(value="Partition положенного сообщения") - @JsonProperty - private Integer partition; - @ApiModelProperty(value="Название топика, в который было положено сообщение") - @JsonProperty - private String topic; - - public Long getOffset() { - return offset; - } - - public void setOffset(Long offset) { - this.offset = offset; - } - - public Integer getPartition() { - return partition; - } - - public void setPartition(Integer partition) { - this.partition = partition; - } - - public String getTopic() { - return topic; - } - - public void setTopic(String topic) { - this.topic = topic; - } - } } diff --git a/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/controller/cud/CudExceptionHandler.java b/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/controller/cud/CudExceptionHandler.java new file mode 100644 index 000000000..a154197aa --- /dev/null +++ b/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/controller/cud/CudExceptionHandler.java @@ -0,0 +1,43 @@ +package ru.spcex.clearing.backendapi.controller.cud; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; +import ru.spcex.clearing.backendapi.controller.response.BasicSpcexResponse; +import ru.spcex.clearing.backendapi.errors.ActionValidationException; +import ru.spcex.platform.utils.enumeration.EnumMessage; +import ru.spcex.platform.utils.enumeration.IMessageResolver; +import ru.spcex.platform.utils.log.ExceptionUtils; + +@ControllerAdvice("ru.spcex.clearing.backendapi.controller.cud") +@Order(Ordered.HIGHEST_PRECEDENCE) +public class CudExceptionHandler extends ResponseEntityExceptionHandler { + private final Logger log = LoggerFactory.getLogger(getClass()); + private final IMessageResolver errorResolver; + + @Autowired + public CudExceptionHandler(@Qualifier("errorResolver") IMessageResolver errorResolver) { + this.errorResolver = errorResolver; + } + + @ExceptionHandler(value = ActionValidationException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + @ResponseBody + public BasicSpcexResponse handleValidationException(ActionValidationException e) { + log.error("Default scoring exception handler: {}", ExceptionUtils.getStackTrace(e)); + EnumMessage firstError = e.getErrors().iterator().next(); + BasicSpcexResponse errorResponse = new BasicSpcexResponse(); + errorResponse.setCode(firstError.getSubject().getId()); + errorResponse.setMessage(errorResolver.resolve(firstError)); + return errorResponse; + } +} diff --git a/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/controller/request/cud/MoneyMarketSecurityNewAction.java b/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/controller/request/cud/MoneyMarketSecurityNewAction.java index 88cbe8def..d77501a81 100644 --- a/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/controller/request/cud/MoneyMarketSecurityNewAction.java +++ b/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/controller/request/cud/MoneyMarketSecurityNewAction.java @@ -4,10 +4,15 @@ import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import ru.spcex.clearing.backendapi.domain.actions.IAction; +import ru.spcex.clearing.backendapi.errors.BackEndError; import ru.spcex.clearing.platform.messaging.domain.cud.securitites.MoneyMarketSecurityNewRequest; import ru.spcex.clearing.platform.messaging.domain.json.deserialize.InstantDeserializer; +import ru.spcex.platform.utils.enumeration.EnumMessage; import java.time.Instant; +import java.util.Collection; +import java.util.Collections; +import java.util.List; public class MoneyMarketSecurityNewAction implements IAction { @JsonFormat(pattern = "yyyy-MM-dd", timezone = "Europe/Moscow") @@ -31,6 +36,13 @@ public class MoneyMarketSecurityNewAction implements IAction validate() { + if (this.startDate == null) + return List.of(new EnumMessage(BackEndError.ValidationError, "startDate")); + else return Collections.emptyList(); + } + @Override public MoneyMarketSecurityNewRequest toRequest() { var req = new MoneyMarketSecurityNewRequest(); diff --git a/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/controller/response/BasicSpcexResponse.java b/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/controller/response/BasicSpcexResponse.java index bb3b47597..4b1f54869 100644 --- a/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/controller/response/BasicSpcexResponse.java +++ b/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/controller/response/BasicSpcexResponse.java @@ -6,15 +6,15 @@ import io.swagger.annotations.ApiModelProperty; @ApiModel(description="Базовый формат ответа") public class BasicSpcexResponse { @ApiModelProperty(value="Код ответа (успешный 0)", required = true) - private int code; + private long code; @ApiModelProperty(value="Сообщение ответа") private String message; - public int getCode() { + public long getCode() { return code; } - public void setCode(int code) { + public void setCode(long code) { this.code = code; } diff --git a/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/controller/response/cud/CudResponse.java b/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/controller/response/cud/CudResponse.java new file mode 100644 index 000000000..8ffbb43a4 --- /dev/null +++ b/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/controller/response/cud/CudResponse.java @@ -0,0 +1,19 @@ +package ru.spcex.clearing.backendapi.controller.response.cud; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import ru.spcex.clearing.backendapi.controller.response.BasicSpcexResponse; + +@ApiModel(description = "Ответ в результате отправки операции в топик Kafka.") +public class CudResponse extends BasicSpcexResponse { + @ApiModelProperty(value = "Информация о принятом запросе") + QueueSuccessResponse response; + + public QueueSuccessResponse getResponse() { + return response; + } + + public void setResponse(QueueSuccessResponse response) { + this.response = response; + } +} diff --git a/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/controller/response/cud/QueueSuccessResponse.java b/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/controller/response/cud/QueueSuccessResponse.java new file mode 100644 index 000000000..56f080f71 --- /dev/null +++ b/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/controller/response/cud/QueueSuccessResponse.java @@ -0,0 +1,27 @@ +package ru.spcex.clearing.backendapi.controller.response.cud; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.annotations.ApiModelProperty; +import ru.spcex.clearing.platform.messaging.domain.ActionType; + +public class QueueSuccessResponse { + @ApiModelProperty(value = "Тип запроса") + @JsonProperty + private final ActionType action; + @ApiModelProperty(value = "Идентификатор запроса") + @JsonProperty + private final Long id; + + public QueueSuccessResponse(ActionType action, Long id) { + this.action = action; + this.id = id; + } + + public ActionType getAction() { + return action; + } + + public Long getId() { + return id; + } +} diff --git a/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/domain/actions/IAction.java b/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/domain/actions/IAction.java index 53710222b..5d4eb4b52 100644 --- a/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/domain/actions/IAction.java +++ b/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/domain/actions/IAction.java @@ -1,5 +1,12 @@ package ru.spcex.clearing.backendapi.domain.actions; -public interface IAction { - public T toRequest(); +import ru.spcex.clearing.platform.messaging.domain.BaseRequest; +import ru.spcex.platform.utils.enumeration.EnumMessage; + +import java.util.Collection; +import java.util.Collections; + +public interface IAction { + T toRequest(); + default Collection validate() {return Collections.emptyList();} } diff --git a/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/errors/ActionValidationException.java b/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/errors/ActionValidationException.java new file mode 100644 index 000000000..251695991 --- /dev/null +++ b/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/errors/ActionValidationException.java @@ -0,0 +1,22 @@ +package ru.spcex.clearing.backendapi.errors; + +import ru.spcex.platform.utils.enumeration.EnumMessage; + +import java.util.Collection; +import java.util.List; + +public class ActionValidationException extends RuntimeException { + protected Collection errors; + + public ActionValidationException(Collection errors) { + this.errors = errors; + } + + public ActionValidationException(EnumMessage error) { + this.errors = List.of(error); + } + + public Collection getErrors() { + return errors; + } +} diff --git a/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/errors/BackEndError.java b/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/errors/BackEndError.java new file mode 100644 index 000000000..933ef96eb --- /dev/null +++ b/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/errors/BackEndError.java @@ -0,0 +1,17 @@ +package ru.spcex.clearing.backendapi.errors; + +import ru.spcex.platform.utils.enumeration.IEnumId; + +public enum BackEndError implements IEnumId { + ValidationError(3000L); + private final Long id; + + BackEndError(Long id) { + this.id = id; + } + + @Override + public Long getId() { + return id; + } +} diff --git a/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/errors/ErrorResolver.java b/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/errors/ErrorResolver.java new file mode 100644 index 000000000..4eda4e153 --- /dev/null +++ b/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/errors/ErrorResolver.java @@ -0,0 +1,28 @@ +package ru.spcex.clearing.backendapi.errors; + +import org.springframework.context.support.ResourceBundleMessageSource; +import org.springframework.stereotype.Service; +import ru.spcex.platform.utils.enumeration.EnumMessage; +import ru.spcex.platform.utils.enumeration.IMessageResolver; + +import java.util.Locale; + +@Service("errorResolver") +public class ErrorResolver implements IMessageResolver { + private final ResourceBundleMessageSource errorMessages; + + public ErrorResolver(ResourceBundleMessageSource errorMessages) { + this.errorMessages = errorMessages; + } + + @Override + public String resolve(EnumMessage errorMessage) { + Long id = errorMessage.getSubject().getId(); + String messageTemplate = errorMessages.getMessage(id.toString(), null, Locale.ENGLISH); + if (errorMessage.getArgs().length > 0) + return String.format(messageTemplate, errorMessage.getArgs()); + else { + return messageTemplate; + } + } +} diff --git a/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/service/IOperator.java b/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/service/IOperator.java new file mode 100644 index 000000000..6d86d7660 --- /dev/null +++ b/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/service/IOperator.java @@ -0,0 +1,10 @@ +package ru.spcex.clearing.backendapi.service; + +import ru.spcex.clearing.backendapi.controller.response.cud.QueueSuccessResponse; +import ru.spcex.clearing.backendapi.domain.actions.IAction; + +import java.util.concurrent.ExecutionException; + +public interface IOperator { + QueueSuccessResponse sendRequestToQueue(String destination, IAction iAction) throws ExecutionException, InterruptedException; +} diff --git a/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/service/impl/OperatorImpl.java b/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/service/impl/OperatorImpl.java new file mode 100644 index 000000000..274fe87dd --- /dev/null +++ b/clearing-parent/backend-api/src/main/java/ru/spcex/clearing/backendapi/service/impl/OperatorImpl.java @@ -0,0 +1,46 @@ +package ru.spcex.clearing.backendapi.service.impl; + +import org.apache.kafka.clients.producer.Producer; +import org.apache.kafka.clients.producer.ProducerRecord; +import org.apache.kafka.clients.producer.RecordMetadata; +import org.springframework.stereotype.Service; +import ru.spcex.clearing.backendapi.controller.response.cud.QueueSuccessResponse; +import ru.spcex.clearing.backendapi.domain.actions.IAction; +import ru.spcex.clearing.backendapi.errors.ActionValidationException; +import ru.spcex.clearing.backendapi.service.IOperator; +import ru.spcex.clearing.platform.messaging.domain.BaseRequest; +import ru.spcex.platform.imdg.api.ImdgId; +import ru.spcex.platform.imdg.api.ImdgProvider; +import ru.spcex.platform.utils.enumeration.EnumMessage; + +import java.util.Collection; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + +@Service +public class OperatorImpl implements IOperator { + private final Producer kafka; + private final ImdgId idGenerator; + + public OperatorImpl(Producer kafka, ImdgProvider imdgProvider) { + this.kafka = kafka; + this.idGenerator = imdgProvider.getImdgIdGenerator(); + } + + @Override + public QueueSuccessResponse sendRequestToQueue(String destination, IAction iAction) throws ExecutionException, InterruptedException { + throwValidate(iAction); + BaseRequest request = iAction.toRequest(); + request.setId(idGenerator.nextId()); + Future send = kafka.send(new ProducerRecord<>(destination, request)); + send.get(); + return new QueueSuccessResponse(request.getActionType(), request.getId()); + } + + private void throwValidate(IAction iAction) { + Collection validationErrors = iAction.validate(); + if (validationErrors.size() > 0) { + throw new ActionValidationException(validationErrors); + } + } +} diff --git a/clearing-parent/backend-api/src/main/resources/messages/error.properties b/clearing-parent/backend-api/src/main/resources/messages/error.properties new file mode 100644 index 000000000..986e9314e --- /dev/null +++ b/clearing-parent/backend-api/src/main/resources/messages/error.properties @@ -0,0 +1,2 @@ +1=This is error example 1. +3000=Validation error field: %s \ No newline at end of file diff --git a/clearing-parent/backend-api/src/main/resources/messages/error_ru.properties b/clearing-parent/backend-api/src/main/resources/messages/error_ru.properties new file mode 100644 index 000000000..4e12eeded --- /dev/null +++ b/clearing-parent/backend-api/src/main/resources/messages/error_ru.properties @@ -0,0 +1,2 @@ +1=Пример ошибки с идентификатором 1. +3000=Ошибка валидации, поле: %s \ No newline at end of file diff --git a/clearing-parent/backend-api/src/main/resources/messages/response.properties b/clearing-parent/backend-api/src/main/resources/messages/response.properties deleted file mode 100644 index 9dd528c94..000000000 --- a/clearing-parent/backend-api/src/main/resources/messages/response.properties +++ /dev/null @@ -1 +0,0 @@ -backend-api.response.test1=This is response example 1. \ No newline at end of file diff --git a/clearing-parent/backend-api/src/main/resources/messages/response_ru.properties b/clearing-parent/backend-api/src/main/resources/messages/response_ru.properties deleted file mode 100644 index 49b9adf1d..000000000 --- a/clearing-parent/backend-api/src/main/resources/messages/response_ru.properties +++ /dev/null @@ -1 +0,0 @@ -backend-api.response.test1=Это пример ответа 1. \ No newline at end of file diff --git a/platform-parent/platform-imdg-api-hazelcast-impl/src/main/java/ru/spcex/platform/imdg/iml/hazelcast/adapter/ImdgIdGeneratorHazelcast.java b/platform-parent/platform-imdg-api-hazelcast-impl/src/main/java/ru/spcex/platform/imdg/iml/hazelcast/adapter/ImdgIdGeneratorHazelcast.java new file mode 100644 index 000000000..62e0d963c --- /dev/null +++ b/platform-parent/platform-imdg-api-hazelcast-impl/src/main/java/ru/spcex/platform/imdg/iml/hazelcast/adapter/ImdgIdGeneratorHazelcast.java @@ -0,0 +1,21 @@ +package ru.spcex.platform.imdg.iml.hazelcast.adapter; + +import com.hazelcast.core.IdGenerator; +import ru.spcex.platform.imdg.api.ImdgId; + +public class ImdgIdGeneratorHazelcast implements ImdgId { + private IdGenerator idGenerator; + + public IdGenerator getIdGenerator() { + return idGenerator; + } + + public void setIdGenerator(IdGenerator idGenerator) { + this.idGenerator = idGenerator; + } + + @Override + public Long nextId() { + return idGenerator.newId(); + } +} diff --git a/platform-parent/platform-imdg-api-hazelcast-impl/src/main/java/ru/spcex/platform/imdg/iml/hazelcast/service/HazelcastServiceBase.java b/platform-parent/platform-imdg-api-hazelcast-impl/src/main/java/ru/spcex/platform/imdg/iml/hazelcast/service/HazelcastServiceBase.java index d8b90795e..f6d80b3ff 100644 --- a/platform-parent/platform-imdg-api-hazelcast-impl/src/main/java/ru/spcex/platform/imdg/iml/hazelcast/service/HazelcastServiceBase.java +++ b/platform-parent/platform-imdg-api-hazelcast-impl/src/main/java/ru/spcex/platform/imdg/iml/hazelcast/service/HazelcastServiceBase.java @@ -12,8 +12,10 @@ import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import ru.spcex.clearing.imdg.IMDGDistributedNames; import ru.spcex.platform.classes.base.SpcexObjectBase; 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.imdg.iml.hazelcast.adapter.ImdgHazelcast; +import ru.spcex.platform.imdg.iml.hazelcast.adapter.ImdgIdGeneratorHazelcast; import ru.spcex.platform.imdg.iml.hazelcast.config.HazelcastClientParams; import ru.spcex.platform.imdg.iml.hazelcast.util.HazelcastHelper; import ru.spcex.platform.utils.log.ExceptionUtils; @@ -231,6 +233,23 @@ public abstract class HazelcastServiceBase return imdg; } + @Override + public ImdgId getImdgIdGenerator() { + ImdgIdGeneratorHazelcast imdgId = new ImdgIdGeneratorHazelcast(); + statusSubscribe(new IHazelcastClusterStatus() { + @Override + public void getAvailable(HazelcastInstance hazelcastNotInited) { + IdGenerator generator = hazelcastInstance.getIdGenerator(IMDGDistributedNames.MAP_SEQUENCE_NAME); + imdgId.setIdGenerator(generator); + } + + @Override + public void getUnavailable(HazelcastInstance hazelcastNotInited) { + } + }); + return imdgId; + } + // @Override public void waitTillReadyState() throws InterruptedException { boolean done = false; diff --git a/platform-parent/platform-imdg-api/src/main/java/ru/spcex/platform/imdg/api/ImdgId.java b/platform-parent/platform-imdg-api/src/main/java/ru/spcex/platform/imdg/api/ImdgId.java new file mode 100644 index 000000000..8069f7b8c --- /dev/null +++ b/platform-parent/platform-imdg-api/src/main/java/ru/spcex/platform/imdg/api/ImdgId.java @@ -0,0 +1,5 @@ +package ru.spcex.platform.imdg.api; + +public interface ImdgId { + Long nextId(); +} diff --git a/platform-parent/platform-imdg-api/src/main/java/ru/spcex/platform/imdg/api/ImdgProvider.java b/platform-parent/platform-imdg-api/src/main/java/ru/spcex/platform/imdg/api/ImdgProvider.java index 89b1f3f6d..3ccd0405e 100644 --- a/platform-parent/platform-imdg-api/src/main/java/ru/spcex/platform/imdg/api/ImdgProvider.java +++ b/platform-parent/platform-imdg-api/src/main/java/ru/spcex/platform/imdg/api/ImdgProvider.java @@ -8,4 +8,6 @@ public interface ImdgProvider { */ public Imdg getImdg(String key, Class clazz); + public ImdgId getImdgIdGenerator(); + } diff --git a/platform-parent/platform-messaging/pom.xml b/platform-parent/platform-messaging/pom.xml index dd128d8e4..d2afb1602 100644 --- a/platform-parent/platform-messaging/pom.xml +++ b/platform-parent/platform-messaging/pom.xml @@ -39,6 +39,11 @@ platform-utils provided + + ru.spcex.platform + platform-classes-base + provided + \ No newline at end of file diff --git a/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/domain/ActionType.java b/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/domain/ActionType.java new file mode 100644 index 000000000..c393de723 --- /dev/null +++ b/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/domain/ActionType.java @@ -0,0 +1,5 @@ +package ru.spcex.clearing.platform.messaging.domain; + +public enum ActionType { + NEW +} diff --git a/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/domain/BaseRequest.java b/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/domain/BaseRequest.java new file mode 100644 index 000000000..af78dabd2 --- /dev/null +++ b/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/domain/BaseRequest.java @@ -0,0 +1,8 @@ +package ru.spcex.clearing.platform.messaging.domain; + +import ru.spcex.platform.classes.base.interfaces.WithId; + +public interface BaseRequest extends WithId { + void setId(Long id); + ActionType getActionType(); +} diff --git a/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/domain/cud/securitites/MoneyMarketSecurityNewRequest.java b/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/domain/cud/securitites/MoneyMarketSecurityNewRequest.java index 5a4f7dfb8..51c322f01 100644 --- a/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/domain/cud/securitites/MoneyMarketSecurityNewRequest.java +++ b/platform-parent/platform-messaging/src/main/java/ru/spcex/clearing/platform/messaging/domain/cud/securitites/MoneyMarketSecurityNewRequest.java @@ -3,12 +3,16 @@ package ru.spcex.clearing.platform.messaging.domain.cud.securitites; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import ru.spcex.clearing.platform.messaging.domain.ActionType; +import ru.spcex.clearing.platform.messaging.domain.BaseRequest; import ru.spcex.clearing.platform.messaging.domain.json.deserialize.InstantDeserializer; import ru.spcex.clearing.platform.messaging.domain.json.serialize.InstantSerializer; import java.time.Instant; -public class MoneyMarketSecurityNewRequest { +public class MoneyMarketSecurityNewRequest implements BaseRequest { + @JsonProperty + public Long id; @JsonProperty @JsonSerialize(using = InstantSerializer.class) @JsonDeserialize(using = InstantDeserializer.class) @@ -30,6 +34,21 @@ public class MoneyMarketSecurityNewRequest { @JsonProperty public Double lotSize; + @Override + public Long getId() { + return id; + } + + @Override + public ActionType getActionType() { + return ActionType.NEW; + } + + @Override + public void setId(Long id) { + this.id = id; + } + public Instant getStartDate() { return startDate; } diff --git a/platform-parent/platform-utils/src/main/java/ru/spcex/platform/utils/enumeration/EnumMessage.java b/platform-parent/platform-utils/src/main/java/ru/spcex/platform/utils/enumeration/EnumMessage.java new file mode 100644 index 000000000..66fec8cff --- /dev/null +++ b/platform-parent/platform-utils/src/main/java/ru/spcex/platform/utils/enumeration/EnumMessage.java @@ -0,0 +1,35 @@ +package ru.spcex.platform.utils.enumeration; + +import java.util.Collections; + +public class EnumMessage { + private final IEnumId subject; + /** + * Параметры для подстановки в текст сообщения. Все Serializable. + * args not null. + */ + private Object[] args; + + public EnumMessage(IEnumId subject) { + this.subject = subject; + this.args = Collections.emptyList().toArray(new Object[0]); + } + + + public EnumMessage(IEnumId subject, Object... args) { + this.subject = subject; + this.args = args; + } + + public IEnumId getSubject() { + return subject; + } + + public Object[] getArgs() { + return args; + } + + public void setArgs(Object[] args) { + this.args = args; + } +} diff --git a/platform-parent/platform-utils/src/main/java/ru/spcex/platform/utils/enumeration/IEnumId.java b/platform-parent/platform-utils/src/main/java/ru/spcex/platform/utils/enumeration/IEnumId.java new file mode 100644 index 000000000..a45b3c628 --- /dev/null +++ b/platform-parent/platform-utils/src/main/java/ru/spcex/platform/utils/enumeration/IEnumId.java @@ -0,0 +1,64 @@ +package ru.spcex.platform.utils.enumeration; + +import java.io.Serializable; +import java.util.Objects; + +public interface IEnumId extends Serializable { + Long getId(); + + default boolean equalsById(Long id) { + return id != null && getId().equals(id); + } + + /** + * Проверяет что среди данного набора Enum, присутствует элемент с данным id + * id может быть null + */ + static & IEnumId> boolean contains(java.lang.Long id, T... enumSet) { + for (T e : enumSet) { + if (Objects.equals(id, e.getId())) + return true; + } + return false; + } + + static & IEnumId> boolean contains(T e, T... enumSet) { + for (T enumEl : enumSet) { + if (enumEl.equals(e)) + return true; + } + return false; + } + + /** + * Проверяет что среди всех Enum данного класса, присутствует элемент с данным id + * id может быть null + */ + static & IEnumId> boolean contains(Class enumClass, Long id) { + for (T e : enumClass.getEnumConstants()) { + if (Objects.equals(id, e.getId())) + return true; + } + return false; + } + + /** + * Возвращает Enum по id, если в заданном классе такой определен + * id может быть null + * + * @return Enum если нашел, иначе null + */ + static & IEnumId> T getEnumById(Class enumClass, Long id) { + for (T e : enumClass.getEnumConstants()) { + if (Objects.equals(id, e.getId())) + return e; + } + return null; + } + + static Long getIdOrNull(IEnumId enumVal) { + return enumVal == null ? null : enumVal.getId(); + } + + Long UNDEFINED_VALUE = Long.MIN_VALUE; +} diff --git a/platform-parent/platform-utils/src/main/java/ru/spcex/platform/utils/enumeration/IMessageResolver.java b/platform-parent/platform-utils/src/main/java/ru/spcex/platform/utils/enumeration/IMessageResolver.java new file mode 100644 index 000000000..4c1059bfc --- /dev/null +++ b/platform-parent/platform-utils/src/main/java/ru/spcex/platform/utils/enumeration/IMessageResolver.java @@ -0,0 +1,5 @@ +package ru.spcex.platform.utils.enumeration; + +public interface IMessageResolver { + String resolve(EnumMessage errorMessage); +} diff --git a/z-distr/src/main/resources/distr/bin/securities-service.sh b/z-distr/src/main/resources/distr/bin/securities-service.sh index 45f7ed358..f12309928 100644 --- a/z-distr/src/main/resources/distr/bin/securities-service.sh +++ b/z-distr/src/main/resources/distr/bin/securities-service.sh @@ -1,3 +1,3 @@ #!/bin/sh docker build -t securities-service:1.0.0 ../modules/securities-service/ -docker run -p 8050:8080 -v /opt/clearing/logs:/opt/clearing/bin/logs securities-service:1.0.0 & \ No newline at end of file +docker run -v /opt/clearing/logs:/opt/clearing/bin/logs securities-service:1.0.0 & \ No newline at end of file