clearing-service http://jira.mfd.msk:8088/browse/CLS-200 TODO оделать сверку когда уточнят ТЗ и доабвить класс VerificationResult
This commit is contained in:
parent
ade57fd62a
commit
d36794b95d
4 changed files with 342 additions and 2 deletions
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue