package jnpf.fileStorage.dms;
|
|
import jnpf.base.ActionResult;
|
import jnpf.base.ActionResultCode;
|
import jnpf.config.ConfigValueUtil;
|
import jnpf.constant.MsgCode;
|
import jnpf.dms.DmsResourceApi;
|
import jnpf.dmsEntity.storage.form.DmsFileRevisionRegisterForm;
|
import jnpf.dmsEntity.storage.form.DmsFileUploadRegisterForm;
|
import jnpf.dmsEntity.storage.vo.DmsFileUploadVO;
|
import jnpf.exception.DataException;
|
import jnpf.util.OptimizeUtil;
|
import jnpf.util.StringUtil;
|
import jnpf.util.UpUtil;
|
import lombok.RequiredArgsConstructor;
|
import lombok.extern.slf4j.Slf4j;
|
import org.dromara.x.file.storage.core.FileInfo;
|
import org.dromara.x.file.storage.core.FileStorageService;
|
import org.dromara.x.file.storage.core.hash.HashInfo;
|
import org.dromara.x.file.storage.core.platform.MinioFileStorage;
|
import org.springframework.stereotype.Service;
|
import org.springframework.web.multipart.MultipartFile;
|
|
import java.nio.file.Paths;
|
import java.time.LocalDate;
|
import java.time.format.DateTimeFormatter;
|
import java.util.Objects;
|
import java.util.UUID;
|
|
@Slf4j
|
@Service
|
@RequiredArgsConstructor
|
public class DmsFileStorageService {
|
static final String STORAGE_PLATFORM = "seaweedfs-1";
|
static final String SERVICE_ID = "jnpf-file";
|
|
private final FileStorageService fileStorageService;
|
private final DmsResourceApi dmsResourceApi;
|
private final ConfigValueUtil configValueUtil;
|
|
public DmsFileUploadVO upload(UUID folderId, String sourceId, String documentNo,
|
String documentName, String documentType,
|
String responsibleDeptId, String versionLabel,
|
String preparedBy, LocalDate preparedOn, MultipartFile file) {
|
validate(sourceId, documentNo, documentName, documentType, responsibleDeptId,
|
versionLabel, preparedBy, preparedOn, file);
|
String originalFilename = safeFilename(file.getOriginalFilename());
|
String extension = UpUtil.getFileType(file);
|
if (!OptimizeUtil.fileType(configValueUtil.getAllowUploadFileType(), extension)) {
|
throw new DataException(MsgCode.FA017.get());
|
}
|
|
UUID documentId = UuidV7Generator.generate();
|
UUID fileVersionId = UuidV7Generator.generate();
|
UUID fileObjectId = UuidV7Generator.generate();
|
String objectPath = objectPath(documentId);
|
String objectName = fileVersionId + (StringUtil.isEmpty(extension) ? "" : "." + extension);
|
|
FileInfo stored = fileStorageService.of(file)
|
.setPlatform(STORAGE_PLATFORM)
|
.setPath(objectPath)
|
.setSaveFilename(objectName)
|
.setOriginalFilename(originalFilename)
|
.setHashCalculatorSha256()
|
.upload();
|
if (stored == null || stored.getHashInfo() == null
|
|| StringUtil.isEmpty(stored.getHashInfo().getSha256())) {
|
deleteQuietly(stored);
|
throw new DataException("SeaweedFS 上传失败或未生成 SHA-256");
|
}
|
|
DmsFileUploadRegisterForm form = registration(folderId, sourceId, documentNo,
|
documentName, documentType, responsibleDeptId, versionLabel, preparedBy,
|
preparedOn, originalFilename, documentId, fileVersionId, fileObjectId, stored);
|
try {
|
ActionResult<DmsFileUploadVO> result = dmsResourceApi.registerUpload(SERVICE_ID, form);
|
if (result == null || !ActionResultCode.SUCCESS.getCode().equals(result.getCode())
|
|| result.getData() == null) {
|
throw new DataException(result == null ? "DMS 上传登记无响应" : result.getMsg());
|
}
|
if (!sameStoredObject(result.getData(), stored)) {
|
deleteQuietly(stored);
|
}
|
return result.getData();
|
} catch (RuntimeException exception) {
|
deleteQuietly(stored);
|
throw exception;
|
}
|
}
|
|
public DmsFileUploadVO revise(UUID documentId, UUID baseFileVersionId, String sourceId,
|
String versionLabel, String preparedBy, LocalDate preparedOn,
|
String revisionReason, MultipartFile file) {
|
validateRevision(documentId, baseFileVersionId, sourceId, versionLabel, preparedBy,
|
preparedOn, revisionReason, file);
|
String originalFilename = safeFilename(file.getOriginalFilename());
|
String extension = UpUtil.getFileType(file);
|
if (!OptimizeUtil.fileType(configValueUtil.getAllowUploadFileType(), extension)) {
|
throw new DataException(MsgCode.FA017.get());
|
}
|
|
UUID fileVersionId = UuidV7Generator.generate();
|
UUID fileObjectId = UuidV7Generator.generate();
|
String objectPath = objectPath(documentId);
|
String objectName = fileVersionId + (StringUtil.isEmpty(extension) ? "" : "." + extension);
|
|
FileInfo stored = fileStorageService.of(file)
|
.setPlatform(STORAGE_PLATFORM)
|
.setPath(objectPath)
|
.setSaveFilename(objectName)
|
.setOriginalFilename(originalFilename)
|
.setHashCalculatorSha256()
|
.upload();
|
if (stored == null || stored.getHashInfo() == null
|
|| StringUtil.isEmpty(stored.getHashInfo().getSha256())) {
|
deleteQuietly(stored);
|
throw new DataException("SeaweedFS 上传失败或未生成 SHA-256");
|
}
|
|
DmsFileRevisionRegisterForm form = revisionRegistration(documentId, baseFileVersionId,
|
fileVersionId, fileObjectId, sourceId, versionLabel, preparedBy, preparedOn,
|
revisionReason, originalFilename, stored);
|
try {
|
ActionResult<DmsFileUploadVO> result = dmsResourceApi.registerRevision(SERVICE_ID, form);
|
if (result == null || !ActionResultCode.SUCCESS.getCode().equals(result.getCode())
|
|| result.getData() == null) {
|
throw new DataException(result == null ? "DMS 修订登记无响应" : result.getMsg());
|
}
|
if (!sameStoredObject(result.getData(), stored)) {
|
deleteQuietly(stored);
|
}
|
return result.getData();
|
} catch (RuntimeException exception) {
|
deleteQuietly(stored);
|
throw exception;
|
}
|
}
|
|
private DmsFileUploadRegisterForm registration(UUID folderId, String sourceId,
|
String documentNo, String documentName,
|
String documentType, String responsibleDeptId,
|
String versionLabel, String preparedBy,
|
LocalDate preparedOn, String originalFilename,
|
UUID documentId,
|
UUID fileVersionId, UUID fileObjectId,
|
FileInfo stored) {
|
MinioFileStorage storage = fileStorageService.getFileStorage(STORAGE_PLATFORM);
|
HashInfo hashInfo = stored.getHashInfo();
|
DmsFileUploadRegisterForm form = new DmsFileUploadRegisterForm();
|
form.setDocumentId(documentId);
|
form.setFileVersionId(fileVersionId);
|
form.setFileObjectId(fileObjectId);
|
form.setFolderId(folderId);
|
form.setDocumentNo(documentNo.trim());
|
form.setDocumentName(documentName.trim());
|
form.setDocumentType(documentType.trim());
|
form.setResponsibleDeptId(responsibleDeptId.trim());
|
form.setVersionLabel(versionLabel.trim());
|
form.setFileName(originalFilename);
|
form.setMimeType(trimToNull(stored.getContentType()));
|
form.setSizeBytes(stored.getSize());
|
form.setChecksumSha256(hashInfo.getSha256());
|
form.setPreparedBy(preparedBy.trim());
|
form.setPreparedOn(preparedOn);
|
form.setStoragePlatform(stored.getPlatform());
|
form.setBucketName(storage.getBucketName());
|
form.setBasePath(trimToEmpty(stored.getBasePath()));
|
form.setObjectPath(stored.getPath());
|
form.setObjectName(stored.getFilename());
|
form.setSourceId(sourceId.trim());
|
return form;
|
}
|
|
private DmsFileRevisionRegisterForm revisionRegistration(UUID documentId,
|
UUID baseFileVersionId,
|
UUID fileVersionId,
|
UUID fileObjectId,
|
String sourceId,
|
String versionLabel,
|
String preparedBy,
|
LocalDate preparedOn,
|
String revisionReason,
|
String originalFilename,
|
FileInfo stored) {
|
MinioFileStorage storage = fileStorageService.getFileStorage(STORAGE_PLATFORM);
|
HashInfo hashInfo = stored.getHashInfo();
|
DmsFileRevisionRegisterForm form = new DmsFileRevisionRegisterForm();
|
form.setDocumentId(documentId);
|
form.setBaseFileVersionId(baseFileVersionId);
|
form.setFileVersionId(fileVersionId);
|
form.setFileObjectId(fileObjectId);
|
form.setVersionLabel(versionLabel.trim());
|
form.setFileName(originalFilename);
|
form.setMimeType(trimToNull(stored.getContentType()));
|
form.setSizeBytes(stored.getSize());
|
form.setChecksumSha256(hashInfo.getSha256());
|
form.setPreparedBy(preparedBy.trim());
|
form.setPreparedOn(preparedOn);
|
form.setRevisionReason(revisionReason.trim());
|
form.setStoragePlatform(stored.getPlatform());
|
form.setBucketName(storage.getBucketName());
|
form.setBasePath(trimToEmpty(stored.getBasePath()));
|
form.setObjectPath(stored.getPath());
|
form.setObjectName(stored.getFilename());
|
form.setSourceId(sourceId.trim());
|
return form;
|
}
|
|
private boolean sameStoredObject(DmsFileUploadVO registered, FileInfo stored) {
|
return Objects.equals(registered.getStoragePlatform(), stored.getPlatform())
|
&& Objects.equals(registered.getBasePath(), trimToEmpty(stored.getBasePath()))
|
&& Objects.equals(registered.getObjectPath(), stored.getPath())
|
&& Objects.equals(registered.getObjectName(), stored.getFilename());
|
}
|
|
private String objectPath(UUID documentId) {
|
return LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd/"))
|
+ documentId + "/";
|
}
|
|
private String safeFilename(String value) {
|
if (value == null || value.trim().isEmpty()) {
|
throw new DataException("文件名不能为空");
|
}
|
String normalized = value.replace('\\', '/');
|
String filename = Paths.get(normalized).getFileName().toString().trim();
|
if (filename.isEmpty() || filename.length() > 300) {
|
throw new DataException("文件名长度必须在 1 到 300 之间");
|
}
|
return filename;
|
}
|
|
private void validate(String sourceId, String documentNo, String documentName,
|
String documentType, String responsibleDeptId, String versionLabel,
|
String preparedBy, LocalDate preparedOn, MultipartFile file) {
|
if (file == null || file.isEmpty()) {
|
throw new DataException("上传文件不能为空");
|
}
|
requireText(sourceId, "sourceId", 160);
|
requireText(documentNo, "文件编号", 200);
|
requireText(documentName, "文件名称", 300);
|
requireText(documentType, "文件类型", 100);
|
requireText(responsibleDeptId, "归属部门", 100);
|
requireText(versionLabel, "版本号", 50);
|
requireText(preparedBy, "制定人", 100);
|
if (preparedOn == null) {
|
throw new DataException("制定日期不能为空");
|
}
|
fileStorageService.getFileStorageVerify(STORAGE_PLATFORM);
|
}
|
|
private void validateRevision(UUID documentId, UUID baseFileVersionId, String sourceId,
|
String versionLabel, String preparedBy, LocalDate preparedOn,
|
String revisionReason, MultipartFile file) {
|
if (documentId == null || baseFileVersionId == null) {
|
throw new DataException("文件及当前版本不能为空");
|
}
|
if (file == null || file.isEmpty()) {
|
throw new DataException("修订文件不能为空");
|
}
|
requireText(sourceId, "sourceId", 120);
|
requireText(versionLabel, "新版本号", 50);
|
requireText(preparedBy, "制定人", 100);
|
requireText(revisionReason, "修订原因", 1000);
|
if (preparedOn == null) {
|
throw new DataException("制定日期不能为空");
|
}
|
fileStorageService.getFileStorageVerify(STORAGE_PLATFORM);
|
}
|
|
private void requireText(String value, String label, int maxLength) {
|
if (value == null || value.trim().isEmpty() || value.trim().length() > maxLength) {
|
throw new DataException(label + "长度必须在 1 到 " + maxLength + " 之间");
|
}
|
}
|
|
private String trimToEmpty(String value) {
|
return value == null ? "" : value.trim();
|
}
|
|
private String trimToNull(String value) {
|
return value == null || value.trim().isEmpty() ? null : value.trim();
|
}
|
|
private void deleteQuietly(FileInfo stored) {
|
if (stored == null) {
|
return;
|
}
|
try {
|
if (!fileStorageService.delete(stored)) {
|
log.error("DMS 上传补偿删除失败: platform={}, path={}, filename={}",
|
stored.getPlatform(), stored.getPath(), stored.getFilename());
|
}
|
} catch (Exception exception) {
|
log.error("DMS 上传补偿删除异常: platform={}, path={}, filename={}",
|
stored.getPlatform(), stored.getPath(), stored.getFilename(), exception);
|
}
|
}
|
}
|