Merge pull request #310 from Kitzanos/feature/GEPAFINBE-229
GEPAFINBE-229 (Refactor NDG handling)
This commit is contained in:
@@ -18,7 +18,7 @@ public class AppointmentApiConstant {
|
|||||||
public static final boolean CREA_ANAGRAFICA = Boolean.TRUE;
|
public static final boolean CREA_ANAGRAFICA = Boolean.TRUE;
|
||||||
public static final boolean SALVA_DOCUMENTI = Boolean.TRUE;
|
public static final boolean SALVA_DOCUMENTI = Boolean.TRUE;
|
||||||
public static final String VISURA_PROVIDER = "cerved";
|
public static final String VISURA_PROVIDER = "cerved";
|
||||||
public static final String VISURA_TYPE = "StandardReport";
|
public static final String VISURA_TYPE = "FullReport";
|
||||||
public static final String VISURA_MODE = "visure";
|
public static final String VISURA_MODE = "visure";
|
||||||
public static final String COD_AGENTE = "UtenzaAPIPortal";
|
public static final String COD_AGENTE = "UtenzaAPIPortal";
|
||||||
public static final boolean IS_FROM_RATING = Boolean.FALSE;
|
public static final boolean IS_FROM_RATING = Boolean.FALSE;
|
||||||
@@ -38,4 +38,7 @@ public class AppointmentApiConstant {
|
|||||||
public static final String COD_OPERAZIONE = "codOperazione";
|
public static final String COD_OPERAZIONE = "codOperazione";
|
||||||
public static final String MOTIVAZIONE_ID = "id";
|
public static final String MOTIVAZIONE_ID = "id";
|
||||||
public static final String PRODOTTO_CODE = "code";
|
public static final String PRODOTTO_CODE = "code";
|
||||||
|
|
||||||
|
public static final String WS_ANAGRAFICA_URL= "/WSAnagrafica.getList";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,12 +12,7 @@ import net.gepafin.tendermanagement.config.Translator;
|
|||||||
import net.gepafin.tendermanagement.config.jwt.TokenProvider;
|
import net.gepafin.tendermanagement.config.jwt.TokenProvider;
|
||||||
import net.gepafin.tendermanagement.constants.AppointmentApiConstant;
|
import net.gepafin.tendermanagement.constants.AppointmentApiConstant;
|
||||||
import net.gepafin.tendermanagement.constants.GepafinConstant;
|
import net.gepafin.tendermanagement.constants.GepafinConstant;
|
||||||
import net.gepafin.tendermanagement.entities.ApplicationAmendmentRequestEntity;
|
import net.gepafin.tendermanagement.entities.*;
|
||||||
import net.gepafin.tendermanagement.entities.ApplicationEntity;
|
|
||||||
import net.gepafin.tendermanagement.entities.ApplicationEvaluationEntity;
|
|
||||||
import net.gepafin.tendermanagement.entities.CompanyEntity;
|
|
||||||
import net.gepafin.tendermanagement.entities.DocumentEntity;
|
|
||||||
import net.gepafin.tendermanagement.entities.HubEntity;
|
|
||||||
import net.gepafin.tendermanagement.enums.*;
|
import net.gepafin.tendermanagement.enums.*;
|
||||||
import net.gepafin.tendermanagement.model.request.AppointmentCreationRequest;
|
import net.gepafin.tendermanagement.model.request.AppointmentCreationRequest;
|
||||||
import net.gepafin.tendermanagement.model.request.AppointmentNdgRequest;
|
import net.gepafin.tendermanagement.model.request.AppointmentNdgRequest;
|
||||||
@@ -30,11 +25,7 @@ import net.gepafin.tendermanagement.model.response.AppointmentCreationResponse;
|
|||||||
import net.gepafin.tendermanagement.model.response.AppointmentLoginResponse;
|
import net.gepafin.tendermanagement.model.response.AppointmentLoginResponse;
|
||||||
import net.gepafin.tendermanagement.model.response.DocumentUploadResponse;
|
import net.gepafin.tendermanagement.model.response.DocumentUploadResponse;
|
||||||
import net.gepafin.tendermanagement.model.response.NdgResponse;
|
import net.gepafin.tendermanagement.model.response.NdgResponse;
|
||||||
import net.gepafin.tendermanagement.repositories.ApplicationRepository;
|
import net.gepafin.tendermanagement.repositories.*;
|
||||||
import net.gepafin.tendermanagement.repositories.CompanyRepository;
|
|
||||||
import net.gepafin.tendermanagement.repositories.DocumentRepository;
|
|
||||||
import net.gepafin.tendermanagement.repositories.HubRepository;
|
|
||||||
import net.gepafin.tendermanagement.repositories.UserRepository;
|
|
||||||
import net.gepafin.tendermanagement.service.AmazonS3Service;
|
import net.gepafin.tendermanagement.service.AmazonS3Service;
|
||||||
import net.gepafin.tendermanagement.service.ApplicationService;
|
import net.gepafin.tendermanagement.service.ApplicationService;
|
||||||
import net.gepafin.tendermanagement.service.CompanyService;
|
import net.gepafin.tendermanagement.service.CompanyService;
|
||||||
@@ -62,13 +53,8 @@ import java.io.FileOutputStream;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.*;
|
||||||
import java.util.concurrent.ExecutorService;
|
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
import java.util.concurrent.ScheduledFuture;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Component
|
@Component
|
||||||
@@ -154,7 +140,7 @@ public class AppointmentDao {
|
|||||||
|
|
||||||
private final Map<Long, ScheduledExecutorService> executorMap = new ConcurrentHashMap<>();
|
private final Map<Long, ScheduledExecutorService> executorMap = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
|
||||||
private final ConcurrentHashMap<Long, ExecutorService> threadForDocumentMap = new ConcurrentHashMap<>();
|
private final ConcurrentHashMap<Long, ExecutorService> threadForDocumentMap = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
private static final ThreadLocal<Long> threadLocalHubId = new ThreadLocal<>();
|
private static final ThreadLocal<Long> threadLocalHubId = new ThreadLocal<>();
|
||||||
@@ -177,7 +163,7 @@ public class AppointmentDao {
|
|||||||
|
|
||||||
// Update application status
|
// Update application status
|
||||||
log.info("Updating NDG status to IN_PROGRESS. applicationId: {}", applicationId);
|
log.info("Updating NDG status to IN_PROGRESS. applicationId: {}", applicationId);
|
||||||
application.setNdgStatus(GepafinConstant.NDG_IN_PROGRESS);
|
application.setNdgStatus(NdgStatusEnum.NDG_INITITATED.getValue());
|
||||||
applicationRepository.save(application);
|
applicationRepository.save(application);
|
||||||
|
|
||||||
loggingUtil.addVersionHistory(
|
loggingUtil.addVersionHistory(
|
||||||
@@ -401,6 +387,7 @@ public class AppointmentDao {
|
|||||||
}
|
}
|
||||||
throw new RuntimeException("Max retries exceeded. Failed to login to Odessa.");
|
throw new RuntimeException("Max retries exceeded. Failed to login to Odessa.");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CheckPasswordExpiredOrErrorInResponse(ApplicationEntity application, FeignException.Forbidden forbiddenException) {
|
private void CheckPasswordExpiredOrErrorInResponse(ApplicationEntity application, FeignException.Forbidden forbiddenException) {
|
||||||
|
|
||||||
String responseBody = forbiddenException.contentUTF8();
|
String responseBody = forbiddenException.contentUTF8();
|
||||||
@@ -436,89 +423,89 @@ public class AppointmentDao {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
log.error("Unexpected exception during Odessa login.Error: {}",e.getMessage(), e);
|
log.error("Unexpected exception during Odessa login.Error: {}", e.getMessage(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startAsyncNdgProcessing(Long applicationId) {
|
// private void startAsyncNdgProcessing(Long applicationId) {
|
||||||
// If already polling for this applicationId, do nothing:
|
// // If already polling for this applicationId, do nothing:
|
||||||
if (executorMap.containsKey(applicationId)) {
|
// if (executorMap.containsKey(applicationId)) {
|
||||||
log.warn("Async processing already running for applicationId: {}", applicationId);
|
// log.warn("Async processing already running for applicationId: {}", applicationId);
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
ServletRequestAttributes requestAttributes = new ServletRequestAttributes(request);
|
// ServletRequestAttributes requestAttributes = new ServletRequestAttributes(request);
|
||||||
|
//
|
||||||
ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(runnable -> {
|
// ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(runnable -> {
|
||||||
Thread t = new Thread(runnable);
|
// Thread t = new Thread(runnable);
|
||||||
t.setName("AsyncNdgProcessing-" + applicationId);
|
// t.setName("AsyncNdgProcessing-" + applicationId);
|
||||||
return t;
|
// return t;
|
||||||
});
|
// });
|
||||||
executorMap.put(applicationId, scheduler);
|
// executorMap.put(applicationId, scheduler);
|
||||||
|
//
|
||||||
// Record the start time so we can stop after 2 hours:
|
// // Record the start time so we can stop after 2 hours:
|
||||||
long startTime = System.currentTimeMillis();
|
// long startTime = System.currentTimeMillis();
|
||||||
long twoHoursMs = TimeUnit.HOURS.toMillis(2);
|
// long twoHoursMs = TimeUnit.HOURS.toMillis(2);
|
||||||
long fifteenMin = 15; // in MINUTES
|
// long fifteenMin = 15; // in MINUTES
|
||||||
|
//
|
||||||
// We need a reference to cancel the scheduled task from inside itself when we're done:
|
// // We need a reference to cancel the scheduled task from inside itself when we're done:
|
||||||
AtomicReference<ScheduledFuture<?>> futureRef = new AtomicReference<>();
|
// AtomicReference<ScheduledFuture<?>> futureRef = new AtomicReference<>();
|
||||||
|
//
|
||||||
Runnable pollingTask = () -> {
|
// Runnable pollingTask = () -> {
|
||||||
RequestContextHolder.setRequestAttributes(requestAttributes, true);
|
// RequestContextHolder.setRequestAttributes(requestAttributes, true);
|
||||||
Utils.setHttpServletRequestForThread(request,HttpMethodEnum.POST.getValue(),GepafinConstant.CREATE_NDG, (Long) request.getAttribute(GepafinConstant.USER_ACTION_ID));
|
// Utils.setHttpServletRequestForThread(request,HttpMethodEnum.POST.getValue(),GepafinConstant.CREATE_NDG, (Long) request.getAttribute(GepafinConstant.USER_ACTION_ID));
|
||||||
try {
|
// try {
|
||||||
// 1) If 2 hours have already passed, mark as FAILED and shut down:
|
// // 1) If 2 hours have already passed, mark as FAILED and shut down:
|
||||||
if (System.currentTimeMillis() - startTime > twoHoursMs) {
|
// if (System.currentTimeMillis() - startTime > twoHoursMs) {
|
||||||
ApplicationEntity app = applicationService.validateApplication(applicationId);
|
// ApplicationEntity app = applicationService.validateApplication(applicationId);
|
||||||
log.warn("2-hour timeout reached for applicationId {}. Marking NDG_FAILED.", applicationId);
|
// log.warn("2-hour timeout reached for applicationId {}. Marking NDG_FAILED.", applicationId);
|
||||||
app.setNdgStatus(GepafinConstant.NDG_FAILED);
|
// app.setNdgStatus(GepafinConstant.NDG_FAILED);
|
||||||
applicationRepository.save(app);
|
// applicationRepository.save(app);
|
||||||
|
//
|
||||||
futureRef.get().cancel(false);
|
// futureRef.get().cancel(false);
|
||||||
shutdownScheduler(applicationId);
|
// shutdownScheduler(applicationId);
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
// 2) Otherwise, call processNdgGeneration once:
|
// // 2) Otherwise, call processNdgGeneration once:
|
||||||
processNdgGeneration(applicationId);
|
// processNdgGeneration(applicationId);
|
||||||
|
//
|
||||||
// 3) After return, check if NDG is now set or timed out. If so, cancel & shut down:
|
// // 3) After return, check if NDG is now set or timed out. If so, cancel & shut down:
|
||||||
ApplicationEntity updated = applicationService.validateApplication(applicationId);
|
// ApplicationEntity updated = applicationService.validateApplication(applicationId);
|
||||||
if (isNdgValid(updated.getNdg())) {
|
// if (isNdgValid(updated.getNdg())) {
|
||||||
log.info("NDG found for applicationId {}. Shutting down scheduler.", applicationId);
|
// log.info("NDG found for applicationId {}. Shutting down scheduler.", applicationId);
|
||||||
futureRef.get().cancel(false);
|
// futureRef.get().cancel(false);
|
||||||
shutdownScheduler(applicationId);
|
// shutdownScheduler(applicationId);
|
||||||
} else if (updated.getNdgStatus() != null && updated.getNdgStatus().equals(GepafinConstant.NDG_FAILED)) {
|
// } else if (updated.getNdgStatus() != null && updated.getNdgStatus().equals(GepafinConstant.NDG_FAILED)) {
|
||||||
log.info("NDG status is NDG_FAILED for applicationId {}. Shutting down scheduler.", applicationId);
|
// log.info("NDG status is NDG_FAILED for applicationId {}. Shutting down scheduler.", applicationId);
|
||||||
futureRef.get().cancel(false);
|
// futureRef.get().cancel(false);
|
||||||
shutdownScheduler(applicationId);
|
// shutdownScheduler(applicationId);
|
||||||
}
|
// }
|
||||||
// Otherwise: no NDG yet, not timed out → next run happens in 15 minutes automatically.
|
// // Otherwise: no NDG yet, not timed out → next run happens in 15 minutes automatically.
|
||||||
} catch (Exception ex) {
|
// } catch (Exception ex) {
|
||||||
log.error("Unexpected error during scheduled polling for applicationId {}: {}", applicationId, ex.getMessage(), ex);
|
// log.error("Unexpected error during scheduled polling for applicationId {}: {}", applicationId, ex.getMessage(), ex);
|
||||||
try {
|
// try {
|
||||||
ApplicationEntity checkApp = applicationService.validateApplication(applicationId);
|
// ApplicationEntity checkApp = applicationService.validateApplication(applicationId);
|
||||||
if (System.currentTimeMillis() - startTime > twoHoursMs) {
|
// if (System.currentTimeMillis() - startTime > twoHoursMs) {
|
||||||
log.warn("After exception, 2-hour window passed for applicationId {}. Marking NDG_FAILED.", applicationId);
|
// log.warn("After exception, 2-hour window passed for applicationId {}. Marking NDG_FAILED.", applicationId);
|
||||||
checkApp.setNdgStatus(GepafinConstant.NDG_FAILED);
|
// checkApp.setNdgStatus(GepafinConstant.NDG_FAILED);
|
||||||
applicationRepository.save(checkApp);
|
// applicationRepository.save(checkApp);
|
||||||
|
//
|
||||||
futureRef.get().cancel(false);
|
// futureRef.get().cancel(false);
|
||||||
shutdownScheduler(applicationId);
|
// shutdownScheduler(applicationId);
|
||||||
}
|
// }
|
||||||
} catch (Exception ignore) {
|
// } catch (Exception ignore) {
|
||||||
futureRef.get().cancel(false);
|
// futureRef.get().cancel(false);
|
||||||
shutdownScheduler(applicationId);
|
// shutdownScheduler(applicationId);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
};
|
// };
|
||||||
|
//
|
||||||
// Schedule pollingTask: run now (delay=0), then every fifteen minutes:
|
// // Schedule pollingTask: run now (delay=0), then every fifteen minutes:
|
||||||
ScheduledFuture<?> future = scheduler.scheduleWithFixedDelay(pollingTask, 0, // initial delay = 0 min → run immediately
|
// ScheduledFuture<?> future = scheduler.scheduleWithFixedDelay(pollingTask, 0, // initial delay = 0 min → run immediately
|
||||||
fifteenMin, // subsequent runs every 15 minutes
|
// fifteenMin, // subsequent runs every 15 minutes
|
||||||
TimeUnit.MINUTES);
|
// TimeUnit.MINUTES);
|
||||||
futureRef.set(future);
|
// futureRef.set(future);
|
||||||
}
|
// }
|
||||||
|
|
||||||
private void shutdownScheduler(Long applicationId) {
|
private void shutdownScheduler(Long applicationId) {
|
||||||
|
|
||||||
@@ -529,12 +516,10 @@ public class AppointmentDao {
|
|||||||
log.info("Scheduler shut down for applicationId: {}", applicationId);
|
log.info("Scheduler shut down for applicationId: {}", applicationId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processNdgGeneration(Long applicationId) {
|
private void processNdgGeneration(ApplicationEntity application, CompanyEntity company, HubEntity hub) {
|
||||||
// Validate application, company, and hub
|
// Validate application, company, and hub
|
||||||
|
Long applicationId = application.getId();
|
||||||
log.info("Starting NDG generation process for applicationId: {}", applicationId);
|
log.info("Starting NDG generation process for applicationId: {}", applicationId);
|
||||||
ApplicationEntity application = applicationService.validateApplication(applicationId);
|
|
||||||
CompanyEntity company = companyService.validateCompany(application.getCompanyId());
|
|
||||||
HubEntity hub = hubRepository.findByHubId(application.getHubId());
|
|
||||||
|
|
||||||
if (!hub.getUniqueUuid().equals(defaultHubUuid)) {
|
if (!hub.getUniqueUuid().equals(defaultHubUuid)) {
|
||||||
log.info("Ndg cannot be created for another Hub, it is default for Gepafin.");
|
log.info("Ndg cannot be created for another Hub, it is default for Gepafin.");
|
||||||
@@ -552,7 +537,7 @@ public class AppointmentDao {
|
|||||||
// Try retrieving NDG by VAT number
|
// Try retrieving NDG by VAT number
|
||||||
AppointmentLoginResponse ndgResponse = retrieveNdgByVatNumber(company.getVatNumber(), authorizationToken, hub, application);
|
AppointmentLoginResponse ndgResponse = retrieveNdgByVatNumber(company.getVatNumber(), authorizationToken, hub, application);
|
||||||
if (isNdgValid(ndgResponse.getNdg())) {
|
if (isNdgValid(ndgResponse.getNdg())) {
|
||||||
saveNdgAndIdVisura(application, company, ndgResponse.getNdg());
|
saveNdg(application, company, ndgResponse.getNdg());
|
||||||
log.info("NDG successfully generated for applicationId: {}", applicationId);
|
log.info("NDG successfully generated for applicationId: {}", applicationId);
|
||||||
} else {
|
} else {
|
||||||
log.info("Polling for NDG for applicationId: {}", applicationId);
|
log.info("Polling for NDG for applicationId: {}", applicationId);
|
||||||
@@ -568,7 +553,7 @@ public class AppointmentDao {
|
|||||||
|
|
||||||
log.info("Starting single‐shot NDG polling attempt for applicationId: {}, CompanyId: {}, HubId: {}", application.getId(), company.getId(), hub.getId());
|
log.info("Starting single‐shot NDG polling attempt for applicationId: {}, CompanyId: {}, HubId: {}", application.getId(), company.getId(), hub.getId());
|
||||||
ApplicationEntity oldApplication = Utils.getClonedEntityForData(application);
|
ApplicationEntity oldApplication = Utils.getClonedEntityForData(application);
|
||||||
CompanyEntity oldCompanyEntity=Utils.getClonedEntityForData(company);
|
CompanyEntity oldCompanyEntity = Utils.getClonedEntityForData(company);
|
||||||
long startTime = System.currentTimeMillis();
|
long startTime = System.currentTimeMillis();
|
||||||
long twoHoursMs = TimeUnit.HOURS.toMillis(2);
|
long twoHoursMs = TimeUnit.HOURS.toMillis(2);
|
||||||
|
|
||||||
@@ -589,7 +574,7 @@ public class AppointmentDao {
|
|||||||
|
|
||||||
company.setNdg(fetchedNdg);
|
company.setNdg(fetchedNdg);
|
||||||
application.setNdg(fetchedNdg);
|
application.setNdg(fetchedNdg);
|
||||||
application.setNdgStatus(GepafinConstant.NDG_GENERATED);
|
application.setNdgStatus(NdgStatusEnum.NDG_GENERATED.getValue());
|
||||||
application.setStatus(ApplicationStatusTypeEnum.NDG.getValue());
|
application.setStatus(ApplicationStatusTypeEnum.NDG.getValue());
|
||||||
application.setIdVisura(visuraResponse.getIdVisura());
|
application.setIdVisura(visuraResponse.getIdVisura());
|
||||||
|
|
||||||
@@ -678,10 +663,10 @@ public class AppointmentDao {
|
|||||||
return ndg != null && !ndg.isEmpty();
|
return ndg != null && !ndg.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void saveNdgAndIdVisura(ApplicationEntity application, CompanyEntity company, String ndg) {
|
private void saveNdg(ApplicationEntity application, CompanyEntity company, String ndg) {
|
||||||
|
|
||||||
ApplicationEntity oldApplication = Utils.getClonedEntityForData(application);
|
ApplicationEntity oldApplication = Utils.getClonedEntityForData(application);
|
||||||
CompanyEntity oldCompanyEntity=Utils.getClonedEntityForData(company);
|
CompanyEntity oldCompanyEntity = Utils.getClonedEntityForData(company);
|
||||||
application.setNdg(ndg);
|
application.setNdg(ndg);
|
||||||
application.setNdgStatus(GepafinConstant.NDG_GENERATED);
|
application.setNdgStatus(GepafinConstant.NDG_GENERATED);
|
||||||
application.setStatus(ApplicationStatusTypeEnum.NDG.getValue());
|
application.setStatus(ApplicationStatusTypeEnum.NDG.getValue());
|
||||||
@@ -738,13 +723,13 @@ public class AppointmentDao {
|
|||||||
// Parse and return the NDG response
|
// Parse and return the NDG response
|
||||||
return parseNdgResponse(responseJson);
|
return parseNdgResponse(responseJson);
|
||||||
} catch (FeignException.Forbidden forbiddenException) {
|
} catch (FeignException.Forbidden forbiddenException) {
|
||||||
log.error("403 Forbidden during NDG retrieval | ApplicationId: {}, HubId: {}", application.getId(), hub.getId());
|
log.error("403 Forbidden during NDG retrieval | ApplicationId: {}, HubId: {}", application.getId(), hub.getId());
|
||||||
logForbiddenError();
|
logForbiddenError();
|
||||||
// Regenerate the token and retry
|
// Regenerate the token and retry
|
||||||
String newAuthorizationToken = regenerateTokenAndSave(hub, application);
|
String newAuthorizationToken = regenerateTokenAndSave(hub, application);
|
||||||
return retrieveNdgByVatNumber(vatNumber, newAuthorizationToken, hub, application);
|
return retrieveNdgByVatNumber(vatNumber, newAuthorizationToken, hub, application);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Error during NDG retrieval | ApplicationId: {}, HubId: {}, Message: {}", application.getId(), hub.getId(), e.getMessage(), e);
|
log.error("Error during NDG retrieval | ApplicationId: {}, HubId: {}, Message: {}", application.getId(), hub.getId(), e.getMessage(), e);
|
||||||
throw new RuntimeException("NDG retrieval failed.", e);
|
throw new RuntimeException("NDG retrieval failed.", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -993,7 +978,7 @@ public class AppointmentDao {
|
|||||||
return appointmentCreationResponse;
|
return appointmentCreationResponse;
|
||||||
|
|
||||||
} catch (FeignException.Forbidden forbiddenException) {
|
} catch (FeignException.Forbidden forbiddenException) {
|
||||||
log.error("403 Forbidden received while retrieving template. Attempting to regenerate token and retry. Application ID: {}", applicationId);
|
log.error("403 Forbidden received while retrieving template. Attempting to regenerate token and retry. Application ID: {}", applicationId);
|
||||||
regenerateTokenAndSave(hub, application);
|
regenerateTokenAndSave(hub, application);
|
||||||
return createAppointment(applicationId, createAppointmentRequest);
|
return createAppointment(applicationId, createAppointmentRequest);
|
||||||
}
|
}
|
||||||
@@ -1094,7 +1079,7 @@ public class AppointmentDao {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public AppointmentCreationRequest buildAppointmentCreationRequest(Long applicationId, CreateAppointmentRequest createAppointmentRequest, Long areaCode,
|
public AppointmentCreationRequest buildAppointmentCreationRequest(Long applicationId, CreateAppointmentRequest createAppointmentRequest, Long areaCode,
|
||||||
AppointmentCreationRequest templateRichiestaData) {
|
AppointmentCreationRequest templateRichiestaData) {
|
||||||
|
|
||||||
ApplicationEntity application = applicationService.validateApplication(applicationId);
|
ApplicationEntity application = applicationService.validateApplication(applicationId);
|
||||||
CreateAppointmentRequest.Nota nota = createAppointmentRequest.getNota();
|
CreateAppointmentRequest.Nota nota = createAppointmentRequest.getNota();
|
||||||
@@ -1207,7 +1192,7 @@ public class AppointmentDao {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void uploadDocumentToExternalSystemSync(Long documentId, UploadDocToExternalSystemRequest docToExternalSystemRequest, ApplicationEntity application) {
|
private void uploadDocumentToExternalSystemSync(Long documentId, UploadDocToExternalSystemRequest docToExternalSystemRequest, ApplicationEntity application) {
|
||||||
log.info("Starting sync document upload for documentId: {}", documentId);
|
log.info("Starting sync document upload for documentId: {}", documentId);
|
||||||
// Synchronous upload logic
|
// Synchronous upload logic
|
||||||
DocumentEntity systemDoc = documentDao.validateDocument(documentId);
|
DocumentEntity systemDoc = documentDao.validateDocument(documentId);
|
||||||
|
|
||||||
@@ -1248,7 +1233,7 @@ public class AppointmentDao {
|
|||||||
|
|
||||||
log.info("Document uploaded successfully to external system: {}", parsedResponse);
|
log.info("Document uploaded successfully to external system: {}", parsedResponse);
|
||||||
} catch (FeignException.Forbidden forbiddenException) {
|
} catch (FeignException.Forbidden forbiddenException) {
|
||||||
log.error("403 Forbidden from external system during upload for documentId: {}. Retrying with new token...", documentId);
|
log.error("403 Forbidden from external system during upload for documentId: {}. Retrying with new token...", documentId);
|
||||||
regenerateTokenAndSave(hub, application);
|
regenerateTokenAndSave(hub, application);
|
||||||
uploadDocumentToExternalSystemSync(documentId, docToExternalSystemRequest, application);
|
uploadDocumentToExternalSystemSync(documentId, docToExternalSystemRequest, application);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -1328,4 +1313,107 @@ public class AppointmentDao {
|
|||||||
throw new RuntimeException("Failed to parse response: " + e.getMessage(), e);
|
throw new RuntimeException("Failed to parse response: " + e.getMessage(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
private void startAsyncNdgProcessing(Long applicationId) {
|
||||||
|
if (executorMap.containsKey(applicationId)) {
|
||||||
|
log.warn("Async processing already running for applicationId: {}", applicationId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ApplicationEntity application = applicationService.validateApplication(applicationId);
|
||||||
|
CompanyEntity company = companyService.validateCompany(application.getCompanyId());
|
||||||
|
ApplicationEntity oldApplication = Utils.getClonedEntityForData(application);
|
||||||
|
CompanyEntity oldCompanyEntity = Utils.getClonedEntityForData(company);
|
||||||
|
ServletRequestAttributes requestAttributes = new ServletRequestAttributes(request);
|
||||||
|
ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(r -> {
|
||||||
|
Thread t = new Thread(r);
|
||||||
|
t.setName("AsyncNdgPolling-" + applicationId);
|
||||||
|
return t;
|
||||||
|
});
|
||||||
|
|
||||||
|
executorMap.put(applicationId, scheduler);
|
||||||
|
application.setNdgStatus(NdgStatusEnum.NDG_IN_PROGRESS.getValue());
|
||||||
|
applicationRepository.save(application);
|
||||||
|
loggingUtil.addVersionHistory(
|
||||||
|
VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldApplication).newData(application).build());
|
||||||
|
|
||||||
|
long startTime = System.currentTimeMillis();
|
||||||
|
long twoHoursMs = TimeUnit.HOURS.toMillis(2);
|
||||||
|
long fifteenMin = 15;
|
||||||
|
|
||||||
|
HubEntity hub = hubRepository.findByHubId(application.getHubId());
|
||||||
|
|
||||||
|
// 1. Run full NDG generation logic once upfront
|
||||||
|
processNdgGeneration(application, company, hub);
|
||||||
|
|
||||||
|
AtomicReference<ScheduledFuture<?>> futureRef = new AtomicReference<>();
|
||||||
|
|
||||||
|
// 2. Now define the polling logic ONLY
|
||||||
|
Runnable pollingTask = () -> {
|
||||||
|
RequestContextHolder.setRequestAttributes(requestAttributes, true);
|
||||||
|
Utils.setHttpServletRequestForThread(request, HttpMethodEnum.POST.getValue(),
|
||||||
|
GepafinConstant.CREATE_NDG, (Long) request.getAttribute(GepafinConstant.USER_ACTION_ID));
|
||||||
|
try {
|
||||||
|
|
||||||
|
// Stop if timeout
|
||||||
|
if (System.currentTimeMillis() - startTime > twoHoursMs) {
|
||||||
|
log.warn("Polling timed out for applicationId {}. Marking NDG_FAILED.", applicationId);
|
||||||
|
application.setNdgStatus(GepafinConstant.NDG_FAILED);
|
||||||
|
applicationRepository.save(application);
|
||||||
|
loggingUtil.addVersionHistory(
|
||||||
|
VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldApplication).newData(application).build());
|
||||||
|
|
||||||
|
futureRef.get().cancel(false);
|
||||||
|
shutdownScheduler(applicationId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If NDG already present or marked failed
|
||||||
|
if (isNdgValid(application.getNdg()) || GepafinConstant.NDG_FAILED.equals(application.getNdgStatus())) {
|
||||||
|
log.info("NDG already present or failed for applicationId {}. Stopping polling.", applicationId);
|
||||||
|
futureRef.get().cancel(false);
|
||||||
|
shutdownScheduler(applicationId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only Visura polling here:
|
||||||
|
String visuraListJson = getVisuraList(application.getIdVisura(), hub.getAppointmentAuthTokenId(), application, hub);
|
||||||
|
String parsedNdg = parseNdgFromVisuraListResponse(visuraListJson);
|
||||||
|
|
||||||
|
if (isNdgValid(parsedNdg)) {
|
||||||
|
log.info("Valid NDG parsed from VisuraList: {} | applicationId: {}", parsedNdg, application.getId());
|
||||||
|
|
||||||
|
company.setNdg(parsedNdg);
|
||||||
|
application.setNdg(parsedNdg);
|
||||||
|
application.setNdgStatus(GepafinConstant.NDG_GENERATED);
|
||||||
|
application.setStatus(ApplicationStatusTypeEnum.NDG.getValue());
|
||||||
|
// application.setIdVisura(visuraResponse.getIdVisura());
|
||||||
|
|
||||||
|
companyRepository.save(company);
|
||||||
|
applicationRepository.save(application);
|
||||||
|
loggingUtil.addVersionHistory(
|
||||||
|
VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldApplication).newData(application).build());
|
||||||
|
loggingUtil.addVersionHistory(
|
||||||
|
VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldCompanyEntity).newData(company).build());
|
||||||
|
|
||||||
|
ApplicationEvaluationEntity eval = applicationEvaluationService.validateApplicationEvaluation(application.getApplicationEvaluationId());
|
||||||
|
Map<String, String> placeholders = new HashMap<>();
|
||||||
|
placeholders.put("{{call_name}}", application.getCall().getName());
|
||||||
|
placeholders.put("{{protocol_number}}", String.valueOf(application.getProtocol().getProtocolNumber()));
|
||||||
|
notificationDao.sendNotificationToInstructor(placeholders, eval, NotificationTypeEnum.NDG_GENERATION);
|
||||||
|
notificationDao.sendNotificationToSuperUser(application, placeholders, NotificationTypeEnum.NDG_GENERATION);
|
||||||
|
|
||||||
|
log.info("NDG saved successfully for applicationId: {}", application.getId());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception ex) {
|
||||||
|
log.error("Error during NDG polling: {}", ex.getMessage(), ex);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ScheduledFuture<?> future = scheduler.scheduleWithFixedDelay(pollingTask, fifteenMin, fifteenMin, TimeUnit.MINUTES);
|
||||||
|
futureRef.set(future);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package net.gepafin.tendermanagement.enums;
|
||||||
|
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonValue;
|
||||||
|
|
||||||
|
public enum NdgStatusEnum {
|
||||||
|
|
||||||
|
NDG_INITITATED("NDG_INITITATED"),
|
||||||
|
NDG_GENERATED("NDG_GENERATED"),
|
||||||
|
NDG_IN_PROGRESS("NDG_IN_PROGRESS");
|
||||||
|
|
||||||
|
private String value;
|
||||||
|
|
||||||
|
NdgStatusEnum(String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonValue
|
||||||
|
public String getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user