提交代码

This commit is contained in:
lilin 2025-05-24 11:43:44 +08:00
parent 4b08b3f7a9
commit 2b30e30fda

View File

@ -61,6 +61,7 @@ import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
/**
* <p>
* 专项文档节点表 服务实现类
@ -114,7 +115,7 @@ public class NodesServiceImpl extends ServiceImpl<NodesMapper, Nodes> implements
***********************************/
@Override
public List<Map<String, Object>> getNodesTree(String nodeName, String projectId) {
Project project = projectMapper.selectById(projectId);
Project project = projectMapper.selectById(projectId);
// 查询所有节点数据
List<Map<String, Object>> allNodes = getAllNodes(projectId);
@ -132,14 +133,14 @@ public class NodesServiceImpl extends ServiceImpl<NodesMapper, Nodes> implements
// 如果 nodeName 为空返回所有根节点的完整树形结构
if (StringUtils.isEmpty(nodeName)) {
for (Map<String, Object> rootNode : rootNodes) {
rootNode.put("path",basePath);
rootNode.put("path", basePath);
result.addAll(buildFullTree(rootNode, allNodes));
}
return result;
}
// 否则返回从根节点到目标节点的树形结构
for (Map<String, Object> rootNode : rootNodes) {
rootNode.put("path",basePath);
rootNode.put("path", basePath);
List<Map<String, Object>> tree = buildTreeToTargetNode(rootNode, allNodes, nodeName);
if (!tree.isEmpty()) {
result.addAll(tree);
@ -530,7 +531,7 @@ public class NodesServiceImpl extends ServiceImpl<NodesMapper, Nodes> implements
***********************************/
@Override
@Transactional(rollbackFor = Exception.class) // 启用事务
public boolean deleteNodesById(String id,String path) {
public boolean deleteNodesById(String id, String path) {
Boolean value = false;
@ -682,6 +683,10 @@ public class NodesServiceImpl extends ServiceImpl<NodesMapper, Nodes> implements
AbstractBaseFileService<?> fileService = storageSourceContext.getByStorageId(storageId);
//todo 首先获取两个集合 对比出数据库中没有的文件夹以及文件递归增加
List<FileItemResult> fileItemList = fileService.fileList(fileListRequest.getPath());
if (fileItemList.size() == 0) {
throw new Exception("该项目目录不存在或没有项目文档,请先建立项目目录和文档。");
}
//获取数据库父节点为0的文件夹数据 通过所属项目ID和父节点查询
List<Nodes> nodesList = nodesMapper.selectList(new LambdaQueryWrapper<Nodes>().eq(Nodes::getParentId, "00").eq(Nodes::getProjectId, id));
@ -718,38 +723,40 @@ public class NodesServiceImpl extends ServiceImpl<NodesMapper, Nodes> implements
// queryWrapper.eq(Nodes::getNodeOrder, obtainNodeType(index));
Nodes nodeData = nodesMapper.selectOne(queryWrapper);
//如果没有 新增 并且递归
if (nodeData == null){
if (nodeData == null) {
Nodes node = saveNodes(projectId, TOP_LEVEL_PARENT_NODE, item.getName(), obtainNodeType(index));
otherLevelsData(projectId, node.getId(), item.getName(), item.getPath(), index);
}else{
} else {
otherLevelsData(projectId, nodeData.getId(), item.getName(), item.getPath(), index);
}
} else {
//获取节点名称
String nodeName = getLastPathSegment(item.getPath());
//获取节点信息 主要用到ID
QueryWrapper<Nodes> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("node_name", nodeName);
queryWrapper.eq("parent_id", TOP_LEVEL_PARENT_NODE);
Nodes node = nodesMapper.selectOne(queryWrapper);
//新增之前先查询
LambdaQueryWrapper<Files> queryWrapper1 = new LambdaQueryWrapper<>();
queryWrapper1.eq(Files::getProjectId, projectId);
queryWrapper1.eq(Files::getNodeId, node.getId());
queryWrapper1.eq(Files::getFilePath, item.getPath());
queryWrapper1.eq(Files::getFileName, item.getName());
Files files = filesMapper.selectOne(queryWrapper1);
if (files == null){
// 获取文件大小字节
long fileSizeInBytes = item.getSize();
// 转换为 MB 并保留两位小数
double fileSizeInMB = fileSizeInBytes / (1024.0 * 1024.0);
String fileSizeFormatted = String.format("%.2f", fileSizeInMB); // 保留两位小数
//保存文件信息
saveFiles(projectId, node.getId(), item.getPath(), item.getName(), fileSizeFormatted);
}
//第一层属于节点同级别的文件不做处理
continue;
// //获取节点名称
// String nodeName = getLastPathSegment(item.getPath());
// //获取节点信息 主要用到ID
// QueryWrapper<Nodes> queryWrapper = new QueryWrapper<>();
// queryWrapper.eq("node_name", nodeName);
// queryWrapper.eq("parent_id", TOP_LEVEL_PARENT_NODE);
// Nodes node = nodesMapper.selectOne(queryWrapper);
//
// //新增之前先查询
// LambdaQueryWrapper<Files> queryWrapper1 = new LambdaQueryWrapper<>();
// queryWrapper1.eq(Files::getProjectId, projectId);
// queryWrapper1.eq(Files::getNodeId, node.getId());
// queryWrapper1.eq(Files::getFilePath, item.getPath());
// queryWrapper1.eq(Files::getFileName, item.getName());
// Files files = filesMapper.selectOne(queryWrapper1);
// if (files == null) {
// // 获取文件大小字节
// long fileSizeInBytes = item.getSize();
// // 转换为 MB 并保留两位小数
// double fileSizeInMB = fileSizeInBytes / (1024.0 * 1024.0);
// String fileSizeFormatted = String.format("%.2f", fileSizeInMB); // 保留两位小数
// //保存文件信息
// saveFiles(projectId, node.getId(), item.getPath(), item.getName(), fileSizeFormatted);
// }
}
}
}
@ -791,11 +798,11 @@ public class NodesServiceImpl extends ServiceImpl<NodesMapper, Nodes> implements
// queryWrapper.eq(Nodes::getNodeOrder, obtainNodeType(index));
Nodes nodeData = nodesMapper.selectOne(queryWrapper);
//如果没有 新增 并且递归
if (nodeData == null){
if (nodeData == null) {
//保存节点
Nodes node = saveNodes(projectId, parentId, item.getName(), obtainNodeType(index));
otherLevelsData(projectId, node.getId(), item.getName(), item.getPath(), index);
}else{
} else {
otherLevelsData(projectId, nodeData.getId(), item.getName(), item.getPath(), index);
}
} else {
@ -809,7 +816,7 @@ public class NodesServiceImpl extends ServiceImpl<NodesMapper, Nodes> implements
queryWrapper1.eq(Files::getFilePath, item.getPath());
queryWrapper1.eq(Files::getFileName, item.getName());
Files files = filesMapper.selectOne(queryWrapper1);
if (files == null){
if (files == null) {
// 获取文件大小字节
long fileSizeInBytes = item.getSize();
// 转换为 MB 并保留两位小数
@ -884,9 +891,13 @@ public class NodesServiceImpl extends ServiceImpl<NodesMapper, Nodes> implements
*/
private void saveFiles(String projectId, String nodeId, String filePath, String fileName, String fileSize) {
//获取当前登录用户 上传人是当前登录人
// UsernamePasswordAuthenticationToken authentication =
// (UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
// LoginUser loginuser = (LoginUser) authentication.getPrincipal();
UsernamePasswordAuthenticationToken authentication =
(UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
LoginUser loginuser = null;
if (authentication != null) {
loginuser = (LoginUser) authentication.getPrincipal();
}
// 设置当前时间
LocalDateTime now = LocalDateTime.now();
// 转换为 Timestamp
@ -897,8 +908,11 @@ public class NodesServiceImpl extends ServiceImpl<NodesMapper, Nodes> implements
files.setNodeId(nodeId);
files.setFilePath(filePath);
files.setUploadTime(currentTime);
files.setUploader("admin");
//files.setUploader(loginuser.getUsername());
if (loginuser == null) {
files.setUploader(null);
} else {
files.setUploader(loginuser.getUsername());
}
files.setFileName(fileName);
files.setFileSize(fileSize);
filesMapper.insert(files);
@ -941,15 +955,20 @@ public class NodesServiceImpl extends ServiceImpl<NodesMapper, Nodes> implements
//解压以后的文件夹名称
String zipName = getFileNameWithoutExtension(fileName); // 示例222
//查询项目信息
Project project = projectMapper.selectById(id);
if(!project.getProjectName().equals(zipName)){
throw new RuntimeException("压缩包名称需要和项目名称保持一致");
}
String decompressionPath = "/" + "temporary";
//构建要解压的zip文件路径 例如 D:\yun\temporary\111.zip
Path zipFilePath = Paths.get(storageSourceConfig.getValue(), decompressionPath, fileName);
// 4. 构建解压目标路径 例如 D:\yun\temporary\111
Path destRoot = Paths.get(storageSourceConfig.getValue(), decompressionPath, zipName);
//查询项目信息
Project project = projectMapper.selectById(id);
//todo 如果项目不存在 直接拷贝到目标路径下 拷贝完成以后 然后新增一个项目结构 然后走一遍扫描
//源文件夹路径
@ -960,11 +979,15 @@ public class NodesServiceImpl extends ServiceImpl<NodesMapper, Nodes> implements
//首先执行解压缩
unzipToTemp(zipFilePath, String.valueOf(destRoot));
// 执行上传并且插入数据库
this.documentUploadById(id,sourceFolderPath,targetFolderPath,storageSourceConfig.getValue());
this.documentUploadById(id, sourceFolderPath, targetFolderPath, storageSourceConfig.getValue());
//开始执行更新表数据 名称是去掉后缀以后的
uploadProject(zipName);
//获取文件路径
String path = storageSourceConfig.getValue() + decompressionPath; // 替换为实际路径
File target = new File(path);
deleteDirectory(target);
} catch (IOException e) {
throw new RuntimeException(e);
} catch (Exception e) {
@ -979,6 +1002,29 @@ public class NodesServiceImpl extends ServiceImpl<NodesMapper, Nodes> implements
//todo 首先得上传到一个临时目录 然后找到这个目录我去解压缩到当前文件夹 解压缩以后 然后开始循环 我通过第一层去查询 如果没有就新增 如果有就循环 看看需不需要覆盖
public static void deleteDirectory(File directory) throws Exception {
if (!directory.exists()) {
return; // 路径不存在直接返回
}
File[] files = directory.listFiles();
if (files != null) {
for (File file : files) {
if (file.isDirectory()) {
deleteDirectory(file); // 递归删除子目录
} else {
if (!file.delete()) { // 删除文件
throw new Exception("无法删除文件: " + file.getAbsolutePath());
}
}
}
}
if (!directory.delete()) { // 删除空目录
throw new Exception("无法删除目录: " + directory.getAbsolutePath());
}
}
/**********************************
* 用途说明: 专项文档管理扫描接口实际接口
* 参数说明 id 所属项目ID
@ -986,12 +1032,12 @@ public class NodesServiceImpl extends ServiceImpl<NodesMapper, Nodes> implements
* 参数说明 zipName 解压以后的文件夹名称
* 返回值说明: com.yfd.platform.config.ResponseResult 返回成功或者失败
***********************************/
public String documentUploadById(String id, String sourceFolderPath, String targetFolderPath,String value) throws Exception {
public String documentUploadById(String id, String sourceFolderPath, String targetFolderPath, String value) throws Exception {
//源目录
File sourceDir = new File(value + sourceFolderPath);
//目标目录
File targetDir = new File( value + targetFolderPath);
File targetDir = new File(value + targetFolderPath);
if (!targetDir.exists()) {
FileUtils.copyDirectory(sourceDir, targetDir);
@ -1021,11 +1067,11 @@ public class NodesServiceImpl extends ServiceImpl<NodesMapper, Nodes> implements
//如果存在 判断是文件夹还是文件 如果是文件夹 则继续递归
if (fileItemResult.getType() == FileTypeEnum.FOLDER) {
try {
documentUploadById(id, fileItemResult.getPath() + "/" + fileItemResult.getName(), targetFileItem.getPath()+ "/" + targetFileItem.getName(),value);
documentUploadById(id, fileItemResult.getPath() + "/" + fileItemResult.getName(), targetFileItem.getPath() + "/" + targetFileItem.getName(), value);
} catch (Exception e) {
throw new RuntimeException(e);
}
}else {
} else {
if (fileItemResult.getType() == FileTypeEnum.FILE) {
//源路径
String sourcePath = value + sourceFolderPath + "/" + fileItemResult.getName();
@ -1048,24 +1094,24 @@ public class NodesServiceImpl extends ServiceImpl<NodesMapper, Nodes> implements
//更改目标路径下的文件名称
renameFile(targetPath);
//将源目录文件 复制到 目标目录文件
copyWithOverride(sourcePath,targetPath);
copyWithOverride(sourcePath, targetPath);
}else {
} else {
//将源目录文件 复制到 目标目录文件 todo这个地方是覆盖
copyWithOverride(sourcePath,targetPath);
copyWithOverride(sourcePath, targetPath);
}
}
}
}else{
} else {
//如果为空对象不存在 则直接拷贝
String sourcePath = value + sourceFolderPath;
//目标路径 就是源路径去掉/temporary的部分
String targetPath = sourcePath.replace("/" + "temporary", "");
String targetPath = sourcePath.replace("/" + "temporary", "");
//将源目录文件 复制到 目标目录文件
copyFolderWithOverride(sourcePath,targetPath);
copyFolderWithOverride(sourcePath, targetPath);
}
}
@ -1082,7 +1128,7 @@ public class NodesServiceImpl extends ServiceImpl<NodesMapper, Nodes> implements
projectLambdaQueryWrapper.eq(Project::getProjectName, projectName);
Project project = projectMapper.selectOne(projectLambdaQueryWrapper);
//如果项目不存在
if(project == null){
if (project == null) {
String projectCode = this.generateNextProjectCode();
project.setProjectCode("1");
@ -1108,8 +1154,6 @@ public class NodesServiceImpl extends ServiceImpl<NodesMapper, Nodes> implements
}
//获取文件列表
String absolutePath = "/" + project.getProjectName() + "/";
FileListRequest fileListRequest = buildFileRequest(absolutePath);
@ -1138,8 +1182,6 @@ public class NodesServiceImpl extends ServiceImpl<NodesMapper, Nodes> implements
}
// 编号生成工具方法
@Transactional(rollbackFor = Exception.class)
public synchronized String generateNextProjectCode() {
@ -1236,9 +1278,6 @@ public class NodesServiceImpl extends ServiceImpl<NodesMapper, Nodes> implements
}
//获取路径下的所有文件和文件夹
private List<FileItemResult> obtainFileItemResultData(FileListRequest fileListRequest) throws Exception {
@ -1336,9 +1375,18 @@ public class NodesServiceImpl extends ServiceImpl<NodesMapper, Nodes> implements
try (ZipInputStream zis = new ZipInputStream(
java.nio.file.Files.newInputStream(sourcePath), StandardCharsets.UTF_8)) {
ZipEntry entry;
while ((entry = zis.getNextEntry()) != null) {
ZipEntry entry = null;
while (true) { // 循环结构改为无限循环
try {
entry = zis.getNextEntry(); // 尝试获取下一个条目
if (entry == null) {
break; // 没有更多条目退出循环
}
// 打印当前处理的条目名称
LOGGER.info("Processing ZIP entry: {}", entry.getName());
// 跳过 macOS 系统文件
if (entry.getName().startsWith("__MACOSX")) {
continue;
@ -1347,7 +1395,7 @@ public class NodesServiceImpl extends ServiceImpl<NodesMapper, Nodes> implements
// 标准化路径并处理目录标识
String entryName = entry.getName()
.replace("\\", "/")
.replaceFirst("^/+", ""); // 去除开头的斜杠
.replaceFirst("^/+", ""); // 去除开头的斜杠
// 检测是否为目录兼容以'/'结尾的条目
boolean isDirectory = entry.isDirectory() || entry.getName().endsWith("/");
@ -1364,16 +1412,15 @@ public class NodesServiceImpl extends ServiceImpl<NodesMapper, Nodes> implements
}
Path targetPath = destRoot.resolve(entryName).normalize();
validatePathSafetya(targetPath, destRoot);
validatePathSafetya(targetPath, destRoot); // 确保路径安全
// 处理目录
if (isDirectory) {
java.nio.file.Files.createDirectories(targetPath);
}
// 处理文件
else {
} else {
// 处理文件
if (java.nio.file.Files.exists(targetPath)) {
LOGGER.warn("文件已存在,跳过覆盖: {}", targetPath);
LOGGER.warn("File already exists, skip overwriting: {}", targetPath);
continue;
}
// 确保父目录存在
@ -1382,8 +1429,17 @@ public class NodesServiceImpl extends ServiceImpl<NodesMapper, Nodes> implements
IOUtils.copy(zis, os);
}
}
} catch (IllegalArgumentException e) {
// 捕获 MALFORMED 异常并记录条目名称
String errorEntry = (entry != null) ? entry.getName() : "Unknown Entry";
LOGGER.error("MALFORMED ZIP Entry Detected! Entry Name: {}", errorEntry, e);
} finally {
zis.closeEntry(); // 确保每个条目只关闭一次
if (entry != null) {
zis.closeEntry(); // 关闭当前条目
}
}
}
}
@ -1391,6 +1447,7 @@ public class NodesServiceImpl extends ServiceImpl<NodesMapper, Nodes> implements
return destRoot.toFile();
}
/**
* 路径安全验证增强版
*/