diff --git a/backend/src/main/java/ru/micord/ervu/account_applications/component/rpc/UserApplicationListRpcService.java b/backend/src/main/java/ru/micord/ervu/account_applications/component/rpc/UserApplicationListRpcService.java index 1b8cb27d..cfcefb68 100644 --- a/backend/src/main/java/ru/micord/ervu/account_applications/component/rpc/UserApplicationListRpcService.java +++ b/backend/src/main/java/ru/micord/ervu/account_applications/component/rpc/UserApplicationListRpcService.java @@ -6,6 +6,8 @@ import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import ru.micord.ervu.account_applications.security.context.SecurityContext; +import ru.micord.ervu.account_applications.security.model.UserSession; import ru.micord.ervu.account_applications.service.AccountFetchService; import ru.micord.ervu.account_applications.service.UserApplicationListService; @@ -24,16 +26,14 @@ public class UserApplicationListRpcService extends Behavior { private UserApplicationListService applicationListService; @Autowired private AccountFetchService accountService; + @Autowired + private SecurityContext securityContext; @RpcCall public void saveError(long appNumber, String errorMsg) { + UserSession userSession = securityContext.getUserSession(); LOGGER.error("error for application = {}, message: {}", appNumber, errorMsg); - applicationListService.saveError(appNumber, errorMsg); - } - - @RpcCall - public void saveAcceptedStatus(long appNumber) { - applicationListService.saveAcceptedStatus(appNumber); + applicationListService.saveError(appNumber, errorMsg, userSession.name(), userSession.userId()); } @RpcCall diff --git a/backend/src/main/java/ru/micord/ervu/account_applications/kafka/ApplicationStatusListener.java b/backend/src/main/java/ru/micord/ervu/account_applications/kafka/ApplicationStatusListener.java index 777e0252..38e8459f 100644 --- a/backend/src/main/java/ru/micord/ervu/account_applications/kafka/ApplicationStatusListener.java +++ b/backend/src/main/java/ru/micord/ervu/account_applications/kafka/ApplicationStatusListener.java @@ -5,10 +5,13 @@ import com.fasterxml.jackson.databind.ObjectMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.kafka.annotation.KafkaListener; +import org.springframework.messaging.handler.annotation.Header; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import ru.micord.ervu.account_applications.kafka.model.ApplicationStatus; +import ru.micord.ervu.account_applications.security.model.jwt.UserClaims; import ru.micord.ervu.account_applications.security.service.EncryptionService; +import ru.micord.ervu.account_applications.security.service.JwtTokenService; import ru.micord.ervu.account_applications.service.UserApplicationListService; /** @@ -20,17 +23,26 @@ public class ApplicationStatusListener { private final ObjectMapper mapper; private final UserApplicationListService applicationService; private final EncryptionService encryptionService; + private final JwtTokenService jwtTokenService; public ApplicationStatusListener(ObjectMapper mapper, - UserApplicationListService applicationService, EncryptionService encryptionService) { + UserApplicationListService applicationService, EncryptionService encryptionService, + JwtTokenService jwtTokenService) { this.mapper = mapper; this.applicationService = applicationService; this.encryptionService = encryptionService; + this.jwtTokenService = jwtTokenService; } - @KafkaListener(id = "${kafka.application.status.group.id}", topics = "${kafka.application.status}") - public void listenKafkaDomain(String kafkaMessage) { + @KafkaListener(id = "${kafka.application.status.group.id}", + topics = "${kafka.application.status}") + public void listenKafkaDomain(String kafkaMessage, @Header("authorization") String authHeader) { try { + String token = authHeader.replace("Bearer ", ""); + UserClaims userClaims = jwtTokenService.getUserClaims(token); + String name = userClaims.name(); + String userId = userClaims.userId(); + ApplicationStatus applicationStatus = mapper.readValue(kafkaMessage, ApplicationStatus.class); Long applicationNumber = applicationStatus.applicationNumber(); if (applicationStatus.status()) { @@ -38,16 +50,16 @@ public class ApplicationStatusListener { String tempPass = applicationStatus.password(); if (StringUtils.hasText(tempPass)) { String encryptedPassword = encryptionService.encrypt(tempPass); - applicationService.savePassword(applicationNumber, encryptedPassword); + applicationService.savePassword(applicationNumber, encryptedPassword, name, userId); } else { - applicationService.saveAcceptedStatus(applicationNumber); + applicationService.saveAcceptedStatus(applicationNumber, name, userId); } } else { String errorMsg = applicationStatus.errorMsg(); LOGGER.error("error by appNumber = {}, message: {}", applicationNumber, errorMsg); - applicationService.saveError(applicationNumber, errorMsg); + applicationService.saveError(applicationNumber, errorMsg, name, userId); } } catch (JsonProcessingException e) { diff --git a/backend/src/main/java/ru/micord/ervu/account_applications/service/UserApplicationListService.java b/backend/src/main/java/ru/micord/ervu/account_applications/service/UserApplicationListService.java index 96c27042..345dc1f1 100644 --- a/backend/src/main/java/ru/micord/ervu/account_applications/service/UserApplicationListService.java +++ b/backend/src/main/java/ru/micord/ervu/account_applications/service/UserApplicationListService.java @@ -8,8 +8,6 @@ import java.util.Map; import org.springframework.stereotype.Service; import ru.micord.ervu.account_applications.component.dao.AuditDao; import ru.micord.ervu.account_applications.dao.UserApplicationListDao; -import ru.micord.ervu.account_applications.security.context.SecurityContext; -import ru.micord.ervu.account_applications.security.model.UserSession; import utils.DateTimeUtil; import static ru.micord.ervu.account_applications.enums.ApplicationStatus.ACCEPTED; @@ -23,42 +21,37 @@ public class UserApplicationListService { private final UserApplicationListDao dao; private final AuditDao auditDao; - private final SecurityContext securityContext; - public UserApplicationListService(UserApplicationListDao dao, AuditDao auditDao, SecurityContext securityContext) { + public UserApplicationListService(UserApplicationListDao dao, AuditDao auditDao) { this.dao = dao; this.auditDao = auditDao; - this.securityContext = securityContext; } public Map getStatusesBatch(List appNumbers) { return dao.getStatusesBatch(appNumbers); } - public void savePassword(Long appNumber, String encodedPass) { + public void savePassword(Long appNumber, String encodedPass, String name, String userId) { dao.savePassword(appNumber, encodedPass); - saveAuditStatusByAppNumber(appNumber, ACCEPTED.name()); + saveAuditStatusByAppNumber(appNumber, ACCEPTED.name(), name, userId); } public boolean userExists(String login){ return dao.userExists(login); } - public void saveAcceptedStatus(long appNumber) { + public void saveAcceptedStatus(long appNumber, String name, String userId) { dao.saveAcceptedStatus(appNumber); - saveAuditStatusByAppNumber(appNumber, ACCEPTED.name()); + saveAuditStatusByAppNumber(appNumber, ACCEPTED.name(), name, userId); } - public void saveError(long appNumber, String errorMsg) { + public void saveError(long appNumber, String errorMsg, String name, String userId) { dao.saveError(appNumber, errorMsg); - saveAuditStatusByAppNumber(appNumber, AGREED.name()); + saveAuditStatusByAppNumber(appNumber, AGREED.name(), name, userId); } - private void saveAuditStatusByAppNumber(long appNumber, String status) { + private void saveAuditStatusByAppNumber(long appNumber, String status, String name, String userId) { List appIds = auditDao.selectAppListIdsByAppNumber(appNumber); - UserSession userSession = securityContext.getUserSession(); - String name = userSession.name(); - String userId = userSession.userId(); appIds.forEach(id -> { auditDao.insert(id, name, userId, status, Timestamp.valueOf( DateTimeUtil.dateToLocalDateTimeUtc(new Date()))); diff --git a/frontend/src/ts/account_applications/component/button/UserManagementService.ts b/frontend/src/ts/account_applications/component/button/UserManagementService.ts index 76956e32..eb742280 100644 --- a/frontend/src/ts/account_applications/component/button/UserManagementService.ts +++ b/frontend/src/ts/account_applications/component/button/UserManagementService.ts @@ -7,7 +7,7 @@ import { TextField, Visible } from "@webbpm/base-package"; -import {HttpClient, HttpErrorResponse, HttpResponse} from "@angular/common/http"; +import {HttpClient} from "@angular/common/http"; import {FormField} from "../field/FormField"; import {AuthorizationService} from "../../../modules/app/service/authorization.service"; import {ApplicationKind} from "../enum/ApplicationKind"; diff --git a/frontend/src/ts/modules/app/service/authorization.service.ts b/frontend/src/ts/modules/app/service/authorization.service.ts index 57f83c27..d6be7356 100644 --- a/frontend/src/ts/modules/app/service/authorization.service.ts +++ b/frontend/src/ts/modules/app/service/authorization.service.ts @@ -1,4 +1,4 @@ -import {Injectable, OnDestroy} from "@angular/core"; +import {Injectable} from "@angular/core"; import {Subject} from "rxjs"; import {HttpClient} from "@angular/common/http"; diff --git a/frontend/src/ts/modules/app/service/status-update.service.ts b/frontend/src/ts/modules/app/service/status-update.service.ts index 7a23925d..ddc8a1da 100644 --- a/frontend/src/ts/modules/app/service/status-update.service.ts +++ b/frontend/src/ts/modules/app/service/status-update.service.ts @@ -20,23 +20,30 @@ export class StatusUpdateService { } public statusMessage = new BehaviorSubject(null); - private pendingApplications = new Set(); + private pendingApplications = new Map(); private pollingInterval: any; + private readonly MAX_ATTEMPTS = 12; public trackApplication(appNumber: number): void { - this.pendingApplications.add(appNumber); - this.startPolling(); + if (!this.pendingApplications.has(appNumber)) { + this.pendingApplications.set(appNumber, 0); + this.startPolling(); + } } public publishStatus(appNumber: number, accepted: boolean) { this.statusMessage.next({ appNumber: appNumber, - status: accepted ? ApplicationStatus.ACCEPTED : ApplicationStatus.AGREED + status: accepted + ? ApplicationStatus.ACCEPTED + : ApplicationStatus.AGREED }); } private startPolling(): void { - if (this.pendingApplications.size === 0 || this.pollingInterval) return; + if (this.pendingApplications.size === 0 || this.pollingInterval) { + return; + } this.pollingInterval = setInterval(() => { this.checkPendingStatuses(); @@ -51,7 +58,7 @@ export class StatusUpdateService { } private checkPendingStatuses(): void { - const appNumbers = Array.from(this.pendingApplications); + const appNumbers = Array.from(this.pendingApplications.keys()); if (appNumbers.length === 0) { this.stopPolling(); return; @@ -61,16 +68,34 @@ export class StatusUpdateService { .toPromise() .then(responses => { responses.forEach(response => { + const attemptCount = (this.pendingApplications.get(response.appNumber) || 0) + 1; + this.pendingApplications.set(response.appNumber, attemptCount); + if (response.status !== 'SENT') { this.pendingApplications.delete(response.appNumber); this.publishStatus(response.appNumber, response.status === 'ACCEPTED'); } + else if (attemptCount >= this.MAX_ATTEMPTS) { + this.pendingApplications.delete(response.appNumber); + console.warn(`Max attempts exceeded for application ${response.appNumber}`); + } }); if (this.pendingApplications.size === 0) { this.stopPolling(); } }) - .catch(err => console.error('Failed to check statuses', err)); + .catch(err => { + console.error('Failed to check statuses', err); + appNumbers.forEach(appNumber => { + const attemptCount = (this.pendingApplications.get(appNumber) || 0) + 1; + this.pendingApplications.set(appNumber, attemptCount); + + if (attemptCount >= this.MAX_ATTEMPTS) { + this.pendingApplications.delete(appNumber); + console.warn(`Max attempts exceeded for application ${appNumber} due to errors`); + } + }); + }); } } \ No newline at end of file