clearing-service http://jira.mfd.msk:8088/browse/CLS-200 TODO оделать сверку когда уточнят ТЗ и доабвить класс VerificationResult

This commit is contained in:
AKurakin 2022-12-30 16:22:15 +03:00
parent ade57fd62a
commit d36794b95d
4 changed files with 342 additions and 2 deletions

View file

@ -2,6 +2,7 @@ package ru.spcex.clearing.service;
import org.slf4j.Logger;
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;
@ -12,16 +13,19 @@ import java.util.concurrent.Executors;
@Service
@EnableScheduling
public class ClearingService {
public class ClearingService implements DisposableBean {
private final Logger log = LoggerFactory.getLogger(getClass());
private final ExecutorService executor;
private final SdfCreatorBySTLDPayment sdfCreator;
private final PaymentUpdateBySdf04 paymentUpdater;
private final VerificationResultComponent verificationResultComponent;
@Autowired
public ClearingService(SdfCreatorBySTLDPayment sdfCreator, PaymentUpdateBySdf04 paymentUpdater) {
public ClearingService(SdfCreatorBySTLDPayment sdfCreator, PaymentUpdateBySdf04 paymentUpdater,
VerificationResultComponent verificationResultComponent) {
this.sdfCreator = sdfCreator;
this.paymentUpdater = paymentUpdater;
this.verificationResultComponent = verificationResultComponent;
this.executor = Executors.newSingleThreadExecutor();
}
@ -35,4 +39,14 @@ public class ClearingService {
log.info("updating payment.transactionStatus by sdf04 task added to queue");
executor.execute(() -> paymentUpdater.updatePayments(sdf04GroupId));
}
public void executeVerification() {
log.info("execute verification");
executor.execute(() -> verificationResultComponent.executeRevision());
}
@Override
public void destroy() throws Exception {
executor.shutdown();
}
}

View file

@ -28,6 +28,9 @@ public class EventsReceiver extends QueueConsumer implements InitializingBean {
callback(LauncherCommandRequest.class)
.setConsumer(event -> clearingService.sdfCreate())
.forDestination(Task.createOrder.topic(), callbacks::put);
callback(LauncherCommandRequest.class)
.setConsumer(event -> clearingService.executeVerification())
.forDestination(Task.getVerification.topic(), callbacks::put);
init();
}
}

View file

@ -0,0 +1,301 @@
package ru.spcex.clearing.service;
import org.slf4j.Logger;
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.Component;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import ru.clearing.classes.objects.BusinessObject;
import ru.clearing.classes.statics.data.account.Account;
import ru.clearing.classes.statics.data.sdf.SDf01;
import ru.clearing.classes.statics.data.account.AccountBalance;
import ru.clearing.classes.statics.data.sdf.SDf03;
import ru.spcex.clearing.imdg.IMDGDistributedNames;
import ru.spcex.platform.classes.base.interfaces.WithId;
import ru.spcex.platform.enumeration.AccountType;
import ru.spcex.platform.enumeration.ResultStatuses;
import ru.spcex.platform.imdg.api.Imdg;
import ru.spcex.platform.imdg.api.ImdgProvider;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* 1.70. verificationResult - Результаты сверки
* Раздел I - Функционал сверок
*/
@Component
public class VerificationResultComponent {
private final Logger log = LoggerFactory.getLogger(getClass());
// private final ImdgProvider imdgProvider;
private Imdg<SDf01> sdf01Imdg;
private Imdg<AccountBalance> accountBalanceImdg;
private Imdg<Account> accountImdg;
@Autowired
public VerificationResultComponent(ImdgProvider imdgProvider
) {
// this.imdgProvider = imdgProvider;
this.sdf01Imdg = imdgProvider.getImdg(IMDGDistributedNames.Map_SDf01, SDf01.class);
this.accountBalanceImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_AccountBalance, AccountBalance.class);
this.accountImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_Account, Account.class);
//this.idGenerator = imdgProvider.getImdgIdGenerator();
//this.kafkaSender = KafkaSender kafkaSender
}
protected void executeRevision() {
Collection<SDf01> allSDf01 = sdf01Imdg.getAllValues();
Collection<AccountBalance> allAccountBalance = accountBalanceImdg.getAllValues();
Collection<Account> allAccount = accountImdg.getAllValues();
executeRevision(allSDf01, allAccountBalance, allAccount);
//todo результат logic... см. как было в корпе?
}
protected void executeRevision(Collection<SDf01> allSDf01, Collection<AccountBalance> allAccountBalance, Collection<Account> allAccount) {
// 1. Сумму по остаткам
List<Object/*verificationResult*/> vResLst = new ArrayList<>();
/* todo use:
<verificationResult name="Результаты сверки" destination="verification-results" class="com.spicex.Static." table="verification_result">
<clearingCode type="2" length="255" name="Код участника клиринга" shortname="Клиринговый код" searchable="true" sortable="true" visible="true"/>
<accountId type="1" name="Счет УК, по которому проводится сверка" shortname="Счет УК" searchable="true" sortable="true"/>
<inSum type="11" name="Входящая сумма остатков" shortname="Остатки" visible="true" searchable="true" sortable="true"/>
<outIntSum type="11" name="Исходящая сумма остатков, полученная в КС" shortname="Остатки, полученные в КС" visible="true" searchable="true" sortable="true"/>
<outExtSum type="11" name="Исходящая сумма остатков из отчета ПРЦ" shortname="Остатки, полученные из ПРЦ" visible="true" searchable="true" sortable="true"/>
<diffSum type="11" name="Сумма расхождений" shortname="Сумма расхождений" visible="true" searchable="true" sortable="true"/>
<generationId type="1" name="Идентификатор взаимодействия" shortname="ID взаимодействия" searchable="true" sortable="true"/>
<generationStatus type="12" name="Общий статус сверки" shortname="Статус" searchable="true" sortable="true" visible="true" link="resultStatus"/>
<resultStatus type="12" name="Статус сверки" shortname="Статус" searchable="true" sortable="true" visible="true" link="resultStatus"/>
<id type="1" name="Идентификатор записи" shortname="ID" searchable="true" sortable="true"/>
<createdAt type="4" name="Дата и время создания записи" shortname="Создано" searchable="true" sortable="true" ignore="true"/>
<updatedAt type="4" name="Дата и время изменения записи" shortname="Изменено" searchable="true" sortable="true" ignore="true"/>
</verificationResult>
*/
{
Map<String, SDf01> indexSDf01 = index(allSDf01, SDf01::getAccount);
for (AccountBalance account : allAccountBalance) {
VerificationResult vResult = new VerificationResult();
vResult.setAccountId(account.getAccountId());
SDf01 document = indexSDf01.get(account.getAccount());
if (document == null) {
log.warn("s_df01 not found by AccountBalance[{}].account=\"{}\"", account.getId(), account.getAccount());
} else {
BigDecimal remainder = BigDecimal.ZERO;
if (document.getRemainder() != null) {
try {
remainder = new BigDecimal(document.getRemainder());
} catch (NumberFormatException nfe) {
log.warn("SDf01[{}].remainder=\"{}\" is not parseable: {}",
document.getId(), document.getRemainder(), nfe.getMessage());
}
}
BigDecimal balance = account.getCloseBalanceAmount() == null ? BigDecimal.ZERO : account.getCloseBalanceAmount();
//todo сравнить правильно, remainder там без точек. remainder==balance
BigDecimal diff = balance.subtract(remainder).abs();
vResult.setDiffSum(diff);
if (BigDecimal.ZERO.compareTo(diff) == 0) {
vResult.setResultStatus(ResultStatuses.success.getKey());
} else {
vResult.setResultStatus(ResultStatuses.notSuccess.getKey());
}
}
}
}
// 2.1 Номера торговых/клиринговых счетов, соответствующие загруженным в КС
// -sDf01.deal = account.account & accountType=CLRN & accountStatus=UNBL
{
// Map<String, SDf01> indexSDf01 = index(allSDf01, SDf01::getDeal);
Set<String> sDf01Keys = allSDf01.stream()
.filter(sdf01 -> sdf01.getDeal() != null)
.map(sdf01 -> sdf01.getDeal())
.collect(Collectors.toSet());
Set<String> accountKeys = allAccount.stream()
.filter(account -> AccountType.Clrn.equalsByKey(account.getAccountType()) && "UNBL".equals(account.getAccountStatus()))
.map(sdf01 -> sdf01.getAccount())
.collect(Collectors.toSet());
boolean isOk = sDf01Keys.size() == accountKeys.size() &&
sDf01Keys.containsAll(accountKeys) && accountKeys.containsAll(sDf01Keys);
// for (Account account:allAccount) {
// if (AccountType.Clrn.equalsByKey(account.getAccountType()) & "UNBL".equals(account.getAccountStatus())) {
// // todo use enum/const ?
// SDf01 document = indexSDf01.get(account.getAccount());
// if (document == null) {
// //todo найдено несоответствие.
// } else {
// }
// }
// }
}
// 2.2 Количество торговых/клиринговых счетов, соответствующие загруженным в КС - COUNT[sDf01.deal] = COUNT [account.account]
{
long countSDf01 = allSDf01.stream().filter(sDf01 -> sDf01.getDeal() != null).count();
long countAccount = allAccount.stream().filter(account -> account.getAccount() != null).count();
log.info("countSDf01={}, countAccount={}", countSDf01 ,countAccount);
//todo countSDf01 != countAccount
}
// 2.3 Суммарный остаток по счетам
{
//todo не понял условия [sDf01.account = accountBalance.account]
long summSDf01 = 0;
for (SDf01 document : allSDf01) {
if (StringUtils.isEmpty(document.getRemainder())) {
try {
long value = Long.parseLong(document.getRemainder());
summSDf01 += value;
} catch (NumberFormatException nfe) {
log.warn("SDf01[{}].remainder=\"{}\" is not parseable: {}",
document.getId(), document.getRemainder(), nfe.getMessage());
}
} else {
log.debug("SDf01[{}].remainder is null or empty", document.getId());
}
}
long summAccount = 0;
for (AccountBalance account : allAccountBalance) {
if (account.getCloseBalanceAmount() != null) {
long value = account.getCloseBalanceAmount().longValue();
summAccount += value;
}
}
// todo summSDf01==summAccount теперь с точностью и погрешностью сравнений проблема.
}
// 3. Сверка остатков по клиринговым счетам СПВБ
{
//todo не понял [sDf01.account = accountBalance.account & accountType=INFO]
}
}
protected void onSuccessRevisionResult() {
//todo 17.1 - Сообщение пользователю об успешной сверке
}
protected void onDetectDiffRevisionResult() {
//todo 17.2 - Сообщение пользователю в случае выявления расхождений
}
/*
Сумму по остаткам в бизнес-объекте accountBalance - sDf01.remainder = accountBalance.closeBalanceAmount [sDf01.account = accountBalance.account]
2. Расчет контрольных сумм:
2.1 Номера торговых/клиринговых счетов, соответствующие загруженным в КС -sDf01.deal = account.account & accountType=CLRN & accountStatus=UNBL
2.2 Количество торговых/клиринговых счетов, соответствующие загруженным в КС - COUNT[sDf01.deal] = COUNT [account.account]
2.3 Суммарный остаток по счетам - sDf01.remainder = accountBalance.closeBalanceAmount[sDf01.account = accountBalance.account]
3. Сверка остатков по клиринговым счетам СПВБ в бизнес-объекте accountBalance - sDf01.remainder = accountBalance.closeBalanceAmount [sDf01.account = accountBalance.account & accountType=INFO]
*/
<K, V extends WithId> Map<K, V> index(Collection<V> from, Function<V, K> indexFieldExtractor) {
Map<K, V> index = new HashMap<>();
for (V obj : from) {
K key = indexFieldExtractor.apply(obj);
V exist = index.put(key, obj);
if (exist != null) {
log.warn("{} with id={},{} has duplicate Account=\"{}\"",
exist.getClass().getSimpleName(), exist.getId(), obj.getId(), key);
}
}
return index;
}
//todo to classes: VerificationResult!
static class VerificationResult extends BusinessObject {
private String clearingCode; // type="2"
private Long accountId; // type="1"
private BigDecimal inSum; // type="11"
private BigDecimal outIntSum; // type="11"
private BigDecimal outExtSum; // type="11"
private BigDecimal diffSum; // type="11"
private Long generationId; // type="1"
private String generationStatus; // type="12"
private String resultStatus; // type="12"
public String getClearingCode() {
return clearingCode;
}
public void setClearingCode(String clearingCode) {
this.clearingCode = clearingCode;
}
public Long getAccountId() {
return accountId;
}
public void setAccountId(Long accountId) {
this.accountId = accountId;
}
public BigDecimal getInSum() {
return inSum;
}
public void setInSum(BigDecimal inSum) {
this.inSum = inSum;
}
public BigDecimal getOutIntSum() {
return outIntSum;
}
public void setOutIntSum(BigDecimal outIntSum) {
this.outIntSum = outIntSum;
}
public BigDecimal getOutExtSum() {
return outExtSum;
}
public void setOutExtSum(BigDecimal outExtSum) {
this.outExtSum = outExtSum;
}
public BigDecimal getDiffSum() {
return diffSum;
}
public void setDiffSum(BigDecimal diffSum) {
this.diffSum = diffSum;
}
public Long getGenerationId() {
return generationId;
}
public void setGenerationId(Long generationId) {
this.generationId = generationId;
}
public String getGenerationStatus() {
return generationStatus;
}
public void setGenerationStatus(String generationStatus) {
this.generationStatus = generationStatus;
}
public String getResultStatus() {
return resultStatus;
}
public void setResultStatus(String resultStatus) {
this.resultStatus = resultStatus;
}
}
}

View file

@ -0,0 +1,22 @@
package ru.spcex.platform.enumeration;
import ru.spcex.platform.utils.enumeration.IEnumKey;
/**
* ResultStatusDictionary
*/
public enum ResultStatuses implements IEnumKey {
notSuccess("NACK"), // Неуспешно
success("ACK"); // Успешно
private final String key;
ResultStatuses(String key) {
this.key = key;
}
@Override
public String getKey() {
return key;
}
}