package net.gepafin.tendermanagement.dao; import lombok.extern.slf4j.Slf4j; import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; import jakarta.servlet.http.HttpServletRequest; import net.gepafin.tendermanagement.entities.*; import net.gepafin.tendermanagement.enums.*; import net.gepafin.tendermanagement.model.request.AmendmentFormField; import net.gepafin.tendermanagement.model.request.EvaluationDocumentRequest; import net.gepafin.tendermanagement.model.request.VersionHistoryRequest; import net.gepafin.tendermanagement.model.response.ContentResponseBean; import net.gepafin.tendermanagement.model.response.SettingResponseBean; import net.gepafin.tendermanagement.repositories.*; import net.gepafin.tendermanagement.util.LoggingUtil; import net.gepafin.tendermanagement.util.Utils; import net.gepafin.tendermanagement.web.rest.api.errors.CustomValidationException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.web.multipart.MultipartFile; import net.gepafin.tendermanagement.config.Translator; import net.gepafin.tendermanagement.constants.GepafinConstant; import net.gepafin.tendermanagement.model.response.DocumentResponseBean; import net.gepafin.tendermanagement.model.response.UploadFileOnAmazonS3Response; import net.gepafin.tendermanagement.service.AmazonS3Service; import net.gepafin.tendermanagement.service.ApplicationAmendmentRequestService; import net.gepafin.tendermanagement.service.ApplicationService; import net.gepafin.tendermanagement.service.CallService; import net.gepafin.tendermanagement.web.rest.api.errors.ResourceNotFoundException; import net.gepafin.tendermanagement.web.rest.api.errors.Status; import org.springframework.beans.factory.annotation.Value; @Slf4j @Component public class DocumentDao { @Autowired private AmazonS3Service amazonS3Service; @Autowired private DocumentRepository documentRepository; @Autowired private CallDao callDao; @Autowired private CallService callService; @Autowired private S3PathConfig s3ConfigBean; @Autowired private ApplicationRepository applicationRepository; @Autowired ApplicationService applicationService; @Autowired ApplicationAmendmentRequestService applicationAmendmentRequestService; @Autowired ApplicationAmendmentRequestRepository applicationAmendmentRequestRepository; @Autowired private ApplicationEvaluationRepository applicationEvaluationRepository; @Value("${aws.s3.bucket.name}") private String bucketName; @Autowired private LoggingUtil loggingUtil; @Autowired private HttpServletRequest request; @Autowired private ApplicationFormRepository applicationFormRepository; @Autowired private ApplicationFormFieldRepository applicationFormFieldRepository; @Autowired private ApplicationAmendmentRequestDao applicationAmendmentRequestDao; @Autowired private ApplicationEvaluationDao applicationEvaluationDao; @Autowired private CommunicationRepository communicationRepository; // @Value("${aws.s3.url.folder}") // private String s3Folder; public List uploadFiles(Long userId,List files, Long sourceId, DocumentSourceTypeEnum sourceType, DocumentTypeEnum fileType) { log.info("Uploading files userId={}, sourceType={}, fileType={}", userId,sourceType,fileType); List documentEntities = new ArrayList<>(); Long source = resolveSourceId(sourceId, sourceType); if(files!=null) { for (MultipartFile file : files) { log.info("Uploading file '{}'", file.getOriginalFilename()); UploadFileOnAmazonS3Response uploadFileOnAmazonS3Response = uploadFileOnAmazonS3(file, sourceType, sourceId); if (uploadFileOnAmazonS3Response != null) { DocumentEntity documentEntity = new DocumentEntity(); documentEntity.setFileName(uploadFileOnAmazonS3Response.getFileName()); documentEntity.setSource(sourceType.getValue()); documentEntity.setSourceId(source); documentEntity.setType(fileType.getValue()); documentEntity.setFilePath(uploadFileOnAmazonS3Response.getFilePath()); documentEntity.setIsDeleted(false); documentEntity.setUploadedBy(userId); documentEntities.add(documentEntity); } } } documentRepository.saveAll(documentEntities); /** This code is responsible for adding a version history log for the "Upload call or application document based on source type" operation. **/ documentEntities.forEach(entity -> loggingUtil.addVersionHistory( VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.INSERT).oldData(null).newData(entity).build())); return documentEntities.stream().map(callDao::convertToDocumentResponseBean).collect(Collectors.toList()); } public UserActionContextEnum getUserActionContextEnum(DocumentSourceTypeEnum sourceType, DocumentTypeEnum fileType) { UserActionContextEnum userActionContext = null; if (fileType.equals(DocumentTypeEnum.DOCUMENT) && sourceType.equals(DocumentSourceTypeEnum.CALL)) { userActionContext = UserActionContextEnum.UPLOAD_CALL_DOCUMENT; } else if (fileType.equals(DocumentTypeEnum.IMAGES) && sourceType.equals(DocumentSourceTypeEnum.CALL)) { userActionContext = UserActionContextEnum.UPLOAD_CALL_IMAGES; } else if (fileType.equals(DocumentTypeEnum.DOCUMENT) && sourceType.equals(DocumentSourceTypeEnum.APPLICATION)) { userActionContext = UserActionContextEnum.UPLOAD_APPLICATION_DOCUMENT; } else if (fileType.equals(DocumentTypeEnum.IMAGES) && sourceType.equals(DocumentSourceTypeEnum.APPLICATION)) { userActionContext = UserActionContextEnum.UPLOAD_APPLICATION_IMAGES; }else if (fileType.equals(DocumentTypeEnum.DOCUMENT) && sourceType.equals(DocumentSourceTypeEnum.AMENDMENT)) { userActionContext = UserActionContextEnum.UPLOAD_AMENDMENT_DOCUMENT; } else if (fileType.equals(DocumentTypeEnum.IMAGES) && sourceType.equals(DocumentSourceTypeEnum.AMENDMENT)) { userActionContext = UserActionContextEnum.UPLOAD_AMENDMENT_IMAGES; }else if (fileType.equals(DocumentTypeEnum.DOCUMENT) && sourceType.equals(DocumentSourceTypeEnum.EVALUATION)) { userActionContext = UserActionContextEnum.UPLOAD_EVALUATION_DOCUMENT; } else if (fileType.equals(DocumentTypeEnum.IMAGES) && sourceType.equals(DocumentSourceTypeEnum.EVALUATION)) { userActionContext = UserActionContextEnum.UPLOAD_EVALUATION_IMAGES; } return userActionContext; } public UserActionContextEnum getUserActionContextForUpdatingDocOrImage(DocumentTypeEnum documentTypeEnum) { UserActionContextEnum userActionContext; if (DocumentTypeEnum.DOCUMENT.equals(documentTypeEnum)) { userActionContext = UserActionContextEnum.UPDATE_DOCUMENT; } else{ userActionContext = UserActionContextEnum.UPDATE_IMAGES; } return userActionContext; } private UploadFileOnAmazonS3Response uploadFileOnAmazonS3(MultipartFile file, DocumentSourceTypeEnum type, Long sourceId) { log.info("Starting S3 upload: fileName={}, documentType={}, sourceId={}", file.getOriginalFilename(), type, sourceId); Long applicationId = 0L; Long amendmentId = 0L; Long evaluationId = 0L; Long communicationId = 0L; Long callId = sourceId; if (type == DocumentSourceTypeEnum.APPLICATION) { applicationId = sourceId; callId = applicationRepository.findCallIdById(applicationId); log.info("Processing document of type APPLICATION .Resolved applicationId={}, callId={}", applicationId, callId); } else if (type == DocumentSourceTypeEnum.AMENDMENT) { amendmentId = sourceId; ApplicationEntity applicationEntity = applicationAmendmentRequestRepository.findApplicationByAmendmentId(amendmentId); applicationId = applicationEntity.getId(); callId = applicationEntity.getCall().getId(); log.info("Processing document of type AMENDMENT .Resolved amendmentId={}, applicationId={}, callId={}", amendmentId, applicationId, callId); }else if (type == DocumentSourceTypeEnum.EVALUATION) { evaluationId = sourceId; ApplicationEntity applicationEntity = applicationEvaluationRepository.findApplicationByEvaluationId(evaluationId); applicationId = applicationEntity.getId(); callId = applicationEntity.getCall().getId(); log.info("Processing document of type EVALUATION .Resolved evaluationId={}, applicationId={}, callId={}", evaluationId, applicationId, callId); }else if (type == DocumentSourceTypeEnum.COMMUNICATION) { communicationId = sourceId; Optional communicationEntity=communicationRepository.findByIdAndIsDeletedFalse(communicationId); Optional applicationAmendmentRequestEntity=applicationAmendmentRequestRepository.findByIdAndIsDeletedFalse(communicationEntity.get().getApplicationAmendmentRequest().getId()); amendmentId=applicationAmendmentRequestEntity.get().getId(); applicationId=applicationAmendmentRequestEntity.get().getApplicationId(); callId = applicationAmendmentRequestEntity.get().getApplicationEvaluationEntity().getAssignedApplicationsEntity().getApplication().getCall().getId(); log.info("Processing document of type COMMUNICATION .Resolved evaluationId={}, applicationId={}, callId={}", evaluationId, applicationId, callId); } try { String s3Path = generateS3Path(type, callId, applicationId, amendmentId,communicationId); log.info("Generated S3 path {}", s3Path); return amazonS3Service.uploadFileOnAmazonS3(s3Path, file); } catch (Exception e) { log.error("Error uploading file to S3: fileName={}, documentType={}, sourceId={}, error={}", file.getOriginalFilename(), type, sourceId, e.getMessage(), e); throw new CustomValidationException(Status.VALIDATION_ERROR, Translator.toLocale(GepafinConstant.UPLOAD_ERROR_S3)); } } public String generateS3Path(DocumentSourceTypeEnum typeOfDocument, Long callId, Long applicationId, Long amendmentId,Long communicationId) { try { return s3ConfigBean.generateDocumentPath(typeOfDocument, callId, applicationId, amendmentId,communicationId); } catch (IllegalArgumentException e) { throw new CustomValidationException(Status.VALIDATION_ERROR, Translator.toLocale(GepafinConstant.S3_PATH_GENERATION_ERROR_MSG)); } } private Long resolveSourceId(Long sourceId, DocumentSourceTypeEnum sourceType) { if (sourceType == DocumentSourceTypeEnum.CALL) { CallEntity callEntity = callService.validateCall(sourceId); // callDao.validateUpdate(callEntity); return callEntity.getId(); } // else if (sourceType == SourceTypeEnum.APPLICATION) { // ApplicationEntity applicationEntity = applicationService.validateApplication(sourceId); // return applicationEntity.getId(); // Assuming ApplicationEntity has getId() // } // return sourceId; } public void deleteFile(Long documentId) { DocumentEntity documentEntity = documentRepository.findById(documentId).orElseThrow(() -> new ResourceNotFoundException(Status.NOT_FOUND, Translator.toLocale(GepafinConstant.DOCUMENT_NOT_FOUND))); if(Boolean.TRUE.equals(documentEntity.getIsDeleted())){ log.info("Document with id={} is already marked as deleted. Skipping deletion.", documentId); return; } Long callId = null; Long applicationId = null; Long amendmentId = null; Long evaluationId = null; Long communicationId=null; if (DocumentSourceTypeEnum.CALL.getValue().equalsIgnoreCase(documentEntity.getSource())) { callId = documentEntity.getSourceId(); log.info("Processing document of type CALL. Resolved callId={}", callId); } else if (DocumentSourceTypeEnum.APPLICATION.getValue().equalsIgnoreCase(documentEntity.getSource())) { applicationId = documentEntity.getSourceId(); ApplicationEntity applicationEntity = applicationService.validateApplication(applicationId); List applicationFormEntity=applicationFormRepository.findByApplicationId(applicationId); for (ApplicationFormEntity applicationForm:applicationFormEntity){ FormEntity formEntity=applicationForm.getForm(); List contentList = Utils.convertJsonStringToList(formEntity.getContent(), ContentResponseBean.class); List applicationFormFieldEntityList=applicationFormFieldRepository.findByApplicationFormId(applicationForm.getId()); for (ApplicationFormFieldEntity applicationFormFieldEntity:applicationFormFieldEntityList) { contentList.forEach(contentResponseBean -> { if (("fileupload".equals(contentResponseBean.getName()) || GepafinConstant.FILE_SELECT.equals(contentResponseBean.getName())) && contentResponseBean.getId().equals(applicationFormFieldEntity.getFieldId())) { String updatedValue = removeDocumentIdFromFieldValue(applicationFormFieldEntity.getFieldValue(), documentId); applicationFormFieldEntity.setFieldValue(updatedValue); applicationFormFieldRepository.save(applicationFormFieldEntity); } }); } } callId = applicationEntity.getCall().getId(); log.info("Processing document of type APPLICATION. Resolved applicationId={}, callId={}", applicationId, callId); } else if(DocumentSourceTypeEnum.AMENDMENT.getValue().equalsIgnoreCase(documentEntity.getSource())){ amendmentId = documentEntity.getSourceId(); ApplicationEntity applicationEntity = applicationAmendmentRequestRepository.findApplicationByAmendmentId(amendmentId); Optional applicationAmendmentRequestEntity=applicationAmendmentRequestRepository.findByIdAndIsDeletedFalse(amendmentId); Map amendmentFormFieldMap = Utils .convertJsonStringToList(applicationAmendmentRequestEntity.get().getFormFields(), AmendmentFormField.class) .stream().collect(Collectors.toMap(AmendmentFormField::getFieldId, Function.identity())); for (Map.Entry entry : amendmentFormFieldMap.entrySet()) { AmendmentFormField amendmentFormField=entry.getValue(); String updatedValue = removeDocumentIdFromFieldValue(amendmentFormField.getFieldValue(), documentId); amendmentFormField.setFieldValue(updatedValue); } String amendmentDocs=applicationAmendmentRequestEntity.get().getAmendmentDocument(); Map amendmentDocument=Utils.convertIntoJson(amendmentDocs); String amendmentDocuments= (String) amendmentDocument.get("amendmentDocuments"); if(amendmentDocuments!=null){ String updatedValue = removeDocumentIdFromFieldValue(amendmentDocuments, documentId); amendmentDocument.put("amendmentDocuments", updatedValue); // Step 4: Convert map back to JSON string String updatedAmendmentDocs = Utils.convertMapIntoJsonString(amendmentDocument); // implement this if not available // Step 5: Set it back to entity applicationAmendmentRequestEntity.get().setAmendmentDocument(updatedAmendmentDocs); } applicationAmendmentRequestEntity.get().setFormFields(Utils.convertListToJsonString(amendmentFormFieldMap.values().stream().toList())); applicationAmendmentRequestRepository.save(applicationAmendmentRequestEntity.get()); applicationId = applicationEntity.getId(); callId = applicationEntity.getCall().getId(); log.info("Processing document of type AMENDMENT. Resolved amendmentId={}, applicationId={}, callId={}", amendmentId, applicationId, callId); } else if(DocumentSourceTypeEnum.EVALUATION.getValue().equalsIgnoreCase(documentEntity.getSource())){ evaluationId = documentEntity.getSourceId(); ApplicationEntity applicationEntity = applicationEvaluationRepository.findApplicationByEvaluationId(evaluationId); ApplicationEvaluationEntity entity=applicationEvaluationRepository.findByApplicationId(applicationEntity.getId()); List allDocs = applicationEvaluationDao.prepareEvaluationDocumentBeanList(entity); List updatedDocs=allDocs; allDocs = allDocs.stream() .filter(doc -> doc.getFileValue().equals(removeDocumentIdFromFieldValue(doc.getFileValue(), documentId))) .collect(Collectors.toList()); String updatedEvaluationDocJson = Utils.convertObjectToJson(allDocs); entity.setEvaluationDocument(updatedEvaluationDocJson); applicationEvaluationRepository.save(entity); applicationId = applicationEntity.getId(); callId = applicationEntity.getCall().getId(); log.info("Processing document of type EVALUATION. Resolved evaluationId={}, applicationId={}, callId={}", evaluationId, applicationId, callId); } deleteFileFromS3(documentEntity, callId, applicationId,amendmentId,communicationId); log.info("Successfully deleted file from S3 for documentId={}", documentId); } public DocumentEntity validateDocument(Long id) { return documentRepository.findByIdAndNotDeleted(id).orElseThrow(() -> new ResourceNotFoundException(Status.NOT_FOUND, Translator.toLocale(GepafinConstant.DOCUMENT_NOT_FOUND))); } public DocumentResponseBean updateDocument(Long documentId, MultipartFile file, DocumentTypeEnum documentTypeEnum) { log.info("Starting document update: documentId={} , newDocumentType={}", documentId , documentTypeEnum); DocumentEntity documentEntity = validateDocument(documentId); //cloned entity for old data DocumentEntity oldDocumentData = Utils.getClonedEntityForData(documentEntity); String type = documentEntity.getSource(); UploadFileOnAmazonS3Response uploadFileOnAmazonS3Response = updateFileOnAmazonS3(file, DocumentSourceTypeEnum.valueOf(type), documentEntity.getSourceId()); if (uploadFileOnAmazonS3Response != null) { log.info("Successfully uploaded new file to S3: fileName={}", uploadFileOnAmazonS3Response.getFileName()); documentEntity.setFileName(uploadFileOnAmazonS3Response.getFileName()); documentEntity.setFilePath(uploadFileOnAmazonS3Response.getFilePath()); documentEntity.setType(documentTypeEnum.getValue()); documentEntity.setSource(documentEntity.getSource()); documentEntity.setSourceId(documentEntity.getSourceId()); documentRepository.save(documentEntity); log.info("Document updated in database for documentId={}", documentId); /** This code is responsible for adding a version history log for the "updating doc or image" operation. **/ loggingUtil.addVersionHistory( VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.UPDATE).oldData(oldDocumentData).newData(documentEntity).build()); } return callDao.convertToDocumentResponseBean(documentEntity); } private UploadFileOnAmazonS3Response updateFileOnAmazonS3(MultipartFile file, DocumentSourceTypeEnum type, Long id) { try { Long callId=null; Long applicationId=null; Long amendmentId=null; Long evaluationId=null; Long communicationId=null; if (type.equals(DocumentSourceTypeEnum.APPLICATION)) { callId = applicationRepository.findCallIdById(id); applicationId = id; log.info("Processing document of type APPLICATION . Resolved applicationId={}, callId={}", applicationId, callId); } else if(type.equals(DocumentSourceTypeEnum.AMENDMENT)){ amendmentId = id; ApplicationEntity applicationEntity = applicationAmendmentRequestRepository.findApplicationByAmendmentId(amendmentId); applicationId = applicationEntity.getId(); callId = applicationEntity.getCall().getId(); log.info("Processing document of type AMENDMENT . Resolved amendmentId={}, applicationId={}, callId={}", amendmentId, applicationId, callId); }else if(type.equals(DocumentSourceTypeEnum.EVALUATION)){ evaluationId = id; ApplicationEntity applicationEntity = applicationEvaluationRepository.findApplicationByEvaluationId(evaluationId); applicationId = applicationEntity.getId(); callId = applicationEntity.getCall().getId(); log.info("Processing document of type EVALUATION . Resolved evaluationId={}, applicationId={}, callId={}", evaluationId, applicationId, callId); }else if (type == DocumentSourceTypeEnum.COMMUNICATION) { communicationId = id; Optional communicationEntity=communicationRepository.findByIdAndIsDeletedFalse(communicationId); Optional applicationAmendmentRequestEntity=applicationAmendmentRequestRepository.findByIdAndIsDeletedFalse(communicationEntity.get().getApplicationAmendmentRequest().getId()); amendmentId=applicationAmendmentRequestEntity.get().getId(); applicationId=applicationAmendmentRequestEntity.get().getApplicationId(); callId = applicationAmendmentRequestEntity.get().getApplicationEvaluationEntity().getAssignedApplicationsEntity().getApplication().getCall().getId(); log.info("Processing document of type EVALUATION .Resolved evaluationId={}, applicationId={}, callId={}", evaluationId, applicationId, callId); } else { callId = id; applicationId = 0L; log.info("Processing document of type CALL . Resolved callId={}", callId); } String s3Path = generateS3Path(type, callId, applicationId,amendmentId,communicationId); log.info("Generated S3 path {}", s3Path); return amazonS3Service.uploadFileOnAmazonS3(s3Path, file); } catch (Exception e) { log.error("Error during file update to S3: documentType={}, sourceId={}, error={}",type, id, e.getMessage(), e); throw new CustomValidationException(Status.VALIDATION_ERROR, Translator.toLocale(GepafinConstant.UPLOAD_ERROR_S3)); } } public DocumentResponseBean getDocument(Long documentId) { DocumentEntity documentEntity = validateDocument(documentId); return callDao.convertToDocumentResponseBean(documentEntity); } public void deleteFileFromS3(DocumentEntity documentEntity, Long callId, Long applicationId,Long amendmentId,Long communicationId) { try { DocumentEntity oldDocumentEntity = Utils.getClonedEntityForData(documentEntity); String oldS3Path = documentEntity.getFilePath(); String newS3Path = s3ConfigBean.generateDocumentPathForOther(DocOtherSourceTypeEnum.valueOf("DELETED_" + documentEntity.getSource().toUpperCase()), callId, applicationId,amendmentId,communicationId); log.info("Moving file to deleted path: oldS3Path={}, newS3Path={}", oldS3Path, newS3Path); UploadFileOnAmazonS3Response response = amazonS3Service.moveFile(documentEntity.getFileName(), oldS3Path, newS3Path); documentEntity.setFileName(response.getFileName()); documentEntity.setFilePath(response.getFilePath()); documentEntity.setIsDeleted(true); documentRepository.save(documentEntity); /** This code is responsible for adding a version history log for the "Soft delete document" operation. **/ loggingUtil.addVersionHistory( VersionHistoryRequest.builder().request(request).actionType(VersionActionTypeEnum.SOFT_DELETE).oldData(oldDocumentEntity).newData(documentEntity).build()); log.info("File for document ID {} successfully moved to deleted folder.", documentEntity.getId()); } catch (Exception e) { log.error("Error moving file for document ID {} to deleted folder: {}", documentEntity.getId(), e.getMessage()); throw new CustomValidationException(Status.VALIDATION_ERROR, Translator.toLocale(GepafinConstant.ERROR_MOVING_FILE_TO_DELETED_FOLDER)); } } public String removeDocumentIdFromFieldValue(String fieldValue, Long documentId) { if (fieldValue == null || fieldValue.isBlank()) { return fieldValue; } List documentIdList = new ArrayList<>(Arrays.asList(fieldValue.split(","))); documentIdList.replaceAll(String::trim); // Trim spaces for safety boolean removed = documentIdList.removeIf(id -> id.equals(String.valueOf(documentId))); // Return updated value only if modified, else return original return removed ? String.join(",", documentIdList) : fieldValue; } }