company-service account-service http://jira.mfd.msk:8088/browse/CLS-284 ACCOUNT_TERMINATION/ACCOUNT_TERMINATION_STEP2

This commit is contained in:
AKurakin 2023-06-02 20:24:46 +03:00
parent f046ffb3ab
commit 46c6d73cd9
7 changed files with 186 additions and 47 deletions

View file

@ -19,6 +19,7 @@ import ru.spcex.clearing.platform.messaging.domain.cud.account.CorrespondentAcco
import ru.spcex.clearing.platform.messaging.domain.cud.account.CorrespondentAccountUpdateRequest;
import ru.spcex.clearing.platform.messaging.domain.cud.account.sdf01.AccountSdf01Request;
import ru.spcex.clearing.platform.messaging.domain.cud.account.sdf01.AccountSdfRequestPart;
import ru.spcex.clearing.platform.messaging.domain.cud.account.sdf01.AccountTerminationRequest;
import ru.spcex.clearing.platform.messaging.domain.cud.balance.AccountSdfToStatementRequestPart;
import ru.spcex.clearing.platform.messaging.domain.cud.balance.StatementRequest;
import ru.spcex.clearing.platform.messaging.domain.cud.common.CommonIdRequest;
@ -34,6 +35,7 @@ import ru.spcex.platform.enumeration.ServiceStatus;
import ru.spcex.platform.enumeration.WorkflowStatus;
import ru.spcex.platform.imdg.api.Imdg;
import ru.spcex.platform.imdg.api.ImdgProvider;
import ru.spcex.platform.imdg.api.ImdgTransaction;
import ru.spcex.platform.imdg.api.predicate.ImdgPredicate;
import ru.spcex.platform.imdg.api.predicate.ImdgPredicateBuilder;
import ru.spcex.platform.utils.enumeration.EnumMessage;
@ -50,6 +52,7 @@ import java.util.function.Function;
@Service
public class AccountService extends QueueConsumer implements InitializingBean {
private final Logger log = LoggerFactory.getLogger(getClass());
private final ImdgProvider imdgProvider;
private final Imdg<Account> accountMap;
private final Imdg<ClearingMemberCategory> clearingMemberCategoryMap;
private final Imdg<Relation> relationMap;
@ -76,6 +79,7 @@ public class AccountService extends QueueConsumer implements InitializingBean {
@Qualifier("correspondentAccountBlockRequestValidator")
Function<CommonIdRequest, IValidator> accountBlockRequestValidator) {
super(kafkaQueue, kafkaProducer);
this.imdgProvider = imdgProvider;
this.accountMap = imdgProvider.getImdg(
IMDGDistributedNames.Map_Account, Account.class
);
@ -108,6 +112,10 @@ public class AccountService extends QueueConsumer implements InitializingBean {
callback(CommonIdRequest.class)
.setFunction(this::correspondentAccountBlock)
.forDestination(Consts.DESTINATION_CORRESPONDENT_ACCOUNT_BLOCK, callbacks::put);
callback(AccountTerminationRequest.class)
.setFunction(this::accountTerminationForCompany)
.forDestination(Consts.ACCOUNT_TERMINATION, callbacks::put);
init();
}
@ -184,6 +192,48 @@ public class AccountService extends QueueConsumer implements InitializingBean {
return null;
}
public RequestInfoUpdate accountTerminationForCompany(BaseRequest<AccountTerminationRequest> userRequest) {
log.debug("AccountTerminationRequest received, id={}", userRequest.getId());
RequestInfoUpdate requestInfoUpdate = userRoleVerification.validateRoleAndGetResult(userRequest);
if (requestInfoUpdate != null) return requestInfoUpdate; // never - system
AccountTerminationRequest req = userRequest.getRequestPayload();
final Long companyId = req.getCompanyId();
Collection<Account> accounts = accountMap.getCollectionObjectsBySQL("companyId=" + companyId);
log.trace("Found {} accounts (will be terminated) by company {}", accounts.size(), companyId);
int blockCount = 0;
ImdgTransaction tx = imdgProvider.newTransaction();
boolean txOk = false;
try {
tx.beginTransaction();
for (Account account : accounts) {
//Account account = accountMap.getSingleObjectByID(request.getId());
if (!ServiceStatus.Blocked.equalsByKey(account.getStatus())) {
account.setStatus(ServiceStatus.Blocked.getKey());
account.setUpdated(Instant.now());
accountMap.update(account);
blockCount++;
}
}
txOk = true;
} finally {
if (txOk)
tx.commitTransaction();
else
tx.beginTransaction();
}
log.debug("For company {} block {} account (of {} account)", companyId, blockCount, accounts.size());
Long reqId = kafkaSender.sendRequestToQueue(Consts.ACCOUNT_TERMINATION_STEP2, req);
log.info("On termination request id={} send to next step {} new request id={}",
userRequest.getId(), Consts.ACCOUNT_TERMINATION_STEP2, reqId);
log.debug("successfully processed, account termination");
return null;
}
@Deprecated
public RequestInfoUpdate accountNewSdf01(BaseRequest<AccountSdf01Request> userRequest) {
log.debug("AccountSdf01Request received");

View file

@ -19,7 +19,9 @@ import ru.spcex.platform.imdg.api.Imdg;
import ru.spcex.platform.imdg.api.ImdgProvider;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@Service
public class AccountNotificationHelper {
@ -51,25 +53,22 @@ public class AccountNotificationHelper {
}
}
log.debug("Sending messages to account-service for {} account", accounts.size());
for (Account account : accounts) {
Long reqId = kafkaSender.sendRequestToQueue(Consts.ACCOUNT_TERMINATION, createAccountsRequest(companyId, account));
log.debug("For account[{}] sended termination request id={}", account.getId(), reqId);
}
Long reqId = kafkaSender.sendRequestToQueue(Consts.ACCOUNT_TERMINATION, createAccountsRequest(companyId, null));
log.debug("Send termination request id={}", reqId);
return null;
}
private AccountTerminationRequest createAccountsRequest(Long companyId, Account account) {
AccountTerminationRequest r = new AccountTerminationRequest();
r.setCompanyId(companyId);
r.setAccount(account.getAccount());
r.setAccountId(account.getId());
return r;
private AccountTerminationRequest createAccountsRequest(Long companyId, Long terminationDocumentId) {
AccountTerminationRequest requestBatch = new AccountTerminationRequest();
requestBatch.setCompanyId(companyId);
requestBatch.setTerminationDocumentId(terminationDocumentId);
return requestBatch;
}
public CompanyErrors accountTerminationByDocumentNotification(Long companyId) {
Collection<Account> accounts = accountMap.getCollectionObjectsBySQL("companyId=" + companyId);
log.trace("Found {} accounts (will be terminated) by company {}", accounts.size(), companyId);
public CompanyErrors accountTerminationByDocumentNotification(Long companyId, Long terminationDocumentId) {
// Collection<Account> accounts = accountMap.getCollectionObjectsBySQL("companyId=" + companyId);
// log.trace("Found {} accounts (will be terminated) by company {}", accounts.size(), companyId);
// for (Account account : accounts) {
// Collection<LiabilitiesClaimsAssets> liabilities = liabilitiesClaimsAssetsMap.getCollectionObjectsBySQL("accountId=" + account.getId());
// for (LiabilitiesClaimsAssets liability : liabilities) {
@ -78,13 +77,12 @@ public class AccountNotificationHelper {
// }
// }
// }
log.debug("Sending messages to account-service for {} account", accounts.size());
for (Account account : accounts) {
Long reqId = kafkaSender.sendRequestToQueue(Consts.ACCOUNT_TERMINATION, createAccountsRequest(companyId, account));
log.debug("For account[{}] send termination by document request id={}", account.getId(), reqId);
}
//todo group!
// ответ должен послаться в DESTINATION_COMPANY_BLOCK = "company-block";
// log.debug("Sending messages to account-service for {} account", accounts.size());
AccountTerminationRequest requestBatch = createAccountsRequest(companyId, terminationDocumentId);
Long reqId = kafkaSender.sendRequestToQueue(Consts.ACCOUNT_TERMINATION, requestBatch);
log.debug("For terminate account send termination by document request id={}", reqId);
// ответ должен послаться в ACCOUNT_TERMINATION_STEP2 = "company-block";
return null;
}

View file

@ -15,6 +15,7 @@ import ru.spcex.clearing.company.error.CompanyErrors;
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.account.sdf01.AccountTerminationRequest;
import ru.spcex.clearing.platform.messaging.domain.cud.common.CommonDeleteRequest;
import ru.spcex.clearing.platform.messaging.domain.cud.company.CompanyNewRequest;
import ru.spcex.clearing.platform.messaging.service.QueueConsumer;
@ -105,6 +106,10 @@ public class CompanyService extends QueueConsumer implements InitializingBean {
callback(CompanyNewRequest.class)
.setFunction(request -> requestHelper.requestFunction(this::updateCompany, request))
.forDestination(Consts.DESTINATION_COMPANY_UPDATE, callbacks::put);
callback(AccountTerminationRequest.class)
.setFunction(request -> requestHelper.requestFunction(this::finishCompanyTermination, request))
.forDestination(Consts.ACCOUNT_TERMINATION_STEP2, callbacks::put);
init();
}
@ -373,4 +378,45 @@ public class CompanyService extends QueueConsumer implements InitializingBean {
return null;
}
/**
* Блокировка компании после блокировки счетов
*
* @return
*/
private synchronized RequestInfoUpdate finishCompanyTermination(BaseRequest<AccountTerminationRequest> terminationRequest) throws ValidationException {
AccountTerminationRequest request = terminationRequest.getRequestPayload();
log.debug("AccountTerminationRequest (block request) received id = {}", terminationRequest.getId());
{ // Валидация
RequestInfoUpdate requestInfoUpdate = userRoleVerification.validateRoleAndGetResult(terminationRequest);
if (requestInfoUpdate != null) return requestInfoUpdate;
}
log.debug("Finish block company {}", request.getCompanyId());
ImdgTransaction tx = imdgProvider.newTransaction();
boolean txOk = false;
tx.beginTransaction();
try {
Company company = companyIMap.getSingleObjectByID(request.getCompanyId());
if (company == null) {
log.warn("Company {} not found", request.getCompanyId());
//return requestHelper.makeErrorResponse(companyBlockRequestBaseRequest, CompanyErrors.CompanyNotFound);
}
if (WorkflowStatus.Blocked.equalsByKey(company.getWorkflowStatus())) {
log.info("Company {} already blocked", company.getId());
//return requestHelper.makeErrorResponse(companyBlockRequestBaseRequest, CompanyErrors.CompanyDisabled);
} else {
company.setWorkflowStatus(WorkflowStatus.Blocked.getKey());
company.setUpdated(Instant.now());
companyIMap.update(company);
}
relationService.cancelAllRelationForCompany(tx, request.getCompanyId());
txOk = true;
} finally {
if (txOk) tx.commitTransaction();
else tx.rollbackTransaction();
}
return null;
}
}

View file

@ -249,14 +249,15 @@ public class ProfileDocumentService extends QueueConsumer implements Initializin
existCompany.getWorkflowStatus(), existCompany.getId());
}
CompanyErrors hasError = accountNotificationHelper.accountTerminationByDocumentNotification(profileDocument.getCompanyId());
CompanyErrors hasError = accountNotificationHelper.accountTerminationByDocumentNotification(profileDocument.getCompanyId(), profileDocument.getId());
if (hasError != null) {
log.error("Error at termination account of company {} by document {}: {}",
profileDocument.getCompanyId(), profileDocument.getId(), hasError + "(" + hasError.getId() + ")");
}
log.debug("Now wait notification from account-service.");
/* todo processXCNT
/* processXCNT
Дальнейшая обработка документа о расторжении, то есть обновление статусов в company и relation, возможна только после ответа об успешном обновлении статуса в account.
ACCOUNT_TERMINATION -> ACCOUNT_TERMINATION_STEP2
1.2. Обновить статус в бизнес-объекте company [profileDocument.companyId=company.id] согласно описанию с тегом "При расторжении".
1.3. Обновить статус в бизнес-объекте relation [profileDocument.companyId=relation.consumerId] согласно описанию с тегом "При расторжении".
*/

View file

@ -137,7 +137,7 @@ public class RelationService extends QueueConsumer implements InitializingBean {
Company company = companyMap.getSingleObjectByID(req.getCompanyId());
Relation existRelation = searchRelation(req.getClearingMemberCategory(), req.getCompanyId());
Relation existRelation = searchRelation(null, req.getClearingMemberCategory(), req.getCompanyId());
boolean isClosedRelation = existRelation != null && IEnumKey.contains
(existRelation.getServiceStatus(), Closed, ServiceStatus.Blocked, ServiceStatus.Suspended);
if (existRelation != null && !isClosedRelation) {
@ -254,13 +254,14 @@ public class RelationService extends QueueConsumer implements InitializingBean {
}
}
private Relation searchRelation(String clearingMemberCategory, Long consumerId) {
private Relation searchRelation(ImdgTransaction tx, String clearingMemberCategory, Long consumerId) {
String svc = serviceForCMC(clearingMemberCategory);
if (svc == null) {
log.warn("Unexpected relation clearingMemberCategory={}", clearingMemberCategory);
return null;
}
Relation existRelation = relationMap.getSingleObjectByFieldValues(Map.of(
Imdg<Relation> currentRelationMap = tx == null ? relationMap : tx.getImdg(IMDGDistributedNames.Map_Relation, Relation.class);
Relation existRelation = currentRelationMap.getSingleObjectByFieldValues(Map.of(
"consumerId", consumerId,
"service", svc
));
@ -333,6 +334,7 @@ public class RelationService extends QueueConsumer implements InitializingBean {
ImdgTransaction tx = imdgProvider.newTransaction();
boolean txOk = false;
try {
tx.beginTransaction();
relationUpdate0(tx, req, relation); // первоначальное ТЗ.
boolean needAddNewRelation = false; // call relationNew0(req, companyId, firstCMC);
@ -501,6 +503,42 @@ public class RelationService extends QueueConsumer implements InitializingBean {
Relation relation = relationMap.getSingleObjectByID(req.getId());
Long companyId = relation.getConsumerId();
Collection<String> companyCMC = selectCompanyClearingMemberCategory(companyId);
boolean companyCMCisBIV = companyCMC.contains(ClearingCategory.B.getKey()) || companyCMC.contains(ClearingCategory.I.getKey())
|| companyCMC.contains(ClearingCategory.V.getKey());
boolean companyCMCisCF = companyCMC.contains(ClearingCategory.C.getKey()) || companyCMC.contains(ClearingCategory.F.getKey());
String firstCMC = companyCMC.isEmpty() ? null : companyCMC.iterator().next();
if (companyCMCisBIV) {
// relation не изменяем
} else if (companyCMCisCF) {
// relation не изменяем
}
// else ? todo bad logic! Проверить ТЗ.
{
ImdgTransaction tx = imdgProvider.newTransaction();
boolean txOk = false;
try {
tx.beginTransaction();
if (!companyCMCisBIV) {
Relation relation1 = selectRelation(tx, companyId, MKR.getKey(), false);
if (relation1 != null) relationClose0(tx, relation1);
}
if (!companyCMCisCF) {
Relation relation1 = selectRelation(tx, companyId, FOND.getKey(), false);
if (relation1 != null) relationClose0(tx, relation1);
}
txOk = true;
} finally {
if (txOk)
tx.commitTransaction();
else
tx.rollbackTransaction();
}
}
if (WorkflowStatus.Blocked.equalsByKey(relation.getServiceStatus())) {
log.warn("Relation {} already blocked.", relation.getId());
} else {
@ -530,7 +568,7 @@ public class RelationService extends QueueConsumer implements InitializingBean {
serviceStatus = serviceForCMC(clearingCategory).getKey();
}
Relation relation = searchRelation(clearingMemberCategory.getClearingMemberCategory(), companyId);
Relation relation = searchRelation(transaction, clearingMemberCategory.getClearingMemberCategory(), companyId);
if (relation == null) {
relation = new Relation();
relation.setId(idSequence.nextId());
@ -561,7 +599,7 @@ public class RelationService extends QueueConsumer implements InitializingBean {
serviceStatus = serviceForCMC(clearingCategory).getKey();
}
Relation relation = searchRelation(clearingMemberCategory.getClearingMemberCategory(), companyId);
Relation relation = searchRelation(transaction, clearingMemberCategory.getClearingMemberCategory(), companyId);
if (relation == null) {
log.debug("Relation for clearingMemberCategory.id={} {} not found", clearingMemberCategory.getId(), clearingMemberCategory.getClearingMemberCategory());
return;
@ -574,6 +612,21 @@ public class RelationService extends QueueConsumer implements InitializingBean {
relationMap.insert(relation);
}
protected void cancelAllRelationForCompany(ImdgTransaction transaction, Long companyId) {
Objects.requireNonNull(companyId, "companyId");
Imdg<Relation> relationMap = transaction.getImdg(IMDGDistributedNames.Map_Relation, Relation.class);
Collection<Relation> relations = relationMap.getCollectionObjectsByFieldValues(Map.of("consumerId", companyId));
int cancelledN = 0;
for (Relation relation : relations) {
if (!Closed.equalsByKey(relation.getServiceStatus())) {
relation.setServiceStatus(Closed.getKey());
relationMap.update(relation);
cancelledN++;
}
}
log.debug("Cancelled {} relation (of {}) by company {}", cancelledN, relations.size(), companyId);
}
protected void cancelingOfAgreementUpdateRelation(ImdgTransaction transaction, Long companyId) {
Imdg<Relation> relationMap = transaction.getImdg(IMDGDistributedNames.Map_Relation, Relation.class);
Relation relation = relationMap.getSingleObjectByFieldValues(Map.of("consumerId", companyId));

View file

@ -132,6 +132,7 @@ public interface Consts {
String LIM_EXPORTED = "lim_exported";
String JOURNAL_SERVICE = "journal-service-exported";
String ACCOUNT_TERMINATION = "account-termination";
String ACCOUNT_TERMINATION_STEP2 = "account-termination-step2";
String BALANCE_ACCOUNT_NEW = "balance-account-new";
String BALANCE_ACCOUNT_UPDATE = "balance-account-update";
String CONTINUE_CLEARING = "continue-clearing";

View file

@ -3,28 +3,10 @@ package ru.spcex.clearing.platform.messaging.domain.cud.account.sdf01;
import com.fasterxml.jackson.annotation.JsonProperty;
public class AccountTerminationRequest {
@JsonProperty
private String account;
@JsonProperty
private Long accountId;
@JsonProperty
private Long companyId;
public String getAccount() {
return account;
}
public void setAccount(String account) {
this.account = account;
}
public Long getAccountId() {
return accountId;
}
public void setAccountId(Long accountId) {
this.accountId = accountId;
}
@JsonProperty
private Long terminationDocumentId;
public Long getCompanyId() {
return companyId;
@ -33,4 +15,12 @@ public class AccountTerminationRequest {
public void setCompanyId(Long companyId) {
this.companyId = companyId;
}
public Long getTerminationDocumentId() {
return terminationDocumentId;
}
public void setTerminationDocumentId(Long terminationDocumentId) {
this.terminationDocumentId = terminationDocumentId;
}
}