diff --git a/.gitignore b/.gitignore index bc7e164d..b6c9870f 100644 --- a/.gitignore +++ b/.gitignore @@ -78,3 +78,5 @@ /outbridge/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst /outbridge/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst /outbridge/target/outbridge-0.0.1-SNAPSHOT.jar +/.cursor/rules/1.mdc +/.trae/rules/1.md 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 45b1c545..a62d91be 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 @@ -2642,60 +2642,74 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService { fileMetadataInfoService.updateById(fileMetadataInfo); } + /** - * 上传文件并更新元数据。 - * @param req - * @param oldFileMetadataInfo - * @param versionNo - * @param fileGroupId - * @param dirMetadataInfo + * 覆盖上传新文件(鸠占鹊巢):保持主记录 ID 不变,同时保留历史版本。 + *

+ * 最小方案约束: + * 1) 仅保留“文件内容 + 主元数据”的历史版本;标签/工况/扩展信息始终以最新为准,不做历史克隆; + * 2) 上传的新文件使用新的 objectKey(带版本号后缀),避免覆盖 MinIO 旧对象。 + *

*/ - public void uploadAndUpdateFile(UpdateFileReq req, FileMetadataInfo oldFileMetadataInfo, Long versionNo, Long fileGroupId,FileMetadataInfo dirMetadataInfo) { + public void uploadAndUpdateFile(UpdateFileReq req, FileMetadataInfo oldFileMetadataInfo, Long versionNo, Long fileGroupId, FileMetadataInfo dirMetadataInfo) { String newFileMinioObjectKey = ""; try { - // 保留历史文件版本,新增一条文件版本记录 + // 1) 备份:克隆当前主记录为历史行(新 ID),用于版本回溯 + FileMetadataInfo historyFileInfo = new FileMetadataInfo(); + BeanUtils.copyProperties(oldFileMetadataInfo, historyFileInfo); + historyFileInfo.setId(null); + historyFileInfo.setTempMetadata(null); + historyFileInfo.setIsLatest(false); + fileMetadataInfoService.save(historyFileInfo); + + // 2) 生成新版本文件名与 objectKey String originalName = req.getFileName(); String versionSuffix = "_V" + (versionNo + 1); - String modifiedFileName; - int dotIndex = originalName.lastIndexOf('.'); - if (dotIndex != -1) { - // 如果文件有后缀,则在文件名和后缀之间插入版本号 - modifiedFileName = originalName.substring(0, dotIndex) + versionSuffix + originalName.substring(dotIndex); - } else { - // 如果文件没有后缀,报错 + if (dotIndex == -1) { log.error("文件没有后缀"); throw new RuntimeException("文件没有后缀"); } + String modifiedFileName = originalName.substring(0, dotIndex) + versionSuffix + originalName.substring(dotIndex); - // 新文件名 modifiedFileName = newFile_V2.txt ,处理历史版本 oldFileMinioObjectKey= a/b/c/oldFile_V1.txt originalName=oldFile.txt , 替换为 newFileMinioObjectKey= a/b/c/test1_V2.txt String parDirObjectKey = dirMetadataInfo.getObjectKey(); newFileMinioObjectKey = getFileMinioObjectKey(parDirObjectKey + modifiedFileName); - minioService.uploadFile(req.getFile(), newFileMinioObjectKey, null,oldFileMetadataInfo.getBucketName()); - // 创建目录元数据并保存到数据库 - FileMetadataInfo newFileInfo = createFileMetadata(newFileMinioObjectKey, req.getFileName(), - req.getProjectId(), req.getAnalysisDirectionId(), req.getRemarks(), oldFileMetadataInfo.getParentId(), req.getFile().getSize(), null - ); - newFileInfo.setRemarks(oldFileMetadataInfo.getRemarks()); - newFileInfo.setProjectId(oldFileMetadataInfo.getProjectId()); - newFileInfo.setAnalysisDirectionName(oldFileMetadataInfo.getAnalysisDirectionName()); - newFileInfo.setFileGroupId(fileGroupId); - newFileInfo.setVersionNo(versionNo + 1); + // 3) 上传新文件 + minioService.uploadFile(req.getFile(), newFileMinioObjectKey, null, oldFileMetadataInfo.getBucketName()); - fileMetadataInfoService.save(newFileInfo); + // 4) 原地更新:主记录 ID 不变,指向最新版本 + oldFileMetadataInfo.setObjectKey(newFileMinioObjectKey); + oldFileMetadataInfo.setOriginalName(req.getFileName()); + oldFileMetadataInfo.setFileSize(req.getFile().getSize()); + oldFileMetadataInfo.setVersionNo(versionNo + 1); + oldFileMetadataInfo.setFileGroupId(fileGroupId); + oldFileMetadataInfo.setIsLatest(true); + oldFileMetadataInfo.setUpdateTime(LocalDateTime.now()); + oldFileMetadataInfo.setUpdaterId(ThreadLocalContext.getUserId()); - // 创建文件标签关系(如果有) + // 同步本次更新的元数据字段 + oldFileMetadataInfo.setRemarks(req.getRemarks()); + oldFileMetadataInfo.setProjectId(req.getProjectId()); + oldFileMetadataInfo.setAnalysisDirectionId(req.getAnalysisDirectionId()); + oldFileMetadataInfo.setFileType(req.getFileType()); + + fileMetadataInfoService.updateById(oldFileMetadataInfo); + + // 5) 更新关联(仅更新主记录关联,不做历史克隆) if (CollectionUtils.isNotEmpty(req.getDictTags())) { - updateFileTags(req, newFileInfo, dirMetadataInfo); + updateFileTags(req, oldFileMetadataInfo, dirMetadataInfo); } - //绑定文件和工况库的关系 if (CollectionUtils.isNotEmpty(req.getSimulationPoolInfoList())) { for (SimulationPoolInfo simulationPoolInfo : req.getSimulationPoolInfoList()) { + fileSimulationMappingService.lambdaUpdate() + .eq (FileSimulationMapping::getSimulationPoolId, oldFileMetadataInfo.getId()) + .eq (FileSimulationMapping::getSimulationPoolVersion, simulationPoolInfo.getSimulationPoolVersion()) + .eq(FileSimulationMapping::getFileId, oldFileMetadataInfo.getId()).remove(); for (String simulationPoolTaskId : simulationPoolInfo.getSimulationPoolTaskIds()) { FileSimulationMapping fileSimulationMapping = new FileSimulationMapping(); - fileSimulationMapping.setFileId(newFileInfo.getId()); + fileSimulationMapping.setFileId(oldFileMetadataInfo.getId()); fileSimulationMapping.setSimulationPoolId(simulationPoolInfo.getSimulationPoolId()); fileSimulationMapping.setSimulationPoolVersion(simulationPoolInfo.getSimulationPoolVersion()); fileSimulationMapping.setSimulationPoolTaskId(simulationPoolTaskId); @@ -2704,84 +2718,61 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService { } } - // 创建文件扩展信息并保存到数据库 - List fileMetadataExtensionList = new ArrayList<>(); + // 扩展字段:作为“最新态”数据,覆盖更新主记录对应的扩展信息 List fileMetadataExtensionRequestList = req.getFileMetadataExtensionRequest(); if (fileMetadataExtensionRequestList != null && !fileMetadataExtensionRequestList.isEmpty()) { + fileMetadataExtensionService.lambdaUpdate().eq(FileMetadataExtension::getTFilemetaId, oldFileMetadataInfo.getId()).remove(); + List fileMetadataExtensionList = new ArrayList<>(); fileMetadataExtensionRequestList.forEach(extensionRequest -> { FileMetadataExtension fileMetadataExtension = new FileMetadataExtension(); - fileMetadataExtension.setTFilemetaId(newFileInfo.getId()); + fileMetadataExtension.setTFilemetaId(oldFileMetadataInfo.getId()); fileMetadataExtension.setExtensionKey(extensionRequest.getExtensionKey()); fileMetadataExtension.setExtensionValue(extensionRequest.getExtensionValue()); - fileMetadataExtension.setDataType(Objects.toString(extensionRequest.getDataType(), "string")); // 默认为字符串类型,可根据需要调整 + fileMetadataExtension.setDataType(Objects.toString(extensionRequest.getDataType(), "string")); fileMetadataExtensionList.add(fileMetadataExtension); }); - } - fileMetadataExtensionService.saveBatch(fileMetadataExtensionList); - - // 循环查询当前文件每一级父目录id,并保存为一条file_storage,用户后续文件搜索统计 - Long parentDirId = dirMetadataInfo.getId(); - FileStorage fileStorage = new FileStorage(); - - fileStorage.setFileId(newFileInfo.getId()); - fileStorage.setFileName(newFileInfo.getOriginalName()); - fileStorage.setUserId(ThreadLocalContext.getUserId()); - fileStorage.setTenantId(ThreadLocalContext.getTenantId()); - fileStorage.setFileBizType(newFileInfo.getFileType()); - // 文件后缀 - fileStorage.setFileSuffix(getSuffixWithoutDot(newFileInfo.getOriginalName())); - fileStorage.setFileSize(req.getFile().getSize()); - while (parentDirId != null) { - fileStorage.setId(null); - fileStorage.setDirId(parentDirId); - fileStorageService.save(fileStorage); - parentDirId = fileMetadataInfoService.lambdaQuery().eq(FileMetadataInfo::getId, parentDirId).oneOpt() - .map(FileMetadataInfo::getParentId) - .orElse(null); + fileMetadataExtensionService.saveBatch(fileMetadataExtensionList); } - // 创建默认权限记录 - createFilePermission(newFileInfo.getId()); + // FileStorage:只更新主记录 fileId 对应的统计信息,避免新增一堆重复统计 + fileStorageService.lambdaUpdate() + .eq(FileStorage::getFileId, oldFileMetadataInfo.getId()) + .set(FileStorage::getFileName, oldFileMetadataInfo.getOriginalName()) + .set(FileStorage::getFileSize, oldFileMetadataInfo.getFileSize()) + .set(FileStorage::getFileBizType, oldFileMetadataInfo.getFileType()) + .update(); - // 需要审批的目录类型创建审批流 + // 6) 审批:beforeData 使用备份历史行,afterData 使用更新后的主行 boolean isKnowledge = dirMetadataInfo != null && DirTypeEnum.isApprovalRequired(dirMetadataInfo.getDirType()); - // 需要审批的目录类型修改 - if(isKnowledge){ - // 新增的一条暂时不展示 - newFileInfo.setIsLatest(false); - FileApproveRequestBuilder uploadAndUpdateFileApproveRequestBuilder = FileApproveRequestBuilder.builder() - .toUpdateFileIds(List.of(newFileInfo.getId())) + if (isKnowledge) { + FileApproveRequestBuilder builder = FileApproveRequestBuilder.builder() + .toUpdateFileIds(List.of(oldFileMetadataInfo.getId())) .contents(DirTypeEnum.buildApprovalTitle(dirMetadataInfo.getDirType(), "文件修改")) .approveType(ApproveTypeEnum.KNOWLEDGE_APPROVE) .approveFileActionENUM(ApproveFileActionENUM.MODIFY) - .beforeData(List.of(oldFileMetadataInfo)) - .afterData(List.of(newFileInfo)) + .beforeData(List.of(historyFileInfo)) + .afterData(List.of(oldFileMetadataInfo)) .templateId(req.getTemplateId()) .templateName(req.getTemplateName()) .knowledgeBaseName(extractRelativePath(dirMetadataInfo)) .build(); - if(CollectionUtils.isNotEmpty(uploadAndUpdateFileApproveRequestBuilder.getBeforeData())){ - setCreatorNames(uploadAndUpdateFileApproveRequestBuilder.getBeforeData()); - setProjectName(uploadAndUpdateFileApproveRequestBuilder.getBeforeData()); - setAnalysisDirectionName(uploadAndUpdateFileApproveRequestBuilder.getBeforeData()); - setSimulationPoolAndTaskInfo(uploadAndUpdateFileApproveRequestBuilder.getBeforeData()); + + if (CollectionUtils.isNotEmpty(builder.getBeforeData())) { + setCreatorNames(builder.getBeforeData()); + setProjectName(builder.getBeforeData()); + setAnalysisDirectionName(builder.getBeforeData()); + setSimulationPoolAndTaskInfo(builder.getBeforeData()); } - // 只有修改有 afterData 值 - if (CollectionUtils.isNotEmpty(uploadAndUpdateFileApproveRequestBuilder.getAfterData())) { - setCreatorNames(uploadAndUpdateFileApproveRequestBuilder.getAfterData()); - setProjectName(uploadAndUpdateFileApproveRequestBuilder.getAfterData()); - setAnalysisDirectionName(uploadAndUpdateFileApproveRequestBuilder.getAfterData()); - setSimulationPoolAndTaskInfo(uploadAndUpdateFileApproveRequestBuilder.getAfterData()); + if (CollectionUtils.isNotEmpty(builder.getAfterData())) { + setCreatorNames(builder.getAfterData()); + setProjectName(builder.getAfterData()); + setAnalysisDirectionName(builder.getAfterData()); + setSimulationPoolAndTaskInfo(builder.getAfterData()); } - fileApproveExecutor.launchApproveAndUpdateStatus(uploadAndUpdateFileApproveRequestBuilder, ApproveFileDataTypeEnum.MODIFY_REVIEWING); - }else { - // 非知识库的,设置 历史版本 文件为非最新 - oldFileMetadataInfo.setIsLatest(false); - fileMetadataInfoService.updateById(oldFileMetadataInfo); + fileApproveExecutor.launchApproveAndUpdateStatus(builder, ApproveFileDataTypeEnum.MODIFY_REVIEWING); } - } catch (Exception e) { minioService.deleteFile(newFileMinioObjectKey, oldFileMetadataInfo.getBucketName()); log.error("更新文件失败", e); diff --git a/data/src/main/java/com/sdm/data/service/impl/dataFileHandle/ModifyFileApproveStrategy.java b/data/src/main/java/com/sdm/data/service/impl/dataFileHandle/ModifyFileApproveStrategy.java index 7ebb04cc..258d813f 100644 --- a/data/src/main/java/com/sdm/data/service/impl/dataFileHandle/ModifyFileApproveStrategy.java +++ b/data/src/main/java/com/sdm/data/service/impl/dataFileHandle/ModifyFileApproveStrategy.java @@ -10,68 +10,75 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import java.time.LocalDateTime; +import java.util.List; @Service @Slf4j public class ModifyFileApproveStrategy implements ApproveStrategy { @Override public boolean handle(ApproveContext context) { -// FileMetadataInfo metadata = context.getApproveMetadataInfo(); - FileMetadataInfo metadata = context.getApproveMetadataInfos().get(0); int status = context.getApproveStatus(); - IFileMetadataInfoService service = context.getFileMetadataInfoService(); IMinioService minioService = context.getMinioService(); + IFileMetadataInfoService fileMetadataInfoService = context.getFileMetadataInfoService(); IFileMetadataExtensionService fileMetadataExtensionService = context.getFileMetadataExtensionService(); IFileUserPermissionService fileUserPermissionService = context.getFileUserPermissionService(); IFileStorageService fileStorageService = context.getFileStorageService(); IFileSimulationMappingService fileSimulationMappingService = context.getFileSimulationMappingService(); IFileTagRelService fileTagRelService = context.getFileTagRelService(); - // 最新的历史版本为 - FileMetadataInfo historyMetaData = service.lambdaQuery() - .eq(FileMetadataInfo::getFileGroupId, metadata.getFileGroupId()) - // 当前老的还在生效的数据 - .eq(FileMetadataInfo::getIsLatest, true) - .orderByDesc(FileMetadataInfo::getUpdateTime) - .last("limit 1") + List metadataList = context.getApproveMetadataInfos(); + if (metadataList == null || metadataList.isEmpty()) { + return false; + } + + FileMetadataInfo mainMetadata = metadataList.get(0); + + // 备份的历史记录(id 不同于主记录,且 group 相同,version减1查询) + FileMetadataInfo backupHistory = fileMetadataInfoService.lambdaQuery() + .eq(FileMetadataInfo::getFileGroupId, mainMetadata.getFileGroupId()) + .eq(FileMetadataInfo::getVersionNo, mainMetadata.getVersionNo() - 1) .one(); - - // 审批通过 + // 审批通过:主记录正式生效,备份行保留为历史版本(isLatest=false) if (NumberConstants.TWO == status) { - metadata.setApprovalStatus(ApprovalFileDataStatusEnum.APPROVED.getKey()); - metadata.setApproveType(ApproveFileDataTypeEnum.COMPLETED.getCode()); - // 设置成最新的文件 - metadata.setIsLatest(true); - service.updateById(metadata); - // 历史的设置成非最新 - if (historyMetaData != null) { - historyMetaData.setIsLatest(false); - service.updateById(historyMetaData); + mainMetadata.setApprovalStatus(ApprovalFileDataStatusEnum.APPROVED.getKey()); + mainMetadata.setApproveType(ApproveFileDataTypeEnum.COMPLETED.getCode()); + mainMetadata.setIsLatest(true); + fileMetadataInfoService.updateById(mainMetadata); + + if (backupHistory != null) { + backupHistory.setIsLatest(false); + fileMetadataInfoService.updateById(backupHistory); } return true; } - // 审批不通过 + // 审批不通过:回滚主记录,删除新 MinIO 对象和备份行 if (NumberConstants.THREE == status) { - // 删除MinIO中修改的文件 - minioService.deleteFile(metadata.getObjectKey(), metadata.getBucketName()); - // 删除修改产生的新记录 - service.removeById(metadata.getId()); - fileMetadataExtensionService.remove(new LambdaQueryWrapper().eq(FileMetadataExtension::getTFilemetaId, metadata.getId())); - fileUserPermissionService.remove(new LambdaQueryWrapper().eq(FileUserPermission::getTFilemetaId, metadata.getId())); - fileStorageService.remove(new LambdaQueryWrapper().eq(FileStorage::getFileId, metadata.getId())); - fileSimulationMappingService.remove(new LambdaQueryWrapper().eq(FileSimulationMapping::getFileId, metadata.getId())); - // 删除文件标签关系 - fileTagRelService.remove(new LambdaQueryWrapper().eq(FileTagRel::getFileId, metadata.getId())); + // 1. 删除新上传的文件(主记录当前指向的 objectKey) + minioService.deleteFile(mainMetadata.getObjectKey(), mainMetadata.getBucketName()); + + // 2. 将主记录(ID 不变那个)回滚为备份行的数据 + if (backupHistory != null) { + mainMetadata.setObjectKey(backupHistory.getObjectKey()); + mainMetadata.setFileSize(backupHistory.getFileSize()); + mainMetadata.setVersionNo(backupHistory.getVersionNo()); + mainMetadata.setOriginalName(backupHistory.getOriginalName()); + mainMetadata.setRemarks(backupHistory.getRemarks()); + mainMetadata.setProjectId(backupHistory.getProjectId()); + mainMetadata.setAnalysisDirectionId(backupHistory.getAnalysisDirectionId()); + mainMetadata.setFileType(backupHistory.getFileType()); + } - if (historyMetaData != null) { - historyMetaData.setTempMetadata(null); - historyMetaData.setIsLatest(true); - historyMetaData.setApprovalStatus(ApprovalFileDataStatusEnum.REJECTED.getKey()); - historyMetaData.setApproveType(ApproveFileDataTypeEnum.COMPLETED.getCode()); - historyMetaData.setUpdateTime(LocalDateTime.now()); - service.updateById(historyMetaData); + mainMetadata.setApprovalStatus(ApprovalFileDataStatusEnum.REJECTED.getKey()); + mainMetadata.setApproveType(ApproveFileDataTypeEnum.COMPLETED.getCode()); + mainMetadata.setIsLatest(true); + mainMetadata.setUpdateTime(LocalDateTime.now()); + fileMetadataInfoService.updateById(mainMetadata); + + // 3. 删除备份行(因为新版本已废弃,不需要保留这条历史备份) + if (backupHistory != null) { + fileMetadataInfoService.removeById(backupHistory.getId()); } return true; }