From 202448fc38a8d9e3f41cce4ba1dd7a508780d5f1 Mon Sep 17 00:00:00 2001 From: jiang Date: Wed, 20 Nov 2024 09:47:52 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8F=90=E4=BA=A4=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DataSetBasicFileController.java | 103 ++++ .../ai/domain/DataSetBasicFileEntity.java | 3 +- .../ai/mapper/DataSetBasicFileMapper.java | 40 ++ .../ai/service/DataSetBasicFileService.java | 49 ++ .../Impl/DataSetBasicFileServiceImpl.java | 506 ++++++++++++++++-- .../java/com/bonus/ai/utils/MinioUtil.java | 69 ++- .../mapper/DataSetBasicFileMapper.xml | 56 +- 7 files changed, 759 insertions(+), 67 deletions(-) diff --git a/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/controller/DataSetBasicFileController.java b/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/controller/DataSetBasicFileController.java index 15058e8..56f38f1 100644 --- a/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/controller/DataSetBasicFileController.java +++ b/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/controller/DataSetBasicFileController.java @@ -2,6 +2,8 @@ package com.bonus.ai.controller; import com.bonus.ai.domain.DataSetBasicFileEntity; import com.bonus.ai.service.DataSetBasicFileService; +import com.bonus.common.core.constant.HttpStatus; +import com.bonus.common.core.utils.Base64Utils; import com.bonus.common.core.utils.poi.ExcelUtil; import com.bonus.common.core.web.controller.BaseController; import com.bonus.common.core.web.domain.AjaxResult; @@ -9,11 +11,16 @@ import com.bonus.common.core.web.page.TableDataInfo; import com.bonus.common.log.annotation.SysLog; import com.bonus.common.log.enums.OperaType; import com.bonus.common.security.annotation.RequiresPermissions; +import org.aspectj.weaver.loadtime.Aj; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; +import java.net.URLDecoder; import java.util.ArrayList; import java.util.List; @@ -42,6 +49,44 @@ public class DataSetBasicFileController extends BaseController List list = dataSetBasicFileService.selectDataSetBasicFileList(entity); return getDataTable(list); }catch (Exception e){ + e.printStackTrace(); + return getDataTable(new ArrayList<>()); + } + + } + + + /** + * 查询文件基础列表 + */ + @RequiresPermissions("dataCenter:dataSetBasicFile:list") + @GetMapping("/delList") + public TableDataInfo delList(DataSetBasicFileEntity entity) + { + try { + startPage(); + List list = dataSetBasicFileService.selectDataSetBasicDelFileList(entity); + return getDataTable(list); + }catch (Exception e){ + e.printStackTrace(); + return getDataTable(new ArrayList<>()); + } + + } + + /** + * 查询文件基础列表 + */ + @RequiresPermissions("dataCenter:dataSetBasicFile:isPublicList") + @GetMapping("/isPublicList") + public TableDataInfo isPublicList(DataSetBasicFileEntity entity) + { + try { + startPage(); + List list = dataSetBasicFileService.selectDataSetBasicFileIsPublicList(entity); + return getDataTable(list); + }catch (Exception e){ + e.printStackTrace(); return getDataTable(new ArrayList<>()); } @@ -103,6 +148,27 @@ public class DataSetBasicFileController extends BaseController return dataSetBasicFileService.deleteDataSetBasicFileByFileIds(fileIds); } + /** + * 删除文件基础 + */ + @RequiresPermissions("dataCenter:dataSetBasicFile:dataRecovery") + @PostMapping("/dataRecovery/{fileIds}") + @SysLog(title = "文件基础", businessType = OperaType.DELETE,logType = 0,module = "文件基础",details = "导出文件基础列表") + public AjaxResult dataRecoveryFileByFileIds(@PathVariable Long[] fileIds) + { + return dataSetBasicFileService.dataRecoveryFileByFileIds(fileIds); + } + + /** + * 文件上传 + * @param file 文件流 + * @param filename 文件名称 + * @param chunk 分片序号 + * @param totalChunks 总分片 + * @param parentId 父类id + * @param fileUrl 文件地址 + * @return 是否成功 + */ @PostMapping("/uploadFiles") @SysLog(title = "文件基础", businessType = OperaType.DELETE,logType = 0,module = "文件基础",details = "导出文件基础列表") @@ -117,4 +183,41 @@ public class DataSetBasicFileController extends BaseController return dataSetBasicFileService.uploadDataSetBasicFiles(file,filename,chunk,totalChunks,parentId,fileUrl); } + /** + * 文件下载 + * @param response 请求 + * @param fileIds 文件id + */ + @PostMapping("/download/{fileIds}") + public void downloadFile(HttpServletResponse response, HttpServletRequest request, @PathVariable Long[] fileIds) { + dataSetBasicFileService.downloadFile(response,request,fileIds); + } + + /** + * 批量修改文件共享状态 + * @param fileIds 文件id + */ + @PostMapping("/sharedFilesByIds/{fileIds}") + public AjaxResult sharedFilesByIds(@PathVariable Long[] fileIds) { + return dataSetBasicFileService.sharedFilesByIds(fileIds); + } + + /** + * 修改单个文件共享状态 + * @param entity 实体 + */ + @PostMapping("/changeIsPublic") + public AjaxResult changeIsPublic(@RequestBody DataSetBasicFileEntity entity) { + return dataSetBasicFileService.changeIsPublic(entity); + } + + /** + * 获取文件树型结构 + * @return + */ + @PostMapping("/getFileTerr") + public AjaxResult getFileTerr(){ + return dataSetBasicFileService.getFileTerr(); + } + } diff --git a/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/domain/DataSetBasicFileEntity.java b/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/domain/DataSetBasicFileEntity.java index 64afd33..2ab40fe 100644 --- a/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/domain/DataSetBasicFileEntity.java +++ b/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/domain/DataSetBasicFileEntity.java @@ -1,6 +1,7 @@ package com.bonus.ai.domain; import com.bonus.common.core.web.domain.BaseEntity; +import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; @@ -39,7 +40,7 @@ public class DataSetBasicFileEntity extends BaseEntity { * 文件大小,以字符串形式存储文件的大小信息。 * 通常用于显示文件的大小,例如:"15MB"。 */ - private String fileSize; + private Long fileSize; /** * 是否是文件夹的标识。 diff --git a/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/mapper/DataSetBasicFileMapper.java b/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/mapper/DataSetBasicFileMapper.java index 4c41b90..96f8253 100644 --- a/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/mapper/DataSetBasicFileMapper.java +++ b/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/mapper/DataSetBasicFileMapper.java @@ -1,8 +1,10 @@ package com.bonus.ai.mapper; import com.bonus.ai.domain.DataSetBasicFileEntity; +import org.apache.ibatis.annotations.Param; import java.util.List; +import java.util.Set; /** * 文件基础Mapper接口 @@ -20,6 +22,14 @@ public interface DataSetBasicFileMapper */ public DataSetBasicFileEntity selectDataSetBasicFileByFileId(Long fileId); + /** + * 查询文件基础 + * + * @param parentId 文件基础主键 + * @return 文件基础 + */ + public List selectDataSetBasicFileByParentId(Long parentId); + /** * 查询文件基础列表 * @@ -28,6 +38,22 @@ public interface DataSetBasicFileMapper */ public List selectDataSetBasicFileList(DataSetBasicFileEntity entity); + /** + * 查询共享文件 + * + * @param entity 文件基础 + * @return 文件基础集合 + */ + public List selectDataSetBasicFileIsPublicList(DataSetBasicFileEntity entity); + + /** + * 获取删除文件列表 + * + * @param entity 文件基础 + * @return 文件基础集合 + */ + public List selectDataSetBasicDelFileList(DataSetBasicFileEntity entity); + /** * 新增文件基础 * @@ -51,4 +77,18 @@ public interface DataSetBasicFileMapper * @return 结果 */ public int deleteDataSetBasicFileByFileIds(Long[] fileIds); + + /** + * 批量删除文件基础 + * + * @param fileIds 需要删除的数据主键集合 + * @return 结果 + */ + public int dataRecoveryFileByFileIds(Long[] fileIds); + + /** + * 修改文件共享状态 + * @param ancestors + */ + void updateSharedFilesByIds(@Param("ancestors")Set ancestors ,@Param("isPublic") String isPublic); } diff --git a/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/service/DataSetBasicFileService.java b/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/service/DataSetBasicFileService.java index 514f9e3..66d9e9a 100644 --- a/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/service/DataSetBasicFileService.java +++ b/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/service/DataSetBasicFileService.java @@ -4,6 +4,8 @@ import com.bonus.ai.domain.DataSetBasicFileEntity; import com.bonus.common.core.web.domain.AjaxResult; import org.springframework.web.multipart.MultipartFile; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import java.util.List; /** @@ -30,6 +32,21 @@ public interface DataSetBasicFileService */ public List selectDataSetBasicFileList(DataSetBasicFileEntity entity); + /** + * 查询共享文件 + * + * @param entity 文件基础 + * @return 文件基础集合 + */ + public List selectDataSetBasicFileIsPublicList(DataSetBasicFileEntity entity); + + /** + * 删除文件列表 + * + * @param entity 文件基础 + * @return 文件基础集合 + */ + public List selectDataSetBasicDelFileList(DataSetBasicFileEntity entity); /** * 新增文件基础 * @@ -53,6 +70,13 @@ public interface DataSetBasicFileService * @return 结果 */ public AjaxResult deleteDataSetBasicFileByFileIds(Long[] fileIds); + /** + * 还原数据 + * + * @param fileIds 需要删除的文件基础主键集合 + * @return 结果 + */ + public AjaxResult dataRecoveryFileByFileIds(Long[] fileIds); /** * 文件分片上传 @@ -63,4 +87,29 @@ public interface DataSetBasicFileService * @return */ AjaxResult uploadDataSetBasicFiles(MultipartFile file, String filename, int chunk, int totalChunks,String parentId, String fileUrl); + + /** + * 下载文件 + * @param response + * @param fileIds + */ + void downloadFile(HttpServletResponse response, HttpServletRequest request, Long[] fileIds); + + /** + * 共享文件 + * @param fileIds 文件id + */ + AjaxResult sharedFilesByIds(Long[] fileIds); + + /** + * 修改单个文件的共享状态 + * @param entity 实体 + */ + AjaxResult changeIsPublic(DataSetBasicFileEntity entity); + + /** + * 获取树形结构 + * @return + */ + AjaxResult getFileTerr(); } diff --git a/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/service/Impl/DataSetBasicFileServiceImpl.java b/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/service/Impl/DataSetBasicFileServiceImpl.java index ffa87c4..75c9317 100644 --- a/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/service/Impl/DataSetBasicFileServiceImpl.java +++ b/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/service/Impl/DataSetBasicFileServiceImpl.java @@ -1,24 +1,31 @@ package com.bonus.ai.service.Impl; -import java.io.IOException; -import java.util.List; - import com.bonus.ai.domain.DataSetBasicFileEntity; +import com.bonus.ai.domain.TreeNode; import com.bonus.ai.mapper.DataSetBasicFileMapper; import com.bonus.ai.service.DataSetBasicFileService; import com.bonus.ai.utils.MinioUtil; import com.bonus.common.core.utils.DateUtils; import com.bonus.common.core.web.domain.AjaxResult; import com.bonus.common.security.utils.SecurityUtils; -import com.bonus.system.api.domain.SysFile; -import io.minio.PutObjectArgs; +import io.minio.Result; import io.minio.errors.MinioException; +import io.minio.messages.Item; +import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.ObjectUtils; -import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; import javax.annotation.Resource; +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.*; +import java.net.URLEncoder; +import java.nio.file.Files; +import java.util.*; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; /** * 文件基础Service业务层处理 @@ -27,13 +34,14 @@ import javax.annotation.Resource; * @date 2024-11-14 */ @Service -public class DataSetBasicFileServiceImpl implements DataSetBasicFileService -{ +public class DataSetBasicFileServiceImpl implements DataSetBasicFileService { @Resource private DataSetBasicFileMapper dataSetBasicFileMapper; @Resource private MinioUtil minioUtil; + + /** * 查询文件基础 * @@ -41,16 +49,15 @@ public class DataSetBasicFileServiceImpl implements DataSetBasicFileService * @return 文件基础 */ @Override - public AjaxResult selectDataSetBasicFileByFileId(Long fileId) - { + public AjaxResult selectDataSetBasicFileByFileId(Long fileId) { try { - DataSetBasicFileEntity aiBasicFile =dataSetBasicFileMapper.selectDataSetBasicFileByFileId(fileId); + DataSetBasicFileEntity aiBasicFile = dataSetBasicFileMapper.selectDataSetBasicFileByFileId(fileId); if (ObjectUtils.isEmpty(aiBasicFile)) { return AjaxResult.error(); - }else { + } else { return AjaxResult.success(); } - }catch (Exception e){ + } catch (Exception e) { return AjaxResult.error(); } } @@ -62,11 +69,34 @@ public class DataSetBasicFileServiceImpl implements DataSetBasicFileService * @return 文件基础 */ @Override - public List selectDataSetBasicFileList(DataSetBasicFileEntity aiBasicFile) - { + public List selectDataSetBasicFileList(DataSetBasicFileEntity aiBasicFile) { + aiBasicFile.setCreateBy(SecurityUtils.getUserId().toString()); return dataSetBasicFileMapper.selectDataSetBasicFileList(aiBasicFile); } + /** + * 查询共享文件 + * + * @param entity 文件基础 + * @return 文件基础集合 + */ + @Override + public List selectDataSetBasicFileIsPublicList(DataSetBasicFileEntity entity) { + return dataSetBasicFileMapper.selectDataSetBasicFileIsPublicList(entity); + } + + /** + * 查询文件基础列表 + * + * @param aiBasicFile 文件基础 + * @return 文件基础 + */ + @Override + public List selectDataSetBasicDelFileList(DataSetBasicFileEntity aiBasicFile) { + aiBasicFile.setCreateBy(SecurityUtils.getUserId().toString()); + return dataSetBasicFileMapper.selectDataSetBasicDelFileList(aiBasicFile); + } + /** * 新增文件基础 * @@ -74,13 +104,15 @@ public class DataSetBasicFileServiceImpl implements DataSetBasicFileService * @return 结果 */ @Override - public AjaxResult insertDataSetBasicFile(DataSetBasicFileEntity aiBasicFile) - { + public AjaxResult insertDataSetBasicFile(DataSetBasicFileEntity aiBasicFile) { aiBasicFile.setCreateTime(DateUtils.getNowDate()); - try{ - int rows = dataSetBasicFileMapper.insertDataSetBasicFile(aiBasicFile); - return rows>0?AjaxResult.success():AjaxResult.error(); - }catch (Exception e){ + aiBasicFile.setCreateBy(SecurityUtils.getUserId().toString()); + aiBasicFile.setFileUrl(minioUtil.joinPath(SecurityUtils.getUserId().toString(), aiBasicFile.getFileUrl())); + aiBasicFile.setFileUrl(minioUtil.joinPath(aiBasicFile.getFileUrl(), aiBasicFile.getFileName())); + try { + int rows = dataSetBasicFileMapper.insertDataSetBasicFile(aiBasicFile); + return rows > 0 ? AjaxResult.success() : AjaxResult.error(); + } catch (Exception e) { return AjaxResult.error(); } } @@ -92,13 +124,12 @@ public class DataSetBasicFileServiceImpl implements DataSetBasicFileService * @return 结果 */ @Override - public AjaxResult updateDataSetBasicFile(DataSetBasicFileEntity aiBasicFile) - { + public AjaxResult updateDataSetBasicFile(DataSetBasicFileEntity aiBasicFile) { aiBasicFile.setUpdateTime(DateUtils.getNowDate()); - try{ - int rows = dataSetBasicFileMapper.updateDataSetBasicFile(aiBasicFile); - return rows>0?AjaxResult.success():AjaxResult.error(); - }catch (Exception e){ + try { + int rows = dataSetBasicFileMapper.updateDataSetBasicFile(aiBasicFile); + return rows > 0 ? AjaxResult.success() : AjaxResult.error(); + } catch (Exception e) { return AjaxResult.error(); } } @@ -110,12 +141,27 @@ public class DataSetBasicFileServiceImpl implements DataSetBasicFileService * @return 结果 */ @Override - public AjaxResult deleteDataSetBasicFileByFileIds(Long[] fileIds) - { - try{ - int rows = dataSetBasicFileMapper.deleteDataSetBasicFileByFileIds(fileIds); - return rows>0?AjaxResult.success():AjaxResult.error(); - }catch (Exception e){ + public AjaxResult deleteDataSetBasicFileByFileIds(Long[] fileIds) { + try { + int rows = dataSetBasicFileMapper.deleteDataSetBasicFileByFileIds(fileIds); + return rows > 0 ? AjaxResult.success() : AjaxResult.error(); + } catch (Exception e) { + return AjaxResult.error(); + } + } + + /** + * 批量删除文件基础 + * + * @param fileIds 需要删除的文件基础主键 + * @return 结果 + */ + @Override + public AjaxResult dataRecoveryFileByFileIds(Long[] fileIds) { + try { + int rows = dataSetBasicFileMapper.dataRecoveryFileByFileIds(fileIds); + return rows > 0 ? AjaxResult.success() : AjaxResult.error(); + } catch (Exception e) { return AjaxResult.error(); } } @@ -133,16 +179,402 @@ public class DataSetBasicFileServiceImpl implements DataSetBasicFileService public AjaxResult uploadDataSetBasicFiles(MultipartFile file, String filename, int chunk, int totalChunks, String parentId, String fileUrl) { try { // 使用唯一路径保存分片到 MinIO - String objectName = SecurityUtils.getUserId().toString()+"/temp/"+filename+"/chunk_" + chunk; + String objectName = SecurityUtils.getUserId().toString() + "/temp/" + filename + "/chunk_" + chunk; minioUtil.uploadFile(file, objectName); // 检查是否是最后一个分片,若是则合并 if (chunk == totalChunks) { - SysFile sysFile = minioUtil.mergeChunks(filename, totalChunks, minioUtil.joinPath(SecurityUtils.getUserId().toString(),fileUrl)); + DataSetBasicFileEntity entity = minioUtil.mergeChunks(filename, totalChunks, minioUtil.joinPath(SecurityUtils.getUserId().toString(), fileUrl)); + entity.setCreateBy(SecurityUtils.getUserId().toString()); + entity.setCreateTime(DateUtils.getNowDate()); + entity.setIsDirectory("0"); + entity.setParentId(Long.valueOf(parentId)); + dataSetBasicFileMapper.insertDataSetBasicFile(entity); } return AjaxResult.success(); - } catch (Exception e) { - return AjaxResult.error(); + e.printStackTrace(); + return AjaxResult.error(); + } + } + + /** + * 下载文件 + * + * @param response 请求 + * @param fileIds 文件id + */ + @Override + public void downloadFile(HttpServletResponse response, HttpServletRequest request, Long[] fileIds) { + try { + // 获取文件信息列表 + List list = getFilesByIds(fileIds); + boolean exists = list.stream().anyMatch(file -> "1".equals(file.getIsDirectory())); + // 如果文件列表中只有一个文件 + if (list.size() == 1 && !exists) { + // 下载单个文件 + downloadSingleFile(response, request, list.get(0)); + } else { + // 下载多个文件,可能需要压缩成一个文件 + downloadMultipleFiles(response, list); + } + + } catch (MinioException | IOException e) { + response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + // 记录异常(控制台或日志中) + e.printStackTrace(); + } + } + + /** + * 共享文件 + * + * @param fileIds 文件id + */ + @Override + public AjaxResult sharedFilesByIds(Long[] fileIds) { + List list = getFilesByIds(fileIds); + for (DataSetBasicFileEntity entity :list){ + if ("1".equals(entity.getIsDirectory())){ + for (Long fileId : fileIds) { + Set ancestors = getAncestorsByFileIds(fileId); + Set children = getFileWithChildren(fileId); + ancestors.addAll(children); + ancestors.add(fileId); + dataSetBasicFileMapper.updateSharedFilesByIds(ancestors,"1"); + } + }else { + for (Long fileId : fileIds) { + Set ancestors = getAncestorsByFileIds(fileId); + ancestors.add(fileId); + dataSetBasicFileMapper.updateSharedFilesByIds(ancestors,"1"); + } + } + } + return AjaxResult.success(); + } + + /** + * 修改单个文件的共享状态 + * + * @param entity 实体 + */ + @Override + public AjaxResult changeIsPublic(DataSetBasicFileEntity entity) { + DataSetBasicFileEntity file = dataSetBasicFileMapper.selectDataSetBasicFileByFileId(entity.getFileId()); + if ("1".equals(file.getIsDirectory())){ + Set ancestors = getAncestorsByFileIds(entity.getFileId()); + Set children = getFileWithChildren(entity.getFileId()); + children.add(entity.getFileId()); + dataSetBasicFileMapper.updateSharedFilesByIds(children,entity.getIsPublic()); + Set isPublic = getIsPublic(ancestors); + if (!isPublic.isEmpty()){ + dataSetBasicFileMapper.updateSharedFilesByIds(isPublic,entity.getIsPublic()); + } + + }else { + Set fileId = new HashSet<>(); + fileId.add(entity.getFileId()); + Set ancestors = getAncestorsByFileIds(entity.getFileId()); + fileId.addAll(ancestors); + dataSetBasicFileMapper.updateSharedFilesByIds(fileId,entity.getIsPublic()); + Set isPublic = getIsPublic(ancestors); + if (!isPublic.isEmpty()){ + dataSetBasicFileMapper.updateSharedFilesByIds(isPublic,entity.getIsPublic()); + } + } + return null; + } + + /** + * 获取树形结构 + * + * @return + */ + @Override + public AjaxResult getFileTerr() { + DataSetBasicFileEntity entity = new DataSetBasicFileEntity(); + entity.setIsDirectory("1"); + entity.setCreateBy(SecurityUtils.getUserId().toString()); + List allFiles = dataSetBasicFileMapper.selectDataSetBasicFileList(entity); + return AjaxResult.success(allFiles); + } + + /** + * 递归树形结构 + * @param allFiles + * @param parentId + * @return + */ + private List buildTree(List allFiles, Long parentId) { + List result = new ArrayList<>(); + for (DataSetBasicFileEntity entity : allFiles) { + // 判断当前节点的 parentId 是否与给定的 parentId 相等 + if (entity.getParentId().equals(parentId) && "1".equals(entity.getIsDirectory())) { + // 将当前实体转换成 TreeNode + TreeNode node = new TreeNode(); + node.setId(entity.getFileId()); + node.setLabel(entity.getFileName()); + node.setParentId(entity.getParentId()); + // 递归查找当前节点的子节点 + node.setChildren(buildTree(allFiles, entity.getFileId())); + // 将该节点添加到结果列表中 + result.add(node); + } + } + return result; + } + + + + + /** + * 获取同级目录是否是否存在共享数据 + * @return + */ + public Set getIsPublic(Set ancestors){ + // 将 Set 转换为 List + List ancestorList = new ArrayList<>(ancestors); + Set isPublic = new HashSet<>(); + for (int i = ancestorList.size() - 1; i >= 0; i--) { + List list = dataSetBasicFileMapper.selectDataSetBasicFileByParentId(ancestorList.get(i)); + boolean exists = list.stream().anyMatch(file -> "1".equals(file.getIsPublic())); + if (exists){ + return isPublic; + }else { + isPublic.add(ancestorList.get(i)); + } + } + return isPublic; + } + + /** + * 获取文件及其子节点 + * @param fileId 文件 ID + * @return 当前文件及其所有子节点 + */ + public Set getFileWithChildren(Long fileId) { + // 查询所有文件数据,假设你已经根据需求修改了查询方法 + DataSetBasicFileEntity entity =new DataSetBasicFileEntity(); + entity.setCreateBy(SecurityUtils.getUserId().toString()); + List allFiles = dataSetBasicFileMapper.selectDataSetBasicFileList(entity); + // 构建父子关系映射 + Map> parentToChildrenMap = new HashMap<>(); + for (DataSetBasicFileEntity file : allFiles) { + parentToChildrenMap + .computeIfAbsent(file.getParentId(), k -> new ArrayList<>()) + .add(file.getFileId()); + } + + // 使用递归或迭代方式获取所有子节点 + Set childIds = new HashSet<>(); + collectChildren(fileId, parentToChildrenMap, childIds); + + return childIds; + } + + private void collectChildren(Long parentId, Map> parentToChildrenMap, Set childIds) { + // 获取当前父节点的所有子节点 + List children = parentToChildrenMap.get(parentId); + if (children != null) { + for (Long childId : children) { + // 将当前子节点 ID 添加到结果集中 + childIds.add(childId); + // 递归获取子节点的子节点 + collectChildren(childId, parentToChildrenMap, childIds); + } + } + } + + /** + * 根据多个 fileId 查询所有祖先节点 + * @param fileId 起始节点 ID + * @return 每个文件对应的祖先节点列表 Map + */ + public Set getAncestorsByFileIds(Long fileId) { + Set ancestors = new HashSet<>(); + Long currentId = fileId; + while (currentId != null && currentId != 0) { + DataSetBasicFileEntity entity = dataSetBasicFileMapper.selectDataSetBasicFileByFileId(currentId); + if (entity == null) { + break; // 没有更多父节点 + } + ancestors.add(entity.getParentId()); + // 更新当前节点为父节点 + currentId = entity.getParentId(); + } + + return ancestors; + } + + // 获取文件信息 + private List getFilesByIds(Long[] fileIds) { + List list = new ArrayList<>(); + for (long fileId : fileIds) { + DataSetBasicFileEntity entity = dataSetBasicFileMapper.selectDataSetBasicFileByFileId(fileId); + list.add(entity); + } + return list; + } + + // 下载单个文件(支持分片下载) + private void downloadSingleFile(HttpServletResponse response, HttpServletRequest request, DataSetBasicFileEntity entity) throws IOException, MinioException { + String fileUrl = entity.getFileUrl(); + File cachedFile = getDiskCachedFile(entity.getFileName()); + if (cachedFile == null) { + InputStream fileStream = minioUtil.downloadFile(fileUrl); + cachedFile = cacheFile(fileStream, entity); + } + // 获取文件大小 + long fileSize = entity.getFileSize(); + String range = request.getHeader("Range"); + if (range != null) { + handlePartialContent(response, range, cachedFile, fileSize, entity); + } else { + handleFullContent(response, cachedFile, fileSize, entity.getFileName()); + } + } + + // 获取磁盘缓存文件 + private File getDiskCachedFile(String fileName) { + String tempDir = System.getProperty("java.io.tmpdir") + "cacheFile"; + // 使用文件名的哈希值作为文件名的一部分,并添加时间戳避免命名冲突 + fileName = String.valueOf(fileName.hashCode()) + ".tmp"; + // 构造临时文件路径 + File tempFile = new File(tempDir, fileName); + // 检查文件是否存在且有效 + if (tempFile.exists()) { + // 文件存在且有效,返回缓存文件 + return tempFile; + } + // 文件不存在或无效,返回 null + return null; + } + + // 缓存文件(仅存储到磁盘) + private File cacheFile(InputStream fileStream, DataSetBasicFileEntity entity) throws IOException { + // 获取系统的临时目录 + String tempDir = System.getProperty("java.io.tmpdir") + "cacheFile"; + File directory = new File(tempDir); + + // 如果目录不存在,则创建它 + if (!directory.exists()) { + directory.mkdirs(); + } + // 使用文件名的哈希值作为文件名的一部分,并添加时间戳避免命名冲突 + String fileHash = String.valueOf(entity.getFileName().hashCode()); + String fileName = fileHash + ".tmp"; + // 创建临时文件 + File tempFile = new File(directory, fileName); + + // 如果文件不存在,则创建文件并将流写入 + if (!tempFile.exists()) { + try (OutputStream fileOutputStream = Files.newOutputStream(tempFile.toPath())) { + IOUtils.copy(fileStream, fileOutputStream); // 将文件流写入临时文件 + } + } + // 返回缓存的临时文件 + return tempFile; + } + + + // 处理分片下载 + private void handlePartialContent(HttpServletResponse response, String range, File cachedFile, long fileSize, DataSetBasicFileEntity entity) throws IOException { + String[] rangeValues = range.replace("bytes=", "").split("-"); + long start = Long.parseLong(rangeValues[0]); + long end = (rangeValues.length > 1) ? Long.parseLong(rangeValues[1]) : fileSize - 1; + + if (start >= fileSize || end >= fileSize) { + response.setStatus(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE); + response.setHeader("Content-Range", "bytes */" + fileSize); + return; + } + response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); + response.setCharacterEncoding("UTF-8"); + response.setContentType("application/octet-stream"); + String encodedFileName = URLEncoder.encode(entity.getFileName(), "UTF-8").replaceAll("\\+", "%20"); + response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + encodedFileName); + response.setHeader("Content-Range", "bytes " + start + "-" + end + "/" + fileSize); + response.setContentLengthLong(end - start + 1); + + try (FileInputStream fileInputStream = new FileInputStream(cachedFile)) { + // 跳过起始字节 + fileInputStream.skip(start); + // 使用 IOUtils.copy 进行文件复制 + try (ServletOutputStream outputStream = response.getOutputStream()) { + // 将文件流直接复制到输出流 + IOUtils.copyLarge(fileInputStream, outputStream, 0, end - start + 1); + outputStream.flush(); // 确保数据立即发送 + + + } + } catch (IOException e) { + System.err.println("文件下载过程中出现错误:" + e.getMessage()); + } finally { + // 检查是否需要删除临时文件 + if (cachedFile.exists() && end + 1 == fileSize) { + boolean deleted = cachedFile.delete(); + if (deleted) { + System.out.println("临时文件已成功删除:" + cachedFile.getAbsolutePath()); + } else { + System.err.println("临时文件删除失败:" + cachedFile.getAbsolutePath()); + } + } + } + } + + // 处理完整文件下载 + private void handleFullContent(HttpServletResponse response, File cachedFile, long fileSize, String fileName) throws IOException { + response.setContentType("application/octet-stream"); + response.setHeader("Content-Disposition", "attachment; filename=" + fileName); + response.setContentLengthLong(fileSize); + + try (FileInputStream fileStream = new FileInputStream(cachedFile); + ServletOutputStream outputStream = response.getOutputStream()) { + IOUtils.copy(fileStream, outputStream); + } + } + + // 下载多个文件(可以选择压缩成 ZIP) + private void downloadMultipleFiles(HttpServletResponse response, List list) throws IOException, MinioException { + // 示例:将多个文件打包成一个 ZIP 文件 + response.setContentType("application/zip"); + response.setHeader("Content-Disposition", "attachment; filename=files.zip"); + try (ZipOutputStream zos = new ZipOutputStream(response.getOutputStream())) { + for (DataSetBasicFileEntity entity : list) { + if ("1".equals(entity.getIsDirectory())) { + Iterable> objectList = minioUtil.listObjects(entity.getFileUrl()); + // 遍历每个对象 + for (Result itemResult : objectList) { + Item item = itemResult.get(); + // 跳过目录(如果 MinIO 中有虚拟目录) + if (item.isDir()) { + continue; + } + // 获取文件路径和文件流 + String objectName = item.objectName(); + InputStream fileStream = minioUtil.downloadFile(item.objectName()); + // 在 ZIP 中创建对应的条目,保持目录结构 + String result = objectName.replace(entity.getFileUrl(), ""); + ZipEntry zipEntry = new ZipEntry(entity.getFileName()+File.separator+result); + zos.putNextEntry(zipEntry); + IOUtils.copy(fileStream, zos); + zos.closeEntry(); + fileStream.close(); + + } + } else { + InputStream fileStream = minioUtil.downloadFile(entity.getFileUrl()); + // 创建 ZIP 条目 + ZipEntry zipEntry = new ZipEntry(entity.getFileName()); + zos.putNextEntry(zipEntry); + // 使用 IOUtils.copy 直接复制文件流 + IOUtils.copy(fileStream, zos); + zos.closeEntry(); + } + + } + } catch (Exception e) { + e.printStackTrace(); } } } diff --git a/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/utils/MinioUtil.java b/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/utils/MinioUtil.java index bcefcd9..fcff693 100644 --- a/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/utils/MinioUtil.java +++ b/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/utils/MinioUtil.java @@ -1,6 +1,7 @@ package com.bonus.ai.utils; import com.bonus.ai.config.MinioConfig; +import com.bonus.ai.domain.DataSetBasicFileEntity; import com.bonus.common.security.utils.SecurityUtils; import com.bonus.system.api.domain.SysFile; import io.minio.*; @@ -111,7 +112,7 @@ public class MinioUtil { * @param folderPath 文件夹路径 * @return 合并后的文件 */ - public SysFile mergeChunks(String filename, int totalChunks ,String folderPath) { + public DataSetBasicFileEntity mergeChunks(String filename, int totalChunks ,String folderPath) { try { List partNames = getPartNames(filename, totalChunks); List sources = new ArrayList<>(); @@ -119,30 +120,60 @@ public class MinioUtil { sources.add(ComposeSource.builder().bucket(minioConfig.getBucketName()).object(partName).build()); } String finalPath = joinPath(folderPath, filename); - // 异步合并文件 - CompletableFuture.runAsync(() -> { - try { - minioClient.composeObject(ComposeObjectArgs.builder() - .bucket(minioConfig.getBucketName()) - .object(finalPath) - .sources(sources) - .build()); - removeParts(partNames); - LOGGER.info("Merged and uploaded file: {}", finalPath); - } catch (Exception e) { - LOGGER.error("Error merging chunks asynchronously: {}", e.getMessage(), e); - } - }); - - return SysFile.builder() - .name(filename) - .url(folderPath).build(); + minioClient.composeObject(ComposeObjectArgs.builder() + .bucket(minioConfig.getBucketName()) + .object(finalPath) + .sources(sources) + .build()); + removeParts(partNames); + LOGGER.info("Merged and uploaded file: {}", finalPath); + // 获取合并后文件的详细信息 + StatObjectResponse stat = minioClient.statObject(StatObjectArgs.builder() + .bucket(minioConfig.getBucketName()) + .object(finalPath) + .build()); + DataSetBasicFileEntity entity = new DataSetBasicFileEntity(); + entity.setFileName(filename); + entity.setFileUrl(finalPath); + entity.setFileSize(stat.size()); + return entity; } catch (Exception e) { LOGGER.error("Error merging chunks: {}", e.getMessage(), e); return null; } } + /** + * 下载指定文件,以 InputStream 形式返回 + + * @param objectName 存储对象名称(文件名) + * @return 文件的输入流 + */ + public InputStream downloadFile(String objectName){ + try { + // 返回读取的流 + return minioClient.getObject(GetObjectArgs.builder() + .bucket(minioConfig.getBucketName()) + .object(objectName) + .build()); + } catch (Exception e) { + LOGGER.error("Error downloading file: {}", e.getMessage(), e); + return null; + } + + } + + /** + * 遍历文件夹下的文件 + * @param folderPath 文件夹路径 + * @return 返回条目 + */ + public Iterable> listObjects(String folderPath){ + return minioClient.listObjects( + ListObjectsArgs.builder().bucket(minioConfig.getBucketName()).prefix(folderPath).recursive(true).build() + ); + } + /** * 获取分片文件名 */ diff --git a/bonus-modules/bonus-ai/src/main/resources/mapper/DataSetBasicFileMapper.xml b/bonus-modules/bonus-ai/src/main/resources/mapper/DataSetBasicFileMapper.xml index 1fd1a6a..89761a1 100644 --- a/bonus-modules/bonus-ai/src/main/resources/mapper/DataSetBasicFileMapper.xml +++ b/bonus-modules/bonus-ai/src/main/resources/mapper/DataSetBasicFileMapper.xml @@ -26,15 +26,11 @@ @@ -43,6 +39,26 @@ where file_id = #{fileId} + + + + + insert into ai_basic_file @@ -77,6 +93,11 @@ #{updateBy}, #{updateTime}, + ON DUPLICATE KEY UPDATE + file_url = VALUES(file_url), + file_size = VALUES(file_size), + create_time = VALUES(create_time), + create_by = VALUES(create_by) @@ -99,9 +120,24 @@ where file_id = #{fileId} - + + UPDATE ai_basic_file + SET is_public = #{isPublic} + WHERE file_id IN + + #{fileId} + + + + + update ai_basic_file set del_flag='0' where file_id in + + #{fileId} + + + - delete from ai_basic_file where file_id in + update ai_basic_file set del_flag='1' where file_id in #{fileId}