fix:优化更新文件接口

This commit is contained in:
2026-02-13 13:52:42 +08:00
parent a606f108e5
commit f68757a9b3
4 changed files with 355 additions and 455 deletions

View File

@@ -49,6 +49,7 @@ import com.sdm.data.service.approve.FileApproveRequestBuilder;
import com.sdm.data.service.impl.dataFileHandle.ApproveContext;
import com.sdm.data.service.impl.dataFileHandle.ApproveStrategy;
import com.sdm.data.service.impl.dataFileHandle.ApproveStrategyFactory;
import com.sdm.data.service.impl.dataFileHandle.FileModifyApproveHelper;
import io.minio.Result;
import io.minio.messages.Item;
import jakarta.servlet.http.HttpServletResponse;
@@ -182,6 +183,9 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService {
@Autowired
private DictTagHelper dictTagHelper;
@Autowired
private FileModifyApproveHelper fileModifyApproveHelper;
private static final String TEMP_FILE_PATH = "/usr/local/nginx/html/storage/";
@@ -2495,11 +2499,7 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService {
}
// 不需要审批:直接更新或上传
if (ObjectUtils.isEmpty(req.getFile())) {
updateMetadataDirectly(req, fileMetadataInfo, dirMetadataInfo);
} else {
uploadAndUpdateFile(req, fileMetadataInfo, fileMetadataInfo.getVersionNo(), fileMetadataInfo.getFileGroupId(), dirMetadataInfo);
}
updateFileDirectly(req, fileMetadataInfo, dirMetadataInfo);
return SdmResponse.success("更新成功");
}
@@ -2656,184 +2656,83 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService {
}
/**
* 直接更新元数据(无需审批、无需上传新文件)。
* 直接更新文件(无需审批)。
* <p>
* 包含:基础字段更新、工况库绑定关系同步、标签关系更新
* 统一处理“仅元数据更新”和“含文件上传更新”的逻辑
* 含文件上传时:保留历史版本,主记录指向新文件(鸠占鹊巢)。
* </p>
*
* @param req 更新请求
* @param fileMetadataInfo 当前文件元数据
* @param dirMetadataInfo 父目录元数据(用于标签更新时的目录上下文)
*/
private void updateMetadataDirectly(UpdateFileReq req, FileMetadataInfo fileMetadataInfo, FileMetadataInfo dirMetadataInfo) {
private void updateFileDirectly(UpdateFileReq req, FileMetadataInfo fileMetadataInfo, FileMetadataInfo dirMetadataInfo) {
boolean hasFileUpload = !ObjectUtils.isEmpty(req.getFile());
// 1. 如果有文件上传,先处理文件上传和版本备份
if (hasFileUpload) {
// 1.1 备份当前主记录为历史版本
FileMetadataInfo historyFileInfo = new FileMetadataInfo();
BeanUtils.copyProperties(fileMetadataInfo, historyFileInfo);
historyFileInfo.setId(null); // 清空ID插入新记录
historyFileInfo.setTempMetadata(null);
historyFileInfo.setIsLatest(false);
fileMetadataInfoService.save(historyFileInfo);
// 1.1.1 复制原文件的权限到历史版本
fileModifyApproveHelper.copyFilePermissions(fileMetadataInfo.getId(), historyFileInfo.getId(), fileUserPermissionService);
// 1.2 上传新文件并获取新 ObjectKey
String originalName = req.getFileName();
Long newVersionNo = fileMetadataInfo.getVersionNo() + 1;
String versionSuffix = "_V" + newVersionNo;
int dotIndex = originalName.lastIndexOf('.');
if (dotIndex == -1) {
throw new RuntimeException("文件没有后缀");
}
String modifiedFileName = originalName.substring(0, dotIndex) + versionSuffix + originalName.substring(dotIndex);
String newFileMinioObjectKey = getFileMinioObjectKey(dirMetadataInfo.getObjectKey() + modifiedFileName);
try {
minioService.uploadFile(req.getFile(), newFileMinioObjectKey, null, fileMetadataInfo.getBucketName());
} catch (Exception e) {
log.error("上传文件失败", e);
throw new RuntimeException("上传文件失败: " + e.getMessage());
}
// 1.3 更新主记录的文件相关属性
fileMetadataInfo.setObjectKey(newFileMinioObjectKey);
fileMetadataInfo.setOriginalName(originalName);
fileMetadataInfo.setFileSize(req.getFile().getSize());
fileMetadataInfo.setVersionNo(newVersionNo);
// 更新文件存储统计信息
fileStorageService.lambdaUpdate()
.eq(FileStorage::getFileId, fileMetadataInfo.getId())
.set(FileStorage::getFileName, originalName)
.set(FileStorage::getFileSize, req.getFile().getSize())
.update();
}
// 2. 更新通用元数据字段
fileMetadataInfo.setProjectId(req.getProjectId());
fileMetadataInfo.setAnalysisDirectionId(req.getAnalysisDirectionId());
fileMetadataInfo.setRemarks(req.getRemarks());
fileMetadataInfo.setUpdateTime(LocalDateTime.now());
fileMetadataInfo.setUpdaterId(ThreadLocalContext.getUserId());
fileMetadataInfo.setFileType(req.getFileType());
fileMetadataInfo.setIsLatest(true); // 确保为主版本
if (CollectionUtils.isNotEmpty(req.getSimulationPoolInfoList())) {
fileSimulationMappingService.lambdaUpdate().eq(FileSimulationMapping::getFileId, fileMetadataInfo.getId()).remove();
for (SimulationPoolInfo poolInfo : req.getSimulationPoolInfoList()) {
fileSimulationMappingService.lambdaUpdate()
.eq (FileSimulationMapping::getSimulationPoolId, poolInfo.getSimulationPoolId())
.eq (FileSimulationMapping::getSimulationPoolVersion, poolInfo.getSimulationPoolVersion())
.eq(FileSimulationMapping::getFileId, fileMetadataInfo.getId()).remove();
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);
}
}
}
// 3. 更新工况库绑定(按工况库版本更新:先删指定版本下的绑定,再插入)
fileModifyApproveHelper.updateSimulationMappings(fileMetadataInfo.getId(), req.getSimulationPoolInfoList(), fileSimulationMappingService);
// 4. 更新扩展字段
fileModifyApproveHelper.updateExtensions(fileMetadataInfo.getId(), req.getFileMetadataExtensionRequest(), fileMetadataExtensionService);
// 5. 更新标签
if (CollectionUtils.isNotEmpty(req.getDictTags())) {
updateFileTags(req, fileMetadataInfo, dirMetadataInfo);
}
// 6. 保存主记录更新
fileMetadataInfoService.updateById(fileMetadataInfo);
}
/**
* 覆盖上传新文件(鸠占鹊巢):保持主记录 ID 不变,同时保留历史版本。
* <p>
* 最小方案约束:
* 1) 仅保留“文件内容 + 主元数据”的历史版本;标签/工况/扩展信息始终以最新为准,不做历史克隆;
* 2) 上传的新文件使用新的 objectKey带版本号后缀避免覆盖 MinIO 旧对象。
* </p>
*/
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);
int dotIndex = originalName.lastIndexOf('.');
if (dotIndex == -1) {
log.error("文件没有后缀");
throw new RuntimeException("文件没有后缀");
}
String modifiedFileName = originalName.substring(0, dotIndex) + versionSuffix + originalName.substring(dotIndex);
String parDirObjectKey = dirMetadataInfo.getObjectKey();
newFileMinioObjectKey = getFileMinioObjectKey(parDirObjectKey + modifiedFileName);
// 3) 上传新文件
minioService.uploadFile(req.getFile(), newFileMinioObjectKey, null, oldFileMetadataInfo.getBucketName());
// 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, oldFileMetadataInfo, dirMetadataInfo);
}
if (CollectionUtils.isNotEmpty(req.getSimulationPoolInfoList())) {
for (SimulationPoolInfo simulationPoolInfo : req.getSimulationPoolInfoList()) {
fileSimulationMappingService.lambdaUpdate()
.eq (FileSimulationMapping::getSimulationPoolId, simulationPoolInfo.getSimulationPoolId())
.eq (FileSimulationMapping::getSimulationPoolVersion, simulationPoolInfo.getSimulationPoolVersion())
.eq(FileSimulationMapping::getFileId, oldFileMetadataInfo.getId()).remove();
for (String simulationPoolTaskId : simulationPoolInfo.getSimulationPoolTaskIds()) {
FileSimulationMapping fileSimulationMapping = new FileSimulationMapping();
fileSimulationMapping.setFileId(oldFileMetadataInfo.getId());
fileSimulationMapping.setSimulationPoolId(simulationPoolInfo.getSimulationPoolId());
fileSimulationMapping.setSimulationPoolVersion(simulationPoolInfo.getSimulationPoolVersion());
fileSimulationMapping.setSimulationPoolTaskId(simulationPoolTaskId);
fileSimulationMappingService.save(fileSimulationMapping);
}
}
}
// 扩展字段:作为“最新态”数据,覆盖更新主记录对应的扩展信息
List<FileMetadataExtensionRequest> fileMetadataExtensionRequestList = req.getFileMetadataExtensionRequest();
if (fileMetadataExtensionRequestList != null && !fileMetadataExtensionRequestList.isEmpty()) {
fileMetadataExtensionService.lambdaUpdate().eq(FileMetadataExtension::getTFilemetaId, oldFileMetadataInfo.getId()).remove();
List<FileMetadataExtension> fileMetadataExtensionList = new ArrayList<>();
fileMetadataExtensionRequestList.forEach(extensionRequest -> {
FileMetadataExtension fileMetadataExtension = new FileMetadataExtension();
fileMetadataExtension.setTFilemetaId(oldFileMetadataInfo.getId());
fileMetadataExtension.setExtensionKey(extensionRequest.getExtensionKey());
fileMetadataExtension.setExtensionValue(extensionRequest.getExtensionValue());
fileMetadataExtension.setDataType(Objects.toString(extensionRequest.getDataType(), "string"));
fileMetadataExtensionList.add(fileMetadataExtension);
});
fileMetadataExtensionService.saveBatch(fileMetadataExtensionList);
}
// 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) {
FileApproveRequestBuilder builder = FileApproveRequestBuilder.builder()
.toUpdateFileIds(List.of(oldFileMetadataInfo.getId()))
.contents(DirTypeEnum.buildApprovalTitle(dirMetadataInfo.getDirType(), "文件修改"))
.approveType(ApproveTypeEnum.KNOWLEDGE_APPROVE)
.approveFileActionENUM(ApproveFileActionENUM.MODIFY)
.beforeData(List.of(historyFileInfo))
.afterData(List.of(oldFileMetadataInfo))
.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_REVIEWING);
}
} catch (Exception e) {
minioService.deleteFile(newFileMinioObjectKey, oldFileMetadataInfo.getBucketName());
log.error("更新文件失败", e);
throw new RuntimeException("更新文件失败: " + e.getMessage(), e);
}
}
private List<String> buildDictTagsFromEnum(UpdateFileReq req) {
List<String> dictTags = new ArrayList<>();

View File

@@ -0,0 +1,200 @@
package com.sdm.data.service.impl.dataFileHandle;
import com.sdm.common.entity.req.data.FileMetadataExtensionRequest;
import com.sdm.common.entity.req.data.SimulationPoolInfo;
import com.sdm.data.model.entity.*;
import com.sdm.data.service.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* 文件修改审批辅助类
* <p>
* 提取 ModifyFileApproveStrategy 和 ModifyMetadataApproveStrategy 的公共逻辑
* </p>
*/
@Component
@Slf4j
public class FileModifyApproveHelper {
/**
* 更新扩展参数
*/
public void updateExtensions(Long fileId, List<FileMetadataExtensionRequest> requests,
IFileMetadataExtensionService extensionService) {
if (ObjectUtils.isNotEmpty(requests)) {
// 先删除旧的
extensionService.lambdaUpdate().eq(FileMetadataExtension::getTFilemetaId, fileId).remove();
// 插入新的
List<FileMetadataExtension> list = new ArrayList<>();
requests.forEach(req -> {
FileMetadataExtension ext = new FileMetadataExtension();
ext.setTFilemetaId(fileId);
ext.setExtensionKey(req.getExtensionKey());
ext.setExtensionValue(req.getExtensionValue());
ext.setDataType(Objects.toString(req.getDataType(), "string"));
list.add(ext);
});
extensionService.saveBatch(list);
}
}
/**
* 更新工况库绑定关系
* 按工况库维度更新:先删除指定工况库版本下的绑定,再插入新绑定
*/
public void updateSimulationMappings(Long fileId, List<SimulationPoolInfo> poolInfos,
IFileSimulationMappingService mappingService) {
if (CollectionUtils.isNotEmpty(poolInfos)) {
for (SimulationPoolInfo poolInfo : poolInfos) {
// 1. 删除当前文件在指定工况库版本下的旧绑定
mappingService.lambdaUpdate()
.eq(FileSimulationMapping::getFileId, fileId)
.eq(FileSimulationMapping::getSimulationPoolId, poolInfo.getSimulationPoolId())
.eq(FileSimulationMapping::getSimulationPoolVersion, poolInfo.getSimulationPoolVersion())
.remove();
// 2. 插入新绑定
List<FileSimulationMapping> mappings = new ArrayList<>();
if (CollectionUtils.isNotEmpty(poolInfo.getSimulationPoolTaskIds())) {
for (String taskId : poolInfo.getSimulationPoolTaskIds()) {
FileSimulationMapping mapping = new FileSimulationMapping();
mapping.setFileId(fileId);
mapping.setSimulationPoolId(poolInfo.getSimulationPoolId());
mapping.setSimulationPoolVersion(poolInfo.getSimulationPoolVersion());
mapping.setSimulationPoolTaskId(taskId);
mappings.add(mapping);
}
mappingService.saveBatch(mappings);
}
}
}
}
/**
* 复制文件权限
*
* @param sourceFileId 源文件ID
* @param targetFileId 目标文件ID
* @param permissionService 权限服务
*/
public void copyFilePermissions(Long sourceFileId, Long targetFileId, IFileUserPermissionService permissionService) {
if (sourceFileId == null || targetFileId == null) {
return;
}
// 1. 查询源文件的所有权限记录
List<FileUserPermission> sourcePermissions = permissionService.lambdaQuery()
.eq(FileUserPermission::getTFilemetaId, sourceFileId)
.list();
if (CollectionUtils.isEmpty(sourcePermissions)) {
return;
}
// 2. 构建目标文件的权限记录
List<FileUserPermission> targetPermissions = new ArrayList<>();
for (FileUserPermission sourcePerm : sourcePermissions) {
FileUserPermission targetPerm = new FileUserPermission();
targetPerm.setTFilemetaId(targetFileId);
targetPerm.setUserId(sourcePerm.getUserId());
targetPerm.setPermission(sourcePerm.getPermission());
targetPerm.setTenantId(sourcePerm.getTenantId());
targetPermissions.add(targetPerm);
}
// 3. 批量保存
if (CollectionUtils.isNotEmpty(targetPermissions)) {
permissionService.saveBatch(targetPermissions);
}
}
/**
* 审批通过后更新文件标签
*/
public void updateFileTagsAfterApproval(FileMetadataInfo metadata, FileMetadataInfo updateInfo,
IFileMetadataInfoService fileMetadataInfoService,
IFileTagRelService fileTagRelService) {
try {
Long tenantId = metadata.getTenantId();
Long creatorId = metadata.getCreatorId();
Long fileId = metadata.getId();
// 从 updateInfo 中获取标签缓存
Map<String, Map<String, Integer>> dictTagIdsCache = updateInfo.getDictTagIdsCache();
if (dictTagIdsCache == null || dictTagIdsCache.isEmpty()) {
return;
}
// 删除旧标签关系(只删除当前租户的)
fileTagRelService.lambdaUpdate()
.eq(FileTagRel::getTenantId, tenantId)
.eq(FileTagRel::getFileId, fileId)
.remove();
// 查询当前目录及所有祖先目录 ID
Long dirId = updateInfo.getParentId();
List<Long> dirIdAndAncestors = dirId != null ? collectDirIdAndAncestorIds(dirId, fileMetadataInfoService) : new ArrayList<>();
// 构建新标签关系(为当前目录及每个祖先目录各创建一条)
List<FileTagRel> newTagRelList = new ArrayList<>();
Long fileSize = updateInfo.getFileSize() != null ? updateInfo.getFileSize() : 0L;
for (Map.Entry<String, Map<String, Integer>> classEntry : dictTagIdsCache.entrySet()) {
Map<String, Integer> valueMap = classEntry.getValue();
for (Integer dictId : valueMap.values()) {
if (dictId == null) {
continue;
}
for (Long ancestorDirId : dirIdAndAncestors) {
FileTagRel tagRel = new FileTagRel();
tagRel.setFileId(fileId);
tagRel.setTagId(dictId);
tagRel.setDirId(ancestorDirId);
tagRel.setTenantId(tenantId);
tagRel.setCreatorId(creatorId);
tagRel.setFileSize(fileSize);
newTagRelList.add(tagRel);
}
}
}
// 批量插入新标签关系
if (CollectionUtils.isNotEmpty(newTagRelList)) {
fileTagRelService.saveBatch(newTagRelList);
log.info("Approved and updated file tags for fileId: {}, total: {} tags", fileId, newTagRelList.size());
}
} catch (Exception e) {
log.error("Failed to update file tags after approval for fileId: {}", metadata.getId(), e);
// 不抛出异常,避免影响审批流程
}
}
/**
* 收集当前目录及其所有祖先目录的 ID从当前目录到根路径
*/
private List<Long> collectDirIdAndAncestorIds(Long dirId, IFileMetadataInfoService fileMetadataInfoService) {
List<Long> dirIds = new ArrayList<>();
Long currentId = dirId;
int maxDepth = 100; // 防止循环引用导致死循环
while (currentId != null && currentId > 0 && dirIds.size() < maxDepth) {
dirIds.add(currentId);
FileMetadataInfo dirMeta = fileMetadataInfoService.getById(currentId);
if (dirMeta == null || dirMeta.getParentId() == null || dirMeta.getParentId() <= 0) {
break;
}
currentId = dirMeta.getParentId();
}
return dirIds;
}
}

View File

@@ -4,204 +4,125 @@ import com.alibaba.fastjson2.JSONObject;
import com.sdm.common.entity.constants.NumberConstants;
import com.sdm.common.entity.enums.ApprovalFileDataStatusEnum;
import com.sdm.common.entity.enums.ApproveFileDataTypeEnum;
import com.sdm.common.entity.req.data.FileMetadataExtensionRequest;
import com.sdm.common.entity.req.data.SimulationPoolInfo;
import com.sdm.data.model.entity.*;
import com.sdm.data.model.entity.FileMetadataInfo;
import com.sdm.data.model.entity.FileStorage;
import com.sdm.data.service.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@Service
@Slf4j
public class ModifyFileApproveStrategy implements ApproveStrategy {
@Autowired
private FileModifyApproveHelper fileModifyApproveHelper;
@Autowired
private IFileUserPermissionService fileUserPermissionService;
@Override
@Transactional(rollbackFor = Exception.class)
public boolean handle(ApproveContext context) {
int status = context.getApproveStatus();
FileMetadataInfo backupHistory = context.getApproveMetadataInfos().get(0);
FileMetadataInfo currentFileMetadata = context.getApproveMetadataInfos().get(0);
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();
// 解析 tempMetadata 获取审批相关信息
String tempMetadata = backupHistory.getTempMetadata();
FileMetadataInfo updateInfo = JSONObject.parseObject(tempMetadata, FileMetadataInfo.class);
String tempMetadata = currentFileMetadata.getTempMetadata();
FileMetadataInfo newVersionInfo = JSONObject.parseObject(tempMetadata, FileMetadataInfo.class);
// 审批通过主记录正式生效备份行保留为历史版本isLatest=false
if (NumberConstants.TWO == status) {
// updateInfo作为新记录顶替原来的老文件复用老文件的id需要更新扩展参数、标签、工况库映射、文件存储表的文件名等信息
// 1. 备份:克隆当前主记录为历史行(新 ID用于版本回溯
// 必须新建一个对象进行复制,避免直接修改 currentFileMetadata 导致后续更新主表时 ID 丢失
FileMetadataInfo historyFileInfo = new FileMetadataInfo();
BeanUtils.copyProperties(currentFileMetadata, historyFileInfo);
historyFileInfo.setId(null); // ID 置空,插入新记录
historyFileInfo.setIsLatest(false);
historyFileInfo.setApprovalStatus(null);
historyFileInfo.setApproveType(null);
historyFileInfo.setTempMetadata(null);
historyFileInfo.setUpdateTime(LocalDateTime.now());
fileMetadataInfoService.save(historyFileInfo);
// 1.1 复制原文件的权限到历史版本
fileModifyApproveHelper.copyFilePermissions(currentFileMetadata.getId(), historyFileInfo.getId(), fileUserPermissionService);
// 2. 更新附属信息 (扩展参数、标签、工况)
// 处理扩展参数
if(ObjectUtils.isNotEmpty(updateInfo.getFileMetadataExtensionRequest())){
fileMetadataExtensionService.lambdaUpdate().eq(FileMetadataExtension::getTFilemetaId, updateInfo.getId()).remove();
fileModifyApproveHelper.updateExtensions(currentFileMetadata.getId(), newVersionInfo.getFileMetadataExtensionRequest(), fileMetadataExtensionService);
List<FileMetadataExtensionRequest> fileMetadataExtensionRequestList = updateInfo.getFileMetadataExtensionRequest();
List<FileMetadataExtension> fileMetadataExtensionList = new ArrayList<>();
fileMetadataExtensionRequestList.forEach(extensionRequest -> {
FileMetadataExtension fileMetadataExtension = new FileMetadataExtension();
fileMetadataExtension.setTFilemetaId(updateInfo.getId());
fileMetadataExtension.setExtensionKey(extensionRequest.getExtensionKey());
fileMetadataExtension.setExtensionValue(extensionRequest.getExtensionValue());
fileMetadataExtension.setDataType(Objects.toString(extensionRequest.getDataType(), "string"));
fileMetadataExtensionList.add(fileMetadataExtension);
});
fileMetadataExtensionService.saveBatch(fileMetadataExtensionList);
// 处理标签更新
if (newVersionInfo.getDictTagIdsCache() != null && !newVersionInfo.getDictTagIdsCache().isEmpty()) {
fileModifyApproveHelper.updateFileTagsAfterApproval(currentFileMetadata, newVersionInfo, fileMetadataInfoService, fileTagRelService);
}
// 处理标签更新(如果 tempMetadata 中包含标签信息)
if (updateInfo.getDictTagIdsCache() != null && !updateInfo.getDictTagIdsCache().isEmpty()) {
updateFileTagsAfterApproval(backupHistory, updateInfo, fileMetadataInfoService, fileTagRelService);
}
//绑定文件和工况库的关系
List<SimulationPoolInfo> simulationPoolInfoList = updateInfo.getSimulationPoolInfoList();
if (CollectionUtils.isNotEmpty(simulationPoolInfoList)) {
for (SimulationPoolInfo simulationPoolInfo : simulationPoolInfoList) {
// 先删除原先所有的文件绑定关系
fileSimulationMappingService.lambdaUpdate()
.eq (FileSimulationMapping::getSimulationPoolId, simulationPoolInfo.getSimulationPoolId())
.eq (FileSimulationMapping::getSimulationPoolVersion, simulationPoolInfo.getSimulationPoolVersion())
.eq(FileSimulationMapping::getFileId, updateInfo.getId()).remove();
for (String simulationPoolTaskId : simulationPoolInfo.getSimulationPoolTaskIds()) {
FileSimulationMapping fileSimulationMapping = new FileSimulationMapping();
fileSimulationMapping.setFileId(updateInfo.getId());
fileSimulationMapping.setSimulationPoolId(simulationPoolInfo.getSimulationPoolId());
fileSimulationMapping.setSimulationPoolVersion(simulationPoolInfo.getSimulationPoolVersion());
fileSimulationMapping.setSimulationPoolTaskId(simulationPoolTaskId);
fileSimulationMappingService.save(fileSimulationMapping);
}
}
}
// 绑定文件和工况库的关系
fileModifyApproveHelper.updateSimulationMappings(currentFileMetadata.getId(), newVersionInfo.getSimulationPoolInfoList(), fileSimulationMappingService);
// 更新文件存储信息
fileStorageService.lambdaUpdate()
.eq(FileStorage::getFileId, updateInfo.getId())
.set(FileStorage::getFileName, updateInfo.getOriginalName())
.eq(FileStorage::getFileId, currentFileMetadata.getId())
.set(FileStorage::getFileName, newVersionInfo.getOriginalName())
.update();
updateInfo.setIsLatest(true);
updateInfo.setTempMetadata( null);
updateInfo.setApproveType(ApproveFileDataTypeEnum.COMPLETED.getCode());
updateInfo.setApprovalStatus(ApprovalFileDataStatusEnum.APPROVED.getKey());
// 3. 更新主记录为新版本数据
// 锁定主记录 ID更新核心字段
FileMetadataInfo updateEntity = new FileMetadataInfo();
updateEntity.setId(currentFileMetadata.getId());
// 复制关键元数据(新文件路径、版本号、大小、名称等)
updateEntity.setObjectKey(newVersionInfo.getObjectKey());
updateEntity.setOriginalName(newVersionInfo.getOriginalName());
updateEntity.setFileSize(newVersionInfo.getFileSize());
updateEntity.setVersionNo(newVersionInfo.getVersionNo());
// 复制业务属性
updateEntity.setProjectId(newVersionInfo.getProjectId());
updateEntity.setAnalysisDirectionId(newVersionInfo.getAnalysisDirectionId());
updateEntity.setRemarks(newVersionInfo.getRemarks());
// 更新状态
updateEntity.setIsLatest(true);
updateEntity.setTempMetadata(null);
updateEntity.setApproveType(ApproveFileDataTypeEnum.COMPLETED.getCode());
updateEntity.setApprovalStatus(ApprovalFileDataStatusEnum.APPROVED.getKey());
updateEntity.setUpdateTime(LocalDateTime.now());
fileMetadataInfoService.updateById(updateEntity);
// backupHistory作为历史备份, 更新为历史版本设置新id,
backupHistory.setId(null);
backupHistory.setIsLatest(false);
backupHistory.setApprovalStatus(null);
backupHistory.setApproveType(null);
backupHistory.setUpdateTime(LocalDateTime.now());
backupHistory.setTempMetadata(null);
fileMetadataInfoService.save(backupHistory);
return true;
}
// 审批不通过,删除新 MinIO 临时文件对象
if (NumberConstants.THREE == status) {
// 1. 删除临时文件
minioService.deleteFile(updateInfo.getObjectKey(), updateInfo.getBucketName());
backupHistory.setTempMetadata(null);
backupHistory.setApprovalStatus(ApprovalFileDataStatusEnum.REJECTED.getKey());
backupHistory.setApproveType(ApproveFileDataTypeEnum.COMPLETED.getCode());
backupHistory.setIsLatest(true);
backupHistory.setUpdateTime(LocalDateTime.now());
fileMetadataInfoService.updateById(backupHistory);
if (newVersionInfo != null && newVersionInfo.getObjectKey() != null) {
minioService.deleteFile(newVersionInfo.getObjectKey(), newVersionInfo.getBucketName());
}
// 2. 更新主记录状态
fileMetadataInfoService.lambdaUpdate()
.eq(FileMetadataInfo::getId, currentFileMetadata.getId())
.set(FileMetadataInfo::getTempMetadata, null)
.set(FileMetadataInfo::getApprovalStatus, ApprovalFileDataStatusEnum.REJECTED.getKey())
.set(FileMetadataInfo::getApproveType, ApproveFileDataTypeEnum.COMPLETED.getCode())
.set(FileMetadataInfo::getUpdateTime, LocalDateTime.now())
.update();
return true;
}
return false;
}
/**
* 审批通过后更新文件标签
* 从 updateInfo 中获取标签信息并更新到 file_tag_rel 表
* 为当前文件所在目录及其所有祖先目录创建标签关系
*/
private void updateFileTagsAfterApproval(FileMetadataInfo metadata, FileMetadataInfo updateInfo,
IFileMetadataInfoService fileMetadataInfoService,
IFileTagRelService fileTagRelService) {
try {
Long tenantId = metadata.getTenantId();
Long creatorId = metadata.getCreatorId();
Long fileId = metadata.getId();
// 从 updateInfo 中获取标签缓存
Map<String, Map<String, Integer>> dictTagIdsCache = updateInfo.getDictTagIdsCache();
if (dictTagIdsCache == null || dictTagIdsCache.isEmpty()) {
return;
}
// 删除旧标签关系(只删除当前租户的)
fileTagRelService.lambdaUpdate()
.eq(FileTagRel::getTenantId, tenantId)
.eq(FileTagRel::getFileId, fileId)
.remove();
// 查询当前目录及所有祖先目录 ID
Long dirId = updateInfo.getParentId();
List<Long> dirIdAndAncestors = dirId != null ? collectDirIdAndAncestorIds(dirId, fileMetadataInfoService) : new ArrayList<>();
// 构建新标签关系(为当前目录及每个祖先目录各创建一条)
List<FileTagRel> newTagRelList = new ArrayList<>();
Long fileSize = updateInfo.getFileSize() != null ? updateInfo.getFileSize() : 0L;
for (Map.Entry<String, Map<String, Integer>> classEntry : dictTagIdsCache.entrySet()) {
Map<String, Integer> valueMap = classEntry.getValue();
for (Integer dictId : valueMap.values()) {
if (dictId == null) {
continue;
}
for (Long ancestorDirId : dirIdAndAncestors) {
FileTagRel tagRel = new FileTagRel();
tagRel.setFileId(fileId);
tagRel.setTagId(dictId);
tagRel.setDirId(ancestorDirId);
tagRel.setTenantId(tenantId);
tagRel.setCreatorId(creatorId);
tagRel.setFileSize(fileSize);
newTagRelList.add(tagRel);
}
}
}
// 批量插入新标签关系
if (CollectionUtils.isNotEmpty(newTagRelList)) {
fileTagRelService.saveBatch(newTagRelList);
log.info("Approved and updated file tags for fileId: {}, total: {} tags", fileId, newTagRelList.size());
}
} catch (Exception e) {
log.error("Failed to update file tags after approval for fileId: {}", metadata.getId(), e);
// 不抛出异常,避免影响审批流程
}
}
/**
* 收集当前目录及其所有祖先目录的 ID从当前目录到根路径
*/
private List<Long> collectDirIdAndAncestorIds(Long dirId, IFileMetadataInfoService fileMetadataInfoService) {
List<Long> dirIds = new ArrayList<>();
Long currentId = dirId;
int maxDepth = 100; // 防止循环引用导致死循环
while (currentId != null && currentId > 0 && dirIds.size() < maxDepth) {
dirIds.add(currentId);
FileMetadataInfo dirMeta = fileMetadataInfoService.getById(currentId);
if (dirMeta == null || dirMeta.getParentId() == null || dirMeta.getParentId() <= 0) {
break;
}
currentId = dirMeta.getParentId();
}
return dirIds;
}
}
}

View File

@@ -4,32 +4,26 @@ import com.alibaba.fastjson2.JSONObject;
import com.sdm.common.entity.constants.NumberConstants;
import com.sdm.common.entity.enums.ApprovalFileDataStatusEnum;
import com.sdm.common.entity.enums.ApproveFileDataTypeEnum;
import com.sdm.common.entity.req.data.FileMetadataExtensionRequest;
import com.sdm.common.entity.req.data.SimulationPoolInfo;
import com.sdm.data.model.entity.FileMetadataExtension;
import com.sdm.data.model.entity.FileMetadataInfo;
import com.sdm.data.model.entity.FileSimulationMapping;
import com.sdm.data.model.entity.FileTagRel;
import com.sdm.data.service.IFileMetadataExtensionService;
import com.sdm.data.service.IFileMetadataInfoService;
import com.sdm.data.service.IFileSimulationMappingService;
import com.sdm.data.service.IFileTagRelService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import org.springframework.transaction.annotation.Transactional;
@Service
@Slf4j
public class ModifyMetadataApproveStrategy implements ApproveStrategy {
@Autowired
private FileModifyApproveHelper fileModifyApproveHelper;
@Override
@Transactional(rollbackFor = Exception.class)
public boolean handle(ApproveContext context) {
// FileMetadataInfo metadata = context.getApproveMetadataInfo();
FileMetadataInfo metadata = context.getApproveMetadataInfos().get(0);
int status = context.getApproveStatus();
IFileMetadataInfoService service = context.getFileMetadataInfoService();
@@ -52,47 +46,16 @@ public class ModifyMetadataApproveStrategy implements ApproveStrategy {
.set(FileMetadataInfo::getApproveType, ApproveFileDataTypeEnum.COMPLETED.getCode())
.update();
//绑定文件和工况库的关系
List<SimulationPoolInfo> simulationPoolInfoList = updateInfo.getSimulationPoolInfoList();
if (CollectionUtils.isNotEmpty(simulationPoolInfoList)) {
for (SimulationPoolInfo simulationPoolInfo : simulationPoolInfoList) {
// 先删除原先所有的文件绑定关系
fileSimulationMappingService.lambdaUpdate()
.eq (FileSimulationMapping::getSimulationPoolId, simulationPoolInfo.getSimulationPoolId())
.eq (FileSimulationMapping::getSimulationPoolVersion, simulationPoolInfo.getSimulationPoolVersion())
.eq(FileSimulationMapping::getFileId, metadata.getId()).remove();
for (String simulationPoolTaskId : simulationPoolInfo.getSimulationPoolTaskIds()) {
FileSimulationMapping fileSimulationMapping = new FileSimulationMapping();
fileSimulationMapping.setFileId(metadata.getId());
fileSimulationMapping.setSimulationPoolId(simulationPoolInfo.getSimulationPoolId());
fileSimulationMapping.setSimulationPoolVersion(simulationPoolInfo.getSimulationPoolVersion());
fileSimulationMapping.setSimulationPoolTaskId(simulationPoolTaskId);
fileSimulationMappingService.save(fileSimulationMapping);
}
}
}
// 绑定文件和工况库的关系
fileModifyApproveHelper.updateSimulationMappings(metadata.getId(), updateInfo.getSimulationPoolInfoList(), fileSimulationMappingService);
// 处理标签更新(如果 tempMetadata 中包含标签信息)
if (updateInfo.getDictTagIdsCache() != null && !updateInfo.getDictTagIdsCache().isEmpty()) {
updateFileTagsAfterApproval(metadata, updateInfo, service, fileTagRelService);
fileModifyApproveHelper.updateFileTagsAfterApproval(metadata, updateInfo, service, fileTagRelService);
}
// 处理扩展参数
if(ObjectUtils.isNotEmpty(updateInfo.getFileMetadataExtensionRequest())){
fileMetadataExtensionService.lambdaUpdate().eq(FileMetadataExtension::getTFilemetaId, metadata.getId()).remove();
List<FileMetadataExtensionRequest> fileMetadataExtensionRequestList = updateInfo.getFileMetadataExtensionRequest();
List<FileMetadataExtension> fileMetadataExtensionList = new ArrayList<>();
fileMetadataExtensionRequestList.forEach(extensionRequest -> {
FileMetadataExtension fileMetadataExtension = new FileMetadataExtension();
fileMetadataExtension.setTFilemetaId(metadata.getId());
fileMetadataExtension.setExtensionKey(extensionRequest.getExtensionKey());
fileMetadataExtension.setExtensionValue(extensionRequest.getExtensionValue());
fileMetadataExtension.setDataType(Objects.toString(extensionRequest.getDataType(), "string"));
fileMetadataExtensionList.add(fileMetadataExtension);
});
fileMetadataExtensionService.saveBatch(fileMetadataExtensionList);
}
fileModifyApproveHelper.updateExtensions(metadata.getId(), updateInfo.getFileMetadataExtensionRequest(), fileMetadataExtensionService);
return true;
}
@@ -108,87 +71,4 @@ public class ModifyMetadataApproveStrategy implements ApproveStrategy {
}
return false;
}
/**
* 审批通过后更新文件标签
* 从 updateInfo 中获取标签信息并更新到 file_tag_rel 表
* 为当前文件所在目录及其所有祖先目录创建标签关系
*/
private void updateFileTagsAfterApproval(FileMetadataInfo metadata, FileMetadataInfo updateInfo,
IFileMetadataInfoService fileMetadataInfoService,
IFileTagRelService fileTagRelService) {
try {
Long tenantId = metadata.getTenantId();
Long creatorId = metadata.getCreatorId();
Long fileId = metadata.getId();
// 从 updateInfo 中获取标签缓存
Map<String, Map<String, Integer>> dictTagIdsCache = updateInfo.getDictTagIdsCache();
if (dictTagIdsCache == null || dictTagIdsCache.isEmpty()) {
return;
}
// 删除旧标签关系(只删除当前租户的)
fileTagRelService.lambdaUpdate()
.eq(FileTagRel::getTenantId, tenantId)
.eq(FileTagRel::getFileId, fileId)
.remove();
// 查询当前目录及所有祖先目录 ID
Long dirId = updateInfo.getParentId();
List<Long> dirIdAndAncestors = dirId != null ? collectDirIdAndAncestorIds(dirId, fileMetadataInfoService) : new ArrayList<>();
// 构建新标签关系(为当前目录及每个祖先目录各创建一条)
List<FileTagRel> newTagRelList = new ArrayList<>();
Long fileSize = updateInfo.getFileSize() != null ? updateInfo.getFileSize() : 0L;
for (Map.Entry<String, Map<String, Integer>> classEntry : dictTagIdsCache.entrySet()) {
Map<String, Integer> valueMap = classEntry.getValue();
for (Integer dictId : valueMap.values()) {
if (dictId == null) {
continue;
}
for (Long ancestorDirId : dirIdAndAncestors) {
FileTagRel tagRel = new FileTagRel();
tagRel.setFileId(fileId);
tagRel.setTagId(dictId);
tagRel.setDirId(ancestorDirId);
tagRel.setTenantId(tenantId);
tagRel.setCreatorId(creatorId);
tagRel.setFileSize(fileSize);
newTagRelList.add(tagRel);
}
}
}
// 批量插入新标签关系
if (CollectionUtils.isNotEmpty(newTagRelList)) {
fileTagRelService.saveBatch(newTagRelList);
log.info("Approved and updated file tags for fileId: {}, total: {} tags", fileId, newTagRelList.size());
}
} catch (Exception e) {
log.error("Failed to update file tags after approval for fileId: {}", metadata.getId(), e);
// 不抛出异常,避免影响审批流程
}
}
/**
* 收集当前目录及其所有祖先目录的 ID从当前目录到根路径
*/
private List<Long> collectDirIdAndAncestorIds(Long dirId, IFileMetadataInfoService fileMetadataInfoService) {
List<Long> dirIds = new ArrayList<>();
Long currentId = dirId;
int maxDepth = 100; // 防止循环引用导致死循环
while (currentId != null && currentId > 0 && dirIds.size() < maxDepth) {
dirIds.add(currentId);
FileMetadataInfo dirMeta = fileMetadataInfoService.getById(currentId);
if (dirMeta == null || dirMeta.getParentId() == null || dirMeta.getParentId() <= 0) {
break;
}
currentId = dirMeta.getParentId();
}
return dirIds;
}
}
}