ialbert 2023-01-31 15:24:57 +03:00
parent 8c4e5c3d40
commit 52ff312044
14 changed files with 259 additions and 28 deletions

View file

@ -0,0 +1,34 @@
package ru.spcex.clearing.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import ru.clearing.classes.statics.data.generated.ClearingMemberCategory;
import ru.spcex.clearing.imdg.IMDGDistributedNames;
import ru.spcex.platform.enumeration.ClearingCategory;
import ru.spcex.platform.imdg.api.Imdg;
import ru.spcex.platform.imdg.api.ImdgProvider;
import ru.spcex.platform.utils.enumeration.IEnumKey;
import java.util.Map;
import java.util.function.Function;
@Configuration
public class SortingConfig {
private final Imdg<ClearingMemberCategory> clearingCategoryImdg;
@Autowired
public SortingConfig(ImdgProvider imdgProvider) {
this.clearingCategoryImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_ClearingMemberCategory, ClearingMemberCategory.class);
}
@Bean
public Function<Long, ClearingCategory> clearingMemberCategoryProvider() {
return companyId -> {
ClearingMemberCategory category = clearingCategoryImdg.getSingleObjectByFieldValues(
Map.of("companyId", companyId));
return IEnumKey.getEnumByKey(ClearingCategory.class,
category.getClearingMemberCategory());
};
}
}

View file

@ -0,0 +1,46 @@
package ru.spcex.clearing.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import ru.clearing.classes.statics.data.execution.ExecutionDeposit;
import ru.clearing.classes.statics.data.generated.ClearingMemberCategory;
import ru.spcex.clearing.imdg.IMDGDistributedNames;
import ru.spcex.clearing.service.order.ExecutionDepositSorter;
import ru.spcex.platform.enumeration.ClearingCategory;
import ru.spcex.platform.imdg.api.Imdg;
import ru.spcex.platform.imdg.api.ImdgProvider;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
@Service
public class Clearing {
private final Imdg<ExecutionDeposit> executionDepositImdg;
private final Imdg<ClearingMemberCategory> clearingCategoryImdg;
private final ExecutionDepositSorter sorter;
@Autowired
public Clearing(ImdgProvider imdgProvider, ExecutionDepositSorter sorter) {
this.executionDepositImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_ExecutionDeposit, ExecutionDeposit.class);
this.clearingCategoryImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_ClearingMemberCategory, ClearingMemberCategory.class);
this.sorter = sorter;
}
public void startClearing() {
//выгружаем ExecutionDeposit с пустым sessionId
Collection<ExecutionDeposit> execDeposits = executionDepositImdg.getCollectionObjectsBySQL("sessionId = null");
//делаем группировку ExecutionDeposit по категории и компании, делаем сортировку
LinkedHashMap<ClearingCategory, List<List<ExecDepositWCategory>>> executionDeposits = sorter.sortExecutionDeposit(execDeposits);
executionDeposits.forEach((category, companies) -> {
for (List<ExecDepositWCategory> companyExecDeposits : companies) {
processSingleCompany(category, companyExecDeposits);
}
});
}
private void processSingleCompany(ClearingCategory category, List<ExecDepositWCategory> executionDeposits) {
}
}

View file

@ -5,7 +5,6 @@ import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import ru.spcex.platform.utils.log.ExceptionUtils;
@ -20,13 +19,15 @@ public class ClearingService implements DisposableBean {
private final SdfCreatorBySTLDPayment sdfCreator;
private final PaymentUpdateBySdf04 paymentUpdater;
private final VerificationResultComponent verificationResultComponent;
private final Clearing clearing;
@Autowired
public ClearingService(SdfCreatorBySTLDPayment sdfCreator, PaymentUpdateBySdf04 paymentUpdater,
VerificationResultComponent verificationResultComponent) {
VerificationResultComponent verificationResultComponent, Clearing clearing) {
this.sdfCreator = sdfCreator;
this.paymentUpdater = paymentUpdater;
this.verificationResultComponent = verificationResultComponent;
this.clearing = clearing;
this.executor = Executors.newSingleThreadExecutor();
}
@ -69,4 +70,15 @@ public class ClearingService implements DisposableBean {
log.debug("Shutdown {}", getClass().getSimpleName());
executor.shutdown();
}
public void executeClearing() {
log.info("start clearing");
executor.execute(() -> {
try {
clearing.startClearing();
} catch (Throwable e) {
log.error("{}", ExceptionUtils.getStackTrace(e));
}
});
}
}

