|
@@ -1,14 +1,289 @@
|
|
|
package org.springblade.archive.service.impl;
|
|
|
|
|
|
-
|
|
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|
|
import lombok.AllArgsConstructor;
|
|
|
import org.springblade.archive.entity.ScanFile;
|
|
|
+import org.springblade.archive.entity.ScanFolder;
|
|
|
import org.springblade.archive.mapper.ScanFileMapper;
|
|
|
+import org.springblade.archive.mapper.ScanFolderMapper;
|
|
|
import org.springblade.archive.service.ScanFileService;
|
|
|
+import org.springblade.archive.utils.FileUtils;
|
|
|
+import org.springblade.common.utils.SnowFlakeUtil;
|
|
|
+import org.springblade.core.oss.model.BladeFile;
|
|
|
+import org.springblade.core.tool.api.R;
|
|
|
+import org.springblade.resource.feign.NewIOSSClient;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
+import java.io.File;
|
|
|
+import java.io.IOException;
|
|
|
+import java.nio.file.Files;
|
|
|
+import java.nio.file.StandardCopyOption;
|
|
|
+import java.nio.file.attribute.BasicFileAttributes;
|
|
|
+import java.text.SimpleDateFormat;
|
|
|
+import java.util.Date;
|
|
|
+
|
|
|
|
|
|
@Service
|
|
|
@AllArgsConstructor
|
|
|
public class ScanFileServiceImpl extends ServiceImpl<ScanFileMapper, ScanFile> implements ScanFileService {
|
|
|
+ //文件根目录
|
|
|
+ private static final String ROOT_PREFIX = "D:" + File.separator + "ScanPDF";
|
|
|
+ //备份文件根目录
|
|
|
+ private static final String ROOT_PREFIX_back = "D:" + File.separator + "ScanBackPDF";
|
|
|
+
|
|
|
+ private final ScanFolderMapper scanFolderMapper;
|
|
|
+ private final ScanFileMapper scanFileMapper;
|
|
|
+ private final NewIOSSClient newIOSSClient;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 入口方法:扫描并入库指定contractId的所有文件夹
|
|
|
+ * @param contractId 传入的合同ID(对应D:\PDF下的文件夹名)
|
|
|
+ * @param projectId 传入的项目ID
|
|
|
+ */
|
|
|
+ public R scanAndSave(Long contractId, Long projectId) {
|
|
|
+ // 1. 构建合同对应的根文件夹路径(D:\PDF\${contractId})
|
|
|
+ String contractFolderPath = ROOT_PREFIX + File.separator + contractId;
|
|
|
+ File contractFolder = new File(contractFolderPath);
|
|
|
+
|
|
|
+ // 校验根文件夹是否存在且为目录
|
|
|
+ if (!contractFolder.exists() || !contractFolder.isDirectory()) {
|
|
|
+ return R.fail("错误:根文件夹不存在或非法,路径=" + contractFolderPath);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 扫描一级文件夹(parent_id=0)并入库,同时递归子文件夹
|
|
|
+ File[] firstLevelFolders = contractFolder.listFiles(File::isDirectory);
|
|
|
+ if (firstLevelFolders == null || firstLevelFolders.length == 0) {
|
|
|
+ return R.fail("提示:合同ID=" + contractId + "的根文件夹下无任何子文件夹");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 遍历一级文件夹,逐个入库并递归子目录
|
|
|
+ for (File firstLevelFolder : firstLevelFolders) {
|
|
|
+ String folderName = firstLevelFolder.getName();
|
|
|
+ Long folderId = scanFolderMapper.getId(folderName,contractId,projectId);
|
|
|
+ if(folderId==null){
|
|
|
+ folderId=SnowFlakeUtil.getId(); // 生成随机ID
|
|
|
+ }
|
|
|
+ String filePath = firstLevelFolder.getAbsolutePath();
|
|
|
+ // 创建一级目录的ScanFolder对象(parent_id=0)
|
|
|
+ ScanFolder firstLevelScanFolder = new ScanFolder(
|
|
|
+ folderId,
|
|
|
+ 0L, // 一级目录父ID固定为0
|
|
|
+ projectId,
|
|
|
+ contractId,
|
|
|
+ folderName,
|
|
|
+ filePath,
|
|
|
+ 0
|
|
|
+ );
|
|
|
+ // 入库一级目录
|
|
|
+ checkInsertScanFolder(firstLevelScanFolder);
|
|
|
+ // 递归扫描当前一级目录下的所有子文件夹(子目录的父ID=当前一级目录ID)
|
|
|
+ recursiveScanSubFolders(firstLevelFolder, folderId, projectId, contractId);
|
|
|
+
|
|
|
+ }
|
|
|
+ return R.success("扫描并入库成功");
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 递归扫描子文件夹并入库
|
|
|
+ * @param parentFolder 父文件夹对象(当前要扫描子目录的文件夹)
|
|
|
+ * @param parentFolderId 父文件夹在数据库中的ID(子目录的parent_id)
|
|
|
+ * @param projectId 项目ID(透传)
|
|
|
+ * @param contractId 合同ID(透传)
|
|
|
+ */
|
|
|
+ private void recursiveScanSubFolders(File parentFolder, Long parentFolderId, Long projectId, Long contractId) {
|
|
|
+ // 获取父文件夹下的所有子文件夹
|
|
|
+ File[] subFolders = parentFolder.listFiles(File::isDirectory);
|
|
|
+ if (subFolders == null || subFolders.length == 0) {
|
|
|
+ System.out.println("递归终止:父ID=" + parentFolderId + "(名称=" + parentFolder.getName() + ")下无子文件夹");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 遍历每个子文件夹,入库并继续递归其下的子目录
|
|
|
+ for (File subFolder : subFolders) {
|
|
|
+ String subFolderName = subFolder.getName();
|
|
|
+ Long subFolderId = scanFolderMapper.getId(subFolderName,contractId,projectId);
|
|
|
+ if(subFolderId==null){
|
|
|
+ subFolderId=SnowFlakeUtil.getId(); // 生成随机ID
|
|
|
+ }
|
|
|
+ String filePath = subFolder.getAbsolutePath();
|
|
|
+
|
|
|
+ // 创建子目录的ScanFolder对象(parent_id=父文件夹ID)
|
|
|
+ ScanFolder subScanFolder = new ScanFolder(
|
|
|
+ subFolderId,
|
|
|
+ parentFolderId, // 关联父目录ID
|
|
|
+ projectId,
|
|
|
+ contractId,
|
|
|
+ subFolderName,
|
|
|
+ filePath,
|
|
|
+ 0
|
|
|
+ );
|
|
|
+ // 入库子目录
|
|
|
+ checkInsertScanFolder(subScanFolder);
|
|
|
+ // 递归扫描当前子目录下的子文件夹
|
|
|
+ recursiveScanSubFolders(subFolder, subFolderId, projectId, contractId);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 检查文件夹是否存在,不存在则新增
|
|
|
+ * @param scanFolder
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ public boolean checkInsertScanFolder(ScanFolder scanFolder) {
|
|
|
+ int result = scanFolderMapper.exists(scanFolder.getProjectId(), scanFolder.getContractId(), scanFolder.getFolderName());
|
|
|
+ if(result>0){
|
|
|
+ return false;
|
|
|
+ }else {
|
|
|
+ scanFolderMapper.insert(scanFolder);
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 扫描文件并入库
|
|
|
+ * @param contractId
|
|
|
+ * @param projectId
|
|
|
+ */
|
|
|
+ public R scanAndSaveFiles(Long contractId, Long projectId) {
|
|
|
+ // 1. 构建合同对应的根文件夹路径(D:\PDF\${contractId})
|
|
|
+ String contractFolderPath = ROOT_PREFIX + File.separator + contractId;
|
|
|
+ File contractFolder = new File(contractFolderPath);
|
|
|
+
|
|
|
+ // 校验根文件夹是否存在且为目录
|
|
|
+ if (!contractFolder.exists() || !contractFolder.isDirectory()) {
|
|
|
+ return R.fail("错误:根文件夹不存在或非法,路径=" + contractFolderPath);
|
|
|
+ }
|
|
|
+
|
|
|
+ System.out.println("开始扫描合同ID=" + contractId + "下的所有文件...");
|
|
|
+ // 递归扫描所有文件
|
|
|
+ recursiveScanFiles(contractFolder, projectId, contractId);
|
|
|
+ System.out.println("合同ID=" + contractId + "的文件扫描入库完成");
|
|
|
+ return R.success("扫描并入库完成");
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 递归扫描文件夹中的所有文件并入库
|
|
|
+ * @param currentFolder 当前扫描的文件夹
|
|
|
+ * @param projectId 项目ID
|
|
|
+ * @param contractId 合同ID
|
|
|
+ */
|
|
|
+ private void recursiveScanFiles(File currentFolder, Long projectId, Long contractId) {
|
|
|
+ // 1. 先处理当前文件夹中的文件
|
|
|
+ File[] files = currentFolder.listFiles(File::isFile);
|
|
|
+ if (files != null && files.length > 0) {
|
|
|
+ for (File file : files) {
|
|
|
+ processFile(file, currentFolder, projectId, contractId);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 递归处理子文件夹
|
|
|
+ File[] subFolders = currentFolder.listFiles(File::isDirectory);
|
|
|
+ if (subFolders != null && subFolders.length > 0) {
|
|
|
+ for (File subFolder : subFolders) {
|
|
|
+ recursiveScanFiles(subFolder, projectId, contractId);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 处理单个文件:获取信息并入库
|
|
|
+ */
|
|
|
+ private void processFile(File file, File parentFolder, Long projectId, Long contractId) {
|
|
|
+ try {
|
|
|
+ // 获取文件创建时间
|
|
|
+ BasicFileAttributes attr = Files.readAttributes(file.toPath(), BasicFileAttributes.class);
|
|
|
+ Date date = new Date(attr.creationTime().toMillis());
|
|
|
+ SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
|
|
+ String createTime = sdf1.format(date); // 2023-12-25 14:30:45
|
|
|
+ // 获取文件信息
|
|
|
+ String fileName = file.getName();
|
|
|
+ String filePath = file.getAbsolutePath();
|
|
|
+ String folderName = parentFolder.getName();
|
|
|
+ // 查询对应的folder_id
|
|
|
+ Long folderId = scanFolderMapper.getId(folderName,contractId,projectId);
|
|
|
+ if (folderId == null) {
|
|
|
+ System.err.println("警告:未找到对应的文件夹记录,跳过文件:" + filePath +
|
|
|
+ " (folderName=" + folderName + ")");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ // 检查文件是否已存在(根据project_id, contract_id, file_path判断)
|
|
|
+ if (fileExists(projectId, contractId, folderName)) {
|
|
|
+ System.out.println("文件已存在,跳过:" + filePath);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ BladeFile bladeFile = newIOSSClient.uploadFile(fileName, filePath);
|
|
|
+ if(bladeFile==null){
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ String pdfNum = FileUtils.getPdfNum(bladeFile.getLink());
|
|
|
+ // 生成随机ID
|
|
|
+ Long fileId = SnowFlakeUtil.getId();
|
|
|
+ // 创建文件实体
|
|
|
+ ScanFile scanFile = new ScanFile(
|
|
|
+ fileId,
|
|
|
+ projectId,
|
|
|
+ contractId,
|
|
|
+ folderId,
|
|
|
+ null,//序号
|
|
|
+ null,//数字编号
|
|
|
+ null,//文件编号
|
|
|
+ fileName,
|
|
|
+ pdfNum,//文件页数
|
|
|
+ createTime,
|
|
|
+ filePath,
|
|
|
+ bladeFile.getLink(),//OSS路径
|
|
|
+ null,//负责人
|
|
|
+ 0
|
|
|
+ );
|
|
|
+ // 入库
|
|
|
+ if (scanFileMapper.insert(scanFile)>0) {
|
|
|
+ System.out.println("文件入库成功:" + filePath);
|
|
|
+ try {
|
|
|
+ // 构建备份文件路径,保持原有的文件夹结构
|
|
|
+ if (filePath.startsWith(ROOT_PREFIX)) {
|
|
|
+ // 截取原始路径中除根目录外的部分
|
|
|
+ String relativePath = filePath.substring(ROOT_PREFIX.length());
|
|
|
+ // 构建备份文件完整路径
|
|
|
+ String backupFilePath = ROOT_PREFIX_back + relativePath;
|
|
|
+ File backupFile = new File(backupFilePath);
|
|
|
+ // 创建备份文件所在的目录
|
|
|
+ File backupDir = backupFile.getParentFile();
|
|
|
+ if (!backupDir.exists()) {
|
|
|
+ boolean dirCreated = backupDir.mkdirs();
|
|
|
+ if (!dirCreated) {
|
|
|
+ throw new IOException("无法创建备份目录: " + backupDir.getAbsolutePath());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 复制文件到备份目录
|
|
|
+ Files.copy(file.toPath(), backupFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
|
|
|
+ System.out.println("文件备份成功:" + backupFilePath);
|
|
|
+ // 备份成功后删除源文件
|
|
|
+ boolean deleted = file.delete();
|
|
|
+ if (deleted) {
|
|
|
+ System.out.println("源文件已删除:" + filePath);
|
|
|
+ } else {
|
|
|
+ System.err.println("警告:备份成功,但无法删除源文件:" + filePath);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ System.err.println("警告:文件路径不在预期的根目录下,无法备份:" + filePath);
|
|
|
+ }
|
|
|
+ } catch (IOException e) {
|
|
|
+ System.err.println("文件备份失败:" + filePath + ",错误信息:" + e.getMessage());
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ System.err.println("文件入库失败:" + filePath);
|
|
|
+ }
|
|
|
+ } catch (IOException e) {
|
|
|
+ System.err.println("处理文件时出错:" + file.getAbsolutePath() + ",错误信息:" + e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private Boolean fileExists(Long projectId, Long contractId, String folderName){
|
|
|
+ Integer i = scanFileMapper.exists(projectId, contractId, folderName);
|
|
|
+ if(i==1){
|
|
|
+ return true;
|
|
|
+ }else {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|