package jnpf.fileStorage.dms;
|
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
import jnpf.dmsEntity.storage.form.DmsFileUploadRegisterForm;
|
import jnpf.exception.DataException;
|
import jnpf.model.filecontent.FileContentImportForm;
|
import jnpf.model.filecontent.FileContentReceipt;
|
import lombok.RequiredArgsConstructor;
|
import org.springframework.beans.BeanUtils;
|
import org.springframework.stereotype.Service;
|
import org.springframework.web.multipart.MultipartFile;
|
|
import java.io.File;
|
import java.io.IOException;
|
import java.io.InputStream;
|
import java.nio.file.Path;
|
import java.util.Locale;
|
|
/** Adapts the existing reader and storage method; never invokes registration or workflow callbacks. */
|
@Service
|
@RequiredArgsConstructor
|
public class FileContentImportService {
|
private final DmsCommonFileReader reader;
|
private final DmsFileStorageService storage;
|
private final DmsApprovalPageMergeService approvalPageMergeService;
|
private final DmsQianpiyeDocxWriter qianpiyeDocxWriter;
|
private final DmsDocxCommentCleaner commentCleaner;
|
private final ObjectMapper json;
|
|
public boolean sameContent(jnpf.model.filecontent.FileContentComparisonForm form) {
|
try {
|
JsonNode before = json.readTree(form.getOriginal()), after = json.readTree(form.getCandidate());
|
if (before == null || after == null || !before.isArray() || !after.isArray()
|
|| before.isEmpty() || before.size() != after.size() || before.size() > 100)
|
throw new DataException("文件内容比较参数无效");
|
for (int i = 0; i < before.size(); i++) {
|
if (!digest(before.get(i)).equals(digest(after.get(i)))) return false;
|
}
|
return true;
|
} catch (java.io.IOException invalid) { throw new DataException("文件内容比较参数无效"); }
|
}
|
|
private String digest(JsonNode file) {
|
// Existing local storage resolver rejects arbitrary URL fetching and traversal.
|
return reader.withFile(file.path("url").asText(), file.toString(), "内容校验", source -> {
|
try (InputStream input = source.getInputStream()) {
|
return cn.hutool.crypto.digest.DigestUtil.sha256Hex(input);
|
} catch (IOException failure) { throw new DataException("无法读取附件内容,请稍后重试"); }
|
});
|
}
|
|
public FileContentReceipt importContent(FileContentImportForm form) {
|
DmsFileUploadRegisterForm stored = reader.withFile(singleFileUrl(form.getFileJson()),
|
form.getFileJson(), form.getFileName(), file -> store(form, file));
|
FileContentReceipt receipt = new FileContentReceipt();
|
BeanUtils.copyProperties(stored, receipt);
|
return receipt;
|
}
|
|
private DmsFileUploadRegisterForm store(FileContentImportForm form, MultipartFile file) {
|
String approvalPage = form.getSignaturePageFiles();
|
MultipartFile source = isDocx(file) && form.getSignaturePageValues() != null
|
&& approvalPage != null && !approvalPage.isBlank()
|
? approvalPageMergeService.withoutApprovalPages(file) : file;
|
MultipartFile renderedFile = isDocx(file) && form.getSignaturePageValues() != null
|
? qianpiyeDocxWriter.render(source, form.getSignaturePageValues(),
|
form.getApprovalFlowVersionId()) : file;
|
if (approvalPage == null || approvalPage.isBlank() || !isDocx(file)) {
|
return storeFormal(form, renderedFile);
|
}
|
return reader.withFile(singleFileUrl(approvalPage), approvalPage, "签批页", page -> {
|
MultipartFile rendered = isDocx(page)
|
? qianpiyeDocxWriter.render(page, form.getSignaturePageValues(),
|
form.getApprovalFlowVersionId()) : page;
|
MultipartFile merged = approvalPageMergeService.replaceOrPrepend(renderedFile, rendered);
|
return storeFormal(form, merged);
|
});
|
}
|
|
private DmsFileUploadRegisterForm storeFormal(FileContentImportForm form, MultipartFile file) {
|
MultipartFile formal = form.isRemoveDocxComments() && isDocx(file) ? commentCleaner.clean(file) : file;
|
return storage.storeWorkflowContent(form.getDocumentId(), form.getFileVersionId(),
|
withDocumentName(formal, form.getFileName(), file.getOriginalFilename()));
|
}
|
|
// Rename only formal content, after signature-page merging and comment cleanup.
|
// Keep the source extension so a business title cannot change the file format.
|
private MultipartFile withDocumentName(MultipartFile file, String documentName, String sourceName) {
|
if (documentName == null || documentName.isBlank()) {
|
throw new DataException("文件名称不能为空");
|
}
|
String name = documentName.trim();
|
int dot = sourceName == null ? -1 : sourceName.lastIndexOf('.');
|
String extension = dot < 0 ? "" : sourceName.substring(dot);
|
if (!extension.isEmpty() && !name.toLowerCase(Locale.ROOT).endsWith(extension.toLowerCase(Locale.ROOT))) {
|
name += extension;
|
}
|
return name.equals(file.getOriginalFilename()) ? file : new NamedMultipartFile(file, name);
|
}
|
|
private record NamedMultipartFile(MultipartFile delegate, String filename) implements MultipartFile {
|
@Override public String getName() { return delegate.getName(); }
|
@Override public String getOriginalFilename() { return filename; }
|
@Override public String getContentType() { return delegate.getContentType(); }
|
@Override public boolean isEmpty() { return delegate.isEmpty(); }
|
@Override public long getSize() { return delegate.getSize(); }
|
@Override public byte[] getBytes() throws IOException { return delegate.getBytes(); }
|
@Override public InputStream getInputStream() throws IOException { return delegate.getInputStream(); }
|
@Override public void transferTo(File dest) throws IOException { delegate.transferTo(dest); }
|
@Override public void transferTo(Path dest) throws IOException { delegate.transferTo(dest); }
|
}
|
|
private boolean isDocx(MultipartFile file) {
|
String name = file == null ? null : file.getOriginalFilename();
|
return name != null && name.toLowerCase(java.util.Locale.ROOT).endsWith(".docx");
|
}
|
|
private String singleFileUrl(String fileJson) {
|
try {
|
JsonNode value = json.readTree(fileJson);
|
if (value != null && value.isArray()) {
|
if (value.size() != 1) throw new DataException("每次导入必须有且只有一个物理文件");
|
value = value.get(0);
|
}
|
JsonNode url = value == null ? null : value.get("url");
|
if (url == null || !url.isTextual() || url.textValue().isBlank())
|
throw new DataException("附件缺少URL");
|
return url.textValue();
|
} catch (JsonProcessingException e) {
|
throw new DataException("附件格式不正确");
|
}
|
}
|
}
|