diff --git a/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/config/validation/ValidationConfig.java b/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/config/validation/ValidationConfig.java index c586e2fd7..d4e84bd42 100644 --- a/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/config/validation/ValidationConfig.java +++ b/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/config/validation/ValidationConfig.java @@ -40,6 +40,7 @@ public class ValidationConfig { addImdg.accept(IMDGDistributedNames.Map_Contact, Contact.class); addImdg.accept(IMDGDistributedNames.Map_Relation, Relation.class); + addImdg.accept(IMDGDistributedNames.Map_ServiceStatusDictionary, ServiceStatusDictionary.class); addImdg.accept(IMDGDistributedNames.Map_WorkflowStatusDictionary, WorkflowStatusDictionary.class); addImdg.accept(IMDGDistributedNames.Map_CompanySymbols, CompanySymbols.class); addImdg.accept(IMDGDistributedNames.Map_CompanySymbolDictionary, CompanySymbolDictionary.class); diff --git a/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/service/AccountNotificationHelper.java b/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/service/AccountNotificationHelper.java index 029c5c890..6c2180b2b 100644 --- a/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/service/AccountNotificationHelper.java +++ b/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/service/AccountNotificationHelper.java @@ -69,7 +69,7 @@ public class AccountNotificationHelper { public CompanyErrors accountTerminationByDocumentNotification(Long companyId) { Collection accounts = accountMap.getCollectionObjectsBySQL("companyId=" + companyId); - log.trace("Found {} accounts by company {}", accounts.size(), companyId); + log.trace("Found {} accounts (will be terminated) by company {}", accounts.size(), companyId); // for (Account account : accounts) { // Collection liabilities = liabilitiesClaimsAssetsMap.getCollectionObjectsBySQL("accountId=" + account.getId()); // for (LiabilitiesClaimsAssets liability : liabilities) { @@ -83,6 +83,7 @@ public class AccountNotificationHelper { 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"; return null; } diff --git a/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/service/ProfileDocumentService.java b/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/service/ProfileDocumentService.java index 69e18e241..f3ad1e08d 100644 --- a/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/service/ProfileDocumentService.java +++ b/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/service/ProfileDocumentService.java @@ -254,6 +254,7 @@ public class ProfileDocumentService extends QueueConsumer implements Initializin 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 Дальнейшая обработка документа о расторжении, то есть обновление статусов в company и relation, возможна только после ответа об успешном обновлении статуса в account. 1.2. Обновить статус в бизнес-объекте company [profileDocument.companyId=company.id] согласно описанию с тегом "При расторжении". diff --git a/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/service/RelationService.java b/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/service/RelationService.java index 9f355922c..21084def8 100644 --- a/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/service/RelationService.java +++ b/clearing-parent/company-service/src/main/java/ru/spcex/clearing/company/service/RelationService.java @@ -1,5 +1,6 @@ package ru.spcex.clearing.company.service; +import org.apache.commons.lang3.StringUtils; import org.apache.kafka.clients.consumer.Consumer; import org.apache.kafka.clients.producer.Producer; import org.slf4j.Logger; @@ -36,10 +37,9 @@ import ru.spcex.platform.utils.error.ValidationException; import ru.spcex.platform.utils.validation.IValidator; import java.time.Instant; -import java.util.Collection; -import java.util.Map; -import java.util.Objects; +import java.util.*; import java.util.function.Function; +import java.util.stream.Collectors; import static ru.spcex.platform.enumeration.Service.FOND; import static ru.spcex.platform.enumeration.Service.MKR; @@ -55,6 +55,7 @@ public class RelationService extends QueueConsumer implements InitializingBean { protected ImdgId idSequence; protected Imdg relationMap; protected Imdg companyMap; + protected Imdg clearingMemberCategoryImdg; protected AccountNotificationHelper accountNotification; protected UserRoleVerification userRoleVerification; protected RequestHelper requestHelper; @@ -90,6 +91,7 @@ public class RelationService extends QueueConsumer implements InitializingBean { relationMap = imdgProvider.getImdg(IMDGDistributedNames.Map_Relation, Relation.class); companyMap = imdgProvider.getImdg(IMDGDistributedNames.Map_Company, Company.class); + clearingMemberCategoryImdg = imdgProvider.getImdg(IMDGDistributedNames.Map_ClearingMemberCategory, ClearingMemberCategory.class); this.requestHelper = requestHelper.setLogger(log); } @@ -177,11 +179,12 @@ public class RelationService extends QueueConsumer implements InitializingBean { WorkflowStatus.Blocked.equalsByKey(company.getWorkflowStatus()) ? Closed.getKey() : null; log.debug("Request has no status. Set status by company[{}].status={}. Relation[{}].serviceSStatus={}", - company.getId(), company.getWorkflowStatus(), toStatus); + company.getId(), company.getWorkflowStatus(), relation.getId(), toStatus); relation.setServiceStatus(toStatus); } } - relation.setService(MKR.getKey()); // MKR + String service = serviceForCMC(req.getClearingMemberCategory()); + relation.setService(service); // MKR relation.setServiceProduct(ServiceProduct.ZERO.getKey()); relation.setComment(req.getComment()); @@ -191,8 +194,42 @@ public class RelationService extends QueueConsumer implements InitializingBean { return null; } - private Relation searchRelation(String clearingMemberCategory, Long consumerId) { - String svc; + Relation relationNew0(ImdgTransaction tx, RelationUpdateRequest req, Long companyId, String clearingMemberCategory) { + Imdg companyMap = tx.getImdg(IMDGDistributedNames.Map_Company, Company.class); + Imdg relationMap = tx.getImdg(IMDGDistributedNames.Map_Relation, Relation.class); + Company company = companyMap.getSingleObjectByID(companyId); + + Relation relation = new Relation(); + relation.setId(idSequence.nextId()); + relation.setCreated(Instant.now()); + relation.setUpdated(relation.getCreated()); + log.debug("New relation.id={}", relation.getId()); + + relation.setConsumerId(companyId); + relation.setSupplierId(SPVB_ID); // 1 СПВБ + if (req.getServiceStatus() != null) { + relation.setServiceStatus(req.getServiceStatus()); + } else { + String toStatus = WorkflowStatus.Active.equalsByKey(company.getWorkflowStatus()) ? ServiceStatus.Active.getKey() : + WorkflowStatus.Blocked.equalsByKey(company.getWorkflowStatus()) ? Closed.getKey() : + null; + log.debug("Request has no status. Set status by company[{}].status={}. Relation[{}].serviceSStatus={}", + company.getId(), company.getWorkflowStatus(), relation.getId(), toStatus); + relation.setServiceStatus(toStatus); + } + String service = serviceForCMC(clearingMemberCategory); + relation.setService(service); // MKR + relation.setServiceProduct(ServiceProduct.ZERO.getKey()); + relation.setComment(req.getComment()); + + relationMap.insert(relation); + log.debug("New Relation[{}] created.", relation.getId()); + + return null; + } + + String serviceForCMC(String clearingMemberCategory) { + String svc = null; if (ClearingCategory.B.equalsByKey(clearingMemberCategory) || ClearingCategory.I.equalsByKey(clearingMemberCategory) || ClearingCategory.V.equalsByKey(clearingMemberCategory) @@ -202,7 +239,24 @@ public class RelationService extends QueueConsumer implements InitializingBean { || ClearingCategory.F.equalsByKey(clearingMemberCategory) ) { svc = ru.spcex.platform.enumeration.Service.FOND.getKey(); - } else { + } + return svc; + } + + ru.spcex.platform.enumeration.Service serviceForCMC(ClearingCategory clearingCategory) { + switch (Objects.requireNonNull(clearingCategory)) { + case B, I, V: + return MKR; + case C, F: + return FOND; + default: + throw new IllegalStateException("Illegal state of clearingCategory " + clearingCategory); + } + } + + private Relation searchRelation(String clearingMemberCategory, Long consumerId) { + String svc = serviceForCMC(clearingMemberCategory); + if (svc == null) { log.warn("Unexpected relation clearingMemberCategory={}", clearingMemberCategory); return null; } @@ -217,6 +271,46 @@ public class RelationService extends QueueConsumer implements InitializingBean { return existRelation; } + Collection selectCompanyClearingMemberCategory(Long companyId) { + Collection companyCategory = clearingMemberCategoryImdg.getCollectionObjectsByFieldValues( + Map.of("companyId", companyId) + ); + return companyCategory.stream() + .map(ClearingMemberCategory::getClearingMemberCategory) + .filter(StringUtils::isNotEmpty) + .distinct() + .collect(Collectors.toList()); + } + + /** + * @param companyId + * @param service + * @param statusActive фильтр, если null - разрешает всё. + * @return + */ + Relation selectRelation(ImdgTransaction tx, Long companyId, String service, Boolean statusActive) { + Objects.requireNonNull(companyId, "companyId"); + Objects.requireNonNull(service, "service"); + Imdg relationMap = tx.getImdg(IMDGDistributedNames.Map_Relation, Relation.class); + Collection relations = relationMap.getCollectionObjectsByFieldValues(Map.of("consumerId", companyId, "service", service)); + Optional r = relations.stream() + .filter(rel -> { + if (statusActive == null) return true; + boolean wasClosed = IEnumKey.contains(rel.getServiceStatus(), + Closed, ServiceStatus.Suspended, ServiceStatus.Blocked); + return statusActive ? !wasClosed : wasClosed; + }).findFirst(); + if (r.isPresent()) { + log.trace("Found relation[{}] by consumerId={} and service={} and active={}", + r.get().getId(), companyId, service, statusActive); + return r.get(); + } else { + log.trace("Relation not found by consumerId={} and service={} and active={}", + companyId, service, statusActive); + return null; + } + } + private synchronized RequestInfoUpdate relationUpdate(BaseRequest relationUpdateBaseRequest) { RelationUpdateRequest req = relationUpdateBaseRequest.getRequestPayload(); log.debug("relation-update request received, BaseRequest.id = {}", relationUpdateBaseRequest.getId()); @@ -228,10 +322,115 @@ public class RelationService extends QueueConsumer implements InitializingBean { if (requestInfoUpdate != null) return requestInfoUpdate; } - Relation relation = relationMap.getSingleObjectByID(req.getId()); - relation.setUpdated(Instant.now()); + final Long companyId = relation.getConsumerId(); + Collection 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(); + ImdgTransaction tx = imdgProvider.newTransaction(); + boolean txOk = false; + try { + relationUpdate0(tx, req, relation); // первоначальное ТЗ. + + boolean needAddNewRelation = false; // call relationNew0(req, companyId, firstCMC); + if (ru.spcex.platform.enumeration.Service.MKR.equalsByKey(relation.getService())) { // relation B or I or V + if (companyCMCisCF) { + // 4.1. Если clearingMemberCategory = B or I or V и для этой компании установлены clearingMemberCategory = C or F: + Relation relation1 = selectRelation(tx, companyId, MKR.getKey(), false); + if (relation1 != null) { + relationUpdate0WithResume(tx, req, relation1); + } else { + needAddNewRelation = true; + } + Relation relation2 = selectRelation(tx, companyId, MKR.getKey(), true); + if (relation2 != null) { + // nothing + } else { + needAddNewRelation = true; + } + } + if (!companyCMCisCF) { + // 4.3. Если clearingMemberCategory = B or I or V и для этой компании не установлены clearingMemberCategory = C or F: + Relation relation1 = selectRelation(tx, companyId, MKR.getKey(), false); + if (relation1 != null) { + relationUpdate0WithResume(tx, req, relation1); + } else { + needAddNewRelation = true; + } + Relation relation2 = selectRelation(tx, companyId, MKR.getKey(), true); + if (relation2 != null) { + // nothing + } else { + needAddNewRelation = true; + } + Relation relation3 = selectRelation(tx, companyId, FOND.getKey(), null); + if (relation3 != null) { + // nothing + relationClose0(tx, relation3); + } + } + } else if (FOND.equalsByKey(relation.getService())) { // relation C or F + if (companyCMCisBIV) { + // 4.2. Если clearingMemberCategory = C or F и для этой компании установлены clearingMemberCategory = B or I or V: + Relation relation1 = selectRelation(tx, companyId, FOND.getKey(), false); + if (relation1 != null) { + relationUpdate0WithResume(tx, req, relation1); + } else { + needAddNewRelation = true; + } + Relation relation2 = selectRelation(tx, companyId, FOND.getKey(), true); + if (relation2 != null) { + // nothing + } else { + needAddNewRelation = true; + } + } + if (!companyCMCisBIV) { + // 4.4. Если clearingMemberCategory = C or F и для этой компании установлены не clearingMemberCategory = B or I or V: + Relation relation1 = selectRelation(tx, companyId, FOND.getKey(), false); + if (relation1 != null) { + relationUpdate0WithResume(tx, req, relation1); + } else { + needAddNewRelation = true; + } + Relation relation2 = selectRelation(tx, companyId, FOND.getKey(), true); + if (relation2 != null) { + // nothing + } else { + needAddNewRelation = true; + } + Relation relation3 = selectRelation(tx, companyId, MKR.getKey(), null); + if (relation3 != null) { + relationClose0(tx, relation3); + } + } + } else { + // unknown type + relationUpdate0(tx, req, relation); + } + + if (needAddNewRelation) { + relationNew0(tx, req, companyId, firstCMC); + } + + txOk = true; + } finally { + if (txOk) { + tx.commitTransaction(); + } else { + tx.rollbackTransaction(); + } + } + + return null; + } + + private synchronized Relation relationUpdate0(ImdgTransaction tx, RelationUpdateRequest req, Relation relation) { + Imdg companyMap = tx.getImdg(IMDGDistributedNames.Map_Company, Company.class); + Imdg relationMap = tx.getImdg(IMDGDistributedNames.Map_Relation, Relation.class); if (req.getServiceStatus() != null) { log.debug("To relation {} set serivceStatus=\"{}\" by user request.", relation.getId(), req.getServiceStatus()); relation.setServiceStatus(req.getServiceStatus()); @@ -254,11 +453,38 @@ public class RelationService extends QueueConsumer implements InitializingBean { } relation.setComment(req.getComment()); + relation.setUpdated(Instant.now()); - relationMap.insert(relation); - log.debug("Update Relation[{}] created.", relation.getId()); + relationMap.update(relation); + log.debug("Update Relation[{}].", relation.getId()); + return relation; + } - return null; + private synchronized Relation relationUpdate0WithResume(ImdgTransaction tx, RelationUpdateRequest req, Relation relation) { + if (ServiceStatus.Reopened.equalsByKey(relation.getServiceStatus())) { + log.debug("Relation {} already reopened(resumed)", relation.getId()); + return relation; + } + relation.setServiceStatus(ServiceStatus.Reopened.getKey()); + relation.setUpdated(Instant.now()); + + Imdg relationMap = tx.getImdg(IMDGDistributedNames.Map_Relation, Relation.class); + relationMap.update(relation); + log.debug("Update Relation[{}] - resume.", relation.getId()); + return relation; + } + + private synchronized void relationClose0(ImdgTransaction tx, Relation relation) { + if (ServiceStatus.Closed.equalsByKey((relation.getServiceStatus()))) { + log.debug("Relation {} already closed.", relation.getId()); + return; + } + relation.setServiceStatus(Closed.getKey()); + relation.setUpdated(Instant.now()); + + Imdg relationMap = tx.getImdg(IMDGDistributedNames.Map_Relation, Relation.class); + relationMap.update(relation); + log.debug("Relation[{}] was closed.", relation.getId()); } @@ -301,19 +527,7 @@ public class RelationService extends QueueConsumer implements InitializingBean { log.warn("clearingMemberCategory not set for companyId={}", companyId); } else { ClearingCategory clearingCategory = getEnumByKey(ClearingCategory.class, clearingMemberCategory.getClearingMemberCategory()); - switch (Objects.requireNonNull(clearingCategory)) { - case B: - case I: - case V: - serviceStatus = MKR.getKey(); - break; - case C: - case F: - serviceStatus = FOND.getKey(); - break; - default: - throw new IllegalStateException("Illegal state of clearingCategory"); - } + serviceStatus = serviceForCMC(clearingCategory).getKey(); } Relation relation = searchRelation(clearingMemberCategory.getClearingMemberCategory(), companyId); @@ -344,19 +558,7 @@ public class RelationService extends QueueConsumer implements InitializingBean { log.warn("clearingMemberCategory not set for companyId={}", companyId); } else { ClearingCategory clearingCategory = getEnumByKey(ClearingCategory.class, clearingMemberCategory.getClearingMemberCategory()); - switch (Objects.requireNonNull(clearingCategory)) { - case B: - case I: - case V: - serviceStatus = MKR.getKey(); - break; - case C: - case F: - serviceStatus = FOND.getKey(); - break; - default: - throw new IllegalStateException("Illegal state of clearingCategory"); - } + serviceStatus = serviceForCMC(clearingCategory).getKey(); } Relation relation = searchRelation(clearingMemberCategory.getClearingMemberCategory(), companyId);