From 77eb106826c3571df84b52a0cd6f0806ee20d8d8 Mon Sep 17 00:00:00 2001 From: gulongcheng <474084054@qq.com> Date: Thu, 12 Feb 2026 16:43:57 +0800 Subject: [PATCH 1/2] =?UTF-8?q?fix:=E4=BC=98=E5=8C=96=E5=9B=9E=E6=94=B6?= =?UTF-8?q?=E7=AB=99=E5=85=A8=E9=87=8F=E5=88=A0=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sdm/data/schedule/RecycleBinCleanupSchedule.java | 11 +++++++---- .../service/impl/MinioFileIDataFileServiceImpl.java | 6 +++--- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/data/src/main/java/com/sdm/data/schedule/RecycleBinCleanupSchedule.java b/data/src/main/java/com/sdm/data/schedule/RecycleBinCleanupSchedule.java index c0bab95d..6ff4cb2f 100644 --- a/data/src/main/java/com/sdm/data/schedule/RecycleBinCleanupSchedule.java +++ b/data/src/main/java/com/sdm/data/schedule/RecycleBinCleanupSchedule.java @@ -1,6 +1,7 @@ package com.sdm.data.schedule; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.sdm.common.entity.enums.DataTypeEnum; import com.sdm.data.model.entity.FileMetadataExtension; import com.sdm.data.model.entity.FileMetadataInfo; import com.sdm.data.model.entity.FileSimulationMapping; @@ -23,6 +24,7 @@ import org.springframework.stereotype.Component; import java.time.LocalDateTime; import java.util.List; +import java.util.Objects; /** * 回收站自动清理定时任务 @@ -66,18 +68,19 @@ public class RecycleBinCleanupSchedule { try { LocalDateTime now = LocalDateTime.now(); - // 查询所有已过期的回收站项 + // 查询所有已过期的回收站项(仅查询顶层节点,避免重复删除子项) List expiredItems = fileMetadataInfoService.lambdaQuery() .isNotNull(FileMetadataInfo::getDeletedAt) .le(FileMetadataInfo::getRecycleExpireAt, now) + .apply("(parentId IS NULL OR EXISTS (SELECT 1 FROM file_metadata_info p WHERE p.id = file_metadata_info.parentId AND p.deletedAt IS NULL))") .list(); if (CollectionUtils.isEmpty(expiredItems)) { - log.info("回收站中没有已过期的文件/目录"); + log.info("回收站中没有已过期的顶层文件/目录"); return; } - log.info("发现 {} 个已过期的回收站项,开始物理删除", expiredItems.size()); + log.info("发现 {} 个已过期的回收站顶层项,开始物理删除", expiredItems.size()); int successCount = 0; int failCount = 0; @@ -85,7 +88,7 @@ public class RecycleBinCleanupSchedule { for (FileMetadataInfo item : expiredItems) { try { // 如果是目录,需要递归删除所有子项 - if (item.getDataType() != null && item.getDataType() == 1) { // 1表示目录 + if (Objects.equals(DataTypeEnum.DIRECTORY.getValue(), item.getDataType())) { minioFileIDataFileService.executeDirectoryDeletion( item.getId(), item.getObjectKey(), diff --git a/data/src/main/java/com/sdm/data/service/impl/MinioFileIDataFileServiceImpl.java b/data/src/main/java/com/sdm/data/service/impl/MinioFileIDataFileServiceImpl.java index 62b33b12..1d1eea12 100644 --- a/data/src/main/java/com/sdm/data/service/impl/MinioFileIDataFileServiceImpl.java +++ b/data/src/main/java/com/sdm/data/service/impl/MinioFileIDataFileServiceImpl.java @@ -4817,11 +4817,11 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService { List ids = req.getIds(); if (CollectionUtils.isEmpty(ids)) { Long userId = ThreadLocalContext.getUserId(); - // 查询回收站中的顶层节点(避免对子节点重复触发递归删除) + // 查询当前租户回收站中的顶层节点(避免对子节点重复触发递归删除) List list = fileMetadataInfoService.lambdaQuery() - .eq(FileMetadataInfo::getCreatorId, userId) + .eq(FileMetadataInfo::getTenantId, ThreadLocalContext.getTenantId()) .isNotNull(FileMetadataInfo::getDeletedAt) - .apply("(parentId IS NULL OR NOT EXISTS (SELECT 1 FROM file_metadata_info p WHERE p.id = file_metadata_info.parentId AND p.deletedAt IS NOT NULL))") + .apply("(parentId IS NULL OR EXISTS (SELECT 1 FROM file_metadata_info p WHERE p.id = file_metadata_info.parentId AND p.deletedAt IS NULL))") .list(); if (CollectionUtils.isEmpty(list)) { return SdmResponse.success("回收站为空"); From 8a57982394cedbfaa36ea0fe9d5d61b91b3d7cd7 Mon Sep 17 00:00:00 2001 From: gulongcheng <474084054@qq.com> Date: Thu, 12 Feb 2026 17:30:16 +0800 Subject: [PATCH 2/2] =?UTF-8?q?fix:=E4=BC=98=E5=8C=96=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../impl/MinioFileIDataFileServiceImpl.java | 293 ++++++++++-------- 1 file changed, 172 insertions(+), 121 deletions(-) diff --git a/data/src/main/java/com/sdm/data/service/impl/MinioFileIDataFileServiceImpl.java b/data/src/main/java/com/sdm/data/service/impl/MinioFileIDataFileServiceImpl.java index 1d1eea12..45b1c545 100644 --- a/data/src/main/java/com/sdm/data/service/impl/MinioFileIDataFileServiceImpl.java +++ b/data/src/main/java/com/sdm/data/service/impl/MinioFileIDataFileServiceImpl.java @@ -2455,150 +2455,201 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService { } } + /** + * 更新文件。 + *

+ * 支持两类场景: + * 1) 仅更新元数据(项目/分析方向/备注/标签/工况绑定等),若目录类型要求审批则发起审批流程; + * 2) 上传新文件并生成新版本。 + *

+ * + * @param req 更新请求 + * @return 统一响应结果 + */ @Transactional(rollbackFor = Exception.class) @PermissionCheckAspect.FilePermissionCheck(value = FilePermissionEnum.UPLOAD, fileIdExpression = "#req.id") public SdmResponse updateFile(UpdateFileReq req) { + SdmResponse prepareResult = prepareUpdateReq(req); + if (prepareResult != null) { + return prepareResult; + } + + FileMetadataInfo fileMetadataInfo = fileMetadataInfoService.lambdaQuery() + .eq(FileMetadataInfo::getId, req.getId()) + .one(); + if (fileMetadataInfo == null) { + return SdmResponse.failed("文件不存在"); + } + + FileMetadataInfo dirMetadataInfo = fileMetadataInfoService.lambdaQuery() + .eq(FileMetadataInfo::getId, fileMetadataInfo.getParentId()) + .eq(FileMetadataInfo::getDataType, DataTypeEnum.DIRECTORY.getValue()) + .one(); + + if (ObjectUtils.isEmpty(req.getFile())) { + if (dirMetadataInfo != null && DirTypeEnum.isApprovalRequired(dirMetadataInfo.getDirType())) { + return handleMetadataApproval(req, fileMetadataInfo, dirMetadataInfo); + } + updateMetadataDirectly(req, fileMetadataInfo, dirMetadataInfo); + return SdmResponse.success("更新成功"); + } + + uploadAndUpdateFile(req, fileMetadataInfo, fileMetadataInfo.getVersionNo(), fileMetadataInfo.getFileGroupId(), dirMetadataInfo); + return SdmResponse.success("更新成功"); + } + + /** + * 更新前置处理: + *

+ * 1) 解析工况绑定入参(JSON 字符串); + * 2) 当未传递标签时,回填原文件标签; + * 3) 缓存/补齐标签对应的字典ID映射,供后续更新/审批写入使用。 + *

+ * + * @param req 更新请求 + * @return 参数不合法时返回失败响应;正常返回 null 表示继续执行主流程 + */ + private SdmResponse prepareUpdateReq(UpdateFileReq req) { if (StringUtils.hasText(req.getSimulationPoolInfoListStr())) { try { - List list = JSON.parseArray(req.getSimulationPoolInfoListStr(), SimulationPoolInfo.class); - req.setSimulationPoolInfoList(list); + req.setSimulationPoolInfoList(JSON.parseArray(req.getSimulationPoolInfoListStr(), SimulationPoolInfo.class)); } catch (Exception e) { return SdmResponse.failed("参数格式错误"); } } - // 如果未传递新的字典标签,则从原有文件获取并填充 if (CollectionUtils.isEmpty(req.getDictTags())) { fileDictTagQueryService.fillFileTagsForRespList(List.of(req), UpdateFileReq::getId); - - // 如果成功填充了标签值,则设置 dictTags 列表,以便 dictTagHelper 能够识别 List dictTags = buildDictTagsFromEnum(req); - if (!dictTags.isEmpty()) { req.setDictTags(dictTags); - // 强制刷新缓存,确保后续逻辑能获取到ID - // 保存标签缓存到 tempFileMetadataInfo(如果有) - if (CollectionUtils.isNotEmpty(req.getDictTags())) { - Map> dictIdMap = req.getDictTagIdsCache(); - if (dictIdMap == null || dictIdMap.isEmpty()) { - dictIdMap = dictTagHelper.queryAndCacheDictTagIds(req); - } - req.setDictTagIdsCache(dictIdMap); - } } } - FileMetadataInfo fileMetadataInfo = fileMetadataInfoService.lambdaQuery().eq(FileMetadataInfo::getId, req.getId()).one(); - if (fileMetadataInfo == null) { - return SdmResponse.failed("文件不存在"); - } - // 文件夹 - FileMetadataInfo dirMetadataInfo = fileMetadataInfoService.lambdaQuery().eq(FileMetadataInfo::getId, fileMetadataInfo.getParentId()).eq(FileMetadataInfo::getDataType, DataTypeEnum.DIRECTORY.getValue()).one(); - /* boolean hasUploadPermission = fileUserPermissionService.hasFilePermission(fileMetadataInfo.getId(), ThreadLocalContext.getUserId(), FilePermissionEnum.UPLOAD); - if (!hasUploadPermission) { - return SdmResponse.failed("没有更新权限"); - }*/ - Long fileGroupId = fileMetadataInfo.getFileGroupId(); - Long versionNo = fileMetadataInfo.getVersionNo(); - // 修改待审批的元数据主键id - if (ObjectUtils.isEmpty(req.getFile())) { - // 需要审批的目录类型,增加审批的逻辑,先将原始数据改成待审核状态 - if(dirMetadataInfo!=null && DirTypeEnum.isApprovalRequired(dirMetadataInfo.getDirType())){ - FileMetadataInfo tempFileMetadataInfo = new FileMetadataInfo(); - BeanUtils.copyProperties(fileMetadataInfo, tempFileMetadataInfo); - // 不需要上传minio新文件,只更新文件元数据 - // 本次待审核的数据 - tempFileMetadataInfo.setProjectId(req.getProjectId()); - tempFileMetadataInfo.setAnalysisDirectionId(req.getAnalysisDirectionId()); - tempFileMetadataInfo.setRemarks(req.getRemarks()); - tempFileMetadataInfo.setSimulationPoolInfoList(req.getSimulationPoolInfoList()); - tempFileMetadataInfo.setCreateTime(fileMetadataInfo.getCreateTime()); - tempFileMetadataInfo.setUpdaterId(ThreadLocalContext.getUserId()); - tempFileMetadataInfo.setUpdateTime(LocalDateTime.now()); - - // 保存标签缓存到 tempFileMetadataInfo(如果有) - if (CollectionUtils.isNotEmpty(req.getDictTags())) { - Map> dictIdMap = req.getDictTagIdsCache(); - if (dictIdMap == null || dictIdMap.isEmpty()) { - dictIdMap = dictTagHelper.queryAndCacheDictTagIds(req); - } - tempFileMetadataInfo.setDictTagIdsCache(dictIdMap); - } - - - fileMetadataInfo.setTempMetadata(JSONObject.toJSONString(tempFileMetadataInfo)); - fileMetadataInfo.setUpdateTime(LocalDateTime.now()); - fileMetadataInfo.setUpdaterId(ThreadLocalContext.getUserId()); - fileMetadataInfoService.updateById(fileMetadataInfo); - - //发起审批 - FileApproveRequestBuilder updateFileMetaIntoApproveRequestBuilder = FileApproveRequestBuilder.builder() - .toUpdateFileIds(List.of(fileMetadataInfo.getId())) - .contents(DirTypeEnum.buildApprovalTitle(dirMetadataInfo.getDirType(), "文件元数据修改")) - .approveType(ApproveTypeEnum.KNOWLEDGE_APPROVE) - .approveFileActionENUM(ApproveFileActionENUM.MODIFY) - .beforeData(List.of(fileMetadataInfo)) - .afterData(List.of(tempFileMetadataInfo)) - .templateId(req.getTemplateId()) - .templateName(req.getTemplateName()) - .knowledgeBaseName(extractRelativePath(dirMetadataInfo)) - .build(); - if(CollectionUtils.isNotEmpty(updateFileMetaIntoApproveRequestBuilder.getBeforeData())){ - setCreatorNames(updateFileMetaIntoApproveRequestBuilder.getBeforeData()); - setProjectName(updateFileMetaIntoApproveRequestBuilder.getBeforeData()); - setAnalysisDirectionName(updateFileMetaIntoApproveRequestBuilder.getBeforeData()); - setSimulationPoolAndTaskInfo(updateFileMetaIntoApproveRequestBuilder.getBeforeData()); - } - - // 只有修改有 afterData 值 - if (CollectionUtils.isNotEmpty(updateFileMetaIntoApproveRequestBuilder.getAfterData())) { - setCreatorNames(updateFileMetaIntoApproveRequestBuilder.getAfterData()); - setProjectName(updateFileMetaIntoApproveRequestBuilder.getAfterData()); - setAnalysisDirectionName(updateFileMetaIntoApproveRequestBuilder.getAfterData()); - setSimulationPoolAndTaskInfo(updateFileMetaIntoApproveRequestBuilder.getAfterData()); - } - fileApproveExecutor.launchApproveAndUpdateStatus(updateFileMetaIntoApproveRequestBuilder, ApproveFileDataTypeEnum.MODIFY_METADATA_REVIEWING); - - }else { - // 其他场景修改 - // 不需要上传minio新文件,只更新文件元数据 - fileMetadataInfo.setProjectId(req.getProjectId()); - fileMetadataInfo.setAnalysisDirectionId(req.getAnalysisDirectionId()); - fileMetadataInfo.setRemarks(req.getRemarks()); - fileMetadataInfo.setUpdateTime(LocalDateTime.now()); - fileMetadataInfo.setUpdaterId(ThreadLocalContext.getUserId()); - fileMetadataInfo.setFileType(req.getFileType()); - - //绑定文件和工况库的关系 - if (CollectionUtils.isNotEmpty(req.getSimulationPoolInfoList())) { - // 先删除原先所有的文件绑定关系 - fileSimulationMappingService.lambdaUpdate().eq(FileSimulationMapping::getFileId, fileMetadataInfo.getId()).remove(); - for (SimulationPoolInfo simulationPoolInfo : req.getSimulationPoolInfoList()) { - for (String simulationPoolTaskId : simulationPoolInfo.getSimulationPoolTaskIds()) { - FileSimulationMapping fileSimulationMapping = new FileSimulationMapping(); - fileSimulationMapping.setFileId(fileMetadataInfo.getId()); - fileSimulationMapping.setSimulationPoolId(simulationPoolInfo.getSimulationPoolId()); - fileSimulationMapping.setSimulationPoolVersion(simulationPoolInfo.getSimulationPoolVersion()); - fileSimulationMapping.setSimulationPoolTaskId(simulationPoolTaskId); - fileSimulationMappingService.save(fileSimulationMapping); - } - } - } - - // 更新标签(如果有) - if (CollectionUtils.isNotEmpty(req.getDictTags())) { - updateFileTags(req, fileMetadataInfo, dirMetadataInfo); - } - fileMetadataInfoService.updateById(fileMetadataInfo); + if (CollectionUtils.isNotEmpty(req.getDictTags())) { + Map> dictIdMap = req.getDictTagIdsCache(); + if (dictIdMap == null || dictIdMap.isEmpty()) { + dictIdMap = dictTagHelper.queryAndCacheDictTagIds(req); } - - } else { - // 需要上传minio新文件,更新文件元数据 - uploadAndUpdateFile(req, fileMetadataInfo, versionNo, fileGroupId,dirMetadataInfo); + req.setDictTagIdsCache(dictIdMap); } + + return null; + } + + /** + * 发起元数据更新审批: + *

+ * 1) 暂存待修改的元数据(JSONObject 序列化)到 tempMetadata 字段; + * 2) 构建审批请求对象,包含变更前后(Before/After)的数据及相关元信息(项目、人员等); + * 3) 提交审批中心,由其统一管理后续的文件状态流转。 + *

+ * + * @param req 更新请求 + * @param fileMetadataInfo 当前生效的元数据 + * @param dirMetadataInfo 父目录元数据(提供审批模板及路径上下文) + * @return 成功响应 + */ + private SdmResponse handleMetadataApproval(UpdateFileReq req, FileMetadataInfo fileMetadataInfo, FileMetadataInfo dirMetadataInfo) { + FileMetadataInfo tempInfo = new FileMetadataInfo(); + BeanUtils.copyProperties(fileMetadataInfo, tempInfo); + tempInfo.setProjectId(req.getProjectId()); + tempInfo.setAnalysisDirectionId(req.getAnalysisDirectionId()); + tempInfo.setRemarks(req.getRemarks()); + tempInfo.setSimulationPoolInfoList(req.getSimulationPoolInfoList()); + tempInfo.setCreateTime(fileMetadataInfo.getCreateTime()); + tempInfo.setUpdaterId(ThreadLocalContext.getUserId()); + tempInfo.setUpdateTime(LocalDateTime.now()); + + if (CollectionUtils.isNotEmpty(req.getDictTags())) { + Map> dictIdMap = req.getDictTagIdsCache(); + if (dictIdMap == null || dictIdMap.isEmpty()) { + dictIdMap = dictTagHelper.queryAndCacheDictTagIds(req); + } + tempInfo.setDictTagIdsCache(dictIdMap); + } + + fileMetadataInfo.setTempMetadata(JSONObject.toJSONString(tempInfo)); + fileMetadataInfo.setUpdateTime(LocalDateTime.now()); + fileMetadataInfo.setUpdaterId(ThreadLocalContext.getUserId()); + fileMetadataInfoService.updateById(fileMetadataInfo); + + FileApproveRequestBuilder builder = FileApproveRequestBuilder.builder() + .toUpdateFileIds(List.of(fileMetadataInfo.getId())) + .contents(DirTypeEnum.buildApprovalTitle(dirMetadataInfo.getDirType(), "文件元数据修改")) + .approveType(ApproveTypeEnum.KNOWLEDGE_APPROVE) + .approveFileActionENUM(ApproveFileActionENUM.MODIFY) + .beforeData(List.of(fileMetadataInfo)) + .afterData(List.of(tempInfo)) + .templateId(req.getTemplateId()) + .templateName(req.getTemplateName()) + .knowledgeBaseName(extractRelativePath(dirMetadataInfo)) + .build(); + + if (CollectionUtils.isNotEmpty(builder.getBeforeData())) { + setCreatorNames(builder.getBeforeData()); + setProjectName(builder.getBeforeData()); + setAnalysisDirectionName(builder.getBeforeData()); + setSimulationPoolAndTaskInfo(builder.getBeforeData()); + } + + if (CollectionUtils.isNotEmpty(builder.getAfterData())) { + setCreatorNames(builder.getAfterData()); + setProjectName(builder.getAfterData()); + setAnalysisDirectionName(builder.getAfterData()); + setSimulationPoolAndTaskInfo(builder.getAfterData()); + } + + fileApproveExecutor.launchApproveAndUpdateStatus(builder, ApproveFileDataTypeEnum.MODIFY_METADATA_REVIEWING); return SdmResponse.success("更新成功"); } + /** + * 直接更新元数据(无需审批、无需上传新文件)。 + *

+ * 包含:基础字段更新、工况库绑定关系同步、标签关系更新。 + *

+ * + * @param req 更新请求 + * @param fileMetadataInfo 当前文件元数据 + * @param dirMetadataInfo 父目录元数据(用于标签更新时的目录上下文) + */ + private void updateMetadataDirectly(UpdateFileReq req, FileMetadataInfo fileMetadataInfo, FileMetadataInfo dirMetadataInfo) { + fileMetadataInfo.setProjectId(req.getProjectId()); + fileMetadataInfo.setAnalysisDirectionId(req.getAnalysisDirectionId()); + fileMetadataInfo.setRemarks(req.getRemarks()); + fileMetadataInfo.setUpdateTime(LocalDateTime.now()); + fileMetadataInfo.setUpdaterId(ThreadLocalContext.getUserId()); + fileMetadataInfo.setFileType(req.getFileType()); + + if (CollectionUtils.isNotEmpty(req.getSimulationPoolInfoList())) { + fileSimulationMappingService.lambdaUpdate().eq(FileSimulationMapping::getFileId, fileMetadataInfo.getId()).remove(); + for (SimulationPoolInfo poolInfo : req.getSimulationPoolInfoList()) { + for (String taskId : poolInfo.getSimulationPoolTaskIds()) { + FileSimulationMapping mapping = new FileSimulationMapping(); + mapping.setFileId(fileMetadataInfo.getId()); + mapping.setSimulationPoolId(poolInfo.getSimulationPoolId()); + mapping.setSimulationPoolVersion(poolInfo.getSimulationPoolVersion()); + mapping.setSimulationPoolTaskId(taskId); + fileSimulationMappingService.save(mapping); + } + } + } + + if (CollectionUtils.isNotEmpty(req.getDictTags())) { + updateFileTags(req, fileMetadataInfo, dirMetadataInfo); + } + fileMetadataInfoService.updateById(fileMetadataInfo); + } + + /** + * 上传文件并更新元数据。 + * @param req + * @param oldFileMetadataInfo + * @param versionNo + * @param fileGroupId + * @param dirMetadataInfo + */ public void uploadAndUpdateFile(UpdateFileReq req, FileMetadataInfo oldFileMetadataInfo, Long versionNo, Long fileGroupId,FileMetadataInfo dirMetadataInfo) { String newFileMinioObjectKey = ""; try {