918 lines
46 KiB
Java
918 lines
46 KiB
Java
package net.gepafin.tendermanagement.dao;
|
|
|
|
import com.amazonaws.services.s3.AmazonS3Client;
|
|
import com.amazonaws.services.s3.model.GetObjectRequest;
|
|
import com.fasterxml.jackson.databind.JsonNode;
|
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
import feign.FeignException;
|
|
import io.jsonwebtoken.Claims;
|
|
import jakarta.servlet.http.HttpServletRequest;
|
|
import lombok.extern.slf4j.Slf4j;
|
|
import net.gepafin.tendermanagement.config.Translator;
|
|
import net.gepafin.tendermanagement.config.jwt.TokenProvider;
|
|
import net.gepafin.tendermanagement.constants.AppointmentApiConstant;
|
|
import net.gepafin.tendermanagement.constants.GepafinConstant;
|
|
import net.gepafin.tendermanagement.entities.ApplicationEntity;
|
|
import net.gepafin.tendermanagement.entities.CompanyEntity;
|
|
import net.gepafin.tendermanagement.entities.DocumentEntity;
|
|
import net.gepafin.tendermanagement.entities.HubEntity;
|
|
import net.gepafin.tendermanagement.entities.UserEntity;
|
|
import net.gepafin.tendermanagement.enums.ApplicationStatusTypeEnum;
|
|
import net.gepafin.tendermanagement.enums.DocumentSourceTypeEnum;
|
|
import net.gepafin.tendermanagement.enums.NotificationTypeEnum;
|
|
import net.gepafin.tendermanagement.enums.RoleStatusEnum;
|
|
import net.gepafin.tendermanagement.enums.VersionActionTypeEnum;
|
|
import net.gepafin.tendermanagement.model.request.AppointmentCreationRequest;
|
|
import net.gepafin.tendermanagement.model.request.AppointmentNdgRequest;
|
|
import net.gepafin.tendermanagement.model.request.AppointmentVisuraListRequest;
|
|
import net.gepafin.tendermanagement.model.request.AppointmentVisuraRequest;
|
|
import net.gepafin.tendermanagement.model.request.CreateAppointmentRequest;
|
|
import net.gepafin.tendermanagement.model.request.NotificationReq;
|
|
import net.gepafin.tendermanagement.model.request.UploadDocToExternalSystemRequest;
|
|
import net.gepafin.tendermanagement.model.request.VersionHistoryRequest;
|
|
import net.gepafin.tendermanagement.model.response.AppointmentCreationResponse;
|
|
import net.gepafin.tendermanagement.model.response.AppointmentLoginResponse;
|
|
import net.gepafin.tendermanagement.model.response.DocumentUploadResponse;
|
|
import net.gepafin.tendermanagement.model.response.NdgResponse;
|
|
import net.gepafin.tendermanagement.repositories.ApplicationRepository;
|
|
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.ApplicationService;
|
|
import net.gepafin.tendermanagement.service.CompanyService;
|
|
import net.gepafin.tendermanagement.service.feignClient.AppointmentApiService;
|
|
import net.gepafin.tendermanagement.util.LoggingUtil;
|
|
import net.gepafin.tendermanagement.util.Utils;
|
|
import net.gepafin.tendermanagement.web.rest.api.errors.CustomValidationException;
|
|
import net.gepafin.tendermanagement.web.rest.api.errors.Status;
|
|
import org.springframework.beans.BeanUtils;
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
import org.springframework.beans.factory.annotation.Value;
|
|
import org.springframework.http.HttpStatus;
|
|
import org.springframework.http.MediaType;
|
|
import org.springframework.http.ResponseEntity;
|
|
import org.springframework.mock.web.MockMultipartFile;
|
|
import org.springframework.stereotype.Component;
|
|
import org.springframework.web.multipart.MultipartFile;
|
|
|
|
import java.io.File;
|
|
import java.io.FileInputStream;
|
|
import java.io.FileOutputStream;
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
import java.util.ArrayList;
|
|
import java.util.Collections;
|
|
import java.util.HashMap;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Objects;
|
|
import java.util.concurrent.ConcurrentHashMap;
|
|
import java.util.concurrent.ExecutorService;
|
|
import java.util.concurrent.Executors;
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
@Slf4j
|
|
@Component
|
|
public class AppointmentDao {
|
|
|
|
@Value("${appointment.portal.user}")
|
|
private String user;
|
|
|
|
@Value("${appointment.portal.password}")
|
|
private String password;
|
|
|
|
@Value("${appointment.portal.source}")
|
|
private String source;
|
|
|
|
@Value("${appointment.portal.context}")
|
|
private String context;
|
|
|
|
@Value("${default.hub.uuid}")
|
|
private String defaultHubUuid;
|
|
|
|
@Value("${aws.s3.url}")
|
|
private String s3Url;
|
|
|
|
@Value("${aws.s3.bucket.name}")
|
|
private String OLD_BUCKET;
|
|
|
|
@Value("${flagDaFirmare}")
|
|
private Boolean flagDaFirmare;
|
|
|
|
@Autowired
|
|
private HubRepository hubRepository;
|
|
|
|
@Autowired
|
|
private AppointmentApiService appointmentApiService;
|
|
|
|
@Autowired
|
|
private ApplicationService applicationService;
|
|
|
|
@Autowired
|
|
private CompanyService companyService;
|
|
|
|
@Autowired
|
|
private ApplicationRepository applicationRepository;
|
|
|
|
@Autowired
|
|
private CompanyRepository companyRepository;
|
|
|
|
@Autowired
|
|
private DocumentDao documentDao;
|
|
|
|
@Autowired
|
|
private AmazonS3Client s3Client;
|
|
|
|
@Autowired
|
|
private DocumentRepository documentRepository;
|
|
|
|
@Autowired
|
|
private HttpServletRequest request;
|
|
|
|
@Autowired
|
|
private LoggingUtil loggingUtil;
|
|
|
|
@Autowired
|
|
private TokenProvider tokenProvider;
|
|
|
|
@Autowired
|
|
private NotificationDao notificationDao;
|
|
|
|
@Autowired
|
|
private UserRepository userRepository;
|
|
|
|
private final Map<Long, ExecutorService> executorMap = new ConcurrentHashMap<>();
|
|
|
|
private final ConcurrentHashMap<Long, ExecutorService> threadForDocumentMap = new ConcurrentHashMap<>();
|
|
|
|
private static final ThreadLocal<Long> threadLocalHubId = new ThreadLocal<>();
|
|
|
|
public NdgResponse checkNdgForAppointment(Long applicationId) {
|
|
|
|
ApplicationEntity application = applicationService.validateApplication(applicationId);
|
|
NdgResponse ndgResponse = new NdgResponse();
|
|
if (application.getNdgStatus() != null && application.getNdgStatus().equalsIgnoreCase(GepafinConstant.NDG_IN_PROGRESS)) {
|
|
throw new CustomValidationException(Status.SUCCESS, Translator.toLocale(GepafinConstant.NDG_GENERATION_IS_IN_PROGRESS));
|
|
}
|
|
|
|
if (application.getNdgStatus() != null && application.getNdgStatus().equalsIgnoreCase(GepafinConstant.NDG_GENERATED) && application.getNdg() != null) {
|
|
ndgResponse.setNdg(application.getNdg());
|
|
return ndgResponse;
|
|
}
|
|
|
|
// Update application status
|
|
application.setNdgStatus(GepafinConstant.NDG_IN_PROGRESS);
|
|
applicationRepository.save(application);
|
|
|
|
// Start async processing
|
|
startAsyncNdgProcessing(applicationId);
|
|
|
|
throw new CustomValidationException(Status.SUCCESS, Translator.toLocale(GepafinConstant.NDG_GENERATION_IS_IN_PROGRESS));
|
|
}
|
|
|
|
private void startAsyncNdgProcessing(Long applicationId) {
|
|
// Check if a thread is already running for this application
|
|
if (executorMap.containsKey(applicationId)) {
|
|
log.warn("Async processing already running for applicationId: {}", applicationId);
|
|
return;
|
|
}
|
|
|
|
// Create a dedicated thread for asynchronous processing
|
|
ExecutorService executor = Executors.newSingleThreadExecutor(runnable -> {
|
|
Thread thread = new Thread(runnable);
|
|
thread.setName("AsyncNdgProcessing-" + applicationId);
|
|
return thread;
|
|
});
|
|
executorMap.put(applicationId, executor);
|
|
|
|
executor.submit(() -> {
|
|
try {
|
|
log.info("Starting async processing for applicationId: {}", applicationId);
|
|
processNdgGeneration(applicationId);
|
|
} catch (Exception e) {
|
|
log.error("Error in async NDG processing for applicationId: {}", applicationId, e);
|
|
} finally {
|
|
// Cleanup resources
|
|
ExecutorService executorToShutdown = executorMap.remove(applicationId);
|
|
if (executorToShutdown != null) {
|
|
executorToShutdown.shutdown();
|
|
}
|
|
log.info("Async processing completed for applicationId: {}", applicationId);
|
|
}
|
|
});
|
|
}
|
|
|
|
private void processNdgGeneration(Long applicationId) {
|
|
// Validate application, company, and hub
|
|
ApplicationEntity application = applicationService.validateApplication(applicationId);
|
|
CompanyEntity company = companyService.validateCompany(application.getCompanyId());
|
|
HubEntity hub = hubRepository.findByHubId(application.getHubId());
|
|
|
|
if (!hub.getUniqueUuid().equals(defaultHubUuid)) {
|
|
log.info("Ndg cannot be created for another Hub, it is default for Gepafin.");
|
|
throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.NO_NDG_FOR_ANOTHER_HUB));
|
|
}
|
|
|
|
try {
|
|
// Authenticate and fetch token if required
|
|
if (hub.getAppointmentAuthTokenId() == null || hub.getAreaCode() == null) {
|
|
authenticateAndSaveToken(hub);
|
|
}
|
|
|
|
String authorizationToken = getBearerToken(hub);
|
|
|
|
// Try retrieving NDG by VAT number
|
|
AppointmentLoginResponse ndgResponse = retrieveNdgByVatNumber(company.getVatNumber(), authorizationToken, hub, application);
|
|
if (isNdgValid(ndgResponse.getNdg())) {
|
|
saveNdgAndIdVisura(application, company, ndgResponse.getNdg(), null);
|
|
log.info("NDG successfully generated for applicationId: {}", applicationId);
|
|
} else {
|
|
// If NDG isn't immediately available, start polling
|
|
handleNdgPolling(application, company, hub, authorizationToken);
|
|
}
|
|
} catch (Exception e) {
|
|
log.error("Error during NDG generation for applicationId: {}", applicationId, e);
|
|
}
|
|
}
|
|
|
|
private void handleNdgPolling(ApplicationEntity application, CompanyEntity company, HubEntity hub, String authorizationToken) {
|
|
|
|
try {
|
|
log.info("Starting NDG polling for applicationId: {}", application.getId());
|
|
long startTime = System.currentTimeMillis();
|
|
|
|
while (true) {
|
|
if (application.getNdg() != null) {
|
|
log.info("NDG retrieved for applicationId: {}", application.getId());
|
|
break;
|
|
}
|
|
|
|
try {
|
|
// Fetch Visura list and attempt to parse NDG
|
|
String visuraListJson = getVisuraList(application.getIdVisura(), authorizationToken, application, hub);
|
|
String ndg = parseNdgFromVisuraListResponse(visuraListJson);
|
|
|
|
if (isNdgValid(ndg)) {
|
|
// CompanyEntity oldCompanyData = Utils.getClonedEntityForData(company);
|
|
// ApplicationEntity oldApplicationData = Utils.getClonedEntityForData(application);
|
|
|
|
company.setNdg(ndg);
|
|
application.setNdg(ndg);
|
|
application.setNdgStatus(GepafinConstant.NDG_GENERATED);
|
|
application.setStatus(ApplicationStatusTypeEnum.NDG.getValue());
|
|
applicationRepository.save(application);
|
|
companyRepository.save(company);
|
|
log.info("NDG saved successfully for applicationId: {}", application.getId());
|
|
|
|
// /** This code is responsible for adding a version history log for the "update application ndg code, status, and Id visura"
|
|
// operation. **/
|
|
// loggingUtil.addVersionHistory(
|
|
// VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldApplicationData)
|
|
// .newData(application).build());
|
|
//
|
|
// /** This code is responsible for adding a version history log for the "update company ndg code" operation. **/
|
|
// loggingUtil.addVersionHistory(
|
|
// VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldCompanyData)
|
|
// .newData(company).build());
|
|
break;
|
|
}
|
|
|
|
// Check if polling has timed out
|
|
if (System.currentTimeMillis() - startTime > TimeUnit.HOURS.toMillis(2)) {
|
|
log.warn("NDG polling timed out for applicationId: {}", application.getId());
|
|
application.setNdgStatus(GepafinConstant.NDG_FAILED);
|
|
applicationRepository.save(application);
|
|
break;
|
|
}
|
|
|
|
// Wait before the next polling attempt
|
|
Thread.sleep(TimeUnit.MINUTES.toMillis(15));
|
|
} catch (InterruptedException e) {
|
|
log.warn("NDG polling interrupted for applicationId: {}", application.getId());
|
|
Thread.currentThread().interrupt();
|
|
break;
|
|
} catch (Exception e) {
|
|
log.error("Error during NDG polling for applicationId: {}", application.getId(), e);
|
|
}
|
|
}
|
|
} finally {
|
|
log.info("NDG polling completed for applicationId: {}", application.getId());
|
|
}
|
|
}
|
|
|
|
private static String getBearerToken(HubEntity hub) {
|
|
|
|
return "Bearer " + hub.getAppointmentAuthTokenId();
|
|
}
|
|
|
|
private boolean isNdgValid(String ndg) {
|
|
|
|
return ndg != null && !ndg.isEmpty();
|
|
}
|
|
|
|
private void saveNdgAndIdVisura(ApplicationEntity application, CompanyEntity company, String ndg, String idVisura) {
|
|
|
|
//cloned for old application and company data
|
|
// ApplicationEntity oldApplicationData = Utils.getClonedEntityForData(application);
|
|
// CompanyEntity oldCompanyData = Utils.getClonedEntityForData(company);
|
|
|
|
application.setNdg(ndg);
|
|
application.setIdVisura(idVisura);
|
|
application.setNdgStatus(GepafinConstant.NDG_GENERATED);
|
|
application.setStatus(ApplicationStatusTypeEnum.NDG.getValue());
|
|
company.setNdg(ndg);
|
|
companyRepository.save(company);
|
|
applicationRepository.save(application);
|
|
Map<String ,String> placeHolders=notificationDao.sendNotificationToBeneficiary(application,NotificationTypeEnum.NDG_GENERATION);
|
|
notificationDao.sendNotificationToSuperUser(application,placeHolders,NotificationTypeEnum.NDG_GENERATION);
|
|
|
|
// /** This code is responsible for adding a version history log for the "update application ndg code, status, and Id visura" operation. **/
|
|
// loggingUtil.addVersionHistory(
|
|
// VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldApplicationData).newData(application).build());
|
|
//
|
|
// /** This code is responsible for adding a version history log for the "update company ndg code" operation. **/
|
|
// loggingUtil.addVersionHistory(VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldCompanyData).newData
|
|
// (company).build());
|
|
|
|
log.info("NDG saved for applicationId: {}, {}", application.getId(), application.getNdg());
|
|
}
|
|
|
|
private String getVisuraList(String idVisura, String authorizationToken, ApplicationEntity application, HubEntity hub) {
|
|
|
|
AppointmentVisuraListRequest visuraListRequest = new AppointmentVisuraListRequest();
|
|
AppointmentVisuraListRequest.VisuraFilter filter = new AppointmentVisuraListRequest.VisuraFilter();
|
|
filter.setIdVisura(idVisura);
|
|
visuraListRequest.setFilter(filter);
|
|
|
|
try {
|
|
String requestJson = Utils.convertObjectToJson(visuraListRequest);
|
|
ResponseEntity<Object> response = appointmentApiService.getVisuraList(requestJson, authorizationToken);
|
|
return Utils.convertObjectToJson(response.getBody());
|
|
} catch (FeignException.Forbidden forbiddenException) {
|
|
log.error("403 Forbidden received while getting visuraList for Ndg code. Regenerating token...");
|
|
// Regenerate the token and retry
|
|
String newAuthorizationToken = regenerateTokenAndSave(hub);
|
|
return getVisuraList(idVisura, newAuthorizationToken, application, hub);
|
|
} catch (Exception e) {
|
|
log.error("Failed to fetch Ndg code: {}", e.getMessage(), e);
|
|
throw new RuntimeException("Error fetching Ndg List", e);
|
|
}
|
|
}
|
|
|
|
private HubEntity authenticateAndSaveToken(HubEntity hub) {
|
|
|
|
// HubEntity oldHubData = Utils.getClonedEntityForData(hub);
|
|
try {
|
|
//code to generate token with payload having "iat" epoch timestamp and secret key with no expiry and send in below method call
|
|
String authJwtToken = Utils.generateAuthTokenForLoginToOdessa();
|
|
log.info("Got the auth for login to odessa {}", authJwtToken);
|
|
hub.setAuthToken(authJwtToken);
|
|
hubRepository.save(hub);
|
|
|
|
// /** This code is responsible for adding a version history log for the "Updating auth token for login api in hub" operation. **/
|
|
// loggingUtil.addVersionHistory(VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldHubData).newData
|
|
// (hub).build());
|
|
|
|
// Prepare the request body (adjust if necessary for login API)
|
|
Map<String, Object> body = Collections.emptyMap();
|
|
// Perform login API call
|
|
ResponseEntity<Object> responseLogin = appointmentApiService.loginWithOdessa(authJwtToken, source, context, user, password, body);
|
|
|
|
// Handle successful login
|
|
if (responseLogin.getStatusCode() == HttpStatus.OK) {
|
|
log.info("Login successful to odessa. Parsing response.");
|
|
String loginResponseJson = Utils.convertObjectToJson(responseLogin.getBody());
|
|
AppointmentLoginResponse parsedResponse = parseLoginResponse(loginResponseJson);
|
|
|
|
// Validate and save token
|
|
if (parsedResponse.getTokenId() != null) {
|
|
hub.setAppointmentAuthTokenId(parsedResponse.getTokenId());
|
|
hub.setAreaCode(parsedResponse.getAreaCode());
|
|
hubRepository.save(hub);
|
|
|
|
// /** This code is responsible for adding a version history log for the "inserting token and areaCode from login odessa response for
|
|
// appointment flow api's"
|
|
// * operation. **/
|
|
// loggingUtil.addVersionHistory(
|
|
// VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldHubData).newData(hub)
|
|
// .build());
|
|
|
|
log.info("Saved new authToken and areaCode for Hub.");
|
|
return hub;
|
|
} else {
|
|
throw new RuntimeException("Login response is missing a valid tokenId for login to odessa system, please try again.");
|
|
}
|
|
}
|
|
// Handle non-OK response
|
|
throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.ERROR_IN_GENERATING_NDG_TRY_AGAIN));
|
|
} catch (Exception e) {
|
|
log.error("Failed to authenticate user on Odessa : {}", e.getMessage(), e);
|
|
throw new RuntimeException("Authentication failed on Odessa. try again", e);
|
|
}
|
|
}
|
|
|
|
private AppointmentLoginResponse retrieveNdgByVatNumber(String vatNumber, String authorizationToken, HubEntity hub, ApplicationEntity application) {
|
|
|
|
try {
|
|
// Prepare the NDG request
|
|
AppointmentNdgRequest ndgRequest = getAppointmentNdgRequest(vatNumber);
|
|
// Call the API to retrieve NDG
|
|
ResponseEntity<Object> response = appointmentApiService.getNdgByVatNumber(ndgRequest, authorizationToken);
|
|
String responseJson = Utils.convertObjectToJson(response.getBody());
|
|
// Parse and return the NDG response
|
|
return parseNdgResponse(responseJson);
|
|
} catch (FeignException.Forbidden forbiddenException) {
|
|
logForbiddenError();
|
|
// Regenerate the token and retry
|
|
String newAuthorizationToken = regenerateTokenAndSave(hub);
|
|
return retrieveNdgByVatNumber(vatNumber, newAuthorizationToken, hub, application);
|
|
} catch (Exception e) {
|
|
log.error("Failed to retrieve NDG by VAT number: {}", e.getMessage(), e);
|
|
throw new RuntimeException("NDG retrieval failed.", e);
|
|
}
|
|
}
|
|
|
|
private String regenerateTokenAndSave(HubEntity hub) {
|
|
|
|
try {
|
|
hub = authenticateAndSaveToken(hub);
|
|
return "Bearer " + hub.getAppointmentAuthTokenId();
|
|
} catch (Exception e) {
|
|
log.error("Failed to regenerate token from Odessa: {}", e.getMessage());
|
|
throw new RuntimeException("Token regeneration failed from Odessa.", e);
|
|
}
|
|
}
|
|
|
|
private AppointmentLoginResponse createVisura(CompanyEntity company, String authorizationToken, HubEntity hub) {
|
|
|
|
try {
|
|
String visuraRequest = getAppointmentVisuraRequest(company, hub.getAreaCode());
|
|
ResponseEntity<Object> response = appointmentApiService.createVisura(visuraRequest, authorizationToken);
|
|
String responseJson = Utils.convertObjectToJson(response.getBody());
|
|
return parseVisuraResponse(responseJson);
|
|
} catch (FeignException.Forbidden forbiddenException) {
|
|
logForbiddenError();
|
|
// Regenerate the token and retry
|
|
String newAuthorizationToken = regenerateTokenAndSave(hub);
|
|
return createVisura(company, newAuthorizationToken, hub);
|
|
} catch (Exception e) {
|
|
log.error("Failed to create Visura for Ndg : {}", e.getMessage());
|
|
throw new RuntimeException("Visura creation failed for Ndg.", e);
|
|
}
|
|
}
|
|
|
|
private static void logForbiddenError() {
|
|
|
|
log.error("403 Forbidden received while retrieving NDG. Regenerating token...");
|
|
}
|
|
|
|
private static AppointmentNdgRequest getAppointmentNdgRequest(String vatNumber) {
|
|
|
|
AppointmentNdgRequest request = new AppointmentNdgRequest();
|
|
AppointmentNdgRequest.Filter filter = new AppointmentNdgRequest.Filter();
|
|
filter.setPartitaIva(vatNumber);
|
|
|
|
AppointmentNdgRequest.Pagination pagination = new AppointmentNdgRequest.Pagination();
|
|
pagination.setTargetPage(AppointmentApiConstant.TARGET_PAGE_SIZE);
|
|
pagination.setRecordsPerPage(AppointmentApiConstant.RECORD_PER_PAGE_SIZE);
|
|
|
|
request.setFilter(filter);
|
|
request.setPagination(pagination);
|
|
return request;
|
|
}
|
|
|
|
private static String getAppointmentVisuraRequest(CompanyEntity company, String areaCode) {
|
|
|
|
AppointmentVisuraRequest visuraRequest = new AppointmentVisuraRequest();
|
|
AppointmentVisuraRequest.VisuraInput input = new AppointmentVisuraRequest.VisuraInput();
|
|
input.setPartitaIva(company.getVatNumber());
|
|
input.setCodiceFiscale(company.getCodiceFiscale());
|
|
input.setCodArea(areaCode);
|
|
input.setVisuraMode(AppointmentApiConstant.VISURA_MODE);
|
|
input.setVisuraProvider(AppointmentApiConstant.VISURA_PROVIDER);
|
|
input.setCodAgente(AppointmentApiConstant.COD_AGENTE);
|
|
input.setAnagraficaLegame(AppointmentApiConstant.IS_ANAGRAFICA_LEGAME);
|
|
input.setCreaAnagrafica(AppointmentApiConstant.CREA_ANAGRAFICA);
|
|
input.setFromRating(AppointmentApiConstant.IS_FROM_RATING);
|
|
input.setSalvaDocumenti(AppointmentApiConstant.SALVA_DOCUMENTI);
|
|
input.setVisuraType(AppointmentApiConstant.VISURA_TYPE);
|
|
visuraRequest.setInput(input);
|
|
return Utils.convertObjectToJson(visuraRequest);
|
|
}
|
|
|
|
private String parseNdgFromVisuraListResponse(String jsonResponse) {
|
|
|
|
try {
|
|
ObjectMapper objectMapper = new ObjectMapper();
|
|
JsonNode rootNode = objectMapper.readTree(jsonResponse);
|
|
JsonNode dataNode = rootNode.get(GepafinConstant.DATA_STRING);
|
|
|
|
if (dataNode != null && dataNode.isArray() && dataNode.size() > 0) {
|
|
JsonNode firstEntry = dataNode.get(0);
|
|
JsonNode ndgClienteNode = firstEntry.get("ndgCliente");
|
|
if (ndgClienteNode != null && ndgClienteNode.get("code") != null) {
|
|
String code = ndgClienteNode.get("code").asText();
|
|
return normalizeNullValue(code);
|
|
}
|
|
}
|
|
log.warn("NDG not found in Visura List API response.");
|
|
return null;
|
|
} catch (Exception e) {
|
|
log.error("Failed to parse NDG from Visura List API response: {}", e.getMessage(), e);
|
|
throw new RuntimeException("Error parsing NDG from Visura List API response", e);
|
|
}
|
|
}
|
|
|
|
public AppointmentLoginResponse parseLoginResponse(String jsonResponse) {
|
|
|
|
try {
|
|
ObjectMapper objectMapper = new ObjectMapper();
|
|
JsonNode rootNode = objectMapper.readTree(jsonResponse);
|
|
JsonNode dataNode = rootNode.get(GepafinConstant.DATA_STRING);
|
|
|
|
if (dataNode != null) {
|
|
AppointmentLoginResponse response = new AppointmentLoginResponse();
|
|
response.setTokenId(dataNode.get("tokenId").asText());
|
|
JsonNode areasNode = dataNode.get("areas");
|
|
if (areasNode != null && areasNode.isArray() && areasNode.size() > 0) {
|
|
response.setAreaCode(areasNode.get(0).get("code").asText());
|
|
}
|
|
response.setCompanyId(dataNode.get("companyId").asLong());
|
|
return response;
|
|
} else {
|
|
throw new RuntimeException("Invalid JSON structure: Missing 'data' node.");
|
|
}
|
|
} catch (Exception e) {
|
|
throw new RuntimeException("Failed to parse response from loginApi for odessa: " + e.getMessage(), e);
|
|
}
|
|
}
|
|
|
|
public AppointmentLoginResponse parseVisuraResponse(String jsonResponse) {
|
|
|
|
try {
|
|
ObjectMapper objectMapper = new ObjectMapper();
|
|
JsonNode rootNode = objectMapper.readTree(jsonResponse);
|
|
JsonNode dataNode = rootNode.get(GepafinConstant.DATA_STRING);
|
|
|
|
if (dataNode != null) {
|
|
AppointmentLoginResponse response = new AppointmentLoginResponse();
|
|
response.setIdVisura(normalizeNullValue(dataNode.get(GepafinConstant.ID_VISURA_STRING).asText()));
|
|
response.setNdg(normalizeNullValue(dataNode.get(GepafinConstant.NDG_STRING).asText()));
|
|
return response;
|
|
} else {
|
|
throw new RuntimeException("Invalid JSON structure: Missing 'data' node.");
|
|
}
|
|
} catch (Exception e) {
|
|
throw new RuntimeException("Failed to parse response: " + e.getMessage(), e);
|
|
}
|
|
}
|
|
|
|
public AppointmentLoginResponse parseNdgResponse(String jsonResponse) {
|
|
|
|
try {
|
|
ObjectMapper objectMapper = new ObjectMapper();
|
|
JsonNode rootNode = objectMapper.readTree(jsonResponse);
|
|
JsonNode dataArray = rootNode.get(GepafinConstant.DATA_STRING);
|
|
if (dataArray == null || !dataArray.isArray() || dataArray.isEmpty()) {
|
|
log.info("NDG data is empty or missing in the response.");
|
|
AppointmentLoginResponse emptyResponse = new AppointmentLoginResponse();
|
|
emptyResponse.setNdg(null);
|
|
return emptyResponse;
|
|
}
|
|
JsonNode firstDataEntry = dataArray.get(0);
|
|
AppointmentLoginResponse response = new AppointmentLoginResponse();
|
|
if (firstDataEntry.has(GepafinConstant.NDG_STRING)) {
|
|
response.setNdg(normalizeNullValue(firstDataEntry.get(GepafinConstant.NDG_STRING).asText()));
|
|
}
|
|
return response;
|
|
} catch (Exception e) {
|
|
log.error("Failed to parse response: {}", e.getMessage(), e);
|
|
throw new RuntimeException("Failed to parse NDG response.", e);
|
|
}
|
|
}
|
|
|
|
private String normalizeNullValue(String value) {
|
|
|
|
return (value == null || GepafinConstant.NULL_STRING.equalsIgnoreCase(value.trim())) ? null : value;
|
|
}
|
|
|
|
public AppointmentCreationResponse createAppointment(Long applicationId, CreateAppointmentRequest createAppointmentRequest) {
|
|
// Validate the application
|
|
ApplicationEntity application = applicationService.validateApplication(applicationId);
|
|
|
|
AppointmentCreationResponse appointmentCreationResponse = new AppointmentCreationResponse();
|
|
|
|
ApplicationEntity oldApplicationData = Utils.getClonedEntityForData(application);
|
|
HubEntity hub = hubRepository.findByHubId(application.getHubId());
|
|
|
|
// Check hub UUID and enforce constraints
|
|
if (!hub.getUniqueUuid().equals(defaultHubUuid)) {
|
|
log.info("Appointment cannot be created for another Hub; default is required for Gepafin.");
|
|
throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.NO_APPOINTMENT_FOR_ANOTHER_HUB));
|
|
}
|
|
|
|
try {
|
|
// Pre-check conditions for appointment creation
|
|
if (application.getNdg() != null && !Objects.equals(application.getNdgStatus(), GepafinConstant.NDG_IN_PROGRESS) && application.getAppointmentId() != null) {
|
|
appointmentCreationResponse.setAppointmentId(application.getAppointmentId());
|
|
throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.APPOINTMENT_ALREADY_CREATED));
|
|
// return appointmentCreationResponse;
|
|
}
|
|
|
|
if (application.getNdg() == null && Objects.equals(application.getNdgStatus(), GepafinConstant.NDG_IN_PROGRESS)) {
|
|
throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.NDG_NOT_FOUND_FOR_APPLICATION));
|
|
}
|
|
|
|
// Generate authorization token and fetch template data
|
|
String authorizationToken = getBearerToken(hub);
|
|
ResponseEntity<Object> response = appointmentApiService.getAppointmentTemplateForTemplateCreation(authorizationToken);
|
|
|
|
if (response.getStatusCode() != HttpStatus.OK) {
|
|
log.error("Failed to retrieve appointment template for appointment creation. Status: {}", response.getStatusCode());
|
|
throw new IllegalStateException("Failed to retrieve appointment template for appointment creation");
|
|
}
|
|
|
|
// Parse template data
|
|
String responseDataForTemplate = Utils.convertObjectToJson(response.getBody());
|
|
AppointmentCreationRequest templateRichiestaData = parseTemplateResponseData(responseDataForTemplate);
|
|
|
|
// Build the appointment request body
|
|
AppointmentCreationRequest appointmentCreationRequest = buildAppointmentCreationRequest(applicationId, createAppointmentRequest, hub.getAreaCode(),
|
|
templateRichiestaData);
|
|
String appointmentRequestBody = Utils.convertObjectToJson(appointmentCreationRequest);
|
|
|
|
// Make API call to create the appointment
|
|
ResponseEntity<Object> appointmentResponse = appointmentApiService.createAppointment(authorizationToken, context, appointmentRequestBody);
|
|
String appointmentId = extractAppointmentIdFromResponse(appointmentResponse);
|
|
|
|
if (appointmentId != null) {
|
|
// Update application with the appointment ID
|
|
application.setAppointmentId(appointmentId);
|
|
application.setStatus(ApplicationStatusTypeEnum.APPOINTMENT.getValue());
|
|
applicationRepository.save(application);
|
|
|
|
// Log version history
|
|
loggingUtil.addVersionHistory(
|
|
VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldApplicationData).newData(application).build());
|
|
}
|
|
|
|
appointmentCreationResponse.setAppointmentId(appointmentId);
|
|
return appointmentCreationResponse;
|
|
|
|
} catch (FeignException.Forbidden forbiddenException) {
|
|
log.error("403 Forbidden received while retrieving template. Regenerating token...");
|
|
regenerateTokenAndSave(hub);
|
|
return createAppointment(applicationId, createAppointmentRequest);
|
|
}
|
|
}
|
|
|
|
private String extractAppointmentIdFromResponse(ResponseEntity<Object> appointmentResponse) {
|
|
|
|
if (appointmentResponse.getBody() != null) {
|
|
Map<String, Object> responseBody = (Map<String, Object>) appointmentResponse.getBody();
|
|
if (responseBody.containsKey(GepafinConstant.DATA_STRING)) {
|
|
Map<String, Object> data = (Map<String, Object>) responseBody.get(GepafinConstant.DATA_STRING);
|
|
if (data != null && data.containsKey(GepafinConstant.ID_STRING)) {
|
|
return data.get(GepafinConstant.ID_STRING).toString();
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public AppointmentCreationRequest parseTemplateResponseData(String jsonResponse) {
|
|
|
|
try {
|
|
ObjectMapper objectMapper = new ObjectMapper();
|
|
JsonNode rootNode = objectMapper.readTree(jsonResponse);
|
|
JsonNode richiestaClienteArray = rootNode.path(GepafinConstant.DATA_STRING).path(GepafinConstant.RICHIESTA_CLIENTE_STRING);
|
|
|
|
AppointmentCreationRequest appointmentCreationRequest = new AppointmentCreationRequest();
|
|
AppointmentCreationRequest.Input input = new AppointmentCreationRequest.Input();
|
|
|
|
// Map `richiestaCliente` array
|
|
List<AppointmentCreationRequest.RichiestaCliente> richiestaClienteList = new ArrayList<>();
|
|
if (richiestaClienteArray.isArray()) {
|
|
for (JsonNode richiestaNode : richiestaClienteArray) {
|
|
richiestaClienteList.add(objectMapper.treeToValue(richiestaNode, AppointmentCreationRequest.RichiestaCliente.class));
|
|
}
|
|
}
|
|
|
|
input.setRichiestaCliente(richiestaClienteList);
|
|
appointmentCreationRequest.setInput(input);
|
|
return appointmentCreationRequest;
|
|
|
|
} catch (Exception e) {
|
|
log.error("Error parsing template response: {}", e.getMessage(), e);
|
|
throw new IllegalStateException("Failed to parse template response", e);
|
|
}
|
|
}
|
|
|
|
public AppointmentCreationRequest buildAppointmentCreationRequest(Long applicationId, CreateAppointmentRequest createAppointmentRequest, String areaCode,
|
|
AppointmentCreationRequest templateRichiestaData) {
|
|
|
|
ApplicationEntity application = applicationService.validateApplication(applicationId);
|
|
CreateAppointmentRequest.Nota nota = createAppointmentRequest.getNota();
|
|
|
|
AppointmentCreationRequest appointmentCreationRequest = new AppointmentCreationRequest();
|
|
AppointmentCreationRequest.Input input = new AppointmentCreationRequest.Input();
|
|
|
|
// Set Input Fields
|
|
input.setId(areaCode);
|
|
input.setNdg(application.getNdg());
|
|
|
|
// Populate richiestaCliente from template data
|
|
List<AppointmentCreationRequest.RichiestaCliente> richiestaClienteList = new ArrayList<>();
|
|
for (AppointmentCreationRequest.RichiestaCliente templateRichiesta : templateRichiestaData.getInput().getRichiestaCliente()) {
|
|
AppointmentCreationRequest.RichiestaCliente richiestaCliente = new AppointmentCreationRequest.RichiestaCliente();
|
|
BeanUtils.copyProperties(templateRichiesta, richiestaCliente);
|
|
|
|
// Add specific `nota`
|
|
AppointmentCreationRequest.Nota requestNota = new AppointmentCreationRequest.Nota();
|
|
requestNota.setTitolo(nota.getTitolo());
|
|
requestNota.setTesto(nota.getTesto());
|
|
richiestaCliente.setNota(requestNota);
|
|
|
|
richiestaClienteList.add(richiestaCliente);
|
|
}
|
|
|
|
input.setRichiestaCliente(richiestaClienteList);
|
|
appointmentCreationRequest.setInput(input);
|
|
return appointmentCreationRequest;
|
|
}
|
|
|
|
public DocumentUploadResponse uploadDocumentToExternalSystem(Long documentId, UploadDocToExternalSystemRequest docToExternalSystemRequest) {
|
|
// Check if the document is already being processed
|
|
DocumentEntity systemDoc = documentDao.validateDocument(documentId);
|
|
|
|
Claims claims = tokenProvider.getClaimsFromToken(tokenProvider.extractTokenFromRequest(request));
|
|
Long hubId = Utils.extractHubIdFromPayload(claims.getSubject());
|
|
if (systemDoc.getDocumentAttachmentId() != null) {
|
|
// If the documentAttachmentId is already set, return the response
|
|
log.info("Document already uploaded with documentAttachmentId: {}", systemDoc.getDocumentAttachmentId());
|
|
DocumentUploadResponse response = new DocumentUploadResponse();
|
|
response.setDocumentAttachmentId(systemDoc.getDocumentAttachmentId());
|
|
return response;
|
|
}
|
|
// Check if a thread is already running for this document upload
|
|
if (threadForDocumentMap.containsKey(documentId)) {
|
|
log.warn("Document upload already running for documentId: {}", documentId);
|
|
throw new CustomValidationException(Status.SUCCESS, Translator.toLocale(GepafinConstant.DOCUMENT_UPLOADING_IN_PROGRESS));
|
|
}
|
|
// Start the upload process in the background
|
|
ExecutorService executor = Executors.newSingleThreadExecutor(runnable -> {
|
|
Thread thread = new Thread(runnable);
|
|
thread.setName(GepafinConstant.ASYNC_DOCUMENT_UPLOAD_NAME + documentId);
|
|
return thread;
|
|
});
|
|
threadForDocumentMap.put(documentId, executor);
|
|
|
|
executor.submit(() -> {
|
|
threadLocalHubId.set(hubId);
|
|
try {
|
|
log.info("Starting async document upload for documentId: {}", documentId);
|
|
uploadDocumentToExternalSystemSync(documentId, docToExternalSystemRequest);
|
|
} catch (Exception e) {
|
|
log.error("Error in async document upload for documentId: {}", documentId, e);
|
|
} finally {
|
|
// Cleanup resources
|
|
ExecutorService executorToShutdown = threadForDocumentMap.remove(documentId);
|
|
if (executorToShutdown != null) {
|
|
executorToShutdown.shutdown();
|
|
threadLocalHubId.remove();
|
|
}
|
|
log.info("Async document upload completed for documentId: {}", documentId);
|
|
}
|
|
});
|
|
return null;
|
|
// Return an immediate response indicating the process is in progress
|
|
// throw new CustomValidationException(Status.SUCCESS, Translator.toLocale(GepafinConstant.DOCUMENT_UPLOADING_IN_PROGRESS));
|
|
}
|
|
|
|
private void uploadDocumentToExternalSystemSync(Long documentId, UploadDocToExternalSystemRequest docToExternalSystemRequest) {
|
|
// Synchronous upload logic
|
|
DocumentEntity systemDoc = documentDao.validateDocument(documentId);
|
|
|
|
Long hubId = threadLocalHubId.get();
|
|
HubEntity hub = hubRepository.findByHubId(hubId);
|
|
|
|
if (!hub.getUniqueUuid().equals(defaultHubUuid)) {
|
|
log.info("Document cannot be uploaded for another Hub, it is default for Gepafin.");
|
|
throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.NO_DOCUMENT_UPLOAD_FOR_ANOTHER_HUB));
|
|
}
|
|
|
|
log.info("Got Document in system: {}", systemDoc);
|
|
String oldUrl = systemDoc.getFilePath();
|
|
String authorizationToken = getBearerToken(hub);
|
|
|
|
try {
|
|
File localFile = downloadFileFromS3(oldUrl);
|
|
MultipartFile multipartFile = convertFileToMultipartFile(localFile);
|
|
|
|
UploadDocToExternalSystemRequest externalSystemRequest = new UploadDocToExternalSystemRequest();
|
|
externalSystemRequest.setInput(getUploadDocumentInput(docToExternalSystemRequest));
|
|
|
|
String uploadDocRequest = Utils.convertObjectToJson(externalSystemRequest);
|
|
ResponseEntity<Object> uploadedDocumentData = appointmentApiService.uploadDocumentToExternalSystemForAppointment(authorizationToken, context, uploadDocRequest,
|
|
multipartFile);
|
|
|
|
String responseData = Utils.convertObjectToJson(uploadedDocumentData.getBody());
|
|
DocumentUploadResponse parsedResponse = parseDocumentUploadResponse(responseData);
|
|
|
|
if (parsedResponse == null) {
|
|
throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.ERROR_UPLOADING_DOCUMENT));
|
|
}
|
|
|
|
// Save the documentAttachmentId to the database
|
|
systemDoc.setDocumentAttachmentId(parsedResponse.getDocumentAttachmentId());
|
|
documentRepository.save(systemDoc);
|
|
|
|
log.info("Document uploaded successfully to external system: {}", parsedResponse);
|
|
} catch (FeignException.Forbidden forbiddenException) {
|
|
log.error("403 Forbidden received while uploading document. Regenerating token...");
|
|
regenerateTokenAndSave(hub);
|
|
uploadDocumentToExternalSystemSync(documentId, docToExternalSystemRequest);
|
|
} catch (Exception e) {
|
|
log.error("Exception during document upload: {}", e.getMessage(), e);
|
|
throw new CustomValidationException(Status.BAD_REQUEST, Translator.toLocale(GepafinConstant.EXTERNAL_DOCUMENT_UPLOAD_FAILURE_MSG));
|
|
}
|
|
}
|
|
|
|
private UploadDocToExternalSystemRequest.Input getUploadDocumentInput(UploadDocToExternalSystemRequest docToExternalSystemRequest) {
|
|
|
|
UploadDocToExternalSystemRequest.Input input = new UploadDocToExternalSystemRequest.Input();
|
|
input.setIdTipoProtocollo(docToExternalSystemRequest.getInput().getIdTipoProtocollo());
|
|
input.setIdClassificazione(docToExternalSystemRequest.getInput().getIdClassificazione());
|
|
input.setFlagDaFirmare(flagDaFirmare);
|
|
input.setDescrizione(docToExternalSystemRequest.getInput().getDescrizione());
|
|
|
|
UploadDocToExternalSystemRequest.Input.Attributes attributes = new UploadDocToExternalSystemRequest.Input.Attributes();
|
|
attributes.setNdg(docToExternalSystemRequest.getInput().getAttributes().getNdg());
|
|
attributes.setEmail(docToExternalSystemRequest.getInput().getAttributes().getEmail());
|
|
|
|
input.setAttributes(attributes);
|
|
return input;
|
|
}
|
|
|
|
public static MultipartFile convertFileToMultipartFile(File file) throws IOException {
|
|
|
|
FileInputStream input = new FileInputStream(file);
|
|
return new MockMultipartFile(file.getName(), file.getName(), MediaType.APPLICATION_OCTET_STREAM_VALUE, input);
|
|
}
|
|
|
|
private File downloadFileFromS3(String fileUrl) throws Exception {
|
|
|
|
String key = extractS3KeyFromUrl(fileUrl);
|
|
File localFile = new File(GepafinConstant.TEMP_FILE_PATH + extractFileName(key));
|
|
|
|
GetObjectRequest getObjectRequest = new GetObjectRequest(OLD_BUCKET, key);
|
|
|
|
try (InputStream s3Stream = s3Client.getObject(getObjectRequest).getObjectContent(); FileOutputStream outputStream = new FileOutputStream(localFile)) {
|
|
s3Stream.transferTo(outputStream);
|
|
}
|
|
|
|
log.info("Downloaded file from old S3 bucket: {}", key);
|
|
return localFile;
|
|
}
|
|
|
|
private String extractS3KeyFromUrl(String url) {
|
|
|
|
return url.replace(s3Url, "");
|
|
}
|
|
|
|
private String extractFileName(String filePath) {
|
|
|
|
String[] parts = filePath.split("/");
|
|
return parts[parts.length - 1];
|
|
|
|
}
|
|
|
|
public DocumentUploadResponse parseDocumentUploadResponse(String jsonResponse) {
|
|
|
|
try {
|
|
ObjectMapper objectMapper = new ObjectMapper();
|
|
JsonNode rootNode = objectMapper.readTree(jsonResponse);
|
|
|
|
// Navigate to the "data" node
|
|
JsonNode dataNode = rootNode.get(GepafinConstant.DATA_STRING);
|
|
if (dataNode != null) {
|
|
DocumentUploadResponse response = new DocumentUploadResponse();
|
|
|
|
// Extract "documentAttachmentId"
|
|
JsonNode documentAttachmentIdNode = dataNode.get(GepafinConstant.DOCUMENT_ATTACHMENT_ID_STRING);
|
|
if (documentAttachmentIdNode != null) {
|
|
response.setDocumentAttachmentId(documentAttachmentIdNode.asText());
|
|
} else {
|
|
throw new RuntimeException("Invalid JSON structure: Missing 'documentAttachmentId' node.");
|
|
}
|
|
|
|
return response;
|
|
} else {
|
|
return null;
|
|
}
|
|
} catch (Exception e) {
|
|
throw new RuntimeException("Failed to parse response: " + e.getMessage(), e);
|
|
}
|
|
}
|
|
} |