View file

@ -31,6 +31,9 @@ public class EventsReceiver extends QueueConsumer implements InitializingBean {
callback(LauncherCommandRequest.class)
.setConsumer(event -> clearingService.executeVerification())
.forDestination(Task.getVerification.topic(), callbacks::put);
callback(Object.class) //todo check Object suitable
.setConsumer(event -> clearingService.executeClearing())
.forDestination(Task.startOfClearing.topic(), callbacks::put);
init();
}
}

View file

@ -0,0 +1,26 @@
package ru.spcex.clearing.service;
import ru.clearing.classes.statics.data.execution.ExecutionDeposit;
import ru.spcex.platform.enumeration.ClearingCategory;
public class ExecDepositWCategory {
private final ExecutionDeposit executionDeposit;
private final ClearingCategory clearingMemberCategory;
public ExecDepositWCategory(ExecutionDeposit executionDeposit, ClearingCategory clearingMemberCategory) {
this.executionDeposit = executionDeposit;
this.clearingMemberCategory = clearingMemberCategory;
}
public ExecutionDeposit getExecutionDeposit() {
return executionDeposit;
}
public ClearingCategory getClearingMemberCategory() {
return clearingMemberCategory;
}
public Long getCompanyId() {
return executionDeposit.getCompanyId();
}
}

View file

