ialbert 2026-04-14 18:22:33 +03:00
parent db2079821c
commit 7bd2131dc9
4 changed files with 317 additions and 39 deletions

View file

@ -1,5 +1,5 @@
package ru.spcex.clearing.dflt.management.component.validation;
public enum OrderCurrencyValidationStored {
OrderCurrency, OrderCurrencySpotPair, Registry, Company, TCR
OrderCurrency, CounterOrderCurrency, OrderCurrencySpotPair, Registry, Company, TCR, CounterTCR
}

View file

@ -19,13 +19,16 @@ import ru.spcex.clearing.dflt.management.component.validation.QuantityLotGreater
import ru.spcex.clearing.dflt.management.component.validation.SpotPairPresent;
import ru.spcex.clearing.dflt.management.error.DefaultManagementErrors;
import ru.spcex.clearing.imdg.IMDGDistributedNames;
import ru.spcex.clearing.platform.messaging.domain.cud.common.CommonDeleteRequest;
import ru.spcex.clearing.platform.messaging.domain.cud.misc.OvernightSettingsNewRequest;
import ru.spcex.clearing.platform.messaging.domain.cud.misc.OvernightSettingsUpdateRequest;
import ru.spcex.clearing.platform.messaging.domain.cud.order.OrderCompanyNewRequest;
import ru.spcex.clearing.platform.messaging.domain.cud.order.OrderCompanyUpdateRequest;
import ru.spcex.clearing.platform.messaging.domain.cud.order.OrderCurrencyUpdateRequest;
import ru.spcex.clearing.validation.common.rules.DictionaryPresentRule;
import ru.spcex.clearing.validation.common.rules.IdPresentRule;
import ru.spcex.clearing.validation.common.rules.SpecialIdPresentRule;
import ru.spcex.platform.enumeration.OrderAffil;
import ru.spcex.platform.imdg.api.Imdg;
import ru.spcex.platform.imdg.api.ImdgProvider;
import ru.spcex.platform.imdg.api.predicate.ImdgPredicateBuilder;
@ -176,6 +179,68 @@ public class ValidationConfig {
};
}
@Bean
public Function<OrderCurrencyUpdateRequest, IValidator> orderCurrencyUpdateValidator() {
return req -> {
ImdgValidationContext<OrderCurrencyUpdateRequest> ctx = new ImdgValidationContext<>();
ctx.setValidatedObject(req);
ctx.setLogPrefix(LogPrefixId.INSTANCE);
ctx.addImdg(IMDGDistributedNames.Map_OrderCurrency, orderCurrencyImdg);
ctx.addImdg(IMDGDistributedNames.Map_Registry, rgsImdg);
ctx.addImdg(IMDGDistributedNames.Map_OvernightTypeDictionary, osTypeDictionaryImdg);
//@JsonProperty
//private Long securityId;
//@JsonProperty
//private BigDecimal price;
//@JsonProperty
//private Long quantityLot;
//@JsonProperty
//private String side;
//@JsonProperty
//private String status;
//@JsonProperty
//private String comment;
var orderCurrencyPresentById = PresentByPredicate
.builder(byId(orderCurrencyImdg.predicateBuilder(), req))
.mapName(IMDGDistributedNames.Map_OrderCurrency)
.errorProvider(r -> recordNotFound(r.getId()))
.store(OrderCurrencyValidationStored.OrderCurrency)
.build();
ImdgPredicateBuilder rgsPb = rgsImdg.predicateBuilder();
var registryPresent = PresentByPredicate
.builder(r -> {
OrderCurrency order = ctx.getStoredObject(OrderCurrencyValidationStored.OrderCurrency);
return rgsPb.equals("id", order.getRegistryId());
})
.mapName(IMDGDistributedNames.Map_Registry)
.errorProvider(r -> {
OrderCurrency order = ctx.getStoredObject(OrderCurrencyValidationStored.OrderCurrency);
return recordNotFound("registry.id=" + order.getRegistryId());
})
.store(OrderCurrencyValidationStored.Registry)
.build();
return new ValidatorImpl<>(ctx,
orderCurrencyPresentById,
OvernightTypeChanged.INSTANCE,
DictionaryPresentRule.instance(
"overnightType",
OvernightSettingsNewRequest::getOvernightType,
IMDGDistributedNames.Map_OvernightTypeDictionary,
OvernightTypeDictionary.class,
DefaultManagementErrors.RequiredFieldIsEmpty,
DefaultManagementErrors.DictionaryNotFound,
true),
SpotPairPresent.INSTANCE,
registryPresent
);
};
}
private EnumMessage dictNotFound(String dictName, String code) {
return new EnumMessage(DefaultManagementErrors.DictionaryNotFound, dictName, code);
}
@ -225,6 +290,17 @@ public class ValidationConfig {
.store(OrderCurrencyValidationStored.TCR)
.build();
var counterTcrPresent = PresentByPredicate
.builder(r -> {
OrderCurrency oc = ctx.getStoredObject(OrderCurrencyValidationStored.OrderCurrency);
ImdgPredicateBuilder pb = tcrImdg.predicateBuilder();
return pb.equals("companyId", oc.getCounterPartyId());
})
.mapName(IMDGDistributedNames.Map_TradingClearingRegistry)
.errorProvider(r -> recordNotFound("counter (CK) TradingClearingRegistry"))
.store(OrderCurrencyValidationStored.CounterTCR)
.build();
//fixme проверить что tcr.getCompanyId() = companyId из запроса?
return new ValidatorImpl<>(ctx,
@ -233,7 +309,112 @@ public class ValidationConfig {
tcrPresent,
QuantityLotGreaterThanZero.INSTANCE,
OrderCurrencyByCompanyAndTcrAlreadyPresent.INSTANCE,
OrderCurrencyPLBLCheck.INSTANCE);
OrderCurrencyPLBLCheck.INSTANCE,
counterTcrPresent);
};
}
@Bean
public Function<OrderCompanyUpdateRequest, IValidator> orderCurrencyUpdateCompany() {
return req -> {
ImdgValidationContext<OrderCompanyUpdateRequest> ctx = new ImdgValidationContext<>();
ctx.setValidatedObject(req);
ctx.setLogPrefix(LogPrefixId.INSTANCE);
ctx.addImdg(IMDGDistributedNames.Map_OrderCurrency, orderCurrencyImdg);
ctx.addImdg(IMDGDistributedNames.Map_Company, companyImdg);
ctx.addImdg(IMDGDistributedNames.Map_TradingClearingRegistry, tcrImdg);
var orderCurrencyPresent = PresentByPredicate
.builder(byId(orderCurrencyImdg.predicateBuilder(), req))
.mapName(IMDGDistributedNames.Map_OrderCurrency)
.errorProvider(r -> recordNotFound(req.getId()))
.store(OrderCurrencyValidationStored.OrderCurrency)
.build();
var companyPresentRule = PresentByPredicate
.builder(new IdEqualPredicate<>(
companyImdg.predicateBuilder(),
OrderCompanyUpdateRequest::getCompanyId)
)
.mapName(IMDGDistributedNames.Map_Company)
.errorProvider(r -> recordNotFound("company.id=" + r.getCompanyId()))
.store(OrderCurrencyValidationStored.Company)
.build();
var tcrPresent = PresentByPredicate
.builder(new IdEqualPredicate<>(
tcrImdg.predicateBuilder(),
OrderCompanyNewRequest::getTradingClearingRegistryId)
)
.mapName(IMDGDistributedNames.Map_TradingClearingRegistry)
.errorProvider(r -> recordNotFound("tcr.id=" + r.getTradingClearingRegistryId()))
.store(OrderCurrencyValidationStored.TCR)
.build();
var counterOrderPresent = PresentByPredicate
.builder(r -> {
OrderCurrency oc = ctx.getStoredObject(OrderCurrencyValidationStored.OrderCurrency);
ImdgPredicateBuilder pb = orderCurrencyImdg.predicateBuilder();
return pb.and(
pb.equals("type", OrderAffil.PLBL.getKey()),
pb.equals("registryId", oc.getRegistryId()),
pb.equals("companyId", oc.getCounterPartyId()),
pb.equals("counterPartyId", oc.getCompanyId())
);
})
.mapName(IMDGDistributedNames.Map_OrderCurrency)
.errorProvider(r -> recordNotFound("counter OrderCurrency"))
.store(OrderCurrencyValidationStored.CounterOrderCurrency)
.build();
//fixme проверить что tcr.getCompanyId() = companyId из запроса?
return new ValidatorImpl<>(ctx,
orderCurrencyPresent,
companyPresentRule,
tcrPresent,
QuantityLotGreaterThanZero.INSTANCE,
OrderCurrencyByCompanyAndTcrAlreadyPresent.INSTANCE,
OrderCurrencyPLBLCheck.INSTANCE,
counterOrderPresent);
};
}
@Bean
public Function<CommonDeleteRequest, IValidator> orderCurrencyDeleteCompany() {
return req -> {
ImdgValidationContext<CommonDeleteRequest> ctx = new ImdgValidationContext<>();
ctx.setValidatedObject(req);
ctx.setLogPrefix(LogPrefixId.INSTANCE);
ctx.addImdg(IMDGDistributedNames.Map_OrderCurrency, orderCurrencyImdg);
ctx.addImdg(IMDGDistributedNames.Map_Company, companyImdg);
ctx.addImdg(IMDGDistributedNames.Map_TradingClearingRegistry, tcrImdg);
var orderCurrencyPresent = PresentByPredicate
.builder(byId(orderCurrencyImdg.predicateBuilder(), req))
.mapName(IMDGDistributedNames.Map_OrderCurrency)
.errorProvider(r -> recordNotFound(req.getId()))
.store(OrderCurrencyValidationStored.OrderCurrency)
.build();
var counterOrderPresent = PresentByPredicate
.builder(r -> {
OrderCurrency oc = ctx.getStoredObject(OrderCurrencyValidationStored.OrderCurrency);
ImdgPredicateBuilder pb = orderCurrencyImdg.predicateBuilder();
return pb.and(
pb.equals("type", OrderAffil.PLBL.getKey()),
pb.equals("registryId", oc.getRegistryId()),
pb.equals("companyId", oc.getCounterPartyId()),
pb.equals("counterPartyId", oc.getCompanyId())
);
})
.mapName(IMDGDistributedNames.Map_OrderCurrency)
.errorProvider(r -> recordNotFound("counter OrderCurrency"))
.store(OrderCurrencyValidationStored.CounterOrderCurrency)
.build();
return new ValidatorImpl<>(ctx,
orderCurrencyPresent,
counterOrderPresent);
};
}

