Initial commit: proyecto ContabilidadSaPolar completo
This commit is contained in:
@@ -0,0 +1,237 @@
|
||||
package com.sapolar.document;
|
||||
|
||||
import com.sapolar.common.exception.BadRequestException;
|
||||
import com.sapolar.common.exception.ResourceNotFoundException;
|
||||
import com.sapolar.config.FileStorageConfig;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.UrlResource;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class DocumentService {
|
||||
|
||||
private final FileStorageConfig fileStorageConfig;
|
||||
private final DocumentRepository documentRepository;
|
||||
private final DocumentEntityRepository documentEntityRepository;
|
||||
private final DocumentTypeRepository documentTypeRepository;
|
||||
private final DocumentTypeEntityAllowedRepository documentTypeEntityAllowedRepository;
|
||||
|
||||
@Transactional
|
||||
public Document uploadFile(MultipartFile file, String entityType, Long entityId,
|
||||
Integer documentTypeId, String description, Long uploaderId) {
|
||||
String originalName = StringUtils.cleanPath(file.getOriginalFilename());
|
||||
if (originalName.isBlank()) {
|
||||
throw new BadRequestException("Nombre de archivo no válido");
|
||||
}
|
||||
|
||||
// Validar que el tipo de documento está permitido para esta entidad
|
||||
if (!isDocumentTypeAllowedForEntity(documentTypeId, entityType)) {
|
||||
throw new BadRequestException(
|
||||
"El tipo de documento no está permitido para entidades de tipo " + entityType);
|
||||
}
|
||||
|
||||
String extension = "";
|
||||
int dotIndex = originalName.lastIndexOf('.');
|
||||
if (dotIndex > 0) {
|
||||
extension = originalName.substring(dotIndex);
|
||||
}
|
||||
String storedName = java.util.UUID.randomUUID().toString() + extension;
|
||||
|
||||
// Guardar archivo en disco (usando la primera entidad para la ruta)
|
||||
Path uploadDir = Paths.get(fileStorageConfig.getPath())
|
||||
.resolve(entityType.toLowerCase())
|
||||
.resolve(entityId.toString());
|
||||
try {
|
||||
Files.createDirectories(uploadDir);
|
||||
Path targetPath = uploadDir.resolve(storedName);
|
||||
Files.copy(file.getInputStream(), targetPath, StandardCopyOption.REPLACE_EXISTING);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Error al subir el archivo: " + originalName, e);
|
||||
}
|
||||
|
||||
// Crear documento
|
||||
Document document = new Document();
|
||||
DocumentType docType = new DocumentType();
|
||||
docType.setId(documentTypeId);
|
||||
document.setDocumentType(docType);
|
||||
document.setOriginalName(originalName);
|
||||
document.setStoredName(storedName);
|
||||
document.setMimeType(file.getContentType());
|
||||
document.setFileSize(file.getSize());
|
||||
document.setDescription(description);
|
||||
|
||||
Document savedDoc = documentRepository.save(document);
|
||||
|
||||
// Crear asociación con la entidad primaria
|
||||
savedDoc.addEntity(entityType, entityId);
|
||||
return documentRepository.save(savedDoc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Valida si un tipo de documento está permitido para una entidad.
|
||||
* Si la relación no existe en la tabla de permitidos, se permite por defecto
|
||||
* (comportamiento backward compatible).
|
||||
*/
|
||||
public boolean isDocumentTypeAllowedForEntity(Integer documentTypeId, String entityType) {
|
||||
// Si no existe la tabla de permitidos, permitir (backward compatible)
|
||||
if (!documentTypeEntityAllowedRepository.existsByDocumentTypeIdAndEntityType(documentTypeId, entityType)) {
|
||||
// Verificar si existe alguna configuración para este tipo de documento
|
||||
List<DocumentTypeEntityAllowed> configs = documentTypeEntityAllowedRepository
|
||||
.findByDocumentTypeIdAndEntityType(documentTypeId, entityType)
|
||||
.stream().toList();
|
||||
if (configs.isEmpty()) {
|
||||
// No hay configuración, verificar si hay alguna para este document type
|
||||
long totalConfigs = documentTypeEntityAllowedRepository.findAll().stream()
|
||||
.filter(c -> c.getDocumentType().getId().equals(documentTypeId))
|
||||
.count();
|
||||
// Si no hay ninguna configuración para este tipo de documento, permitir
|
||||
// (es un tipo antiguo sin restricciones)
|
||||
if (totalConfigs == 0) {
|
||||
return true;
|
||||
}
|
||||
// Hay configuraciones pero ninguna para esta entidad específica
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return documentTypeEntityAllowedRepository.existsByDocumentTypeIdAndEntityType(documentTypeId, entityType);
|
||||
}
|
||||
|
||||
public DocumentDownloadResult downloadFile(Long documentId) {
|
||||
Document document = documentRepository.findById(documentId)
|
||||
.orElseThrow(() -> new ResourceNotFoundException("Documento", documentId));
|
||||
|
||||
// Usar la primera entidad para determinar la ruta del archivo
|
||||
if (document.getEntities().isEmpty()) {
|
||||
throw new ResourceNotFoundException("Documento", documentId);
|
||||
}
|
||||
String entityType = document.getEntities().iterator().next().getEntityType();
|
||||
Long entityId = document.getEntities().iterator().next().getEntityId();
|
||||
|
||||
try {
|
||||
Path filePath = Paths.get(fileStorageConfig.getPath())
|
||||
.resolve(entityType.toLowerCase())
|
||||
.resolve(entityId.toString())
|
||||
.resolve(document.getStoredName());
|
||||
|
||||
Resource resource = new UrlResource(filePath.toUri());
|
||||
if (resource.exists() && resource.isReadable()) {
|
||||
return new DocumentDownloadResult(resource, document.getMimeType(), document.getOriginalName());
|
||||
}
|
||||
throw new RuntimeException("No se puede leer el archivo: " + document.getOriginalName());
|
||||
} catch (MalformedURLException e) {
|
||||
throw new RuntimeException("Error al acceder al archivo", e);
|
||||
}
|
||||
}
|
||||
|
||||
public List<Document> getDocumentsForEntity(String entityType, Long entityId) {
|
||||
return documentRepository.findByEntityTypeAndEntityId(entityType, entityId);
|
||||
}
|
||||
|
||||
public List<Document> searchDocuments(String entityType, Long entityId,
|
||||
Integer documentTypeId, String originalName) {
|
||||
return documentRepository.searchDocuments(entityType, entityId, documentTypeId, originalName);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void deleteDocument(Long documentId) {
|
||||
Document document = documentRepository.findById(documentId)
|
||||
.orElseThrow(() -> new ResourceNotFoundException("Documento", documentId));
|
||||
|
||||
// Eliminar archivo físico
|
||||
if (!document.getEntities().isEmpty()) {
|
||||
DocumentEntity firstEntity = document.getEntities().iterator().next();
|
||||
try {
|
||||
Path filePath = Paths.get(fileStorageConfig.getPath())
|
||||
.resolve(firstEntity.getEntityType().toLowerCase())
|
||||
.resolve(firstEntity.getEntityId().toString())
|
||||
.resolve(document.getStoredName());
|
||||
Files.deleteIfExists(filePath);
|
||||
} catch (IOException e) {
|
||||
// Log error but continue with DB deletion
|
||||
}
|
||||
}
|
||||
|
||||
documentRepository.delete(document);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Document addEntityAssociation(Long documentId, String entityType, Long entityId) {
|
||||
Document document = documentRepository.findById(documentId)
|
||||
.orElseThrow(() -> new ResourceNotFoundException("Documento", documentId));
|
||||
|
||||
// Validar que el tipo de documento está permitido para esta entidad
|
||||
if (!isDocumentTypeAllowedForEntity(document.getDocumentType().getId(), entityType)) {
|
||||
throw new BadRequestException(
|
||||
"El tipo de documento no está permitido para entidades de tipo " + entityType);
|
||||
}
|
||||
|
||||
// Verificar que no exista ya
|
||||
if (documentEntityRepository.findByDocumentIdAndEntityTypeAndEntityId(documentId, entityType, entityId).isPresent()) {
|
||||
throw new BadRequestException("El documento ya está asociado a esta entidad");
|
||||
}
|
||||
|
||||
document.addEntity(entityType, entityId);
|
||||
return documentRepository.save(document);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void removeEntityAssociation(Long documentId, String entityType, Long entityId) {
|
||||
DocumentEntity entity = documentEntityRepository
|
||||
.findByDocumentIdAndEntityTypeAndEntityId(documentId, entityType, entityId)
|
||||
.orElseThrow(() -> new ResourceNotFoundException("Asociación no encontrada"));
|
||||
|
||||
// Verificar que no sea la última asociación (el documento debe tener al menos una)
|
||||
Document document = entity.getDocument();
|
||||
if (document.getEntities().size() <= 1) {
|
||||
throw new BadRequestException("No se puede eliminar la última asociación. Elimine el documento completo.");
|
||||
}
|
||||
|
||||
document.removeEntity(entityType, entityId);
|
||||
documentEntityRepository.delete(entity);
|
||||
}
|
||||
|
||||
public List<DocumentEntity> getDocumentEntities(Long documentId) {
|
||||
Document document = documentRepository.findById(documentId)
|
||||
.orElseThrow(() -> new ResourceNotFoundException("Documento", documentId));
|
||||
return List.copyOf(document.getEntities());
|
||||
}
|
||||
|
||||
public List<DocumentType> getDocumentTypes() {
|
||||
return documentTypeRepository.findAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtiene los tipos de documento permitidos para una entidad específica.
|
||||
* Incluye información sobre si es obligatorio o no.
|
||||
*/
|
||||
public List<DocumentTypeForEntityDto> getDocumentTypesForEntity(String entityType) {
|
||||
List<DocumentTypeEntityAllowed> allowed = documentTypeEntityAllowedRepository
|
||||
.findAllowedTypesForEntity(entityType);
|
||||
|
||||
return allowed.stream()
|
||||
.map(dtea -> new DocumentTypeForEntityDto(
|
||||
dtea.getDocumentType().getId(),
|
||||
dtea.getDocumentType().getName(),
|
||||
dtea.getEntityType(),
|
||||
dtea.getCanUpload(),
|
||||
dtea.getMustHave(),
|
||||
dtea.getDescription()))
|
||||
.toList();
|
||||
}
|
||||
|
||||
// Record para devolver el archivo y su información de cabecera
|
||||
public record DocumentDownloadResult(Resource resource, String mimeType, String filename) {}
|
||||
}
|
||||
Reference in New Issue
Block a user