@ -0,0 +1,108 @@
package ru.spcex.clearing.service.order;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import ru.clearing.classes.statics.data.execution.ExecutionDeposit;
import ru.spcex.clearing.service.ExecDepositWCategory;
import ru.spcex.platform.enumeration.ClearingCategory;
import ru.spcex.platform.enumeration.MoneyFlowSide;
import ru.spcex.platform.utils.enumeration.IEnumKey;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
@Component
public class ExecutionDepositSorter {
private Function<Long, ClearingCategory> clearingMemberCategoryProvider;
@Autowired
public void setClearingMemberCategoryProvider(Function<Long, ClearingCategory> clearingMemberCategoryProvider) {
this.clearingMemberCategoryProvider = clearingMemberCategoryProvider;
}
public LinkedHashMap<ClearingCategory, List<List<ExecDepositWCategory>>> sortExecutionDeposit(Collection<ExecutionDeposit>execDeposits) {
//отсортировали все записи по категориям
// внутри каждой подгруппы по категориям сортируем по компаниям
// обрабатываем почередно все записи первой компании
// когда по первой компании записи закончились, начинаем обработавать все записи второй компании и так далее
// когда по первой категории закончились записи для всех компаниий,
// начинаем аналогично обработавать все записи второй категории и так далее).
//сортируем по категориям
Map<ClearingCategory, List<ExecDepositWCategory>> categoryGrouping = execDeposits.stream().map(executionDeposit -> {
ClearingCategory category = clearingMemberCategoryProvider.apply(executionDeposit.getCompanyId());
return new ExecDepositWCategory(executionDeposit, category);
}).collect(Collectors.groupingBy(ExecDepositWCategory::getClearingMemberCategory));
//внутри категорий сортируем по компаниям
LinkedHashMap<ClearingCategory, List<List<ExecDepositWCategory>>> byCategoryByCompany = new LinkedHashMap<>();
Consumer<ClearingCategory> takeByClearingCategoryAnSortByCompaniId = clearingCategory -> {
List<ExecDepositWCategory> wrappers = categoryGrouping.remove(clearingCategory);
if (wrappers != null) {
List<List<ExecDepositWCategory>> batches = wrappers.stream()
.collect(Collectors.groupingBy(ExecDepositWCategory::getCompanyId))
.entrySet()
.stream()
.sorted(Map.Entry.comparingByKey())
.map(Map.Entry::getValue)
.toList();
byCategoryByCompany.put(clearingCategory, batches);
}
};
//кладем в порядке I, V, B
takeByClearingCategoryAnSortByCompaniId.accept(ClearingCategory.I);
takeByClearingCategoryAnSortByCompaniId.accept(ClearingCategory.V);
takeByClearingCategoryAnSortByCompaniId.accept(ClearingCategory.B);
//для всех остальных порядок не важен, будут последними
categoryGrouping.keySet().forEach(takeByClearingCategoryAnSortByCompaniId);
//метод для дополнительной сортировки внутри групп по категории и компании
BiConsumer<ClearingCategory, Comparator<ExecDepositWCategory>> additionalSorting =
(clearingCategory, comparator) -> byCategoryByCompany
.get(clearingCategory)
.forEach(singleCompanyBatch -> singleCompanyBatch.sort(comparator));
//применяем дополнительные сортировки
//для V сначала Sell, потом Buy, в первую очередь должны обрабатываться новые сделки с наименьшими объемами
additionalSorting.accept(ClearingCategory.V, firstSellThenBuy
.thenComparing(wrapper -> wrapper.getExecutionDeposit().getFirstLegAmount()));
//для V сначала Buy, потом Sell, exchangeExecutionId - возрастающий
additionalSorting.accept(ClearingCategory.B, firstBuyThenSell
.thenComparing(wrapper -> wrapper.getExecutionDeposit().getExchangeExecutionId()));
return byCategoryByCompany;
}
public static Comparator<ExecDepositWCategory> firstSellThenBuy = Comparator.comparing(wrapper -> {
MoneyFlowSide side = IEnumKey.getEnumByKey(MoneyFlowSide.class, wrapper.getExecutionDeposit().getSide());
if (side == null) return Integer.MAX_VALUE;
switch (side) {
case BUY -> {
return 1;
}
case SELL -> {
return 0;
}
default -> {
return Integer.MAX_VALUE;
}
}
});
public static Comparator<ExecDepositWCategory> firstBuyThenSell = Comparator.comparing(wrapper -> {
MoneyFlowSide side = IEnumKey.getEnumByKey(MoneyFlowSide.class, wrapper.getExecutionDeposit().getSide());
if (side == null) return Integer.MAX_VALUE;
switch (side) {
case BUY -> {
return 0;
}
case SELL -> {
return 1;
}
default -> {
return Integer.MAX_VALUE;
}
}
});
}

View file