View file

@ -12,19 +12,24 @@ import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import ru.clearing.classes.statics.data.company.Company;
import ru.clearing.classes.statics.data.misc.Listing;
import ru.clearing.classes.statics.data.misc.Market;
import ru.clearing.classes.statics.data.misc.OrderCurrency;
import ru.clearing.classes.statics.data.misc.OvernightSettings;
import ru.clearing.classes.statics.data.registry.Registry;
import ru.clearing.classes.statics.data.registry.TradingClearingRegistry;
import ru.clearing.classes.statics.data.security.Rates;
import ru.clearing.platform.dictionary.CurrencyPairDictionary;
import ru.spcex.clearing.dflt.management.component.validation.OrderCurrencyValidationStored;
import ru.spcex.clearing.dflt.management.config.ValidationConfig;
import ru.spcex.clearing.dflt.management.service.ImdgQueryService;
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.order.OrderCompanyNewRequest;
import ru.spcex.clearing.platform.messaging.domain.cud.order.OrderCompanyUpdateRequest;
import ru.spcex.clearing.platform.messaging.domain.cud.order.OrderCurrencyUpdateRequest;
import ru.spcex.clearing.platform.messaging.service.QueueConsumer;
import ru.spcex.clearing.platform.messaging.service.RequestInfoUpdate;
@ -51,8 +56,11 @@ public class OrderCurrencyListener extends QueueConsumer implements Initializing
private final Imdg<OrderCurrency> orderCurrencyImdg;
private final Function<OrderCurrencyUpdateRequest, IValidator> overnightTypeUpdateValidator;
private final Function<OrderCompanyNewRequest, IValidator> orderCompanyNewValidator;
private final Function<OrderCompanyUpdateRequest, IValidator> orderCompanyUpdateValidator;
private final Function<CommonDeleteRequest, IValidator> orderCompanyDeleteValidator;
private final ImdgQueryService imdgQueryService;
private final IMessageResolver msgResolver;
private final ValidationConfig validationConfig;
@Autowired
public OrderCurrencyListener(Consumer<String, Object> kafkaQueue,
@ -60,29 +68,51 @@ public class OrderCurrencyListener extends QueueConsumer implements Initializing
ImdgProvider imdgProvider,
UserRoleVerification userRoleVerification,
Function<OrderCurrencyUpdateRequest, IValidator> overnightTypeUpdateValidator,
Function<OrderCompanyNewRequest, IValidator> orderCompanyNewValidator, ImdgQueryService imdgQueryService,
IMessageResolver msgResolver) {
Function<OrderCompanyNewRequest, IValidator> orderCompanyNewValidator,
Function<OrderCompanyUpdateRequest, IValidator> orderCompanyUpdateValidator,
Function<CommonDeleteRequest, IValidator> orderCompanyDeleteValidator,
ImdgQueryService imdgQueryService,
IMessageResolver msgResolver, ValidationConfig validationConfig) {
super(kafkaQueue, kafkaProducer);
this.registryImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_Registry, Registry.class);
this.overnightSettingsImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_OvernightSettings, OvernightSettings.class);
this.overnightTypeUpdateValidator = overnightTypeUpdateValidator;
this.orderCompanyNewValidator = orderCompanyNewValidator;
this.orderCompanyUpdateValidator = orderCompanyUpdateValidator;
this.orderCompanyDeleteValidator = orderCompanyDeleteValidator;
this.imdgQueryService = imdgQueryService;
this.msgResolver = msgResolver;
this.orderCurrencyImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_OrderCurrency, OrderCurrency.class);
this.validationConfig = validationConfig;
}
@Override
public void afterPropertiesSet() {
//Consts.ORDER_CURRENCY_UPDATE
callback(OrderCurrencyUpdateRequest.class)
.setFunction(this::orderCurrencyUpdate)
.forDestination(Consts.ORDER_CURRENCY_UPDATE, callbacks::put);
callback(OrderCurrencyUpdateRequest.class)
.setFunction(this::updateOvernightType)
.forDestination(Consts.ORDER_OVERNIGHT_TYPE_UPDATE, callbacks::put);
callback(OrderCompanyNewRequest.class)
.setFunction(this::updateCompany)
.setFunction(this::newCompany)
.forDestination(Consts.ORDER_CURRENCY_COMPANY_NEW, callbacks::put);
callback(OrderCompanyUpdateRequest.class)
.setFunction(this::updateCompany)
.forDestination(Consts.ORDER_CURRENCY_COMPANY_UPDATE, callbacks::put);
callback(CommonDeleteRequest.class)
.setFunction(this::deleteCompany)
.forDestination(Consts.ORDER_CURRENCY_COMPANY_DELETE, callbacks::put);
init();
}
private RequestInfoUpdate orderCurrencyUpdate(BaseRequest<OrderCurrencyUpdateRequest> req) {
OrderCurrencyUpdateRequest payload = req.getRequestPayload();
return null;
}
private RequestInfoUpdate updateOvernightType(BaseRequest<OrderCurrencyUpdateRequest> req) {
OrderCurrencyUpdateRequest payload = req.getRequestPayload();
IValidator validator = overnightTypeUpdateValidator.apply(payload);
@ -135,24 +165,12 @@ public class OrderCurrencyListener extends QueueConsumer implements Initializing
var pair = new Pair<>(orderT0, orderT1);
Pair.forEach(pair, o -> {
copyCommonFields(swap, o);
o.setSecurityId(listing.getSecurityId());
o.setSecuritySymbol(listing.getSymbolCode());
o.setAccount(swap.getAccount());
o.setTradingClearingRegistryId(swap.getTradingClearingRegistryId());
o.setCompanyId(swap.getCompanyId());
o.setTradingCode(swap.getTradingCode());
o.setCounterPartyId(swap.getCounterPartyId());
o.setCounterTradingCode(swap.getCounterTradingCode());
o.setType(swap.getType());
o.setOvernightType(OvernightType.SPOT.getKey());
o.setListingId(listing.getId());
o.setOvernightType(OvernightType.SPOT.getKey());
o.setMarket(MarketType.SCSP.getKey());
o.setRate(swap.getRate());
o.setQuantityLot(swap.getQuantityLot());
o.setLotSize(swap.getLotSize());
o.setStatus(swap.getStatus());
o.setComment(swap.getComment());
o.setRegistryId(swap.getRegistryId());
o.setCreated(Instant.now());
});
orderT0.setPrice(rate.getValue());
@ -183,30 +201,34 @@ public class OrderCurrencyListener extends QueueConsumer implements Initializing
Market market = imdgQueryService.getSwapMarket();
Listing listing = imdgQueryService.findListing(rgs, market.getCode());
copyCommonFields(order1, swap);
swap.setSecurityId(listing.getSecurityId());
swap.setSecuritySymbol(listing.getSymbolCode());
swap.setAccount(order1.getAccount());
swap.setTradingClearingRegistryId(order1.getTradingClearingRegistryId());
swap.setCompanyId(order1.getCompanyId());
swap.setTradingCode(order1.getTradingCode());
swap.setCounterPartyId(order1.getCounterPartyId());
swap.setCounterTradingCode(order1.getCounterTradingCode());
swap.setType(order1.getType());
swap.setOvernightType(OvernightType.SWAP.getKey());
swap.setListingId(listing.getId());
swap.setOvernightType(OvernightType.SWAP.getKey());
swap.setMarket(MarketType.SCSW.getKey());
swap.setPrice(safeBD(spotT1.getPrice()).subtract(safeBD(spotT0.getPrice())));
swap.setRate(order1.getRate());
swap.setQuantityLot(order1.getQuantityLot());
swap.setLotSize(order1.getLotSize());
swap.setStatus(order1.getStatus());
swap.setComment(order1.getComment());
swap.setRegistryId(order1.getRegistryId());
swap.setCreated(Instant.now());
return swap;
}
private RequestInfoUpdate updateCompany(BaseRequest<OrderCompanyNewRequest> req) {
private void copyCommonFields(OrderCurrency source, OrderCurrency target) {
target.setAccount(source.getAccount());
target.setTradingClearingRegistryId(source.getTradingClearingRegistryId());
target.setCompanyId(source.getCompanyId());
target.setTradingCode(source.getTradingCode());
target.setCounterPartyId(source.getCounterPartyId());
target.setCounterTradingCode(source.getCounterTradingCode());
target.setType(source.getType());
target.setRate(source.getRate());
target.setQuantityLot(source.getQuantityLot());
target.setLotSize(source.getLotSize());
target.setStatus(source.getStatus());
target.setComment(source.getComment());
target.setRegistryId(source.getRegistryId());
}
private RequestInfoUpdate newCompany(BaseRequest<OrderCompanyNewRequest> req) {
OrderCompanyNewRequest payload = req.getRequestPayload();
IValidator validator = orderCompanyNewValidator.apply(payload);
Optional<EnumMessage> error = validator.tillFirstError();
@ -214,10 +236,84 @@ public class OrderCurrencyListener extends QueueConsumer implements Initializing
return new RequestInfoUpdate(req.getId(), Status.Error, msgResolver.resolve(error.get()));
}
OrderCurrency oc = validator.getStored(OrderCurrencyValidationStored.OrderCurrency);
oc.setCompanyId(payload.getCompanyId());
oc.setTradingClearingRegistryId(payload.getTradingClearingRegistryId());
oc.setQuantityLot(new BigDecimal(payload.getQuantityLot()));
orderCurrencyImdg.update(oc);
TradingClearingRegistry tcr = validator.getStored(OrderCurrencyValidationStored.TCR);
TradingClearingRegistry counterCKTcr = validator.getStored(OrderCurrencyValidationStored.CounterTCR);
Company company = validator.getStored(OrderCurrencyValidationStored.Company);
Instant now = Instant.now();
OrderCurrency primaryOrder = new OrderCurrency();
copyCommonFields(oc, primaryOrder);
primaryOrder.setCompanyId(payload.getCompanyId());
primaryOrder.setTradingClearingRegistryId(payload.getTradingClearingRegistryId());
primaryOrder.setAccount(tcr.getCode());
primaryOrder.setTradingCode(company.getTradingCode());
primaryOrder.setQuantityLot(new BigDecimal(payload.getQuantityLot()));
primaryOrder.setCreated(now);
OrderCurrency counterOrder = new OrderCurrency();
copyCommonFields(primaryOrder, counterOrder);
counterOrder.setCompanyId(primaryOrder.getCounterPartyId());
counterOrder.setTradingClearingRegistryId(counterCKTcr.getId());
counterOrder.setAccount(counterCKTcr.getCode());
counterOrder.setTradingCode(primaryOrder.getCounterTradingCode());
counterOrder.setCounterPartyId(primaryOrder.getCompanyId());
counterOrder.setCounterTradingCode(primaryOrder.getTradingCode());
if (Side.SELL.equalsByKey(primaryOrder.getSide())) {
counterOrder.setSide(Side.BUY.getKey());
} else {
counterOrder.setSide(Side.SELL.getKey());
}
counterOrder.setCreated(now);
orderCurrencyImdg.insert(primaryOrder);
orderCurrencyImdg.insert(counterOrder);
return null;
}
private RequestInfoUpdate updateCompany(BaseRequest<OrderCompanyUpdateRequest> req) {
OrderCompanyUpdateRequest payload = req.getRequestPayload();
IValidator validator = orderCompanyUpdateValidator.apply(payload);
Optional<EnumMessage> error = validator.tillFirstError();
if (error.isPresent()) {
return new RequestInfoUpdate(req.getId(), Status.Error, msgResolver.resolve(error.get()));
}
Instant now = Instant.now();
OrderCurrency order = validator.getStored(OrderCurrencyValidationStored.OrderCurrency);
Company company = validator.getStored(OrderCurrencyValidationStored.Company);
TradingClearingRegistry tcr = validator.getStored(OrderCurrencyValidationStored.TCR);
OrderCurrency counterOrder = validator.getStored(OrderCurrencyValidationStored.CounterOrderCurrency);
order.setCompanyId(payload.getCompanyId());
order.setTradingClearingRegistryId(payload.getTradingClearingRegistryId());
order.setQuantityLot(new BigDecimal(payload.getQuantityLot()));
order.setAccount(tcr.getCode());
order.setTradingCode(company.getTradingCode());
order.setUpdated(now);
counterOrder.setCounterPartyId(payload.getCompanyId());
counterOrder.setCounterTradingCode(company.getTradingCode());
counterOrder.setQuantityLot(new BigDecimal(payload.getQuantityLot()));
counterOrder.setUpdated(now);
log.info("updating OrderCurrency[{}, {}]", order.getId(), counterOrder.getId());
orderCurrencyImdg.update(order);
orderCurrencyImdg.update(counterOrder);
return null;
}
private RequestInfoUpdate deleteCompany(BaseRequest<CommonDeleteRequest> req) {
CommonDeleteRequest payload = req.getRequestPayload();
IValidator validator = orderCompanyDeleteValidator.apply(payload);
Optional<EnumMessage> error = validator.tillFirstError();
if (error.isPresent()) {
return new RequestInfoUpdate(req.getId(), Status.Error, msgResolver.resolve(error.get()));
}
OrderCurrency order = validator.getStored(OrderCurrencyValidationStored.OrderCurrency);
OrderCurrency counterOrder = validator.getStored(OrderCurrencyValidationStored.CounterOrderCurrency);
log.info("deleting OrderCurrency[{}, {}]", order.getId(), counterOrder.getId());
orderCurrencyImdg.delete(order);
orderCurrencyImdg.delete(counterOrder);
return null;
}
}

View file

@ -1,8 +1,9 @@
package ru.spcex.clearing.platform.messaging.domain.cud.order;
import com.fasterxml.jackson.annotation.JsonProperty;
import ru.spcex.platform.classes.base.interfaces.WithId;
public class OrderCompanyUpdateRequest {
public class OrderCompanyUpdateRequest implements WithId {
@JsonProperty
private Long id;
@JsonProperty