fix:重构文件标签逻辑

This commit is contained in:
2026-02-03 10:54:17 +08:00
parent c70278e8f2
commit 48bd961c58
16 changed files with 613 additions and 247 deletions

View File

@@ -0,0 +1,16 @@
-- 文件标签关系 + 目录冗余
CREATE TABLE `file_tag_rel` (
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`fileId` BIGINT NOT NULL COMMENT '文件ID',
`tagId` int NOT NULL COMMENT '标签ID',
`dirId` BIGINT NOT NULL COMMENT '目录ID父级冗余',
`tenantId` BIGINT NOT NULL COMMENT '租户ID',
`creatorId` BIGINT NULL COMMENT '创建者ID',
`fileSize` BIGINT NOT NULL DEFAULT 0 COMMENT '文件大小(用于目录统计)',
`createTime` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updateTime` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_tag_rel` (`tenantId`, `fileId`, `tagId`, `dirId`),
KEY `idx_dir_tag` (`tenantId`, `dirId`, `tagId`),
KEY `idx_file_tag` (`tenantId`, `fileId`, `tagId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='文件标签关系表';

View File

@@ -8,6 +8,7 @@ public class DataDictionary extends BaseBean {
{
init();
}
public int id;
public String uuid;
public String dictName;
public String dictValue;

View File

@@ -43,7 +43,26 @@ public enum FileBizTypeEnum {
* 需求附件
*/
@Schema(description = "需求附件", example = "7")
DEMAND_FILE(7),
PROJECT_DEMAND_FILE(7),
/**
* 会议纪要-项目
*/
@Schema(description = "会议纪要-项目", example = "8")
PROJECT_MEETING_FILE(8),
/**
* 知识文件-项目
*/
@Schema(description = "知识文件-项目", example = "9")
PROJECT_KNOWLEDGE_FILE(9),
/**
* 附件-项目
*/
@Schema(description = "附件-项目", example = "10")
PROJECT_ATTACHMENT_FILE(10),
/**
* 交付物文件
@@ -57,6 +76,24 @@ public enum FileBizTypeEnum {
@Schema(description = "试验结果文件", example = "12")
EXPERIMENT_FILE(12),
/**
* 3D模型
*/
@Schema(description = "3D模型", example = "13")
MODEL_3D_FILE(13),
/**
* 试验数据闭环报告
*/
@Schema(description = "试验数据闭环报告", example = "14")
EXPERIMENT_CLOSING_REPORT_FILE(14),
/**
* 复盘报告
*/
@Schema(description = "复盘报告", example = "15")
REVIEW_REPORT_FILE(15),
/**
* 项目文件
*/

View File

@@ -2,6 +2,7 @@ package com.sdm.common.entity.req.data;
import com.alibaba.fastjson2.annotation.JSONField;
import com.sdm.common.entity.enums.FileBizTypeEnum;
import com.sdm.common.entity.req.system.DictTagReq;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.springframework.web.multipart.MultipartFile;
@@ -67,9 +68,13 @@ public class UploadFilesReq {
@Schema(description = "文件名")
private String fileName;
@Schema(description = "文件类型 1模型文件 2仿真报告 3计算文件 4曲线文件 5云图文件",implementation = FileBizTypeEnum.class)
@Schema(description = "文件类型 1模型文件 2仿真报告 3计算文件 4曲线文件 5云图文件", implementation = FileBizTypeEnum.class)
private Integer fileType;
@Schema(description = "字典标签列表")
private List<DictTagReq> tags;
/**
* 关联项目id
*/
@@ -127,7 +132,7 @@ public class UploadFilesReq {
*/
@Schema(description = "扩展字段值")
private String extensionValue;
/**
* 值的数据类型string, number, boolean, json等
*/

View File

@@ -0,0 +1,23 @@
package com.sdm.common.entity.req.system;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.List;
@Data
@Schema(description = "字典标签查询请求")
public class DictTagReq {
@Schema(description = "字典名称", example = "file_type")
private String dictName;
@Schema(description = "字典值", example = "document")
private String dictValue;
@Data
@Schema(description = "批量查询字典ID请求")
public static class BatchDictIdQueryReq {
@Schema(description = "字典标签查询项列表")
private List<DictTagReq> items;
}
}

View File

@@ -3,6 +3,7 @@ package com.sdm.common.feign.impl.system;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.sdm.common.common.SdmResponse;
import com.sdm.common.entity.bo.DataDictionary;
import com.sdm.common.entity.req.system.DictTagReq;
import com.sdm.common.entity.resp.PageDataResp;
import com.sdm.common.entity.resp.system.CIDUserResp;
import com.sdm.common.feign.inter.system.ISysConfigFeignClient;
@@ -11,6 +12,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
@Slf4j
@Component
@@ -32,4 +34,20 @@ public class SysConfigFeignClientImpl implements ISysConfigFeignClient {
return SdmResponse.failed("字典信息未查询");
}
}
@Override
public SdmResponse<Map<String, Map<String, Integer>>> batchQueryDictionaryIds(DictTagReq.BatchDictIdQueryReq req) {
SdmResponse<Map<String, Map<String, Integer>>> sdmResponse;
try {
sdmResponse = sysConfigFeignClient.batchQueryDictionaryIds(req);
if(!sdmResponse.isSuccess() ||ObjectUtils.isEmpty(sdmResponse.getData())){
return SdmResponse.failed("字典信息未查询");
}
return sdmResponse;
} catch (Exception e) {
log.error("字典信息失败", e);
return SdmResponse.failed("字典信息未查询");
}
}
}

View File

@@ -2,11 +2,15 @@ package com.sdm.common.feign.inter.system;
import com.sdm.common.common.SdmResponse;
import com.sdm.common.entity.bo.DataDictionary;
import com.sdm.common.entity.req.system.DictTagReq;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.List;
import java.util.Map;
@FeignClient(name = "system",contextId = "sysConfigFeignClient")
public interface ISysConfigFeignClient {
@@ -14,4 +18,7 @@ public interface ISysConfigFeignClient {
@GetMapping(value = "/systemData/getDictionaryData")
SdmResponse<List<DataDictionary>> getDictionaryData(@RequestParam("dictClass")String dictClass);
@PostMapping(value = "/systemData/batchDictionaryIds")
SdmResponse<Map<String, Map<String, Integer>>> batchQueryDictionaryIds(@RequestBody DictTagReq.BatchDictIdQueryReq req);
}

View File

@@ -0,0 +1,15 @@
package com.sdm.data.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.sdm.data.model.entity.FileTagRel;
/**
* <p>
* 文件标签关联表(合并目录冗余) Mapper 接口
* </p>
*
* @author author
* @since 2026-02-02
*/
public interface FileTagRelMapper extends BaseMapper<FileTagRel> {
}

View File

@@ -0,0 +1,70 @@
package com.sdm.data.model.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* <p>
* 文件标签关联表(合并目录冗余)
* </p>
*
* @author author
* @since 2026-02-02
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("file_tag_rel")
@ApiModel(value = "FileTagRel对象", description = "文件标签关联表(合并目录冗余)")
public class FileTagRel implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "主键ID自增")
@TableId(value = "id", type = IdType.AUTO)
private Long id;
@ApiModelProperty(value = "文件IDfile_metadata_info.id")
@TableField("fileId")
private Long fileId;
@ApiModelProperty(value = "标签IDfile_tag.id")
@TableField("tagId")
private Integer tagId;
@ApiModelProperty(value = "目录ID父级冗余")
@TableField("dirId")
private Long dirId;
@ApiModelProperty(value = "租户ID")
@TableField("tenantId")
private Long tenantId;
@ApiModelProperty(value = "创建者ID")
@TableField("creatorId")
private Long creatorId;
@ApiModelProperty(value = "文件大小(用于目录统计)")
@TableField("fileSize")
private Long fileSize;
@ApiModelProperty(value = "创建时间")
@TableField("createTime")
private LocalDateTime createTime;
@ApiModelProperty(value = "更新时间")
@TableField("updateTime")
private LocalDateTime updateTime;
}

View File

@@ -0,0 +1,15 @@
package com.sdm.data.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.sdm.data.model.entity.FileTagRel;
/**
* <p>
* 文件标签关联表(合并目录冗余) 服务类
* </p>
*
* @author author
* @since 2026-02-02
*/
public interface IFileTagRelService extends IService<FileTagRel> {
}

View File

@@ -0,0 +1,19 @@
package com.sdm.data.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sdm.data.dao.FileTagRelMapper;
import com.sdm.data.model.entity.FileTagRel;
import com.sdm.data.service.IFileTagRelService;
import org.springframework.stereotype.Service;
/**
* <p>
* 文件标签关联表(合并目录冗余) 服务实现类
* </p>
*
* @author author
* @since 2026-02-02
*/
@Service
public class FileTagRelServiceImpl extends ServiceImpl<FileTagRelMapper, FileTagRel> implements IFileTagRelService {
}

View File

@@ -18,6 +18,7 @@ import com.sdm.common.entity.enums.*;
import com.sdm.common.entity.pojo.task.TaskBaseInfo;
import com.sdm.common.entity.req.data.*;
import com.sdm.common.entity.req.project.SpdmNodeListReq;
import com.sdm.common.entity.req.system.DictTagReq;
import com.sdm.common.entity.req.system.LaunchApproveReq;
import com.sdm.common.entity.req.system.UserListReq;
import com.sdm.common.entity.req.system.UserQueryReq;
@@ -26,9 +27,11 @@ import com.sdm.common.entity.resp.data.*;
import com.sdm.common.entity.resp.project.SimulationNodeResp;
import com.sdm.common.entity.resp.system.CIDUserResp;
import com.sdm.common.feign.impl.project.SimulationNodeFeignClientImpl;
import com.sdm.common.feign.impl.system.SysConfigFeignClientImpl;
import com.sdm.common.feign.impl.system.SysUserFeignClientImpl;
import com.sdm.common.feign.inter.project.ISimulationNodeFeignClient;
import com.sdm.common.feign.inter.system.IApproveFeignClient;
import com.sdm.common.feign.inter.system.ISysConfigFeignClient;
import com.sdm.common.feign.inter.task.ISimuluationTaskPoolFeignClient;
import com.sdm.common.log.CoreLogger;
import com.sdm.common.utils.*;
@@ -114,6 +117,9 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService {
@Autowired
private SysUserFeignClientImpl sysUserFeignClient;
@Autowired
private ISysConfigFeignClient sysConfigFeignClient;
@Autowired
private IMinioService minioService;
@@ -126,9 +132,13 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService {
@Autowired
IFileStorageService fileStorageService;
@Autowired
private IFileTagRelService fileTagRelService;
@Autowired
ISimulationParameterLibraryCategoryObjectService paramObjectService;
@Autowired
private ApproveStrategyFactory approveStrategyFactory;
@@ -163,6 +173,8 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService {
private static final String TEMP_NG_URL = "http://192.168.65.161:10031/storage/";
@Override
@Transactional(rollbackFor = Exception.class)
public SdmResponse createDir(CreateDirReq req) {
@@ -1606,147 +1618,34 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService {
@Override
@Transactional(rollbackFor = Exception.class)
public SdmResponse<List<BatchAddFileInfoResp>> batchAddFileInfo(UploadFilesReq req) {
Long dirId = null;
String uuid = req.getUuid();
SdmResponse<FileMetadataInfo> dirResponse = resolveUploadDirectory(req);
if (!dirResponse.isSuccess()) {
return SdmResponse.failed(dirResponse.getMessage());
}
FileMetadataInfo dirMetadataInfo = dirResponse.getData();
if (uuid != null) {
FileMetadataInfo nodeMetadataInfo = fileMetadataInfoService.lambdaQuery().eq(FileMetadataInfo::getRelatedResourceUuid, req.getUuid()).one();
if (nodeMetadataInfo == null) {
return SdmResponse.failed("根据UUID未找到对应的节点目录");
SdmResponse<?> permissionResponse = checkUploadPermission(dirMetadataInfo);
if (!permissionResponse.isSuccess()) {
return SdmResponse.failed(permissionResponse.getMessage());
}
if (CollectionUtils.isEmpty(req.getSourceFiles())) {
return SdmResponse.success();
}
List<BatchAddFileInfoResp> addFileInfoRespList = new ArrayList<>();
for (UploadFilesReq fileReq : req.getSourceFiles()) {
SdmResponse<BatchAddFileInfoResp> handleResponse = handleBatchFileInfo(req, fileReq, dirMetadataInfo);
if (!handleResponse.isSuccess()) {
return SdmResponse.failed(handleResponse.getMessage());
}
dirId = nodeMetadataInfo.getId();
} else if (req.getDirId() != null) {
dirId = req.getDirId();
} else {
return SdmResponse.failed("目录ID不能为空");
addFileInfoRespList.add(handleResponse.getData());
}
FileMetadataInfo dirMetadataInfo = fileMetadataInfoService.lambdaQuery().eq(FileMetadataInfo::getId, dirId).eq(FileMetadataInfo::getDataType, DataTypeEnum.DIRECTORY.getValue()).one();
if (dirMetadataInfo == null) {
return SdmResponse.failed("目录不存在");
}
if (CollectionUtils.isNotEmpty(req.getSourceFiles())) {
List<BatchAddFileInfoResp> addFileInfoRespList = new ArrayList<>();
for (UploadFilesReq fileReq : req.getSourceFiles()) {
String uploadFileId = fileReq.getUploadFileId();
String originalName = fileReq.getFileName();
String versionSuffix = "_V1";
String modifiedFileName;
int dotIndex = originalName.lastIndexOf('.');
if (dotIndex != -1) {
// 如果文件有后缀,则在文件名和后缀之间插入版本号
modifiedFileName = originalName.substring(0, dotIndex) + versionSuffix + originalName.substring(dotIndex);
} else {
log.error("文件没有后缀");
return SdmResponse.failed("文件名没有后缀");
}
String fileMinioObjectKey = getFileMinioObjectKey(dirMetadataInfo.getObjectKey() + modifiedFileName);
// 检查此文件夹下是否有历史同名文件 提示
List<FileMetadataInfo> fileMetadataInfoList = fileMetadataInfoService.lambdaQuery().eq(FileMetadataInfo::getObjectKey, fileMinioObjectKey).list();
if(CollectionUtils.isNotEmpty(fileMetadataInfoList)){
log.error("文件已存在");
return SdmResponse.failed("文件已存在,请修改文件名后重新上传");
}
boolean hasUploadPermission = fileUserPermissionService.hasFilePermission(dirMetadataInfo.getId(), ThreadLocalContext.getUserId(), FilePermissionEnum.UPLOAD);
if (!hasUploadPermission) {
return SdmResponse.failed("没有上传权限");
}
try {
// 创建目录元数据并保存到数据库
FileMetadataInfo fileInfo = createFileMetadata(fileMinioObjectKey, fileReq.getFileName(), fileReq.getFileType(),
req.getProjectId(), req.getAnalysisDirectionId(), req.getRemarks(), dirMetadataInfo.getId(), fileReq.getSize());
fileInfo.setUploadTaskId(req.getUploadTaskId());
fileInfo.setTemplateId(req.getTemplateId());
fileInfo.setTemplateName(req.getTemplateName());
// 知识库和交付物的文件需要审核12月2需求澄清不在上传这一步审核
// 1 知识库文件夹 2 交付物属于项目文件夹
boolean isknowledge = Objects.equals(DirTypeEnum.KNOWLEDGE_BASE_DIR.getValue(), dirMetadataInfo.getDirType());
// boolean isDeliverable = Objects.equals(DirTypeEnum.PROJECT_NODE_DIR.getValue(), dirMetadataInfo.getDirType());
if (isknowledge) {
fileInfo.setApprovalStatus(ApprovalFileDataStatusEnum.PENDING.getKey());
fileInfo.setApproveType(ApproveFileDataTypeEnum.UPLOAD_REVIEWING.getCode());
}
fileMetadataInfoService.save(fileInfo);
// 需要保存文件的历史版本记录同一文件的所有版本共享一个ID
fileInfo.setFileGroupId(fileInfo.getId());
fileMetadataInfoService.updateById(fileInfo);
// 循环查询当前文件每一级父目录id,并保存为一条file_storage,用户后续文件搜索统计
Long parentDirId = dirMetadataInfo.getId();
FileStorage fileStorage = new FileStorage();
fileStorage.setFileId(fileInfo.getId());
fileStorage.setFileName(fileInfo.getOriginalName());
fileStorage.setUserId(ThreadLocalContext.getUserId());
fileStorage.setTenantId(ThreadLocalContext.getTenantId());
fileStorage.setFileBizType(fileInfo.getFileType());
// 文件后缀
fileStorage.setFileSuffix(getSuffixWithoutDot(fileInfo.getOriginalName()));
fileStorage.setFileSize(fileReq.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);
}
// 创建文件扩展信息并保存到数据库
List<FileMetadataExtension> fileMetadataExtensionList = new ArrayList<>();
List<UploadFilesReq.FileMetadataExtensionRequest> fileMetadataExtensionRequestList = req.getFileMetadataExtensionRequest();
if (fileMetadataExtensionRequestList != null && !fileMetadataExtensionRequestList.isEmpty()) {
fileMetadataExtensionRequestList.forEach(extensionRequest -> {
FileMetadataExtension fileMetadataExtension = new FileMetadataExtension();
fileMetadataExtension.setTFilemetaId(fileInfo.getId());
fileMetadataExtension.setExtensionKey(extensionRequest.getExtensionKey());
fileMetadataExtension.setExtensionValue(extensionRequest.getExtensionValue());
fileMetadataExtension.setDataType(Objects.toString(extensionRequest.getDataType(), "string")); // 默认为字符串类型,可根据需要调整
fileMetadataExtensionList.add(fileMetadataExtension);
});
}
fileMetadataExtensionService.saveBatch(fileMetadataExtensionList);
//绑定文件和工况库的关系
if (CollectionUtils.isNotEmpty(req.getSimulationPoolInfoList())) {
for (SimulationPoolInfo simulationPoolInfo : req.getSimulationPoolInfoList()) {
for (String simulationPoolTaskId : simulationPoolInfo.getSimulationPoolTaskIds()) {
FileSimulationMapping fileSimulationMapping = new FileSimulationMapping();
fileSimulationMapping.setFileId(fileInfo.getId());
fileSimulationMapping.setSimulationPoolId(simulationPoolInfo.getSimulationPoolId());
fileSimulationMapping.setSimulationPoolVersion(simulationPoolInfo.getSimulationPoolVersion());
fileSimulationMapping.setSimulationPoolTaskId(simulationPoolTaskId);
fileSimulationMappingService.save(fileSimulationMapping);
}
}
}
// 创建默认权限记录
createFilePermission(fileInfo.getId());
BatchAddFileInfoResp addFileInfoResp = new BatchAddFileInfoResp();
addFileInfoResp.setSourceFileName(originalName);
addFileInfoResp.setBusinessId(fileInfo.getId());
addFileInfoResp.setUploadFileId(uploadFileId);
addFileInfoResp.setUploadTaskId(req.getUploadTaskId());
addFileInfoResp.setObjectKey(fileMinioObjectKey);
addFileInfoRespList.add(addFileInfoResp);
} catch (Exception e) {
log.error("上传文件失败", e);
throw new RuntimeException("上传文件失败: " + e.getMessage(), e);
}
}
return SdmResponse.success(addFileInfoRespList);
}
return SdmResponse.success();
return SdmResponse.success(addFileInfoRespList);
}
@Override
public SdmResponse chunkUploadCallback(KnowledgeCallBackReq req) {
// 处理成功上传的数据
@@ -1858,11 +1757,118 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService {
@Override
@Transactional(rollbackFor = Exception.class)
public SdmResponse uploadFiles(UploadFilesReq req) {
SdmResponse<FileMetadataInfo> dirResponse = resolveUploadDirectory(req);
if (!dirResponse.isSuccess()) {
return dirResponse;
}
FileMetadataInfo dirMetadataInfo = dirResponse.getData();
String originalName = resolveOriginalName(req);
SdmResponse<String> versionedNameResponse = buildVersionedFileName(originalName);
if (!versionedNameResponse.isSuccess()) {
return versionedNameResponse;
}
String modifiedFileName = versionedNameResponse.getData();
String fileMinioObjectKey = buildFileObjectKey(dirMetadataInfo, modifiedFileName);
SdmResponse<?> existsResponse = ensureFileNotExists(fileMinioObjectKey);
if (!existsResponse.isSuccess()) {
return existsResponse;
}
SdmResponse<?> permissionResponse = checkUploadPermission(dirMetadataInfo);
if (!permissionResponse.isSuccess()) {
return permissionResponse;
}
try {
minioService.uploadFile(req.getFile(), fileMinioObjectKey, null, dirMetadataInfo.getBucketName());
FileMetadataInfo fileInfo = createFileMetadata(fileMinioObjectKey, originalName, req.getFileType(),
req.getProjectId(), req.getAnalysisDirectionId(), req.getRemarks(), dirMetadataInfo.getId(), req.getFile().getSize()
);
fileMetadataInfoService.save(fileInfo);
List<Long> ancestorDirIds = collectAncestorDirIds(dirMetadataInfo.getId());
saveFileStorageStats(fileInfo, req, ancestorDirIds);
saveFileTags(req, fileInfo, dirMetadataInfo, ancestorDirIds);
bindSimulationPool(req, fileInfo);
saveFileExtensions(req, fileInfo);
createFilePermission(fileInfo.getId());
triggerKnowledgeApproveIfNeeded(req, fileInfo, dirMetadataInfo);
finalizeFileGroup(fileInfo);
return buildUploadSuccess(fileInfo);
} catch (Exception e) {
minioService.deleteFile(fileMinioObjectKey, dirMetadataInfo.getBucketName());
log.error("上传文件失败", e);
throw new RuntimeException("上传文件失败: " + e.getMessage(), e);
}
}
private SdmResponse<BatchAddFileInfoResp> handleBatchFileInfo(UploadFilesReq req, UploadFilesReq fileReq, FileMetadataInfo dirMetadataInfo) {
String uploadFileId = fileReq.getUploadFileId();
String originalName = fileReq.getFileName();
SdmResponse<String> versionedNameResponse = buildVersionedFileName(originalName);
if (!versionedNameResponse.isSuccess()) {
return SdmResponse.failed(versionedNameResponse.getMessage());
}
String modifiedFileName = versionedNameResponse.getData();
String fileMinioObjectKey = buildFileObjectKey(dirMetadataInfo, modifiedFileName);
SdmResponse<?> existsResponse = ensureFileNotExists(fileMinioObjectKey);
if (!existsResponse.isSuccess()) {
return SdmResponse.failed(existsResponse.getMessage());
}
try {
FileMetadataInfo fileInfo = createFileMetadata(fileMinioObjectKey, fileReq.getFileName(), fileReq.getFileType(),
req.getProjectId(), req.getAnalysisDirectionId(), req.getRemarks(), dirMetadataInfo.getId(), fileReq.getSize());
fileInfo.setUploadTaskId(req.getUploadTaskId());
fileInfo.setTemplateId(req.getTemplateId());
fileInfo.setTemplateName(req.getTemplateName());
boolean isknowledge = Objects.equals(DirTypeEnum.KNOWLEDGE_BASE_DIR.getValue(), dirMetadataInfo.getDirType());
if (isknowledge) {
fileInfo.setApprovalStatus(ApprovalFileDataStatusEnum.PENDING.getKey());
fileInfo.setApproveType(ApproveFileDataTypeEnum.UPLOAD_REVIEWING.getCode());
}
fileMetadataInfoService.save(fileInfo);
fileInfo.setFileGroupId(fileInfo.getId());
fileMetadataInfoService.updateById(fileInfo);
List<Long> ancestorDirIds = collectAncestorDirIds(dirMetadataInfo.getId());
saveFileStorageStats(fileInfo, fileReq, ancestorDirIds);
saveFileTags(req, fileInfo, dirMetadataInfo, ancestorDirIds);
saveFileExtensions(req, fileInfo);
bindSimulationPool(req, fileInfo);
createFilePermission(fileInfo.getId());
BatchAddFileInfoResp addFileInfoResp = new BatchAddFileInfoResp();
addFileInfoResp.setSourceFileName(originalName);
addFileInfoResp.setBusinessId(fileInfo.getId());
addFileInfoResp.setUploadFileId(uploadFileId);
addFileInfoResp.setUploadTaskId(req.getUploadTaskId());
addFileInfoResp.setObjectKey(fileMinioObjectKey);
return SdmResponse.success(addFileInfoResp);
} catch (Exception e) {
log.error("上传文件失败", e);
throw new RuntimeException("上传文件失败: " + e.getMessage(), e);
}
}
private SdmResponse<FileMetadataInfo> resolveUploadDirectory(UploadFilesReq req) {
Long dirId = null;
String uuid = req.getUuid();
if (uuid != null) {
FileMetadataInfo nodeMetadataInfo = fileMetadataInfoService.lambdaQuery().eq(FileMetadataInfo::getRelatedResourceUuid, req.getUuid()).one();
FileMetadataInfo nodeMetadataInfo = fileMetadataInfoService.lambdaQuery()
.eq(FileMetadataInfo::getRelatedResourceUuid, req.getUuid())
.one();
if (nodeMetadataInfo == null) {
return SdmResponse.failed("根据UUID未找到对应的节点目录");
}
@@ -1872,135 +1878,226 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService {
} else {
return SdmResponse.failed("目录ID不能为空");
}
FileMetadataInfo dirMetadataInfo = fileMetadataInfoService.lambdaQuery().eq(FileMetadataInfo::getId, dirId).eq(FileMetadataInfo::getDataType, DataTypeEnum.DIRECTORY.getValue()).one();
FileMetadataInfo dirMetadataInfo = fileMetadataInfoService.lambdaQuery()
.eq(FileMetadataInfo::getId, dirId)
.eq(FileMetadataInfo::getDataType, DataTypeEnum.DIRECTORY.getValue())
.one();
if (dirMetadataInfo == null) {
return SdmResponse.failed("目录不存在");
}
return SdmResponse.success(dirMetadataInfo);
}
private String resolveOriginalName(UploadFilesReq req) {
return ObjectUtils.isNotEmpty(req.getFileName()) ? req.getFileName() : req.getFile().getOriginalFilename();
}
String originalName = ObjectUtils.isNotEmpty(req.getFileName())?req.getFileName():req.getFile().getOriginalFilename();
private SdmResponse<String> buildVersionedFileName(String originalName) {
String versionSuffix = "_V1";
String modifiedFileName;
int dotIndex = originalName.lastIndexOf('.');
if (dotIndex != -1) {
// 如果文件有后缀,则在文件名和后缀之间插入版本号
modifiedFileName = originalName.substring(0, dotIndex) + versionSuffix + originalName.substring(dotIndex);
} else {
if (dotIndex == -1) {
log.error("文件没有后缀");
return SdmResponse.failed("文件名没有后缀");
}
String modifiedFileName = originalName.substring(0, dotIndex) + versionSuffix + originalName.substring(dotIndex);
return SdmResponse.success(modifiedFileName);
}
String fileMinioObjectKey = getFileMinioObjectKey(dirMetadataInfo.getObjectKey() + modifiedFileName);
Optional<FileMetadataInfo> fileMetadataInfoOptional = fileMetadataInfoService.lambdaQuery().eq(FileMetadataInfo::getObjectKey, fileMinioObjectKey).oneOpt();
if(fileMetadataInfoOptional.isPresent()){
private String buildFileObjectKey(FileMetadataInfo dirMetadataInfo, String modifiedFileName) {
return getFileMinioObjectKey(dirMetadataInfo.getObjectKey() + modifiedFileName);
}
private SdmResponse<?> ensureFileNotExists(String fileMinioObjectKey) {
Optional<FileMetadataInfo> fileMetadataInfoOptional = fileMetadataInfoService.lambdaQuery()
.eq(FileMetadataInfo::getObjectKey, fileMinioObjectKey)
.oneOpt();
if (fileMetadataInfoOptional.isPresent()) {
log.error("文件已存在");
return SdmResponse.failed("文件已存在,请修改文件名后重新上传");
}
return SdmResponse.success();
}
boolean hasUploadPermission = fileUserPermissionService.hasFilePermission(dirMetadataInfo.getId(), ThreadLocalContext.getUserId(), FilePermissionEnum.UPLOAD);
private SdmResponse<?> checkUploadPermission(FileMetadataInfo dirMetadataInfo) {
boolean hasUploadPermission = fileUserPermissionService.hasFilePermission(
dirMetadataInfo.getId(), ThreadLocalContext.getUserId(), FilePermissionEnum.UPLOAD
);
if (!hasUploadPermission) {
return SdmResponse.failed("没有上传权限");
}
try {
minioService.uploadFile(req.getFile(), fileMinioObjectKey, null,dirMetadataInfo.getBucketName());
return SdmResponse.success();
}
// 创建目录元数据并保存到数据库
FileMetadataInfo fileInfo = createFileMetadata(fileMinioObjectKey, originalName, req.getFileType(),
req.getProjectId(), req.getAnalysisDirectionId(), req.getRemarks(), dirMetadataInfo.getId(), req.getFile().getSize()
);
fileMetadataInfoService.save(fileInfo);
private List<Long> collectAncestorDirIds(Long dirId) {
Long parentDirId = dirId;
List<Long> ancestorDirIds = new ArrayList<>();
while (parentDirId != null) {
ancestorDirIds.add(parentDirId);
parentDirId = fileMetadataInfoService.lambdaQuery().eq(FileMetadataInfo::getId, parentDirId).oneOpt()
.map(FileMetadataInfo::getParentId)
.orElse(null);
}
return ancestorDirIds;
}
// 循环查询当前文件每一级父目录id,并保存为一条file_storage,用户后续文件搜索统计
Long parentDirId = dirMetadataInfo.getId();
FileStorage fileStorage = new FileStorage();
fileStorage.setFileId(fileInfo.getId());
fileStorage.setFileName(fileInfo.getOriginalName());
fileStorage.setUserId(ThreadLocalContext.getUserId());
fileStorage.setTenantId(ThreadLocalContext.getTenantId());
fileStorage.setFileBizType(fileInfo.getFileType());
// 文件后缀
fileStorage.setFileSuffix(getSuffixWithoutDot(fileInfo.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);
}
//绑定文件和工况库的关系
if (CollectionUtils.isNotEmpty(req.getSimulationPoolInfoList())) {
for (SimulationPoolInfo simulationPoolInfo : req.getSimulationPoolInfoList()) {
for (String simulationPoolTaskId : simulationPoolInfo.getSimulationPoolTaskIds()) {
FileSimulationMapping fileSimulationMapping = new FileSimulationMapping();
fileSimulationMapping.setFileId(fileInfo.getId());
fileSimulationMapping.setSimulationPoolId(simulationPoolInfo.getSimulationPoolId());
fileSimulationMapping.setSimulationPoolVersion(simulationPoolInfo.getSimulationPoolVersion());
fileSimulationMapping.setSimulationPoolTaskId(simulationPoolTaskId);
fileSimulationMappingService.save(fileSimulationMapping);
}
}
}
// 创建文件扩展信息并保存到数据库
List<FileMetadataExtension> fileMetadataExtensionList = new ArrayList<>();
List<UploadFilesReq.FileMetadataExtensionRequest> fileMetadataExtensionRequestList = req.getFileMetadataExtensionRequest();
if (fileMetadataExtensionRequestList != null && !fileMetadataExtensionRequestList.isEmpty()) {
fileMetadataExtensionRequestList.forEach(extensionRequest -> {
FileMetadataExtension fileMetadataExtension = new FileMetadataExtension();
fileMetadataExtension.setTFilemetaId(fileInfo.getId());
fileMetadataExtension.setExtensionKey(extensionRequest.getExtensionKey());
fileMetadataExtension.setExtensionValue(extensionRequest.getExtensionValue());
fileMetadataExtension.setDataType(Objects.toString(extensionRequest.getDataType(), "string")); // 默认为字符串类型,可根据需要调整
fileMetadataExtensionList.add(fileMetadataExtension);
});
}
fileMetadataExtensionService.saveBatch(fileMetadataExtensionList);
// 创建默认权限记录
createFilePermission(fileInfo.getId());
// 只有知识库的文件需要审核
if( Objects.equals(DirTypeEnum.KNOWLEDGE_BASE_DIR.getValue(), dirMetadataInfo.getDirType())){
FileApproveRequestBuilder uploadFileApproveRequestBuilder = FileApproveRequestBuilder.builder()
.toUpdateFileIds(List.of(fileInfo.getId()))
.contents("知识库文件新增")
.approveType(ApproveTypeEnum.KNOWLEDGE_APPROVE)
.approveFileActionENUM(ApproveFileActionENUM.ADD)
.beforeData(List.of(fileInfo))
.templateId(req.getTemplateId())
.templateName(req.getTemplateName())
.knowledgeBaseName(extractRelativePath(dirMetadataInfo))
.build();
if(CollectionUtils.isNotEmpty(uploadFileApproveRequestBuilder.getBeforeData())){
setCreatorNames(uploadFileApproveRequestBuilder.getBeforeData());
setProjectName(uploadFileApproveRequestBuilder.getBeforeData());
setAnalysisDirectionName(uploadFileApproveRequestBuilder.getBeforeData());
setSimulationPoolAndTaskInfo(uploadFileApproveRequestBuilder.getBeforeData());
}
fileApproveExecutor.launchApproveAndUpdateStatus(uploadFileApproveRequestBuilder, ApproveFileDataTypeEnum.UPLOAD_REVIEWING);
}
// 需要保存文件的历史版本记录同一文件的所有版本共享一个ID
fileInfo.setFileGroupId(fileInfo.getId());
fileMetadataInfoService.updateById(fileInfo);
JSONObject jsonObject = new JSONObject();
jsonObject.put("fileId", fileInfo.getId());
return SdmResponse.success(jsonObject);
} catch (Exception e) {
minioService.deleteFile(fileMinioObjectKey, dirMetadataInfo.getBucketName());
log.error("上传文件失败", e);
throw new RuntimeException("上传文件失败: " + e.getMessage(), e);
private void saveFileStorageStats(FileMetadataInfo fileInfo, UploadFilesReq req, List<Long> ancestorDirIds) {
long fileSize = resolveFileSize(req);
FileStorage fileStorage = new FileStorage();
fileStorage.setFileId(fileInfo.getId());
fileStorage.setFileName(fileInfo.getOriginalName());
fileStorage.setUserId(ThreadLocalContext.getUserId());
fileStorage.setTenantId(ThreadLocalContext.getTenantId());
fileStorage.setFileBizType(fileInfo.getFileType());
fileStorage.setFileSuffix(getSuffixWithoutDot(fileInfo.getOriginalName()));
fileStorage.setFileSize(fileSize);
for (Long dirIdItem : ancestorDirIds) {
fileStorage.setId(null);
fileStorage.setDirId(dirIdItem);
fileStorageService.save(fileStorage);
}
}
private long resolveFileSize(UploadFilesReq req) {
if (req.getFile() != null) {
return req.getFile().getSize();
}
return req.getSize() == null ? 0L : req.getSize();
}
private void saveFileTags(UploadFilesReq req, FileMetadataInfo fileInfo, FileMetadataInfo dirMetadataInfo,
List<Long> ancestorDirIds) {
List<DictTagReq> tags = req.getTags();
if (CollectionUtils.isEmpty(tags)) {
return;
}
Long tenantId = ThreadLocalContext.getTenantId();
Long creatorId = ThreadLocalContext.getUserId();
long fileSize = resolveFileSize(req);
DictTagReq.BatchDictIdQueryReq batchReq = new DictTagReq.BatchDictIdQueryReq();
batchReq.setItems(tags);
SdmResponse<Map<String, Map<String, Integer>>> response = sysConfigFeignClient.batchQueryDictionaryIds(batchReq);
if (!response.isSuccess() || response.getData() == null) {
log.warn("Failed to query dictionary ids for tags");
return;
}
Map<String, Map<String, Integer>> dictIdMap = response.getData();
List<FileTagRel> directRelList = new ArrayList<>();
List<FileTagRel> derivedRelList = new ArrayList<>();
for (DictTagReq tag : tags) {
Map<String, Integer> valueMap = dictIdMap.get(tag.getDictName());
if (valueMap == null || valueMap.get(tag.getDictValue()) == null) {
log.warn("Dictionary not found for dictName: {}, dictValue: {}", tag.getDictName(), tag.getDictValue());
continue;
}
Integer dictId = valueMap.get(tag.getDictValue());
if (dictId == null) {
continue;
}
FileTagRel directRel = new FileTagRel();
directRel.setFileId(fileInfo.getId());
directRel.setTagId(dictId);
directRel.setDirId(dirMetadataInfo.getId());
directRel.setTenantId(tenantId);
directRel.setCreatorId(creatorId);
directRel.setFileSize(fileSize);
directRelList.add(directRel);
for (Long dirIdItem : ancestorDirIds) {
FileTagRel derivedRel = new FileTagRel();
derivedRel.setFileId(fileInfo.getId());
derivedRel.setTagId(dictId);
derivedRel.setDirId(dirIdItem);
derivedRel.setTenantId(tenantId);
derivedRel.setCreatorId(creatorId);
derivedRel.setFileSize(fileSize);
derivedRelList.add(derivedRel);
}
}
if (CollectionUtils.isNotEmpty(directRelList)) {
fileTagRelService.saveBatch(directRelList);
}
if (CollectionUtils.isNotEmpty(derivedRelList)) {
fileTagRelService.saveBatch(derivedRelList);
}
}
private void bindSimulationPool(UploadFilesReq req, FileMetadataInfo fileInfo) {
if (CollectionUtils.isEmpty(req.getSimulationPoolInfoList())) {
return;
}
for (SimulationPoolInfo simulationPoolInfo : req.getSimulationPoolInfoList()) {
for (String simulationPoolTaskId : simulationPoolInfo.getSimulationPoolTaskIds()) {
FileSimulationMapping fileSimulationMapping = new FileSimulationMapping();
fileSimulationMapping.setFileId(fileInfo.getId());
fileSimulationMapping.setSimulationPoolId(simulationPoolInfo.getSimulationPoolId());
fileSimulationMapping.setSimulationPoolVersion(simulationPoolInfo.getSimulationPoolVersion());
fileSimulationMapping.setSimulationPoolTaskId(simulationPoolTaskId);
fileSimulationMappingService.save(fileSimulationMapping);
}
}
}
private void saveFileExtensions(UploadFilesReq req, FileMetadataInfo fileInfo) {
List<FileMetadataExtension> fileMetadataExtensionList = new ArrayList<>();
List<UploadFilesReq.FileMetadataExtensionRequest> fileMetadataExtensionRequestList = req.getFileMetadataExtensionRequest();
if (fileMetadataExtensionRequestList != null && !fileMetadataExtensionRequestList.isEmpty()) {
fileMetadataExtensionRequestList.forEach(extensionRequest -> {
FileMetadataExtension fileMetadataExtension = new FileMetadataExtension();
fileMetadataExtension.setTFilemetaId(fileInfo.getId());
fileMetadataExtension.setExtensionKey(extensionRequest.getExtensionKey());
fileMetadataExtension.setExtensionValue(extensionRequest.getExtensionValue());
fileMetadataExtension.setDataType(Objects.toString(extensionRequest.getDataType(), "string"));
fileMetadataExtensionList.add(fileMetadataExtension);
});
}
fileMetadataExtensionService.saveBatch(fileMetadataExtensionList);
}
private void triggerKnowledgeApproveIfNeeded(UploadFilesReq req, FileMetadataInfo fileInfo, FileMetadataInfo dirMetadataInfo) {
if (!Objects.equals(DirTypeEnum.KNOWLEDGE_BASE_DIR.getValue(), dirMetadataInfo.getDirType())) {
return;
}
FileApproveRequestBuilder uploadFileApproveRequestBuilder = FileApproveRequestBuilder.builder()
.toUpdateFileIds(List.of(fileInfo.getId()))
.contents("知识库文件新增")
.approveType(ApproveTypeEnum.KNOWLEDGE_APPROVE)
.approveFileActionENUM(ApproveFileActionENUM.ADD)
.beforeData(List.of(fileInfo))
.templateId(req.getTemplateId())
.templateName(req.getTemplateName())
.knowledgeBaseName(extractRelativePath(dirMetadataInfo))
.build();
if (CollectionUtils.isNotEmpty(uploadFileApproveRequestBuilder.getBeforeData())) {
setCreatorNames(uploadFileApproveRequestBuilder.getBeforeData());
setProjectName(uploadFileApproveRequestBuilder.getBeforeData());
setAnalysisDirectionName(uploadFileApproveRequestBuilder.getBeforeData());
setSimulationPoolAndTaskInfo(uploadFileApproveRequestBuilder.getBeforeData());
}
fileApproveExecutor.launchApproveAndUpdateStatus(uploadFileApproveRequestBuilder, ApproveFileDataTypeEnum.UPLOAD_REVIEWING);
}
private void finalizeFileGroup(FileMetadataInfo fileInfo) {
fileInfo.setFileGroupId(fileInfo.getId());
fileMetadataInfoService.updateById(fileInfo);
}
private SdmResponse<JSONObject> buildUploadSuccess(FileMetadataInfo fileInfo) {
JSONObject jsonObject = new JSONObject();
jsonObject.put("fileId", fileInfo.getId());
return SdmResponse.success(jsonObject);
}
public static String getSuffixWithoutDot(String fileName) {
int lastDotIndex = fileName.lastIndexOf('.');
if (lastDotIndex == -1) {
@@ -2009,6 +2106,7 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService {
return fileName.substring(lastDotIndex + 1);
}
/**
* 从目录的objectKey中提取相对路径移除目录类型前缀
* 如knowledge/知识库A/子目录/ → 知识库A/子目录

View File

@@ -7,6 +7,7 @@ import com.fasterxml.jackson.databind.SerializationFeature;
import com.sdm.common.common.SdmResponse;
import com.sdm.common.common.ThreadLocalContext;
import com.sdm.common.entity.bo.DataDictionary;
import com.sdm.common.entity.req.system.DictTagReq;
import com.sdm.common.feign.inter.system.ISysConfigFeignClient;
import com.sdm.common.log.utils.JavaTimeModule;
import com.sdm.system.model.bo.DictionaryClass;
@@ -171,6 +172,12 @@ public class SimulationSystemConfigController implements ISysConfigFeignClient {
return service.deleteFormConfigure(configure);
}
@PostMapping(value = "/batchDictionaryIds")
@ResponseBody
public SdmResponse<Map<String, Map<String, Integer>>> batchQueryDictionaryIds(@RequestBody DictTagReq.BatchDictIdQueryReq req) {
return service.batchQueryDictionaryIds(req);
}
/**
* 表单管理 展示所有自定义表头的表单表
*/

View File

@@ -1,6 +1,7 @@
package com.sdm.system.dao;
import com.sdm.common.entity.bo.DataDictionary;
import com.sdm.common.entity.req.system.DictTagReq;
import com.sdm.system.model.bo.DictionaryClass;
import com.sdm.system.model.bo.FormConfigure;
import com.sdm.system.model.entity.SimulationApproveFlowMapBean;
@@ -74,4 +75,14 @@ public interface SimulationSystemMapper {
@Insert("UPDATE system_form_configure SET formConfig=#{config.formConfig},comment=#{config.comment} WHERE formName=#{config.formName}")
int updateFormConfigure(@Param("config") FormConfigure config);
@Select("<script>" +
"SELECT id, dictName, dictValue FROM simulation_data_dictionary " +
"WHERE tenantId = #{tenantId} AND " +
"<foreach collection='items' item='item' separator=' OR '>" +
"(dictName = #{item.dictName} AND dictValue = #{item.dictValue})" +
"</foreach>" +
" ORDER BY dictName, dictValue" +
"</script>")
List<DataDictionary> batchQueryDictionaryIds(@Param("tenantId") long tenantId, @Param("items") List<DictTagReq> items);
}

View File

@@ -2,6 +2,7 @@ package com.sdm.system.service;
import com.sdm.common.common.SdmResponse;
import com.sdm.common.entity.bo.DataDictionary;
import com.sdm.common.entity.req.system.DictTagReq;
import com.sdm.system.model.bo.DictionaryClass;
import com.sdm.system.model.bo.FormConfigure;
import com.sdm.system.model.req.system.FormConfigureReq;
@@ -53,4 +54,6 @@ public interface ISimulationSystemConfigService {
SdmResponse updateUserFormConfigure(FormConfigure configure);
SdmResponse<Map<String, Map<String, Integer>>> batchQueryDictionaryIds(DictTagReq.BatchDictIdQueryReq req);
}

View File

@@ -13,6 +13,7 @@ import com.sdm.common.common.ThreadLocalContext;
import com.sdm.common.entity.bo.DataDictionary;
import com.sdm.common.entity.bo.DataPageInfo;
import com.sdm.common.entity.req.data.TenantListReq;
import com.sdm.common.entity.req.system.DictTagReq;
import com.sdm.common.entity.resp.PageDataResp;
import com.sdm.common.entity.resp.system.CIDRoleResp;
import com.sdm.common.entity.resp.system.TenantResp;
@@ -792,6 +793,26 @@ public class SimulationSystemConfigServiceImpl extends BaseService implements IS
}
}
@Override
public SdmResponse<Map<String, Map<String, Integer>>> batchQueryDictionaryIds(DictTagReq.BatchDictIdQueryReq req) {
SdmResponse<Map<String, Map<String, Long>>> response = SdmResponse.success();
long tenantId = ThreadLocalContext.getTenantId();
if (CollectionUtils.isEmpty(req.getItems())) {
return SdmResponse.success(new HashMap<>());
}
List<DataDictionary> dictionaries = mapper.batchQueryDictionaryIds(tenantId, req.getItems());
Map<String, Map<String, Integer>> result = new HashMap<>();
for (DataDictionary dict : dictionaries) {
result.computeIfAbsent(dict.getDictName(), k -> new HashMap<>())
.put(dict.getDictValue(), dict.getId());
}
return SdmResponse.success(result);
}
}