@ -1,7 +1,7 @@
package ru.spcex.clearing.service.order;
import ru.clearing.classes.statics.data.payment.PaymentInstruction;
import ru.spcex.platform.enumeration.ClearingMemberCategoryD;
import ru.spcex.platform.enumeration.ClearingCategory;
import ru.spcex.platform.utils.enumeration.EnumMessage;
import java.util.Collection;
@ -13,13 +13,13 @@ public class PaymentBatchInfo {
private List<PaymentInstruction> initialOrder;
private List<PaymentInstruction> fromClearingToBank;
private List<PaymentInstruction> fromBankToClearing;
private ClearingMemberCategoryD categoryD;
private ClearingCategory categoryD;
private EnumMessage error;
public Stream<PaymentInstruction> getOrderedPaymentInstructions() {
Function<Collection<PaymentInstruction>, Stream<PaymentInstruction>> safeStream
= paymentInstructions -> paymentInstructions != null ? paymentInstructions.stream() : Stream.empty();
if (categoryD.equals(ClearingMemberCategoryD.B)) {
if (categoryD.equals(ClearingCategory.B)) {
return safeStream.apply(initialOrder);
} else {
return Stream.concat(safeStream.apply(fromClearingToBank), safeStream.apply(fromBankToClearing));
@ -43,11 +43,11 @@ public class PaymentBatchInfo {
this.fromBankToClearing = fromBankToClearing;
}
public ClearingMemberCategoryD getCategoryD() {
public ClearingCategory getCategoryD() {
return categoryD;
}
public void setCategoryD(ClearingMemberCategoryD categoryD) {
public void setCategoryD(ClearingCategory categoryD) {
this.categoryD = categoryD;
}

View file

@ -10,7 +10,7 @@ import ru.clearing.classes.statics.data.payment.PaymentInstruction;
import ru.spcex.clearing.error.ClearingError;
import ru.spcex.clearing.imdg.IMDGDistributedNames;
import ru.spcex.platform.enumeration.AccountType;
import ru.spcex.platform.enumeration.ClearingMemberCategoryD;
import ru.spcex.platform.enumeration.ClearingCategory;
import ru.spcex.platform.enumeration.TransactionStatus;
import ru.spcex.platform.imdg.api.Imdg;
import ru.spcex.platform.imdg.api.ImdgProvider;
@ -46,15 +46,15 @@ public class PaymentInstructionSorter {
List<PaymentBatchInfo> result = new ArrayList<>(2);
log.info("processing PaymentInstruction's generationId={} senderId={} size={}", generationId, senderId, payments.size());
Collection<ClearingMemberCategory> cmcList = clrngMmbrImdg.getCollectionObjectsByFieldValues(Map.of("companyId", senderId));
Collection<ClearingMemberCategoryD> companyCategory = cmcList.stream()
Collection<ClearingCategory> companyCategory = cmcList.stream()
.filter(cat -> cat.getClearingMemberCategory() != null)
.map(cat -> IEnumKey.getEnumByKey(ClearingMemberCategoryD.class, cat.getClearingMemberCategory()))
.map(cat -> IEnumKey.getEnumByKey(ClearingCategory.class, cat.getClearingMemberCategory()))
.collect(Collectors.toSet());
for (ClearingMemberCategoryD categoryValue : companyCategory) {
for (ClearingCategory categoryValue : companyCategory) {
log.debug("For category {}", categoryValue);
PaymentBatchInfo batchInfo = new PaymentBatchInfo();
batchInfo.setCategoryD(categoryValue);
if (ClearingMemberCategoryD.I.equals(categoryValue)) {
if (ClearingCategory.I.equals(categoryValue)) {
EnumMessage error = null;
List<PaymentInstruction> aList = new ArrayList<>();
List<PaymentInstruction> bList = new ArrayList<>();
@ -118,7 +118,7 @@ public class PaymentInstructionSorter {
batchInfo.setFromBankToClearing(bList);
result.add(batchInfo);
}
} else if (ClearingMemberCategoryD.B.equals(categoryValue)) {
} else if (ClearingCategory.B.equals(categoryValue)) {
batchInfo.setInitialOrder(payments);
result.add(batchInfo);
} else {

View file

@ -4,7 +4,7 @@ import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.annotations.XStreamAsAttribute;
import ru.spcex.clearing.reports.reports.ReportId;
import ru.spcex.clearing.reports.reports.ReportWithPeriod;
import ru.spcex.platform.enumeration.ClearingMemberCategoryD;
import ru.spcex.platform.enumeration.ClearingCategory;
import java.time.LocalDate;
import java.time.LocalDateTime;
@ -31,7 +31,7 @@ public class ReportPA_B extends ReportWithPeriod {
"Отчет по нетто-позиции участника клиринга в секции МКР (Уполномоченный банк)",
startDate,
endDate);
this.clearingMemberCategory = ClearingMemberCategoryD.B.getKey();
this.clearingMemberCategory = ClearingCategory.B.getKey();
}
@Override

View file

@ -4,7 +4,7 @@ import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.annotations.XStreamAsAttribute;
import ru.spcex.clearing.reports.reports.ReportId;
import ru.spcex.clearing.reports.reports.ReportWithPeriod;
import ru.spcex.platform.enumeration.ClearingMemberCategoryD;
import ru.spcex.platform.enumeration.ClearingCategory;
import java.time.LocalDate;
import java.time.LocalDateTime;
@ -33,8 +33,8 @@ public class ReportPA_IV extends ReportWithPeriod {
startDate,
endDate);
this.clearingMemberCategory = "%s,%s".formatted(
ClearingMemberCategoryD.I.getKey(),
ClearingMemberCategoryD.V.getKey()
ClearingCategory.I.getKey(),
ClearingCategory.V.getKey()
);
}

View file

@ -5,7 +5,7 @@ import com.thoughtworks.xstream.annotations.XStreamAsAttribute;
import ru.spcex.clearing.reports.reports.Destination;
import ru.spcex.clearing.reports.reports.ReportId;
import ru.spcex.clearing.reports.reports.ReportWithClearingStatus;
import ru.spcex.platform.enumeration.ClearingMemberCategoryD;
import ru.spcex.platform.enumeration.ClearingCategory;
import ru.spcex.platform.enumeration.ClearingStatus;
import java.time.LocalDate;
@ -37,8 +37,8 @@ public class ReportBR_0420315_P1 extends ReportWithClearingStatus {
ClearingStatus.OK.getKey());
this.destination = Destination.BR.getKey();
this.clearingMemberCategory = "%s,%s".formatted(
ClearingMemberCategoryD.I.getKey(),
ClearingMemberCategoryD.V.getKey()
ClearingCategory.I.getKey(),
ClearingCategory.V.getKey()
);
}

View file

@ -6,7 +6,7 @@ import ru.clearing.classes.statics.data.liabilities.LiabilitiesClaimsAssets;
import ru.spcex.clearing.imdg.IMDGDistributedNames;
import ru.spcex.clearing.reports.reports.bt_12_2.ReportPA_B;
import ru.spcex.clearing.reports.services.ReportWithPeriodCollector;
import ru.spcex.platform.enumeration.ClearingMemberCategoryD;
import ru.spcex.platform.enumeration.ClearingCategory;
import ru.spcex.platform.imdg.api.Imdg;
import ru.spcex.platform.imdg.api.ImdgProvider;
import ru.spcex.platform.imdg.api.predicate.ImdgPredicate;
@ -41,7 +41,7 @@ public class ReportPA_B_Collector extends ReportWithPeriodCollector<ReportPA_B>
Collection<ClearingMemberCategory> clearingMemberCategories =
clearingMemberCategoryImdg.getCollectionObjectsByFieldValues(Map.of(
"clearingMemberCategory", ClearingMemberCategoryD.B.getKey()
"clearingMemberCategory", ClearingCategory.B.getKey()
));
Set<Long> companyIds = clearingMemberCategories.stream()
.map(ClearingMemberCategory::getCompanyId)

View file

@ -7,7 +7,7 @@ import ru.clearing.platform.dictionary.ClearingStatusDictionary;
import ru.spcex.clearing.imdg.IMDGDistributedNames;
import ru.spcex.clearing.reports.reports.bt_12_2.ReportPA_IV;
import ru.spcex.clearing.reports.services.ReportWithPeriodCollector;
import ru.spcex.platform.enumeration.ClearingMemberCategoryD;
import ru.spcex.platform.enumeration.ClearingCategory;
import ru.spcex.platform.imdg.api.Imdg;
import ru.spcex.platform.imdg.api.ImdgProvider;
import ru.spcex.platform.imdg.api.predicate.ImdgPredicate;
@ -45,7 +45,7 @@ public class ReportPA_IV_Collector extends ReportWithPeriodCollector<ReportPA_IV
validateDateRange(startDate, endDate);
String clearingMemberCategorySql = "clearingMemberCategory='%s' or clearingMemberCategory='%s'".formatted(
ClearingMemberCategoryD.I.getKey(), ClearingMemberCategoryD.V.getKey()
ClearingCategory.I.getKey(), ClearingCategory.V.getKey()
);
Collection<ClearingMemberCategory> clearingMemberCategories =
clearingMemberCategoryImdg.getCollectionObjectsBySQL(clearingMemberCategorySql);

View file

@ -2,12 +2,14 @@ package ru.spcex.platform.enumeration;
import ru.spcex.platform.utils.enumeration.IEnumKey;
public enum ClearingMemberCategoryD implements IEnumKey {
I("I"), V("V"), B("B");
public enum MoneyFlowSide implements IEnumKey {
BUY("BUY"),
SELL("SELL"),
;
private final String key;
ClearingMemberCategoryD(String key) {
MoneyFlowSide(String key) {
this.key = key;
}